Commit 4485b6b0c90bce520852b6c9426ba99cea38088e

Authored by totoro
1 parent fc5a84fc

add List(CoreAttrs) in anonymous aggregation of Webargvalue to allow better formatting of seletor

Showing 2 changed files with 5564 additions and 5558 deletions   Show diff stats
web/CXM_dojo.anubis
1   -/*
2   - * Created by PyramIDE.
3   - * User: Steve Marechal
4   - * Date: 11/06/2008
5   - * Time: 09:54
6   - *
7   - */
8   -
9   -read tools/basis.anubis
10   -read tools/base64.anubis
11   -read locale/L3LanguageInfo.anubis
12   -read system/string.anubis
13   -read system/logger.anubis
14   -
15   -read calexium_lib/web/CXM_common.anubis
16   -read calexium_lib/web/CXM_making_a_web_site.anubis
17   -read calexium_lib/web/CXM_multihost_http_server.anubis
18   -read calexium_lib/net_services_protocols/logger_service.anubis
19   -
20   -public type Dojo_Grid_Data :
21   - grid_data(String).
22   -
23   -public type Position_Direction:
24   - brup,
25   - brleft,
26   - blup,
27   - blright,
28   - trdown,
29   - trleft,
30   - tldown,
31   - tlright.
32   -
33   -define String
34   - pos_dir_to_str
35   - (
36   - Position_Direction pos
37   - )=
38   - if pos is
39   - {
40   - brup then "br-up",
41   - brleft then "br-left",
42   - blup then "bl-up",
43   - blright then "bl-right",
44   - trdown then "tr-down",
45   - trleft then "tr-left",
46   - tldown then "tl-down",
47   - tlright then "tl-right"
48   - }.
49   -
50   -
51   -
52   -public define HTML_Off_Form
53   - dojo_button
54   - (
55   - String name,
56   - String execute,
57   - String id,
58   - )
59   - =
60   - literal("
61   - <button dojoType=\"dijit.form.Button\" id=\""+id+"\" onclick=\""+execute+"\">"+name+"</button>")
62   -.
63   -
64   -public type Input_Type:
65   - file,
66   - text,
67   - password.
68   -
69   -public type InputDial:
70   - input_dial(HtmlId id, Input_Type input_type, String label, Maybe(String) class),
71   - input_date(HtmlId id, String label),
72   - input_combo(HtmlId id, String label, List((WebArgValue,String)) list_data),
73   - input_check(HtmlId id, String label, Bool checked),
74   - input_hidden(HtmlId id, WebArgValue value).
75   -
76   -
77   -define List(HTML_Off_Form)
78   - format_list_data
79   - (
80   - List((WebArgValue,String)) list_data,
81   - List(HTML_Off_Form) result_list
82   - )=
83   - if list_data is
84   - {
85   - [] then result_list,
86   - [h . t] then
87   - if h is (wa, label) then
88   - format_list_data(t, [literal("<option value=\"" + wa.value + "\">" + label + "</option>") . result_list])
89   - }.
90   -
91   -public define HTML_Off_Form
92   - dojo_combobox
93   - (
94   - WebArgName the_name,
95   - HtmlId the_id,
96   - String label,
97   - List((WebArgValue,String)) list_data,
98   - )=
99   - sequence([
100   - literal("<label for=\"" + the_id.id + "\">" + label + "</label>
101   - <select id=\""+the_id.id+"\"
102   - name=\"" + the_name.name + "\"
103   - dojoType=\"dijit.form.ComboBox\"
104   - autoComplete=\"false\">"),
105   - sequence((format_list_data(list_data, []))),
106   - literal("</select>")
107   - ]).
108   -
109   -define HTML_Off_Form
110   - _maybe_label
111   - (
112   - String label_text,
113   - HtmlId html_id
114   - ) =
115   - if length(label_text) > 0 then literal("<label for=\"" + html_id.id + "\">" + label_text + "</label>")
116   - else literal("").
117   -
118   -public define HTML_Off_Form
119   - dojo_checkbox
120   - (
121   - WebArgName the_name,
122   - HtmlId the_id,
123   - String label,
124   - WebArgValue val,
125   - Bool checked,
126   - )=
127   - sequence([
128   - _maybe_label(label, the_id),
129   - literal("<input type=\"checkbox\" name=\"" + the_name.name + "\" id=\"" + the_id.id + "\" value=\"" + val.value + "\""
130   - + (if checked then " checked=\"checked\" " else "") + " dojoType=\"dijit.form.CheckBox\" />"),
131   - ]).
132   -
133   -
134   -define HTML_Off_Form
135   - input_in_dialog
136   - (
137   - InputDial input_data
138   - )=
139   - if input_data is
140   - {
141   - input_dial(html_id, input, label, class) then
142   - sequence([
143   - literal("<div id=\"" + html_id.id +"_block\"" +
144   - if class is
145   - {
146   - failure then " class=\"dojo_diag\"",
147   - success(class_name) then " class=\"dojo_diag block_"+ class_name +"\""
148   - }+"><label for=\"" + html_id.id + "\" id=\"label_" + html_id.id + "\"" +
149   - if class is
150   - {
151   - failure then "",
152   - success(class_name) then " class=\"label_" + class_name + "\""
153   - }+">" + label + "</label>"),
154   - literal("<input dojoType=dijit.form.TextBox " +
155   - if input is
156   - {
157   - file then "type=\"file\"",
158   - text then "type=\"text\"",
159   - password then "type=\"password\""
160   - } +
161   - " name=\""+html_id.id+"\" id=\""+html_id.id+"\" /></div>")
162   - ]),
163   -
164   - input_date(html_id, label) then
165   - sequence([
166   - literal("<div id=\"" + html_id.id +"_block\" class=\"dojo_diag\" ><label for=\"" + html_id.id + "\"> " + label + "</label>"),
167   - literal("<div id=\""+html_id.id+"\" type=\"text\" name=\"date1\"
168   - dojoType=\"dijit.form.DateTextBox\">
169   - </div></div>"
170   - )
171   - ]),
172   -
173   - input_combo(the_id, label, list_data) then
174   - sequence([
175   - literal("<div id=\"" + the_id.id +"_block\" class=\"dojo_diag\">"),
176   - dojo_combobox(wan(the_id.id), the_id, label, list_data),
177   - literal("</div>")
178   - ]),
179   -
180   - input_check(the_id, label, checked) then
181   - sequence([
182   - literal("<div id=\"" + the_id.id +"_block\" class=\"dojo_diag\">"),
183   - dojo_checkbox(wan(the_id.id), the_id, label, wav("1"), checked),
184   - literal("</div>"),
185   - ]),
186   -
187   - input_hidden(the_id, val) then
188   - literal("<input type=\"hidden\" id=\"" + the_id.id +"\" name=\"" + the_id.id +"\" value=\"" + val.value + "\" class=\"dojo_diag\" />"),
189   - }
190   -.
191   -
192   -define List(HTML_Off_Form)
193   - inputs_in_dialog
194   - (
195   - List(InputDial) inputs_data
196   - )=
197   - if inputs_data is
198   - {
199   - [] then [],
200   - [h . t] then [input_in_dialog(h) . inputs_in_dialog(t)]
201   - }.
202   -
203   -public define HTML_Off_Form
204   - dijit_dialog
205   - (
206   - String name,
207   - String dialog_id,
208   - String execute,
209   - String ok_label,
210   - Maybe(String) cancel,
211   - List(InputDial) inputs_data,
212   - Maybe(String) do_cancel
213   - )
214   - =
215   - sequence([
216   - div([attr("dojoType", "dijit.Dialog"), id(dialog_id), title(name), attr("execute", execute)],
217   - sequence([
218   - sequence(inputs_in_dialog(inputs_data)),
219   - literal("<button dojoType=dijit.form.Button type=\"submit\" class=\"dialog_ok\">" + ok_label + "</button>" +
220   - if cancel is
221   - {
222   - failure then "",
223   - success(cancel_label) then "<button dojoType=dijit.form.Button type=\"cancel\" class=\"dialog_cancel\" onclick=\"" +
224   - if do_cancel is
225   - {
226   - failure then "dijit.byId('"+ dialog_id +"').hide()",
227   - success(to_do_cancel) then to_do_cancel
228   - }+ "\">" + cancel_label + "</button>"
229   - }),
230   - literal("<br/>")
231   - ])
232   - )
233   - ])
234   -.
235   -
236   -public define HTML_Off_Form
237   - dojo_dialog
238   - (
239   - String name,
240   - String dialog_id,
241   - String execute,
242   - String ok_label,
243   - Maybe(String) cancel,
244   - List(InputDial) inputs_data,
245   - Maybe(String) do_cancel
246   - )
247   - =
248   - sequence[
249   - dojo_button(name,"dijit.byId('"+dialog_id+"').show()", dialog_id + "_button"),
250   - dijit_dialog(name, dialog_id, execute, ok_label, cancel, inputs_data, do_cancel)
251   - ]
252   -.
253   -
254   -
255   -
256   -
257   -public type DojoGridEditor:
258   - inputEditor,
259   - boolEditor,
260   - selectEditor(List(String) options),
261   - alwaysOnEditor.
262   -
263   -public type DojoGridDefaultColumn:
264   - dojo_grid_default_column(
265   - String styles,
266   - Maybe(String) width,
267   - Maybe(DojoGridEditor) editor).
268   -
269   -public type DojoGridColumn:
270   - dojo_grid_column( String label,
271   - String field_name,
272   - Maybe(String) width,
273   - Maybe(DojoGridEditor) editor).
274   -
275   -public type DojoGridView:
276   - dojo_grid_view(
277   - Maybe(DojoGridDefaultColumn) default_column,
278   - List(DojoGridColumn) columns).
279   -
280   -public type DojoGridLayout:
281   - dojo_grid_layout(
282   - Maybe(String) selectable_row_header_width,
283   - List(DojoGridView) views).
284   -
285   -define String to_String(DojoGridEditor editor) =
286   - "editor: " +
287   - if editor is
288   - {
289   - inputEditor then "dojox.grid.editors.Input",
290   - boolEditor then "dojox.grid.editors.Bool",
291   - selectEditor(options) then "dojox.grid.editors.Select, options: [" + join(",", map((String o) |-> "\"" + o + "\"", options)) + "]",
292   - alwaysOnEditor then "dojox.grid.editors.AlwaysOn"
293   - }.
294   -
295   -define String to_String(DojoGridView view) =
296   - "{ " + (if view.default_column is success(col) then
297   - (if col is dojo_grid_default_column(styles, mb_width, mb_editor) then
298   - "defaultCell: {"
299   - + "styles: \"" + styles + "\""
300   - + (if mb_width is success(w) then ", width=\"" + w + "\"" else "")
301   - + (if mb_editor is success(e) then ", " + to_String(e) else "")
302   - + "}, ")
303   - else "")
304   - + "cells: [[" + join(",", map((DojoGridColumn col) |->
305   - if col is dojo_grid_column(label, field, mb_width, mb_editor) then
306   - "{"
307   - + "name: \"" + label + "\""
308   - + "field: \"" + field + "\""
309   - + (if mb_width is success(w) then ", width=\"" + w + "\"" else "")
310   - + (if mb_editor is success(e) then ", " + to_String(e) else "")
311   - + "}",
312   - view.columns)) + "]]"
313   - +"}".
314   -
315   -
316   -public define String
317   - dojo_make_grid_script
318   - (
319   - HtmlId html_id,
320   - DojoGridLayout layout,
321   - String layout_name,
322   - String store_name,
323   - Bool can_edit
324   - )
325   - =
326   -"<script type=\"text/javascript\">
327   - " + layout_name + " = ["
328   - + (if layout.selectable_row_header_width is success(w) then "{ type: 'dojox.GridRowView', width: '" + w + "'}, " else "")
329   - + join(", ", map(to_String, layout.views))
330   - + "];
331   -</script>".
332   -
333   -public define HTML_Off_Form
334   - dojo_make_grid_script
335   - (
336   - HtmlId html_id,
337   - List(String) columns,
338   - Bool can_edit
339   - )
340   - =
341   - literal(
342   -"<script type=\"text/javascript\">
343   - var cols = ["+join(",", map((String col) |-> "\"" + col + "\"", columns)) + "];
344   - dojo.addOnLoad(function(){
345   - init_dojo_grid(\""+html_id.id+"\", cols, "+ (if can_edit then "1" else "0")+");
346   - });
347   -</script>").
348   -
349   -public define HTML_Off_Form
350   - dojo_grid
351   - (
352   - HtmlId html_id,
353   - Int rowsPerPage,
354   - List(Table_Option) attributes,
355   - )=
356   - table([attr("dojoType", "dojox.grid.DataGrid"), attr("id", html_id.id), attr("jsId", html_id.id),
357   - class("soria"), attr("singleClickEdit", "true"), attr("rowsPerPage", to_decimal(rowsPerPage)) . attributes],
358   - empty, [], empty).
359   -
360   -
361   -public define HTML_Off_Form
362   - dojo_grid
363   - (
364   - HtmlId html_id,
365   - List(CoreAttrs) attributes,
366   -// List(GridColumn) columns,
367   - String colum1,
368   - String colum2,
369   - String colum3,
370   - String colum4,
371   - Bool can_edit
372   - )
373   - =
374   - sequence([
375   - literal(
376   -
377   -"<script type=\"text/javascript\">
378   - dojo.addOnLoad(function(){
379   - init_dojo_grid(\""+html_id.id+"\", \""+colum1+"\",\""+colum2+"\", \""+colum3+"\", \""+colum4+"\", "+ (if can_edit then "1" else "0")+");
380   - });
381   -</script>"),
382   - div_empty([id(html_id.id) . attributes /*attr("dojoType", "dojox.Grid"), */]),
383   - ]).
384   -//<div id=\"gridContainer\"></div>").
385   -
386   -
387   -public define HTML_Off_Form
388   - dojo_message_box
389   - (
390   - HTML_Off_Form name1,
391   - HTML_Off_Form name2,
392   - )
393   - =
394   - sequence([
395   - literal("<br/>"),
396   - div([class("message_box")],
397   - sequence([
398   - div([id("node3"), class("box nopad hidden")], name1),
399   - div([id("node4"), class("box two nopad")], name2),
400   - ])),
401   - literal("<br/>"),
402   - ]).
403   -
404   -
405   -
406   -public define HTML_Off_Form
407   - dojo_tooltip
408   - (
409   - String title,
410   - String keyword,
411   - ) =
412   - actioner(same,same, link([id("dojo_tip_" + keyword),class("dojoToolTip")],title, success(title)), "show_address_mailing", [("address_mailing", keyword)], []).
413   - //actioner(same, same, push_button([id("dojo_tip_" + keyword), class("in"), event(onclick,"new_search(this)")], keyword), "", [], []).
414   -
415   -
416   -public type BorderContainerRegion:
417   - center,
418   - top,
419   - bottom,
420   - leading,
421   - trailing,
422   - left,
423   - right.
424   -
425   -define String
426   - to_String
427   - (
428   - BorderContainerRegion region
429   - )=
430   - if region is
431   - {
432   - center then "center",
433   - top then "top",
434   - bottom then "bottom",
435   - leading then "leading",
436   - trailing then "trailing",
437   - left then "left",
438   - right then "right"
439   - }.
440   -
441   -public type BorderContainerDesign:
442   - headline,
443   - sidebar.
444   -
445   -define String
446   - to_String
447   - (
448   - BorderContainerDesign design
449   - )=
450   - if design is
451   - {
452   - headline then "headline",
453   - sidebar then "sidebar"
454   - }.
455   -
456   -
457   -public define HTML_Off_Form
458   - dojo_ContentPane
459   - (
460   - List(CoreAttrs) attributes,
461   - BorderContainerRegion region,
462   - Bool splitter,
463   - HTML_Off_Form content
464   - ) =
465   - div([ attr("dojoType", "dijit.layout.ContentPane"),
466   - attr("region", to_String(region)),
467   - attr("splitter", if splitter then "true" else "false") . attributes ],
468   - content).
469   -
470   -public define HTML_In_Form
471   - dojo_ContentPane
472   - (
473   - List(CoreAttrs) attributes,
474   - BorderContainerRegion region,
475   - Bool splitter,
476   - HTML_In_Form content
477   - ) =
478   - div([ attr("dojoType", "dijit.layout.ContentPane"),
479   - attr("region", to_String(region)),
480   - attr("splitter", if splitter then "true" else "false") . attributes ],
481   - content).
482   -
483   -public define HTML_Off_Form
484   - dojo_TabContainer
485   - (
486   - List(CoreAttrs) attributes,
487   - HTML_Off_Form content
488   - ) =
489   - div([attr("dojoType", "dijit.layout.TabContainer") . attributes ],
490   - content).
491   -
492   -
493   -public define HTML_Off_Form
494   - dojo_BorderContainer
495   - (
496   - List(CoreAttrs) attributes,
497   - Maybe(String) the_title,
498   - BorderContainerDesign design,
499   - Bool live_splitter,
500   - Bool persist,
501   - Bool closable,
502   - String container,
503   - HTML_Off_Form content
504   - ) =
505   - with the_attributes = (List(CoreAttrs)) [attr("dojoType", "dijit.layout.BorderContainer"), attr("dojoAttachPoint", container),
506   - attr("design", to_String(design)), attr("liveSplitters", live_splitter), attr("persist", persist),
507   - attr("closable",closable) . attributes ],
508   - total_attributes = if the_title is
509   - {
510   - failure then the_attributes,
511   - success(t) then [title(t) . the_attributes]
512   - },
513   - div(total_attributes, content).
514   -
515   -public define HTML_In_Form
516   - dojo_BorderContainer
517   - (
518   - List(CoreAttrs) attributes,
519   - Maybe(String) the_title,
520   - BorderContainerDesign design,
521   - Bool live_splitter,
522   - Bool persist,
523   - Bool closable,
524   - String container,
525   - HTML_In_Form content
526   - ) =
527   - with the_attributes = (List(CoreAttrs)) [attr("dojoType", "dijit.layout.BorderContainer"), attr("dojoAttachPoint", container),
528   - attr("design", to_String(design)), attr("liveSplitters", live_splitter), attr("persist", persist),
529   - attr("closable",closable) . attributes ],
530   - total_attributes = if the_title is
531   - {
532   - failure then the_attributes,
533   - success(t) then [title(t) . the_attributes]
534   - },
535   - div(total_attributes, content).
536   -
537   -public define HTML_Off_Form
538   - dojo_tree
539   - (
540   - List(CoreAttrs) attributes,
541   - )=
542   - div_empty(attributes).
543   -
544   -
545   -
546   -public define HTML_Off_Form
547   - dojo_Toolbar
548   - (
549   - List(CoreAttrs) attributes,
550   - HTML_Off_Form content
551   - )=
552   - div([attr("dojoType", "dijit.Toolbar") . attributes ],
553   - content).
554   -
555   -
556   -public define HTML_Off_Form
557   - dojo_tooltip
558   - (
559   - List(CoreAttrs) attributes,
560   - List(Text_Option) txt_attr,
561   - HtmlId html_id,
562   - String label,
563   - HTML_Off_Form tooltip_content,
564   - )=
565   - sequence([
566   - text([id(html_id.id) . txt_attr], label),
567   - div([id(html_id.id + "_tt"), attr("connectId", html_id.id), attr("dojoType", "dijit.Tooltip") . attributes], tooltip_content)
568   - ]).
569   -
570   -
571   -public define HTML_Off_Form
572   - dojo_button
573   - (
574   - HtmlId html_id,
575   -// Maybe(String) class,
576   - String name,
577   - String iconclass,
578   - Maybe(String) tip_msg,
579   - String execute,
580   - )=
581   - literal("
582   - <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" >"
583   - + name
584   - + "<script type=\"dojo/method\" event=\"onClick\">"
585   - + execute
586   - + "</script>"
587   - + "</button>" +
588   - if tip_msg is
589   - {
590   - failure then "",
591   - success(tip) then
592   - "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
593   - }).
594   -
595   -public define HTML_In_Form
596   - dojo_button
597   - (
598   - HtmlId html_id,
599   -// Maybe(String) class,
600   - String name,
601   - String iconclass,
602   - Maybe(String) tip_msg,
603   - String execute,
604   - )=
605   - literal("
606   - <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" >"
607   - + name
608   - + "<script type=\"dojo/method\" event=\"onClick\">"
609   - + execute
610   - + "</script>"
611   - + "</button>" +
612   - if tip_msg is
613   - {
614   - failure then "",
615   - success(tip) then
616   - "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
617   - }).
618   -
619   -
620   -public define HTML_Off_Form
621   - dojo_declaration
622   - (
623   - String name,
624   - List(CoreAttrs) attributes,
625   - HTML_Off_Form content
626   - )=
627   - div([attr("dojoType", "dijit.Declaration"), attr("widgetClass", name) . attributes ],
628   - content).
629   -
630   -
631   -public define HTML_In_Form
632   - dojo_declaration
633   - (
634   - String name,
635   - List(CoreAttrs) attributes,
636   - HTML_In_Form content
637   - )=
638   - div([attr("dojoType", "dijit.Declaration"), attr("widgetClass", name) . attributes ],
639   - content).
640   -
641   -public define HTML_Off_Form
642   - dojo_editor
643   - (
644   - List(CoreAttrs) attributes,
645   - HtmlId html_id,
646   - WebArgName input_name,
647   - HTML_Off_Form text,
648   - )=
649   - div([id(html_id.id), attr("name", input_name.name), attr("dojoType", "dijit.Editor")
650   - ,attr("extraPlugins", "['|', 'foreColor','hiliteColor',{name:'dojox.editor.plugins.FontChoice', command:'fontName', generic:true},'fontSize','formatBlock','|','createLink','insertImage']")
651   - . attributes], text).
652   -
653   - public define HTML_In_Form
654   - dojo_button
655   - (
656   - HtmlId html_id,
657   - String name,
658   - String iconclass,
659   - Maybe(String) tip_msg,
660   - String execute,
661   - )=
662   - literal("<input id=\"realSubmitButton"+ html_id.id + "\" type=\"submit\" style=\"display:none\">
663   - <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" onclick=\"dojo.byId('realSubmitButton" + html_id.id + "').click();" + execute + "\">" + name + "</button>" +
664   - if tip_msg is
665   - {
666   - failure then "",
667   - success(tip) then
668   - "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
669   - }).
670   -
671   -public define HTML_In_Form
672   - dojo_checkbox
673   - (
674   - List(InputAttrs) attributes,
675   - WebArgName name,
676   - HtmlId id,
677   - String label,
678   - WebArgValue val,
679   - Bool checked,
680   - )=
681   - check_box_r([attr("dojoType", "dijit.form.CheckBox") . attributes] , label, id, name, val, checked).
682   -
683   -public define HTML_In_Form
684   - dojo_combobox
685   - (
686   - List(InputAttrs) attributes,
687   - WebArgName name,
688   - HtmlId id,
689   - String label,
690   - List((WebArgValue,String)) list_data,
691   - InitialValue selected,
692   - )=
693   - // selector_c([attr("dojoType", "dijit.form.ComboBox") . attributes], label, id, name, 1, list_data, selected, non_mandatory).
694   - selector_c(attributes, label, id, name, 1, list_data, selected).
695   -
696   -public define HTML_Off_Form
697   - dojo_toaster
698   - (
699   - List(CoreAttrs) attributes,
700   - HtmlId html_id,
701   - String message_topic,
702   - Position_Direction position_direction,
703   - Bool separator,
704   - Maybe(Int) duration
705   - )=
706   - div(append(append(if separator then [attr("separator","&lt;hr&gt;")]
707   - else [],
708   - if duration is
709   - {
710   - failure then [],
711   - success(dur) then [attr("duration", abs_to_decimal(dur))]
712   - }),
713   - [attr("dojoType", "dojox.widget.Toaster"), attr("messageTopic", message_topic),
714   - attr("positionDirection", pos_dir_to_str(position_direction)), id(html_id.id)
715   - . attributes ]),
716   - literal("")).
717   -
718   -public define HTML_Off_Form
719   - dojo_progressBar
720   - (
721   - List(CoreAttrs) attributes,
722   - List(Text_Option) attributes_text,
723   - HtmlId html_id,
724   - String label
725   - )=
726   - sequence([
727   - text([id("text_" + html_id.id), class("progress_bar") . attributes_text], label),
728   - div([id(html_id.id), class("progress_bar"), attr("indeterminate", "true"), attr("dojoType", "dijit.ProgressBar") . attributes], literal(""))
729   - ]).
730   -
731   -public define HTML_Off_Form
732   - dojo_progressBar
733   - (
734   - List(CoreAttrs) attributes,
735   - List(Text_Option) attributes_text,
736   - HtmlId html_id,
737   - String label,
738   - Int progress,
739   - Int maximum
740   - )=
741   - sequence([
742   - text([id("text_" + html_id.id), class("progress_bar") . attributes_text], label),
743   - div([id(html_id.id),
744   - class("progress_bar"),
745   - attr("maximum", abs_to_decimal(maximum)),
746   - attr("progress", abs_to_decimal(progress)),
747   - attr("dojoType", "dijit.ProgressBar") . attributes], literal(""))
748   - ]).
749   -
750   -define Maybe(Int)
751   - string_to_month
752   - (
753   - String month_s
754   - )=
755   - with month = to_lower(month_s),
756   - if month = "jan" then success(1)
757   - else if month = "feb" then success(2)
758   - else if month = "mar" then success(3)
759   - else if month = "apr" then success(4)
760   - else if month = "may" then success(5)
761   - else if month = "jun" then success(6)
762   - else if month = "jul" then success(7)
763   - else if month = "aug" then success(8)
764   - else if month = "sep" then success(9)
765   - else if month = "oct" then success(10)
766   - else if month = "nov" then success(11)
767   - else if month = "dec" then success(12)
768   - else failure
769   - .
770   -
771   -
772   -// 'Thu Jan 14 2010 00:00:00 GMT+0100' must be replaced by '2010-01-14 00:00:00'
773   -
774   -
775   -
776   -public define Maybe(String)
777   - dojo_date_to_db_datetime
778   - (
779   - String data
780   - )=
781   - with list_data = split_by_token(data, ' '),
782   - if nth(2, list_data) is
783   - {
784   - failure then failure,
785   - success(day) then
786   - if nth(1, list_data) is
787   - {
788   - failure then failure,
789   - success(month) then
790   - if nth(3, list_data) is
791   - {
792   - failure then failure,
793   - success(year) then
794   - if nth(4, list_data) is
795   - {
796   - failure then failure,
797   - success(hour) then
798   - if string_to_month(month) is
799   - {
800   - failure then failure,
801   - success(month_int) then
802   - success(year+"-"+month_int+"-"+day+" "+hour)
803   - }
804   - }
805   - }
806   - }
807   - }.
808   -
809   -public define List(HTML_Head_Tag)
810   - dojo_defaults
811   - =
812   - [
813   - // js(js_file("js/dojo/dojo.js", [attr("djConfig", "parseOnLoad: true, isDebug: " + (if debug_mode then "true" else "false") + ", usePlainJson: true")])),
814   - js(js_file("js/dojo/dojo/dojo.js", [attr("djConfig", "parseOnLoad: true, isDebug: false, usePlainJson: true")])),
815   - js(js_file("js/dojo/dijit/dijit.js")),
816   - css(css_file("js/dojo/dojo/resources/dojo.css"))
817   - ].
818   -
819   -
820   -public define List(HTML_Head_Tag)
821   - dojo_init
822   - (
823   - String web_dir,
824   - String theme_name
825   - )
826   - =
827   - with theme_name =
828   - if file_exists(web_dir+"/public/js/dojo/dijit/themes/"+theme_name+"/"+theme_name+".css")then theme_name else "claro",
829   - [ css(css_file("js/dojo/dijit/themes/"+theme_name+"/"+theme_name+".css")) .
830   - [ css(css_file("js/dojo/dijit/themes/"+theme_name+"/"+theme_name+"_rtl.css")) . dojo_defaults ]
831   - ]
832   - .
  1 +/*
  2 + * Created by PyramIDE.
  3 + * User: Steve Marechal
  4 + * Date: 11/06/2008
  5 + * Time: 09:54
  6 + *
  7 + */
  8 +
  9 +read tools/basis.anubis
  10 +read tools/base64.anubis
  11 +read locale/L3LanguageInfo.anubis
  12 +read system/string.anubis
  13 +read system/logger.anubis
  14 +
  15 +read calexium_lib/web/CXM_common.anubis
  16 +read calexium_lib/web/CXM_making_a_web_site.anubis
  17 +read calexium_lib/web/CXM_multihost_http_server.anubis
  18 +read calexium_lib/net_services_protocols/logger_service.anubis
  19 +
  20 +public type Dojo_Grid_Data :
  21 + grid_data(String).
  22 +
  23 +public type Position_Direction:
  24 + brup,
  25 + brleft,
  26 + blup,
  27 + blright,
  28 + trdown,
  29 + trleft,
  30 + tldown,
  31 + tlright.
  32 +
  33 +define String
  34 + pos_dir_to_str
  35 + (
  36 + Position_Direction pos
  37 + )=
  38 + if pos is
  39 + {
  40 + brup then "br-up",
  41 + brleft then "br-left",
  42 + blup then "bl-up",
  43 + blright then "bl-right",
  44 + trdown then "tr-down",
  45 + trleft then "tr-left",
  46 + tldown then "tl-down",
  47 + tlright then "tl-right"
  48 + }.
  49 +
  50 +
  51 +
  52 +public define HTML_Off_Form
  53 + dojo_button
  54 + (
  55 + String name,
  56 + String execute,
  57 + String id,
  58 + )
  59 + =
  60 + literal("
  61 + <button dojoType=\"dijit.form.Button\" id=\""+id+"\" onclick=\""+execute+"\">"+name+"</button>")
  62 +.
  63 +
  64 +public type Input_Type:
  65 + file,
  66 + text,
  67 + password.
  68 +
  69 +public type InputDial:
  70 + input_dial(HtmlId id, Input_Type input_type, String label, Maybe(String) class),
  71 + input_date(HtmlId id, String label),
  72 + input_combo(HtmlId id, String label, List((WebArgValue,String)) list_data),
  73 + input_check(HtmlId id, String label, Bool checked),
  74 + input_hidden(HtmlId id, WebArgValue value).
  75 +
  76 +
  77 +define List(HTML_Off_Form)
  78 + format_list_data
  79 + (
  80 + List((WebArgValue,String)) list_data,
  81 + List(HTML_Off_Form) result_list
  82 + )=
  83 + if list_data is
  84 + {
  85 + [] then result_list,
  86 + [h . t] then
  87 + if h is (wa, label) then
  88 + format_list_data(t, [literal("<option value=\"" + wa.value + "\">" + label + "</option>") . result_list])
  89 + }.
  90 +
  91 +public define HTML_Off_Form
  92 + dojo_combobox
  93 + (
  94 + WebArgName the_name,
  95 + HtmlId the_id,
  96 + String label,
  97 + List((WebArgValue,String)) list_data,
  98 + )=
  99 + sequence([
  100 + literal("<label for=\"" + the_id.id + "\">" + label + "</label>
  101 + <select id=\""+the_id.id+"\"
  102 + name=\"" + the_name.name + "\"
  103 + dojoType=\"dijit.form.ComboBox\"
  104 + autoComplete=\"false\">"),
  105 + sequence((format_list_data(list_data, []))),
  106 + literal("</select>")
  107 + ]).
  108 +
  109 +define HTML_Off_Form
  110 + _maybe_label
  111 + (
  112 + String label_text,
  113 + HtmlId html_id
  114 + ) =
  115 + if length(label_text) > 0 then literal("<label for=\"" + html_id.id + "\">" + label_text + "</label>")
  116 + else literal("").
  117 +
  118 +public define HTML_Off_Form
  119 + dojo_checkbox
  120 + (
  121 + WebArgName the_name,
  122 + HtmlId the_id,
  123 + String label,
  124 + WebArgValue val,
  125 + Bool checked,
  126 + )=
  127 + sequence([
  128 + _maybe_label(label, the_id),
  129 + literal("<input type=\"checkbox\" name=\"" + the_name.name + "\" id=\"" + the_id.id + "\" value=\"" + val.value + "\""
  130 + + (if checked then " checked=\"checked\" " else "") + " dojoType=\"dijit.form.CheckBox\" />"),
  131 + ]).
  132 +
  133 +
  134 +define HTML_Off_Form
  135 + input_in_dialog
  136 + (
  137 + InputDial input_data
  138 + )=
  139 + if input_data is
  140 + {
  141 + input_dial(html_id, input, label, class) then
  142 + sequence([
  143 + literal("<div id=\"" + html_id.id +"_block\"" +
  144 + if class is
  145 + {
  146 + failure then " class=\"dojo_diag\"",
  147 + success(class_name) then " class=\"dojo_diag block_"+ class_name +"\""
  148 + }+"><label for=\"" + html_id.id + "\" id=\"label_" + html_id.id + "\"" +
  149 + if class is
  150 + {
  151 + failure then "",
  152 + success(class_name) then " class=\"label_" + class_name + "\""
  153 + }+">" + label + "</label>"),
  154 + literal("<input dojoType=dijit.form.TextBox " +
  155 + if input is
  156 + {
  157 + file then "type=\"file\"",
  158 + text then "type=\"text\"",
  159 + password then "type=\"password\""
  160 + } +
  161 + " name=\""+html_id.id+"\" id=\""+html_id.id+"\" /></div>")
  162 + ]),
  163 +
  164 + input_date(html_id, label) then
  165 + sequence([
  166 + literal("<div id=\"" + html_id.id +"_block\" class=\"dojo_diag\" ><label for=\"" + html_id.id + "\"> " + label + "</label>"),
  167 + literal("<div id=\""+html_id.id+"\" type=\"text\" name=\"date1\"
  168 + dojoType=\"dijit.form.DateTextBox\">
  169 + </div></div>"
  170 + )
  171 + ]),
  172 +
  173 + input_combo(the_id, label, list_data) then
  174 + sequence([
  175 + literal("<div id=\"" + the_id.id +"_block\" class=\"dojo_diag\">"),
  176 + dojo_combobox(wan(the_id.id), the_id, label, list_data),
  177 + literal("</div>")
  178 + ]),
  179 +
  180 + input_check(the_id, label, checked) then
  181 + sequence([
  182 + literal("<div id=\"" + the_id.id +"_block\" class=\"dojo_diag\">"),
  183 + dojo_checkbox(wan(the_id.id), the_id, label, wav("1"), checked),
  184 + literal("</div>"),
  185 + ]),
  186 +
  187 + input_hidden(the_id, val) then
  188 + literal("<input type=\"hidden\" id=\"" + the_id.id +"\" name=\"" + the_id.id +"\" value=\"" + val.value + "\" class=\"dojo_diag\" />"),
  189 + }
  190 +.
  191 +
  192 +define List(HTML_Off_Form)
  193 + inputs_in_dialog
  194 + (
  195 + List(InputDial) inputs_data
  196 + )=
  197 + if inputs_data is
  198 + {
  199 + [] then [],
  200 + [h . t] then [input_in_dialog(h) . inputs_in_dialog(t)]
  201 + }.
  202 +
  203 +public define HTML_Off_Form
  204 + dijit_dialog
  205 + (
  206 + String name,
  207 + String dialog_id,
  208 + String execute,
  209 + String ok_label,
  210 + Maybe(String) cancel,
  211 + List(InputDial) inputs_data,
  212 + Maybe(String) do_cancel
  213 + )
  214 + =
  215 + sequence([
  216 + div([attr("dojoType", "dijit.Dialog"), id(dialog_id), title(name), attr("execute", execute)],
  217 + sequence([
  218 + sequence(inputs_in_dialog(inputs_data)),
  219 + literal("<button dojoType=dijit.form.Button type=\"submit\" class=\"dialog_ok\">" + ok_label + "</button>" +
  220 + if cancel is
  221 + {
  222 + failure then "",
  223 + success(cancel_label) then "<button dojoType=dijit.form.Button type=\"cancel\" class=\"dialog_cancel\" onclick=\"" +
  224 + if do_cancel is
  225 + {
  226 + failure then "dijit.byId('"+ dialog_id +"').hide()",
  227 + success(to_do_cancel) then to_do_cancel
  228 + }+ "\">" + cancel_label + "</button>"
  229 + }),
  230 + literal("<br/>")
  231 + ])
  232 + )
  233 + ])
  234 +.
  235 +
  236 +public define HTML_Off_Form
  237 + dojo_dialog
  238 + (
  239 + String name,
  240 + String dialog_id,
  241 + String execute,
  242 + String ok_label,
  243 + Maybe(String) cancel,
  244 + List(InputDial) inputs_data,
  245 + Maybe(String) do_cancel
  246 + )
  247 + =
  248 + sequence[
  249 + dojo_button(name,"dijit.byId('"+dialog_id+"').show()", dialog_id + "_button"),
  250 + dijit_dialog(name, dialog_id, execute, ok_label, cancel, inputs_data, do_cancel)
  251 + ]
  252 +.
  253 +
  254 +
  255 +
  256 +
  257 +public type DojoGridEditor:
  258 + inputEditor,
  259 + boolEditor,
  260 + selectEditor(List(String) options),
  261 + alwaysOnEditor.
  262 +
  263 +public type DojoGridDefaultColumn:
  264 + dojo_grid_default_column(
  265 + String styles,
  266 + Maybe(String) width,
  267 + Maybe(DojoGridEditor) editor).
  268 +
  269 +public type DojoGridColumn:
  270 + dojo_grid_column( String label,
  271 + String field_name,
  272 + Maybe(String) width,
  273 + Maybe(DojoGridEditor) editor).
  274 +
  275 +public type DojoGridView:
  276 + dojo_grid_view(
  277 + Maybe(DojoGridDefaultColumn) default_column,
  278 + List(DojoGridColumn) columns).
  279 +
  280 +public type DojoGridLayout:
  281 + dojo_grid_layout(
  282 + Maybe(String) selectable_row_header_width,
  283 + List(DojoGridView) views).
  284 +
  285 +define String to_String(DojoGridEditor editor) =
  286 + "editor: " +
  287 + if editor is
  288 + {
  289 + inputEditor then "dojox.grid.editors.Input",
  290 + boolEditor then "dojox.grid.editors.Bool",
  291 + selectEditor(options) then "dojox.grid.editors.Select, options: [" + join(",", map((String o) |-> "\"" + o + "\"", options)) + "]",
  292 + alwaysOnEditor then "dojox.grid.editors.AlwaysOn"
  293 + }.
  294 +
  295 +define String to_String(DojoGridView view) =
  296 + "{ " + (if view.default_column is success(col) then
  297 + (if col is dojo_grid_default_column(styles, mb_width, mb_editor) then
  298 + "defaultCell: {"
  299 + + "styles: \"" + styles + "\""
  300 + + (if mb_width is success(w) then ", width=\"" + w + "\"" else "")
  301 + + (if mb_editor is success(e) then ", " + to_String(e) else "")
  302 + + "}, ")
  303 + else "")
  304 + + "cells: [[" + join(",", map((DojoGridColumn col) |->
  305 + if col is dojo_grid_column(label, field, mb_width, mb_editor) then
  306 + "{"
  307 + + "name: \"" + label + "\""
  308 + + "field: \"" + field + "\""
  309 + + (if mb_width is success(w) then ", width=\"" + w + "\"" else "")
  310 + + (if mb_editor is success(e) then ", " + to_String(e) else "")
  311 + + "}",
  312 + view.columns)) + "]]"
  313 + +"}".
  314 +
  315 +
  316 +public define String
  317 + dojo_make_grid_script
  318 + (
  319 + HtmlId html_id,
  320 + DojoGridLayout layout,
  321 + String layout_name,
  322 + String store_name,
  323 + Bool can_edit
  324 + )
  325 + =
  326 +"<script type=\"text/javascript\">
  327 + " + layout_name + " = ["
  328 + + (if layout.selectable_row_header_width is success(w) then "{ type: 'dojox.GridRowView', width: '" + w + "'}, " else "")
  329 + + join(", ", map(to_String, layout.views))
  330 + + "];
  331 +</script>".
  332 +
  333 +public define HTML_Off_Form
  334 + dojo_make_grid_script
  335 + (
  336 + HtmlId html_id,
  337 + List(String) columns,
  338 + Bool can_edit
  339 + )
  340 + =
  341 + literal(
  342 +"<script type=\"text/javascript\">
  343 + var cols = ["+join(",", map((String col) |-> "\"" + col + "\"", columns)) + "];
  344 + dojo.addOnLoad(function(){
  345 + init_dojo_grid(\""+html_id.id+"\", cols, "+ (if can_edit then "1" else "0")+");
  346 + });
  347 +</script>").
  348 +
  349 +public define HTML_Off_Form
  350 + dojo_grid
  351 + (
  352 + HtmlId html_id,
  353 + Int rowsPerPage,
  354 + List(Table_Option) attributes,
  355 + )=
  356 + table([attr("dojoType", "dojox.grid.DataGrid"), attr("id", html_id.id), attr("jsId", html_id.id),
  357 + class("soria"), attr("singleClickEdit", "true"), attr("rowsPerPage", to_decimal(rowsPerPage)) . attributes],
  358 + empty, [], empty).
  359 +
  360 +
  361 +public define HTML_Off_Form
  362 + dojo_grid
  363 + (
  364 + HtmlId html_id,
  365 + List(CoreAttrs) attributes,
  366 +// List(GridColumn) columns,
  367 + String colum1,
  368 + String colum2,
  369 + String colum3,
  370 + String colum4,
  371 + Bool can_edit
  372 + )
  373 + =
  374 + sequence([
  375 + literal(
  376 +
  377 +"<script type=\"text/javascript\">
  378 + dojo.addOnLoad(function(){
  379 + init_dojo_grid(\""+html_id.id+"\", \""+colum1+"\",\""+colum2+"\", \""+colum3+"\", \""+colum4+"\", "+ (if can_edit then "1" else "0")+");
  380 + });
  381 +</script>"),
  382 + div_empty([id(html_id.id) . attributes /*attr("dojoType", "dojox.Grid"), */]),
  383 + ]).
  384 +//<div id=\"gridContainer\"></div>").
  385 +
  386 +
  387 +public define HTML_Off_Form
  388 + dojo_message_box
  389 + (
  390 + HTML_Off_Form name1,
  391 + HTML_Off_Form name2,
  392 + )
  393 + =
  394 + sequence([
  395 + literal("<br/>"),
  396 + div([class("message_box")],
  397 + sequence([
  398 + div([id("node3"), class("box nopad hidden")], name1),
  399 + div([id("node4"), class("box two nopad")], name2),
  400 + ])),
  401 + literal("<br/>"),
  402 + ]).
  403 +
  404 +
  405 +
  406 +public define HTML_Off_Form
  407 + dojo_tooltip
  408 + (
  409 + String title,
  410 + String keyword,
  411 + ) =
  412 + actioner(same,same, link([id("dojo_tip_" + keyword),class("dojoToolTip")],title, success(title)), "show_address_mailing", [("address_mailing", keyword)], []).
  413 + //actioner(same, same, push_button([id("dojo_tip_" + keyword), class("in"), event(onclick,"new_search(this)")], keyword), "", [], []).
  414 +
  415 +
  416 +public type BorderContainerRegion:
  417 + center,
  418 + top,
  419 + bottom,
  420 + leading,
  421 + trailing,
  422 + left,
  423 + right.
  424 +
  425 +define String
  426 + to_String
  427 + (
  428 + BorderContainerRegion region
  429 + )=
  430 + if region is
  431 + {
  432 + center then "center",
  433 + top then "top",
  434 + bottom then "bottom",
  435 + leading then "leading",
  436 + trailing then "trailing",
  437 + left then "left",
  438 + right then "right"
  439 + }.
  440 +
  441 +public type BorderContainerDesign:
  442 + headline,
  443 + sidebar.
  444 +
  445 +define String
  446 + to_String
  447 + (
  448 + BorderContainerDesign design
  449 + )=
  450 + if design is
  451 + {
  452 + headline then "headline",
  453 + sidebar then "sidebar"
  454 + }.
  455 +
  456 +
  457 +public define HTML_Off_Form
  458 + dojo_ContentPane
  459 + (
  460 + List(CoreAttrs) attributes,
  461 + BorderContainerRegion region,
  462 + Bool splitter,
  463 + HTML_Off_Form content
  464 + ) =
  465 + div([ attr("dojoType", "dijit.layout.ContentPane"),
  466 + attr("region", to_String(region)),
  467 + attr("splitter", if splitter then "true" else "false") . attributes ],
  468 + content).
  469 +
  470 +public define HTML_In_Form
  471 + dojo_ContentPane
  472 + (
  473 + List(CoreAttrs) attributes,
  474 + BorderContainerRegion region,
  475 + Bool splitter,
  476 + HTML_In_Form content
  477 + ) =
  478 + div([ attr("dojoType", "dijit.layout.ContentPane"),
  479 + attr("region", to_String(region)),
  480 + attr("splitter", if splitter then "true" else "false") . attributes ],
  481 + content).
  482 +
  483 +public define HTML_Off_Form
  484 + dojo_TabContainer
  485 + (
  486 + List(CoreAttrs) attributes,
  487 + HTML_Off_Form content
  488 + ) =
  489 + div([attr("dojoType", "dijit.layout.TabContainer") . attributes ],
  490 + content).
  491 +
  492 +
  493 +public define HTML_Off_Form
  494 + dojo_BorderContainer
  495 + (
  496 + List(CoreAttrs) attributes,
  497 + Maybe(String) the_title,
  498 + BorderContainerDesign design,
  499 + Bool live_splitter,
  500 + Bool persist,
  501 + Bool closable,
  502 + String container,
  503 + HTML_Off_Form content
  504 + ) =
  505 + with the_attributes = (List(CoreAttrs)) [attr("dojoType", "dijit.layout.BorderContainer"), attr("dojoAttachPoint", container),
  506 + attr("design", to_String(design)), attr("liveSplitters", live_splitter), attr("persist", persist),
  507 + attr("closable",closable) . attributes ],
  508 + total_attributes = if the_title is
  509 + {
  510 + failure then the_attributes,
  511 + success(t) then [title(t) . the_attributes]
  512 + },
  513 + div(total_attributes, content).
  514 +
  515 +public define HTML_In_Form
  516 + dojo_BorderContainer
  517 + (
  518 + List(CoreAttrs) attributes,
  519 + Maybe(String) the_title,
  520 + BorderContainerDesign design,
  521 + Bool live_splitter,
  522 + Bool persist,
  523 + Bool closable,
  524 + String container,
  525 + HTML_In_Form content
  526 + ) =
  527 + with the_attributes = (List(CoreAttrs)) [attr("dojoType", "dijit.layout.BorderContainer"), attr("dojoAttachPoint", container),
  528 + attr("design", to_String(design)), attr("liveSplitters", live_splitter), attr("persist", persist),
  529 + attr("closable",closable) . attributes ],
  530 + total_attributes = if the_title is
  531 + {
  532 + failure then the_attributes,
  533 + success(t) then [title(t) . the_attributes]
  534 + },
  535 + div(total_attributes, content).
  536 +
  537 +public define HTML_Off_Form
  538 + dojo_tree
  539 + (
  540 + List(CoreAttrs) attributes,
  541 + )=
  542 + div_empty(attributes).
  543 +
  544 +
  545 +
  546 +public define HTML_Off_Form
  547 + dojo_Toolbar
  548 + (
  549 + List(CoreAttrs) attributes,
  550 + HTML_Off_Form content
  551 + )=
  552 + div([attr("dojoType", "dijit.Toolbar") . attributes ],
  553 + content).
  554 +
  555 +
  556 +public define HTML_Off_Form
  557 + dojo_tooltip
  558 + (
  559 + List(CoreAttrs) attributes,
  560 + List(Text_Option) txt_attr,
  561 + HtmlId html_id,
  562 + String label,
  563 + HTML_Off_Form tooltip_content,
  564 + )=
  565 + sequence([
  566 + text([id(html_id.id) . txt_attr], label),
  567 + div([id(html_id.id + "_tt"), attr("connectId", html_id.id), attr("dojoType", "dijit.Tooltip") . attributes], tooltip_content)
  568 + ]).
  569 +
  570 +
  571 +public define HTML_Off_Form
  572 + dojo_button
  573 + (
  574 + HtmlId html_id,
  575 +// Maybe(String) class,
  576 + String name,
  577 + String iconclass,
  578 + Maybe(String) tip_msg,
  579 + String execute,
  580 + )=
  581 + literal("
  582 + <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" >"
  583 + + name
  584 + + "<script type=\"dojo/method\" event=\"onClick\">"
  585 + + execute
  586 + + "</script>"
  587 + + "</button>" +
  588 + if tip_msg is
  589 + {
  590 + failure then "",
  591 + success(tip) then
  592 + "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
  593 + }).
  594 +
  595 +public define HTML_In_Form
  596 + dojo_button
  597 + (
  598 + HtmlId html_id,
  599 +// Maybe(String) class,
  600 + String name,
  601 + String iconclass,
  602 + Maybe(String) tip_msg,
  603 + String execute,
  604 + )=
  605 + literal("
  606 + <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" >"
  607 + + name
  608 + + "<script type=\"dojo/method\" event=\"onClick\">"
  609 + + execute
  610 + + "</script>"
  611 + + "</button>" +
  612 + if tip_msg is
  613 + {
  614 + failure then "",
  615 + success(tip) then
  616 + "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
  617 + }).
  618 +
  619 +
  620 +public define HTML_Off_Form
  621 + dojo_declaration
  622 + (
  623 + String name,
  624 + List(CoreAttrs) attributes,
  625 + HTML_Off_Form content
  626 + )=
  627 + div([attr("dojoType", "dijit.Declaration"), attr("widgetClass", name) . attributes ],
  628 + content).
  629 +
  630 +
  631 +public define HTML_In_Form
  632 + dojo_declaration
  633 + (
  634 + String name,
  635 + List(CoreAttrs) attributes,
  636 + HTML_In_Form content
  637 + )=
  638 + div([attr("dojoType", "dijit.Declaration"), attr("widgetClass", name) . attributes ],
  639 + content).
  640 +
  641 +public define HTML_Off_Form
  642 + dojo_editor
  643 + (
  644 + List(CoreAttrs) attributes,
  645 + HtmlId html_id,
  646 + WebArgName input_name,
  647 + HTML_Off_Form text,
  648 + )=
  649 + div([id(html_id.id), attr("name", input_name.name), attr("dojoType", "dijit.Editor")
  650 + ,attr("extraPlugins", "['|', 'foreColor','hiliteColor',{name:'dojox.editor.plugins.FontChoice', command:'fontName', generic:true},'fontSize','formatBlock','|','createLink','insertImage']")
  651 + . attributes], text).
  652 +
  653 + public define HTML_In_Form
  654 + dojo_button
  655 + (
  656 + HtmlId html_id,
  657 + String name,
  658 + String iconclass,
  659 + Maybe(String) tip_msg,
  660 + String execute,
  661 + )=
  662 + literal("<input id=\"realSubmitButton"+ html_id.id + "\" type=\"submit\" style=\"display:none\">
  663 + <button dojoType=\"dijit.form.Button\" id=\"" + html_id.id + "\" iconClass=\"" + iconclass + "\" onclick=\"dojo.byId('realSubmitButton" + html_id.id + "').click();" + execute + "\">" + name + "</button>" +
  664 + if tip_msg is
  665 + {
  666 + failure then "",
  667 + success(tip) then
  668 + "<span dojoType=\"dijit.Tooltip\" connectId=\"" + html_id.id + "\">" + tip + "</span>"
  669 + }).
  670 +
  671 +public define HTML_In_Form
  672 + dojo_checkbox
  673 + (
  674 + List(InputAttrs) attributes,
  675 + WebArgName name,
  676 + HtmlId id,
  677 + String label,
  678 + WebArgValue val,
  679 + Bool checked,
  680 + )=
  681 + check_box_r([attr("dojoType", "dijit.form.CheckBox") . attributes] , label, id, name, val, checked).
  682 +
  683 +public define HTML_In_Form
  684 + dojo_combobox
  685 + (
  686 + List(InputAttrs) attributes,
  687 + WebArgName name,
  688 + HtmlId id,
  689 + String label,
  690 + List((List(CoreAttrs),WebArgValue,String)) list_data,
  691 + InitialValue selected,
  692 + )=
  693 + // selector_c([attr("dojoType", "dijit.form.ComboBox") . attributes], label, id, name, 1, list_data, selected, non_mandatory).
  694 + selector_c(attributes, label, id, name, 1, list_data, selected).
  695 +
  696 +public define HTML_Off_Form
  697 + dojo_toaster
  698 + (
  699 + List(CoreAttrs) attributes,
  700 + HtmlId html_id,
  701 + String message_topic,
  702 + Position_Direction position_direction,
  703 + Bool separator,
  704 + Maybe(Int) duration
  705 + )=
  706 + div(append(append(if separator then [attr("separator","&lt;hr&gt;")]
  707 + else [],
  708 + if duration is
  709 + {
  710 + failure then [],
  711 + success(dur) then [attr("duration", abs_to_decimal(dur))]
  712 + }),
  713 + [attr("dojoType", "dojox.widget.Toaster"), attr("messageTopic", message_topic),
  714 + attr("positionDirection", pos_dir_to_str(position_direction)), id(html_id.id)
  715 + . attributes ]),
  716 + literal("")).
  717 +
  718 +public define HTML_Off_Form
  719 + dojo_progressBar
  720 + (
  721 + List(CoreAttrs) attributes,
  722 + List(Text_Option) attributes_text,
  723 + HtmlId html_id,
  724 + String label
  725 + )=
  726 + sequence([
  727 + text([id("text_" + html_id.id), class("progress_bar") . attributes_text], label),
  728 + div([id(html_id.id), class("progress_bar"), attr("indeterminate", "true"), attr("dojoType", "dijit.ProgressBar") . attributes], literal(""))
  729 + ]).
  730 +
  731 +public define HTML_Off_Form
  732 + dojo_progressBar
  733 + (
  734 + List(CoreAttrs) attributes,
  735 + List(Text_Option) attributes_text,
  736 + HtmlId html_id,
  737 + String label,
  738 + Int progress,
  739 + Int maximum
  740 + )=
  741 + sequence([
  742 + text([id("text_" + html_id.id), class("progress_bar") . attributes_text], label),
  743 + div([id(html_id.id),
  744 + class("progress_bar"),
  745 + attr("maximum", abs_to_decimal(maximum)),
  746 + attr("progress", abs_to_decimal(progress)),
  747 + attr("dojoType", "dijit.ProgressBar") . attributes], literal(""))
  748 + ]).
  749 +
  750 +define Maybe(Int)
  751 + string_to_month
  752 + (
  753 + String month_s
  754 + )=
  755 + with month = to_lower(month_s),
  756 + if month = "jan" then success(1)
  757 + else if month = "feb" then success(2)
  758 + else if month = "mar" then success(3)
  759 + else if month = "apr" then success(4)
  760 + else if month = "may" then success(5)
  761 + else if month = "jun" then success(6)
  762 + else if month = "jul" then success(7)
  763 + else if month = "aug" then success(8)
  764 + else if month = "sep" then success(9)
  765 + else if month = "oct" then success(10)
  766 + else if month = "nov" then success(11)
  767 + else if month = "dec" then success(12)
  768 + else failure
  769 + .
  770 +
  771 +
  772 +// 'Thu Jan 14 2010 00:00:00 GMT+0100' must be replaced by '2010-01-14 00:00:00'
  773 +
  774 +
  775 +
  776 +public define Maybe(String)
  777 + dojo_date_to_db_datetime
  778 + (
  779 + String data
  780 + )=
  781 + with list_data = split_by_token(data, ' '),
  782 + if nth(2, list_data) is
  783 + {
  784 + failure then failure,
  785 + success(day) then
  786 + if nth(1, list_data) is
  787 + {
  788 + failure then failure,
  789 + success(month) then
  790 + if nth(3, list_data) is
  791 + {
  792 + failure then failure,
  793 + success(year) then
  794 + if nth(4, list_data) is
  795 + {
  796 + failure then failure,
  797 + success(hour) then
  798 + if string_to_month(month) is
  799 + {
  800 + failure then failure,
  801 + success(month_int) then
  802 + success(year+"-"+month_int+"-"+day+" "+hour)
  803 + }
  804 + }
  805 + }
  806 + }
  807 + }.
  808 +
  809 +public define List(HTML_Head_Tag)
  810 + dojo_defaults
  811 + =
  812 + [
  813 + // js(js_file("js/dojo/dojo.js", [attr("djConfig", "parseOnLoad: true, isDebug: " + (if debug_mode then "true" else "false") + ", usePlainJson: true")])),
  814 + js(js_file("js/dojo/dojo/dojo.js", [attr("djConfig", "parseOnLoad: true, isDebug: false, usePlainJson: true")])),
  815 + js(js_file("js/dojo/dijit/dijit.js")),
  816 + //css(css_file("js/dojo/dojo/resources/dojo.css"))
  817 + ].
  818 +
  819 +
  820 +public define List(HTML_Head_Tag)
  821 + dojo_init
  822 + (
  823 + String web_dir,
  824 + String theme_name
  825 + )
  826 + =
  827 + with theme_name =
  828 + if file_exists(web_dir+"/public/js/dojo/dijit/themes/"+theme_name+"/"+theme_name+".css")then theme_name else "claro",
  829 + [ css(css_file("js/dojo/dijit/themes/"+theme_name+"/"+theme_name+".css")) .
  830 + [ css(css_file("js/dojo/dijit/themes/"+theme_name+"/"+theme_name+"_rtl.css")) . dojo_defaults ]
  831 + ]
  832 + .
... ...
web/CXM_making_a_web_site.anubis
Changes suppressed. Click to show
1   -๏ปฟ
2   -
3   - *Project* Anubis
4   -
5   - *Title* Making interactive Web sites.
6   -
7   - *Copyright* Copyright (c) Alain Proutรฉ 2004-2005.
8   - Copyright (c) Calexium 2007.
9   -
10   -
11   - *Authors* Alain Proutรฉ
12   - David Renรฉ
13   -
14   - *Revised* May 2007
15   -
16   -
17   - *Overview*
18   -
19   - In this file we propose simple tools for making well structured interactive and secured
20   - web sites.
21   -
22   -
23   - ----------------------------------- Table of Contents ---------------------------------
24   -
25   - * (1) Structure of a web site.
26   - ** (1.1) Three sorts of data.
27   - ** (1.2) How requests are handled.
28   - ** (1.3) What web pages are made of.
29   - ** (1.4) Actions.
30   - ** (1.5) States.
31   -
32   - * (2) Carrying on.
33   - ** (2.1) Describing your web sites.
34   - ** (2.2) Directories on the server's disk.
35   - ** (2.3) Starting your web sites.
36   -
37   - * (3) The HTML interface.
38   - ** (3.1) Types used by the HTML interface.
39   - ** (3.2) ``in form'' versus ``off form''.
40   - ** (3.3) Defining your own style.
41   - ** (3.4) Actioners and forms.
42   - ** (3.5) Local popup.
43   -
44   - ---------------------------------------------------------------------------------------
45   -
46   -
47   -read tools/basis.anubis
48   -read tools/printable_tree.anubis
49   -read tools/base64.anubis
50   -read system/string.anubis
51   -read system/logger.anubis
52   -read CXM_common.anubis
53   -read CXM_multihost_http_server.anubis
54   -read web/mime.anubis
55   -read CXM_cookies.anubis
56   -
57   -
58   - * (1) Structure of a web site.
59   -
60   - First of all we need to explain what a web site should be made of. Ideally, the
61   - visitor (also called the 'client') should see the web site working as any other
62   - interactive computer software. So, it should be clear that a 'session' (i.e. a visit
63   - to the web site, including the consultation of several pages) is some kind of
64   - conversation between the visitor and the web site, and that the web site should
65   - maintain a 'current state' of this conversation. At each new request (click) from the
66   - visitor, this state must be updated. This whole conversation is called a 'session' and
67   - should not be confused with a single request.
68   -
69   -
70   -
71   - ** (1.1) Three sorts of data.
72   -
73   - All the data needed for putting a web site at work may be dispatched into three
74   - categories:
75   -
76   - 1. Constant data (data that never change). These data may be hard coded into the
77   - Anubis source files of the web site.
78   -
79   - 2. Permanent data (data which always exist independantly of the users connected to
80   - the web site). These data are normally recorded into data bases.
81   -
82   - 3. Session data (data which depend on a particular visitor and which exist only
83   - during the time he visits the web site). These data are stored into so-called
84   - 'states'.
85   -
86   -
87   - It is important to determine which data belongs to which category. This is part of your
88   - design decisions.
89   -
90   -
91   -
92   - ** (1.2) How requests are handled.
93   -
94   - We want to separate the following two functionalities (which are used at each request
95   - (click) during a single session):
96   -
97   - - computing the new state from the previous state and from the client request, and
98   - updating the data base,
99   -
100   - - computing the page to be sent to the client from the new current state and from
101   - the informations in the data base.
102   -
103   -
104   - The next picture shows the structure we have in mind:
105   -
106   -
107   - request +---------+ HTML page (with a hidden state name)
108   - .-------------------| client |<--------------.
109   - | .-----------------| | |
110   - | | previous state +---------+ |
111   - | | name (if any) |
112   - | | | client side
113   - ............................................................................
114   - | | | server side
115   - | | |
116   - | | .-------------------. |
117   - | | | previous state | |
118   - V V V | |
119   - +---------------+ +---------------+ +--------------+
120   - | compute state | | server's disk | | compute page |
121   - +---------------+ +---------------+ +--------------+
122   - ^ | | ^ ^ ^ ^ ^
123   - | | | | | | | |
124   - | | `--------------------+--------------------' | |
125   - | | new state | | |
126   - read | `------------------------+--------------------' |
127   - write | new state name |
128   - update V |
129   - +-----------+ |
130   - | data base |--------------------------------------------'
131   - +-----------+ read only
132   -
133   -
134   - When the client begins a session, there is no previous state. In this case, a default
135   - 'initial state' is used instead.
136   -
137   - The data base may be updated by 'compute state' box, but should not be update by the
138   - 'compute page' box. The 'compute page' box should be allowed only to read the data
139   - base.
140   -
141   - In this file, all the above stuff is defined, except the 'compute state' and 'compute
142   - page' boxes. You just have to provide the function for computing a new state (compute
143   - state) and the function for computing the page (compute page) from the new state. You
144   - don't have to worry about state names, saving and retrieving states and the like.
145   -
146   -
147   -
148   -
149   -
150   - ** (1.3) What web pages are made of.
151   -
152   - What the client can see in his browser's window may be called a 'page'. Within a page,
153   - we have several sorts of components:
154   -
155   - - 'local' components, i.e. all components which do not open a connection, like
156   - texts, images, etc... possibly using JavaScript programmation,
157   -
158   - - 'actioners', which, when clicked upon, open a connection with our web site; they
159   - may appear as links or buttons, etc...
160   -
161   - - 'foreign links', which when clicked upon, open a connection with another web site
162   - (or ours eventually).
163   -
164   - Of course, what an actioner does is just ask our web site to perform an action. To that
165   - end, the actioner essentially sends the name of the action to be performed. However, it
166   - may be necessary to provide additional informations which may be seen as 'operands' of
167   - the action. In order to attach operands to an action, HTML provides the notion of
168   - 'form'. Indeed, a form contains essentially a set of input fields into which the client
169   - may put values for the required operands of the action, and a submit button, which is
170   - the actioner itself. Notice that a single form may contain several submit buttons,
171   - which simply means that there are several distincts actions taking the same set of
172   - operands.
173   -
174   - Restrictions must be put on the use of all theses gadgets. Indeed, for example,
175   - putting a form within another form is officially meaningless in HTML, and the client's
176   - browser may be seriously disturbed by this. In this file, we propose an interface to
177   - the HTML language, which forbids such meaningless things, simply by imposing a strict
178   - typing of HTML concepts.
179   -
180   - Each web site may be accessible through two communication channels:
181   -
182   - - a non secured channel (HTTP),
183   - - a secured channel (HTTPS).
184   -
185   - Nevertheless, the whole thing should be considered as a single web site. For example,
186   - you may have a secured page, obtained through HTTPS, containing public images obtained
187   - through HTTP. An actioner in a non secured page may open a secured connection, and
188   - conversely.
189   -
190   - Summarizing, a web page is made of local elements, foreign links and actioners.
191   - Actioners receive operands from forms, and they also choose to communicate through the
192   - non secured or through the secured channel.
193   -
194   -
195   -
196   - +-------------------+
197   - | page |
198   - | | +---------------+
199   - | +--------------+ | | next page |
200   - | | form | | | (non secured) |
201   - | | +----------+ | | HTTP | |
202   - | | | actioner |---------------------------->| |
203   - | | +----------+ | | +---------------+
204   - | | | |
205   - | | +----------+ | | +---------------+
206   - | | | actioner |---------------------------->| next page |
207   - | | +----------+ | | HTTPS | (secured) |
208   - | | | | | |
209   - | +--------------+ | | |
210   - | | +---------------+
211   - | |
212   - +-------------------+
213   -
214   -
215   - Notice that actioners need no be necessarily put into forms. In that case, they work as
216   - ordinary links, but they still may receive operands as we shall see.
217   -
218   -
219   -
220   -
221   - ** (1.4) Actions.
222   -
223   - The client opens a new connection with our web site whenever he clicks on an
224   - actioner. The result is that a request is sent, essentially made of a list of 'web
225   - arguments'. Each web argument is a pair (name,value). One of these web arguments, the
226   - 'action' web argument (whose name is "a"), determines the action to be performed. The
227   - other web arguments (not including "s", used to identify the state) are the operands
228   - for this action.
229   -
230   - Hence, the 'compute state' box in the picture above, splits naturally into as many
231   - sub-boxes as there are actions. For this reason, we define the following type for
232   - representing actions (where '$State' is the type representing session informations):
233   -
234   -public type Web_Action($State):
235   - http_action (String name, // name of action
236   - ($State) -> Bool allow, // true if action allowed
237   - (HTTP_Info http_info,
238   - List(Web_arg) web_args, // actually only 'operands' web arguments
239   - $State state) -> $State do_it),
240   - https_action (String name, // name of action
241   - ($State) -> Bool allow, // true if action allowed
242   - (HTTP_Info http_info,
243   - List(Web_arg) web_args, // actually only 'operands' web arguments
244   - $State state) -> $State do_it),
245   - http_https_action (String name,
246   - ($State) -> Bool allow,
247   - (HTTP_Info http_info,
248   - List(Web_arg) web_args,
249   - $State state) -> $State do_it).
250   -
251   - 'http_action's are executed only under HTTP, and 'https_action's are executed only
252   - under HTTPS. 'http_https_action's may be executed under both types of connections.
253   -
254   - Each action has a name, which is used to identify the action. Each action also has a
255   - function 'allow' whose job is to verify that the action is allowed in the current
256   - state, and a function 'do_it' for performing the action. The function 'do_it' receives
257   - a lot of informations:
258   -
259   - - 'HTTP informations':
260   - - the IP address of the client,
261   - - the URI requested by the client (after redirection),
262   - - the list of HTTP headers generated by the client's browser,
263   - - the list of web arguments sent by the client (except "s" and "a"),
264   - - the previous state (or the 'initial' or 'ticket expired' state if no previous
265   - state can be found).
266   -
267   - In most cases, HTTP informations are not used. This is the reason why they are gathered
268   - for simplicity into a unique datum of type 'HTTP_Info'.
269   -
270   -// For your convenience, we introduce the following simpler variants:
271   -//
272   -//public define Web_Action($State)
273   -// http_action
274   -// (
275   -// String name,
276   -// $State -> Bool allow,
277   -// (List(Web_arg),$State) -> $State do_it
278   -// ) =
279   -// http_action(name,
280   -// (Maybe($State) ms) |-> if ms is
281   -// {
282   -// failure then true,
283   -// success(s) then allow(s)
284   -// },
285   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
286   -// {
287   -// failure then (failure, []),
288   -// success(s2) then (success(do_it(l,s2)), [])
289   -// }).
290   -//
291   -//public define Web_Action($State)
292   -// https_action
293   -// (
294   -// String name,
295   -// $State -> Bool allow,
296   -// (List(Web_arg),$State) -> $State do_it
297   -// ) =
298   -// https_action(name,
299   -// (Maybe($State) ms) |-> if ms is
300   -// {
301   -// failure then true,
302   -// success(s) then allow(s)
303   -// },
304   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
305   -// {
306   -// failure then (failure, []),
307   -// success(s2) then (success(do_it(l,s2)), [])
308   -// }).
309   -//
310   -//public define Web_Action($State)
311   -// http_https_action
312   -// (
313   -// String name,
314   -// $State -> Bool allow,
315   -// (List(Web_arg),$State) -> $State do_it
316   -// ) =
317   -// http_https_action(name,
318   -// (Maybe($State) ms) |-> if ms is
319   -// {
320   -// failure then true,
321   -// success(s) then allow(s)
322   -// },
323   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
324   -// {
325   -// failure then (failure, []),
326   -// success(s2) then (success(do_it(l,s2)), [])
327   -// }).
328   -
329   -
330   -
331   - When you define your web site, you must provide the list of all the actions of the
332   - site. When a new state has been computed, a graphical representation of this state
333   - must be sent to the client. To that end, you must provide a function (named below
334   - 'compute_page') of type:
335   -
336   - $State -> HTTP_Answer
337   -
338   - where the type 'HTTP_Answer' (defined below in this file) abstractly represents HTML
339   - pages.
340   -
341   -public type HTTP_Answer:...
342   -
343   - It should be clear that states and pages are deeply linked together. Indeed, we really
344   - understand the page shown to the client as a representation of the current state of the
345   - conversation between the client and the web site, but also containing informations
346   - taken from the data bases.
347   -
348   -
349   -
350   -
351   -
352   - ** (1.5) States.
353   -
354   - Now, we explain how you can define the type (say 'State') to be used as an instance of
355   - the type parameter '$State'. The following is just a suggestion.
356   -
357   - Each state determines a page (since 'compute_page' computes a page from a
358   - state). However, some components of the state may be independant of the page. It may be
359   - the case for example for the indication of the natural language used by the
360   - client. Hence, a state should be made of (at least) two parts:
361   -
362   - - informations which are the same for all pages,
363   - - informations which are particular to each page.
364   -
365   - For example, you could define:
366   -
367   - type Page: // one alternative per page, with particular informations
368   - login(...), // in the components
369   - main_page(...),
370   - ...etc...
371   -
372   - Now, the type 'State' could be defined as follows:
373   -
374   - type State:
375   - state(Language, // informations valid for all pages
376   - ...,
377   - Page). // informations particular to a page
378   -
379   - However, if you are making a secured web site within which clients should be identified
380   - (by id and password), it may be a good idea to have two sorts of states, one for non
381   - identified clients and one for identified clients. In this case, define the type
382   - 'State' as follows (this is just a suggestion):
383   -
384   - type State:
385   - non_identified(Language),
386   - identified(String id,
387   - Language,
388   - Page).
389   -
390   - When a request arrives, check if the previous state is 'identified(...)' or
391   - 'non_identified(...)', and don't provide access to certain pages to non identified
392   - clients. This is required for security.
393   -
394   - Some more words on security. If your site needs to identify clients, define the
395   - initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
396   - password of the client. If the id and password are correct, then change the state of
397   - the client to 'identified(...)'. No other action should be able to do that. Now, be
398   - confident that clients cannot forge states. The only information they have is the name
399   - of a state, not the state itself which is never sent over the network, but only stored
400   - on the server's disk. The name of the state is constructed using strong cryptographical
401   - methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
402   - state names cannot be seen by a third party. So, if the system retrieves a previous
403   - state of the form 'identified(...)', you can be confident that your client is well
404   - identified, and you can send him confidential informations.
405   -
406   - States have a limited life time. It may happen that a client clicks on a button at a
407   - time its state is out of date. In this case, this system considers that the new state
408   - is a special state named 'ticket expired'. You must provide a function producing this
409   - state when you describe your web site. The page corresponding to this state must just
410   - inform the client that he/she waited a too long time before clicking on a button, and
411   - has to restart (a new conversation) from the begining.
412   -
413   -
414   -
415   - * (2) Carrying on.
416   -
417   - ** (2.1) Describing your web sites.
418   -
419   - Before you may start your web site, you must describe it, i.e. produce a datum of the
420   - opaque type 'Web_Site'.
421   -
422   -public type Web_Site:...
423   -
424   - Producing such a datum may be performed by:
425   -
426   -public define Web_Site
427   - make_web_site_description
428   - (
429   - List(String) common_names, // for example: ["www.our-business.com",
430   - // "192.168.0.1"]
431   - // the second one is just for testing
432   - String site_directory, // where 'public' and other directories are
433   - // located (should NOT end with '/')
434   - String state_directory,
435   - One -> One init,
436   - (HTTP_Info,
437   - List(Web_arg),
438   - Bool is_https) -> $State initial_state,
439   - ($State expired,
440   - Maybe(String),
441   - HTTP_Info,
442   - List(Web_arg),
443   - Bool is_https) -> $State ticket_expired_state,
444   - (Maybe(String),
445   - HTTP_Info,
446   - List(Web_arg),
447   - Bool is_https) -> $State ticket_lost_state,
448   - List(Web_Action($State)) actions,
449   - $State -> HTTP_Answer compute_page,
450   - $State -> List(HTTP_header) additional_headers,
451   - Int timeout, // seconds (todo: minutes)
452   - Redirections redirections,
453   - String charset,
454   - List(String) journal_extensions,
455   - List(String) journal_headers,
456   - String authorization_secret,
457   - List(MIME) known_mime_types,
458   - (String action_name,
459   - List(Web_arg) args)-> One before_send_file
460   - ).
461   -
462   -
463   - Explanations:
464   -
465   - 'common_names' is the list of names of the site (the name the browser must send as the
466   - value of the 'Host' HTTP header in order to access the site must be in that list). Such
467   - a name generally looks like this:
468   -
469   - www.somewhere.com
470   -
471   - If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
472   - the site must be exactly the same as the name on the certificate (which is precisely
473   - called the 'common name' in the X.509 jargon). If the two names do not match, the site
474   - will still work, but the transaction will not be transparent to the client. His browser
475   - will complain that the name of the certificate does not match the name of the site, and
476   - he will have to accept the certificate manually.
477   -
478   - 'site_directory' is the absolute path to the directory where the files needed by the
479   - site are located. Usually this directory looks like:
480   -
481   - my_anubis/web_sites/www.somewhere.com
482   -
483   - However, this information is not computed from 'common_name', so that you can change
484   - the common name (for example temporarily, for networking reasons) without loosing
485   - access to the files.
486   -
487   - 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
488   - whose graphical representation is a page explaining to the user that its 'ticket' (or
489   - 'session information') has expired, and that he/she must close all popup windows and
490   - start a new session. The arguments of the function contain the previous (expired)
491   - state and all current informations concerning the user. This arguments may be useful
492   - for example for producing the expiration message in the language chosen by the user.
493   - You can also (and this may be much smarter) send a 'ticket prolongation page'
494   - (including a new login for example), and resume the same conversation, since you have
495   - all the pertinent informations at hand. In the case the ticket is definitely lost, the
496   - second fonction 'ticket_lost_state' is used.
497   -
498   - Notice that despite the fact that the parameter $State is involved in the arguments of
499   - the above function, the type 'Web_Site' does not depend on this parameter. This allows
500   - to produce lists of web site descriptions, where each description may be constructed
501   - with a different instance of $State. This is required because distinct sites must have
502   - distinct types of session informations. This is made possible by the fact that the
503   - type is obscure, and the constructor replaced by a function which assembles
504   - 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
505   - entity not depending on $State. You should have a look to the private part of this file
506   - if you want more precisions about this programming technique.
507   -
508   - 'charset' is a string which will determine the character encoding to be used by the
509   - browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
510   - etc...
511   -
512   - 'before_send_file' is a function which is executed just before the HTTP server sends a
513   - file. It gets an action name and the web arguments received with the request for that
514   - file. Notice that this action name and these web arguments may be put into a
515   - 'private_download' element, and will come back to the server at the time of the
516   - download.
517   -
518   -
519   -
520   - ** (2.2) Directories on the server's disk.
521   -
522   - The description of your site contains the name of the directory within which the
523   - required files are located. This may be for example:
524   -
525   - my_anubis/web_sites/www.our-business.com/
526   -
527   - This is called the 'site directory' (for the given site). Within the site directory,
528   - the following directories are created by this program:
529   -
530   - states
531   - public
532   - journal
533   - private_download
534   - upload_temporary
535   -
536   - The directory 'states' is used for storing states (session informations). Out of date
537   - states are automatically removed after some time.
538   -
539   - The tree rooted at 'public' contains files that the server is allowed to send to the
540   - clients. For security reasons, the server never sends a file which is not within the
541   - tree whose root is this 'public' directory (except for the 'private download' mecanism;
542   - see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
543   - must have been recognized before the file may be sent.
544   -
545   - The directory 'journal' contains the jounal files. The roles of the remaining
546   - directories 'private_download' and 'upload_temporary' is explained in
547   - 'multihost_http_server.anubis', where you will also find further informations on
548   - 'public' and 'journal'.
549   -
550   -
551   -
552   -
553   - ** (2.3) Web servers parameters.
554   -
555   - The web servers have several parameters useful for administration. They are described
556   - as follows:
557   -
558   - public type WebServersParameters:
559   - wsparms(Var(Bool) shutdown_required,
560   -
561   -
562   -
563   -
564   - ** (2.4) Starting your web sites.
565   -
566   - When you have described all your web sites (you may want to have several web sites, and
567   - they are distinguished by their 'common name'), you may start them all together using
568   - 'start_web_sites' below. This function returns a result of the following type:
569   -
570   -public type Start_Web_Sites_Result:
571   - cannot_bind_to_port(Word32),
572   - cannot_bind_to_port(Word32,Word32),
573   - ok(Server http_server,
574   - Server https_server).
575   -
576   - Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
577   - ports (or to both). The main reason is that another server is already listening on that
578   - port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
579   - the 's' bit has not been set for 'user' and 'group' (there is not such problem under
580   - Windows). Also notice that the Linux kernel may need a rather long time (up to several
581   - minutes) before liberating a listening port. Now, if the system can bind to the two
582   - ports, the pair of the two servers is returned. Two tools are useful for manipulating
583   - servers:
584   -
585   - shutdown of type Server -> One
586   - is_down of type Server -> Bool
587   -
588   - They are defined in 'predefined.anubis' (together with the type 'Server').
589   -
590   -
591   -public define Start_Web_Sites_Result
592   - start_web_sites
593   - (
594   - Word32 ip_address, // the IP address shared by the web sites
595   - Word32 http_port, // usually: 80
596   - Word32 https_port, // usually: 443
597   - String ssl_certificate_common_name,
598   - List(Web_Site) web_sites, // web sites to be started
599   - (One) -> Bool shutdown_required
600   - ).
601   -
602   - 'ip_address' is the IP address on which the two servers listen. If you put 0, the
603   - servers listen on all the IP addresses of the machine. This may be useful if the
604   - machine has several network interfaces.
605   -
606   - 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
607   - loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
608   - certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
609   - and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
610   -
611   - Notice that the number of servers is always 2, regardless of the number of web sites
612   - you are starting.
613   -
614   - The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
615   - servers from within the web site (typically the administration part). The servers will
616   - shutdown as soon as this variable contains 'true'. So you must provide a variable
617   - containing 'false' otherwise your servers will not run. You may also use the primitive
618   - 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
619   -
620   -
621   -
622   -
623   -
624   -
625   - * (3) The HTML interface.
626   -
627   - We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
628   - of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
629   - reactive and attractive on the client side.
630   -
631   -
632   - ** (3.1) Types used by the HTML interface.
633   -
634   - For easy reference, we gather below the definitions of all the types used by the HTML
635   - interface, and we comment them immediately.
636   -
637   -
638   -public type HTML_Size:
639   - absolute(Int), // in pixels
640   - percentage(Int).
641   -
642   -
643   -public type Text_Option:
644   - size(Int), // size of character font to use
645   - font(String), // name of character font to use (such as "helvetica",...)
646   - color(RGB), // color to be used for characters
647   - italic,
648   - oblique,
649   - small_capitals,
650   - bold,
651   - underlined,
652   - left_justified,
653   - right_justified,
654   - justified, // justified on both sides
655   - line_through,
656   - nowrap,
657   - class(String), //CSS class
658   - id(String),
659   - style(String),
660   - title(String),
661   - attr(String, String).
662   -
663   -public define Text_Option
664   - tooltip(String s) = title(s).
665   -
666   - A list of 'Text_Option' must be given with each text you want to put in your page.
667   -
668   - This indicate the way of reading text.
669   -public type Reading_Way:
670   - ltr, //the text is readable from "Left To Right" like english
671   - rtl. //the text is readable from "Right To Left" like arabic
672   -
673   -
674   -// Following types are define to prohib any arguments order error
675   -
676   -/**
677   - * String value for 'id' attribut
678   - */
679   -public type HtmlId:
680   - htmlId(String id).
681   -
682   -/**
683   - * String value for 'class' attribut
684   - */
685   -public type HtmlClass:
686   - htmlClass(String class).
687   -
688   -/**
689   - * Name used in arguments list when form is submitted.
690   - */
691   -public type WebArgName:
692   - wan(String name).
693   -
694   -/**
695   - * Value passed to arguments list when form is submitted (for RadioButton and CheckBox).
696   - */
697   -public type WebArgValue:
698   - wav(String value).
699   -
700   -/**
701   - * Initial value of an input field.
702   - */
703   -public type InitialValue:
704   - init(String value).
705   -
706   -public define Printable_tree [HtmlId x . Printable_tree y] = str_pt(x.id, y).
707   -public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y).
708   -public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y).
709   -public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y).
710   -public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y).
711   -
712   -
713   -public type HtmlEvents:
714   - // Form element events
715   - onchange,
716   - onsubmit,
717   - onreset,
718   - onselect,
719   - onblur,
720   - onfocus,
721   - // Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
722   - onkeydown,
723   - onkeypress,
724   - onkeyup,
725   - // Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
726   - onclick,
727   - ondblclick,
728   - onmousedown,
729   - onmousemove,
730   - onmouseout,
731   - onmouseover,
732   - onmouseup.
733   -
734   -public type CoreAttrs:
735   - id (String),
736   - class (String),
737   - style (String),
738   - title (String),
739   - lang (String),
740   - dir (Reading_Way),
741   - accesskey (Word8),
742   - tabindex (Int),
743   - attr (String, String),
744   - event (HtmlEvents, String).
745   -
746   -public define CoreAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false").
747   -
748   -public type InputAttrs:
749   - class (String),
750   - style (String),
751   - title (String),
752   - lang (String),
753   - dir (Reading_Way),
754   - accesskey (Word8),
755   - tabindex (Int),
756   - attr (String, String),
757   - event (HtmlEvents, String).
758   -
759   -public define InputAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false").
760   -
761   -public define CoreAttrs
762   - tooltip(String s) = title(s).
763   -public define InputAttrs
764   - tooltip(String s) = title(s).
765   -
766   -public type I18n:
767   - lang (String),
768   - dir (Reading_Way).
769   -
770   -//public type DIV_Option:
771   -// id (String),
772   -// class (String),
773   -// style (String),
774   -// tooltip (String),
775   -// lang (String),
776   -// dir (Reading_Way),
777   -// attr (String, String),
778   -// event (HtmlEvents, String).
779   -//
780   -
781   - A list of 'DIV_Option' must be given with each DIV you want to put in your page.
782   -
783   -define String event_name
784   - (
785   - HtmlEvents e
786   - ) =
787   - if e is
788   - {
789   - // Form element events
790   - onchange then "onchange",
791   - onsubmit then "onsubmit",
792   - onreset then "onreset",
793   - onselect then "onselect",
794   - onblur then "onblur",
795   - onfocus then "onfocus",
796   - // Keyboard events
797   - onkeydown then "onkeydown",
798   - onkeypress then "onkeypress",
799   - onkeyup then "onkeyup",
800   - // Mouse events
801   - onclick then "onclick",
802   - ondblclick then "ondblclick",
803   - onmousedown then "onmousedown",
804   - onmousemove then "onmousemove",
805   - onmouseout then "onmouseout",
806   - onmouseover then "onmouseover",
807   - onmouseup then "onmouseup"
808   - }.
809   -
810   -public type Table_Option:
811   - background_color(RGB), // applied to all cells in the table
812   - background_image(String url),
813   - border(Int width_of_outer_edge, // if not present, all values are 0
814   - Int width_of_top_of_relief,
815   - Int width_of_inner_edge,
816   - RGB border_color),
817   - width(Int), // sets a minimal width for the table
818   - percentage_width(Int),
819   - attr(String name, String value),
820   - class(String class_name).
821   -
822   -
823   -public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
824   -
825   -
826   - A list of 'Table_Option' must be given with each table.
827   -
828   -
829   -public type BackgroundOption:
830   - repeat, // repeat the background in both directions
831   - repeat_horizontal, // repeat the background only horizontally
832   - repeat_vertical, // repeat the background only verticall
833   - no_repeat, // don't repeat the background
834   - center.
835   -
836   -
837   -public type Cell_Option:
838   - core_attrs(List(CoreAttrs)),
839   - left, // put the content of the cell on the left
840   - h_center, // center the content of the cell horizontally
841   - right, // put the content of the cell on the right
842   - top, // put the content of the cell upwards
843   - v_center, // center the content of tye cell vertically,
844   - bottom, // put the content of the cell downwards
845   - base_line, // align the content vertically according to base lines
846   - background_color(RGB),
847   - background_image(String url, BackgroundOption),
848   - width(Int), // sets a minimal width for the cell
849   - percentage_width(Int),
850   - height(Int), // sets a minimal height for the cell
851   - columns(Int), // lets the cell span over several columns
852   - rows(Int), // lets the cell span over several rows
853   - nowrap, // do not allow text wrapping within the cell
854   - class(String class_name).
855   -
856   - A list of 'Cell_Option' must be given with each cell and each row in a table. Options
857   - given with a row apply to all the cells in the row, but are superseded by options given
858   - with cells, which apply only to the cell they are given with.
859   -
860   -
861   -public type HTML_Cell($T):
862   - cell(List(Cell_Option) options, $T content).
863   -
864   -public type HTML_Header_Cell($T):
865   - header_cell(List(Cell_Option) options, $T content).
866   -
867   - The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
868   - depending on where you put your table (within a form or not within a form). For your
869   - convenience, we define the following particular case:
870   -
871   -public define HTML_Cell($T)
872   - cell
873   - (
874   - $T content
875   - ) =
876   - cell([],content).
877   -
878   -public define HTML_Header_Cell($T)
879   - header_cell
880   - (
881   - $T content
882   - ) =
883   - header_cell([],content).
884   -
885   -
886   -public type HTML_Row($T):
887   - row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
888   -
889   -public type HTML_Header_Row($T):
890   - empty,
891   - header_row(List(Cell_Option) options, List(HTML_Header_Cell($T)) cells).
892   -
893   -public type HTML_Footer_Row($T):
894   - empty,
895   - footer_row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
896   -
897   - Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
898   -
899   -public define HTML_Row($T)
900   - row
901   - (
902   - List(HTML_Cell($T)) cells
903   - ) =
904   - row([],cells).
905   -
906   -public define HTML_Row($T)
907   - row
908   - (
909   - HTML_Cell($T) cell
910   - ) =
911   - row([],[cell]).
912   -
913   -public define HTML_Header_Row($T)
914   - header_row
915   - (
916   - List(HTML_Header_Cell($T)) cells
917   - ) =
918   - header_row([],cells).
919   -
920   -public define HTML_Header_Row($T)
921   - header_row
922   - (
923   - HTML_Header_Cell($T) cell
924   - ) =
925   - header_row([],[cell]).
926   -
927   -public type Actioner_Connection:
928   - same, // use same type of connection as current page
929   - http, // use non secured connection
930   - https. // use secured connection
931   -
932   -public type Other_Window_Option:
933   - resizable, // the new window may be resized by the client
934   - scrollbars, // the new window has scrollbars
935   - width(Int), // the new window has the specified width
936   - height(Int). // the new window has the specified height
937   -
938   -public type Actioner_Target:
939   - same,
940   - same (String label),
941   - other(String window_name, List(Other_Window_Option)).
942   -
943   -public type Actioner_Aspect:
944   - link (List(Text_Option), String text, Maybe(String) name),// hypertext link
945   - img_link (String img_url, String alt_text),
946   - push_button (List(CoreAttrs),String text),
947   - button (String url_off, String url_on), // rollover button
948   - button (String url_off, String url_on, Int w, Int h), // idem with size
949   - submit (List(CoreAttrs),String text),
950   - immediate_selector (List(CoreAttrs),WebArgName name, Int size, List((WebArgValue,String)) choices),
951   - immediate_selector (List(CoreAttrs),WebArgName name, Int size, List((WebArgValue,String)) choices, InitialValue selected).
952   -
953   -
954   -
955   -public type Actioner_Local_Action:
956   - close_window.
957   -
958   -
959   -public define Actioner_Aspect
960   - link
961   - (
962   - List(Text_Option) options,
963   - String text
964   - ) =
965   - link(options, text, failure).
966   -
967   -public define Actioner_Aspect
968   - link
969   - (
970   - String text
971   - ) =
972   - link([], text, failure).
973   -
974   -
975   -public define Actioner_Aspect
976   - link
977   - (
978   - List(Text_Option) options,
979   - Int i
980   - ) =
981   - link(options,to_decimal(i), failure).
982   -
983   -public define Actioner_Aspect
984   - link
985   - (
986   - Int i
987   - ) =
988   - link([], i).
989   -
990   -public define Actioner_Aspect
991   - button
992   - (
993   - String url_img
994   - ) =
995   - button(url_img,url_img).
996   -
997   - Actioners are explained in details below.
998   -
999   -
1000   -public type TextAreaOption:
1001   - input_attrs(List(InputAttrs)),
1002   - disabled,
1003   - read_only,
1004   - wrap_lines.
1005   -
1006   -public type CSS_Style:
1007   - text_options(List(Text_Option)).
1008   -
1009   -public type CSS_File:
1010   - css_file(String file_name).
1011   -
1012   -public type JS_Attribute:
1013   - attr (String, String).
1014   -
1015   -public type JS_File:
1016   - js_file(String file_name,
1017   - List(JS_Attribute) attributes).
1018   -
1019   -public type Script:
1020   - script( String script_type,
1021   - String content).
1022   -
1023   -public define JS_File
1024   - js_file
1025   - (
1026   - String file_name
1027   - ) =
1028   - js_file(file_name, []).
1029   -
1030   -public type HTML_Meta:...
1031   -
1032   -public type HTML_Off_Form:...
1033   -
1034   -public type HTML_Head_Tag:
1035   - meta(HTML_Meta),
1036   - title(String),
1037   - js(JS_File),
1038   - js_inline(Script),
1039   - css(CSS_File),
1040   - css_inline(String css_styles) /* Note: <style> and </style> tags are not necessaries */
1041   - .
1042   -
1043   - /* It contains
1044   - * - a List of HTML_Head_Tag representing necessaries head tags for the content. ex: jquery js files, js script, css specific styles, ...
1045   - * - a HTML_Off_Form representing the HTML code for the part of the page we want to display
1046   - */
1047   -public type HTML_Partial_Content:
1048   - partial_content
1049   - (
1050   - List(HTML_Head_Tag),
1051   - HTML_Off_Form
1052   - )
1053   - .
1054   -
1055   -
1056   -public define List(HTML_Head_Tag)
1057   - to_HTML_Head_Tag
1058   - (
1059   - List(CSS_File) css_file_list
1060   - )=
1061   - map((CSS_File css_f) |-> css(css_f), css_file_list).
1062   -
1063   -public define List(HTML_Head_Tag)
1064   - to_HTML_Head_Tag
1065   - (
1066   - List(JS_File) js_file_list
1067   - )=
1068   - map((JS_File js_f) |-> js(js_f), js_file_list).
1069   -
1070   -public type HTML_In_Form:
1071   - literal_pt (Printable_tree),
1072   - literal (String),
1073   - sequence (List(HTML_In_Form) items),
1074   - text (List(Text_Option), String the_text),
1075   - preformated (List(Text_Option), String),
1076   - paragraph (List(Text_Option), HTML_In_Form content),
1077   - image (List(CoreAttrs), String url, String alternate),
1078   - image (List(CoreAttrs), String url, String alternate, Int width, Int height),
1079   - table (List(Table_Option), HTML_Header_Row(HTML_In_Form), List(HTML_Row(HTML_In_Form)), HTML_Footer_Row(HTML_In_Form)),
1080   - center (HTML_In_Form),
1081   - mail_to (String email, HTML_In_Form element),
1082   - scroller (Int width, Int height,
1083   - Int content_width, Int content_height,
1084   - HTML_In_Form content),
1085   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
1086   - String action_name, List((String,String)) extra_ops,
1087   - List(Actioner_Local_Action)),
1088   - foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
1089   - foreign_link (List(Text_Option), String url),
1090   - foreign_link (List(Text_Option), String url, String name),
1091   - private_download (String abs_path, String name, String extra_ext,
1092   - Maybe((String,List((String,String)))) action),
1093   - text_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
1094   - text_input_ro (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
1095   - password_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
1096   - text_area (List(TextAreaOption), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width, Int height),
1097   - file_upload (List(InputAttrs), String label, HtmlId id, WebArgName name, Int width),
1098   - selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List(String) choices),
1099   - selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List(String) choices, InitialValue selected),
1100   - // List((String,String)) = List((code,name)) where :
1101   - // code is the web-arg value
1102   - // name appears in selector
1103   - selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List((WebArgValue,String)) choices),
1104   - selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List((WebArgValue,String)) choices, InitialValue selected),
1105   - radio_button (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
1106   - radio_button_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
1107   - check_box (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
1108   - check_box_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
1109   - div (List(CoreAttrs), HTML_In_Form content),
1110   - div_empty (List(CoreAttrs)),
1111   - hidden (HtmlId id, WebArgName name, InitialValue init).
1112   -
1113   -
1114   - 'HTML_In_Form' defines all the elements you may put within a form. We define a
1115   - convenience function:
1116   -
1117   -public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
1118   -
1119   -public define HTML_In_Form image(String url) = image([], url, url).
1120   -
1121   -public define HTML_In_Form image(String url, Int width, Int height) = image([], url, url, width, height).
1122   -
1123   -public define HTML_In_Form
1124   - foreign_link
1125   - (
1126   - Int tsize,
1127   - String url,
1128   - String name
1129   - ) =
1130   - foreign_link([size(tsize)],url,name).
1131   -
1132   -public define HTML_In_Form
1133   - actioner
1134   - (
1135   - Actioner_Connection conn,
1136   - Actioner_Target targ,
1137   - Actioner_Aspect asp,
1138   - String action_name,
1139   - List((String,String)) extra_ops
1140   - ) =
1141   - actioner(conn,targ,asp,action_name,extra_ops,[]).
1142   -
1143   -public define HTML_In_Form
1144   - actioner
1145   - (
1146   - Actioner_Connection conn,
1147   - Actioner_Target targ,
1148   - Actioner_Aspect asp,
1149   - ) =
1150   - actioner(conn, targ, asp, "", [], []).
1151   -
1152   -
1153   -public define HTML_In_Form
1154   - text_area
1155   - (
1156   - WebArgName name,
1157   - InitialValue init,
1158   - Int width,
1159   - Int height
1160   - ) =
1161   - text_area([], "", htmlId(""), name,init,width,height).
1162   -
1163   -public define HTML_In_Form
1164   - text_area
1165   - (
1166   - List(TextAreaOption) options,
1167   - WebArgName name,
1168   - InitialValue init,
1169   - Int width,
1170   - Int height
1171   - ) =
1172   - text_area(options, "", htmlId(""), name,init,width,height).
1173   -
1174   -public define HTML_In_Form
1175   - table
1176   - (
1177   - List(HTML_Row(HTML_In_Form)) rows
1178   - ) =
1179   - table([],empty,rows,empty).
1180   -
1181   -public define HTML_In_Form
1182   - table
1183   - (
1184   - List(Table_Option) options,
1185   - List(HTML_Row(HTML_In_Form)) rows
1186   - ) =
1187   - table(options,empty,rows,empty).
1188   -
1189   -public define HTML_In_Form
1190   - table
1191   - (
1192   - HTML_Header_Row(HTML_In_Form) h_row,
1193   - List(HTML_Row(HTML_In_Form)) rows
1194   - ) =
1195   - table([],h_row,rows,empty).
1196   -
1197   -public define HTML_In_Form
1198   - table
1199   - (
1200   - List(Table_Option) options,
1201   - HTML_Header_Row(HTML_In_Form) h_row,
1202   - List(HTML_Row(HTML_In_Form)) rows
1203   - ) =
1204   - table(options,h_row,rows,empty).
1205   -
1206   -public define HTML_In_Form
1207   - private_download
1208   - (
1209   - String abs_path,
1210   - String name,
1211   - String extra_ext
1212   - ) =
1213   - private_download(abs_path,name,extra_ext,failure).
1214   -
1215   -public define HTML_In_Form
1216   - private_download
1217   - (
1218   - String abs_path,
1219   - String name,
1220   - String extra_ext,
1221   - String action_name,
1222   - List((String,String)) args
1223   - ) =
1224   - private_download(abs_path,name,extra_ext,success((action_name,args))).
1225   -
1226   -public define HTML_In_Form
1227   - text
1228   - (
1229   - String s
1230   - ) =
1231   - text([],s).
1232   -
1233   -public define HTML_In_Form
1234   - radio_button (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
1235   - = radio_button (options, label_text, htmlId(n.name), n, value, checked).
1236   -
1237   -public define HTML_In_Form
1238   - radio_button_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
1239   - = radio_button_r (options, label_text, htmlId(n.name), n, value, checked).
1240   -
1241   -public define HTML_In_Form
1242   - check_box (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
1243   - = check_box (options, label_text, htmlId(n.name), n, value, checked).
1244   -
1245   -public define HTML_In_Form
1246   - check_box_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
1247   - = check_box_r (options, label_text, htmlId(n.name), n, value, checked).
1248   -
1249   -public define HTML_In_Form
1250   - file_upload (WebArgName name, Int width)
1251   - = file_upload([], "", htmlId(""), name, width).
1252   -
1253   -public define HTML_In_Form
1254   - hidden (WebArgName name, InitialValue init)
1255   - = if name is wan(web_name) then
1256   - hidden(htmlId(web_name), name, init).
1257   -
1258   -public type HTML_Body:...
1259   -
1260   -public type HTML_Off_Form:
1261   - literal_pt (Printable_tree),
1262   - literal (String),
1263   - sequence (List(HTML_Off_Form) items),
1264   - text (List(Text_Option), String the_text),
1265   - preformated (List(Text_Option), String),
1266   - paragraph (List(Text_Option), HTML_Off_Form content),
1267   - image (List(CoreAttrs), String url, String alternate),
1268   - image (List(CoreAttrs), String url, String alternate, Int width, Int height),
1269   - table (List(Table_Option), HTML_Header_Row(HTML_Off_Form), List(HTML_Row(HTML_Off_Form)), HTML_Footer_Row(HTML_Off_Form)),
1270   - center (HTML_Off_Form),
1271   - mail_to (String email, HTML_Off_Form element),
1272   - scroller (Int width, Int height,
1273   - Int content_width, Int content_height,
1274   - HTML_Off_Form content),
1275   - fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
1276   - fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
1277   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
1278   - String action_name, List((String,String)) extra_ops,
1279   - List(Actioner_Local_Action)),
1280   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
1281   - String action_name, List((String,String)) extra_ops,
1282   - List(Actioner_Local_Action), String form_name),
1283   - foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
1284   - foreign_link (List(Text_Option), String url),
1285   - foreign_link (List(Text_Option), String url, String name),
1286   - private_download (String abs_path, String name, String extra_ext,
1287   - Maybe((String,List((String,String)))) action),
1288   - label (String name),
1289   - form (HtmlId form_id, List(CoreAttrs), HTML_In_Form content),
1290   - form (HtmlId form_id, List(CoreAttrs),
1291   - String action_name, List((String,String)) extra_ops,
1292   - HTML_In_Form content),
1293   - div (List(CoreAttrs), HTML_Off_Form content),
1294   - div_empty (List(CoreAttrs)),
1295   - iframe (List(CoreAttrs),
1296   - List(CSS_Style) /*styles*/,
1297   - List(CSS_File) /*css_files*/,
1298   - List(JS_File) /*js_files*/,
1299   - HTML_Body /*body*/).
1300   -
1301   - 'HTML_Off_Form' defines all the elements you may put outside any form.
1302   -
1303   -
1304   -public define HTML_Off_Form image(String url) = image([], url, url).
1305   -
1306   -public define HTML_Off_Form image(String url, Int width, Int height) = image([], url, url, width, height).
1307   -
1308   -public define HTML_Off_Form
1309   - form
1310   - (
1311   - String form_name,
1312   - List(CoreAttrs) options,
1313   - HTML_In_Form content
1314   - ) =
1315   - form(htmlId(form_name), options, content).
1316   -
1317   -public define HTML_Off_Form
1318   - form
1319   - (
1320   - String form_name,
1321   - List(CoreAttrs) options,
1322   - String action_name,
1323   - List((String,String)) extra_ops,
1324   - HTML_In_Form content
1325   - ) =
1326   - form(htmlId(form_name), options, action_name, extra_ops, content).
1327   -
1328   -
1329   -public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
1330   -public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
1331   - = fixed_size_2(width,height,name_of_HTML_file).
1332   -
1333   -
1334   -public define HTML_Off_Form
1335   - foreign_link
1336   - (
1337   - Int tsize,
1338   - String url,
1339   - String name
1340   - ) =
1341   - foreign_link([size(tsize)],url,name).
1342   -
1343   -
1344   -public define HTML_Off_Form
1345   - actioner
1346   - (
1347   - Actioner_Connection conn,
1348   - Actioner_Target targ,
1349   - Actioner_Aspect asp,
1350   - String action_name,
1351   - List((String,String)) extra_ops
1352   - ) =
1353   - actioner(conn,targ,asp,action_name,extra_ops,[]).
1354   -
1355   -public define HTML_Off_Form
1356   - table
1357   - (
1358   - List(HTML_Row(HTML_Off_Form)) rows
1359   - ) =
1360   - table([],empty,rows,empty).
1361   -
1362   -public define HTML_Off_Form
1363   - table
1364   - (
1365   - List(Table_Option) options,
1366   - List(HTML_Row(HTML_Off_Form)) rows
1367   - ) =
1368   - table(options,empty,rows,empty).
1369   -
1370   -public define HTML_Off_Form
1371   - table
1372   - (
1373   - HTML_Header_Row(HTML_Off_Form) h_row,
1374   - List(HTML_Row(HTML_Off_Form)) rows
1375   - ) =
1376   - table([],h_row,rows,empty).
1377   -
1378   -public define HTML_Off_Form
1379   - table
1380   - (
1381   - List(Table_Option) options,
1382   - HTML_Header_Row(HTML_Off_Form) h_row,
1383   - List(HTML_Row(HTML_Off_Form)) rows
1384   - ) =
1385   - table(options,h_row,rows,empty).
1386   -
1387   - We add two convenience functions for 'row'. The reason why we add two functions, one
1388   - for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
1389   - arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
1390   - the arguments of the function do not refer to any of the types defined here.
1391   -
1392   -public define HTML_Row(HTML_In_Form)
1393   - row
1394   - (
1395   - HTML_In_Form content
1396   - ) =
1397   - row([],[cell([],content)]).
1398   -
1399   -public define HTML_Row(HTML_Off_Form)
1400   - row
1401   - (
1402   - HTML_Off_Form content
1403   - ) =
1404   - row([],[cell([],content)]).
1405   -
1406   -
1407   -public define HTML_Off_Form
1408   - private_download
1409   - (
1410   - String abs_path,
1411   - String name,
1412   - String extra_ext
1413   - ) =
1414   - private_download(abs_path,name,extra_ext,failure).
1415   -
1416   -
1417   -public define HTML_Off_Form
1418   - private_download
1419   - (
1420   - String abs_path,
1421   - String name,
1422   - String extra_ext,
1423   - String action_name,
1424   - List((String,String)) args
1425   - ) =
1426   - private_download(abs_path,name,extra_ext,success((action_name,args))).
1427   -
1428   -public define HTML_Off_Form
1429   - text
1430   - (
1431   - List(Text_Option) lto,
1432   - Int i
1433   - ) =
1434   - text(lto,to_decimal(i)).
1435   -
1436   -
1437   -public define HTML_Off_Form
1438   - text
1439   - (
1440   - Int i
1441   - ) =
1442   - text([],i).
1443   -
1444   -public define HTML_Off_Form
1445   - text
1446   - (
1447   - String s
1448   - ) =
1449   - text([],s).
1450   -
1451   - - Cell a gap between two other cells :
1452   -
1453   -public define HTML_Cell(HTML_Off_Form)
1454   - width_gap
1455   - (
1456   - Int w
1457   - ) =
1458   - cell([width(w)],text([],"")).
1459   -
1460   -public define HTML_Cell(HTML_In_Form)
1461   - width_gap
1462   - (
1463   - Int w
1464   - ) =
1465   - cell([width(w)],text([],"")).
1466   -
1467   -
1468   - - Row a gap between two other rows :
1469   -
1470   -public define HTML_Row(HTML_Off_Form)
1471   - height_gap
1472   - (
1473   - Int h
1474   - ) =
1475   - row([],[cell([height(h)],text([],""))]).
1476   -
1477   -public define HTML_Row(HTML_In_Form)
1478   - height_gap
1479   - (
1480   - Int h
1481   - ) =
1482   - row([],[cell([height(h)],text([],""))]).
1483   -
1484   -
1485   - Notice that the two types have alternatives in common (same name, same arguments types,
1486   - up to the value of the parameter $T), which correspond to elements which may be put
1487   - anywhere in the page.
1488   -
1489   -
1490   -public define HTML_Partial_Content
1491   - partial_content
1492   - (
1493   - HTML_Off_Form content
1494   - )=
1495   - partial_content([], content).
1496   -
1497   -public define HTML_Partial_Content
1498   - partial_content
1499   - (
1500   - List(HTML_Head_Tag) tags,
1501   - HTML_Partial_Content p_content
1502   - )=
1503   - if p_content is partial_content(ptags, html) then
1504   - partial_content(tags + ptags, html).
1505   -
1506   -define HTML_Partial_Content
1507   - list_to_pcontent
1508   - (
1509   - List(HTML_Partial_Content) l_p_content,
1510   - List(HTML_Head_Tag) so_far_tags,
1511   - List(HTML_Off_Form) so_far_html
1512   - )=
1513   - if l_p_content is
1514   - {
1515   - [] then partial_content(so_far_tags, sequence(reverse(so_far_html))),
1516   - [h . t] then
1517   - //extract resurgent type and symbols of h
1518   - if h is partial_content(tags, html) then
1519   - list_to_pcontent(t, so_far_tags + tags, [html . so_far_html])
1520   - }.
1521   -
1522   -public define HTML_Partial_Content
1523   - partial_content
1524   - (
1525   - List(HTML_Partial_Content) l_p_content
1526   - )=
1527   - list_to_pcontent(l_p_content, [], []).
1528   -
1529   -
1530   -
1531   -define String
1532   - format
1533   - (
1534   - List(Text_Option) l
1535   - ).
1536   -
1537   -
1538   -
1539   -
1540   -define Printable_tree
1541   - format_css_styles
1542   - (
1543   - List(CSS_Style) l
1544   - ) =
1545   - if l is
1546   - {
1547   - [ ] then [ ],
1548   - [h . t] then
1549   - [ if h is
1550   - {
1551   - text_options(tos) then
1552   - [" body, span, p { ", format(tos), " }\n" ]
1553   - }
1554   - . format_css_styles(t)]
1555   - }.
1556   -
1557   -
1558   -
1559   -public type HTML_Meta:
1560   - keywords (List(String)),
1561   - refresh (Actioner_Connection connection,
1562   - Actioner_Target target,
1563   - String action_name,
1564   - Int delay), // in seconds
1565   - refresh (String url, Int delay), // in seconds
1566   - meta (String name, String content),
1567   - http_equiv (String name, String content),
1568   - generic_meta (List((String,String))),
1569   - literal (String).
1570   -
1571   - Meta tags are put in the 'head' of the HTML page.
1572   -
1573   -
1574   -public type Body_Option:
1575   - core_attrs(List(CoreAttrs)),
1576   - background_color (RGB),
1577   - background_image (String url),
1578   - background_image (String url, List(BackgroundOption)).
1579   -
1580   -public type HTML_Body:
1581   - body(List(Body_Option) options, HTML_Off_Form content).
1582   -
1583   -
1584   -public type HTTP_Answer:
1585   - html_page (HTTP_Status /*http_status*/,
1586   - List(HTML_Head_Tag) /*html <head> tags*/,
1587   - HTML_Body /*body*/),
1588   - html_page (HTTP_Status /*http_status*/,
1589   - String /*title*/,
1590   - List(HTML_Meta) /*meta_tags*/,
1591   - List(CSS_Style) /*styles*/,
1592   - List(CSS_File) /*css_files*/,
1593   - List(JS_File) /*js_files*/,
1594   - List(Script) /*script*/,
1595   - HTML_Body /*body*/),
1596   - plain_text (HTTP_Status /*http_status*/,
1597   - String /*text*/),
1598   - custom_text (HTTP_Status /*http_status*/,
1599   - String /*mime_type*/,
1600   - String /*content*/),
1601   - custom_binary(HTTP_Status /*http_status*/,
1602   - String /*mime_type*/,
1603   - ByteArray /*content*/,
1604   - List(HTTP_header)/*other headers*/),
1605   - custom_tree (HTTP_Status /*http_status*/,
1606   - String /*mime_type*/,
1607   - Printable_tree /*content*/),
1608   - http_raw (Printable_tree),
1609   - html_content (HTTP_Status /*http_status*/,
1610   - HTML_Off_Form).
1611   -
1612   -
1613   -public define HTTP_Answer
1614   - html_page
1615   - (
1616   - String title,
1617   - List(HTML_Meta) metas,
1618   - HTML_Body body
1619   - ) =
1620   - html_page(http_ok, title,metas,[],[],[],[],body).
1621   -
1622   -public define HTTP_Answer
1623   - html_page
1624   - (
1625   - String title,
1626   - List(HTML_Meta) metas,
1627   - List(CSS_Style) styles,
1628   - HTML_Body body
1629   - ) =
1630   - html_page(http_ok, title, metas, styles, [], [], [], body).
1631   -
1632   - 'HTTP_Answer' represents the final product of the construction of a web page.
1633   -
1634   -
1635   -
1636   -
1637   - *** (3.2) ``in form'' versus ``off form''.
1638   -
1639   - There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
1640   - of them may have a content, which is yet another HTML element (or several). Hence, it
1641   - is meaningful to say that an element is 'within' another one. Now, putting any element
1642   - within any other one may be meaningless. For example, an input element must be put
1643   - within a form (otherwise, it is useless), and a from within another form has no precise
1644   - meaning (and is forbidden by the HTML specification).
1645   -
1646   - Actually, the main criterium is ``within a form or not within a form''. So, HTML
1647   - elements in a given page are separated into two categories: those who are within a
1648   - form, and the others. Nevertheless, there are elements which may belong to both
1649   - categories, like images and texts. We want to make use of the strong typing mecanism of
1650   - Anubis in order to forbid non meaningful placement of elements.
1651   -
1652   - The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
1653   - 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
1654   - and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
1655   - but the two types are not cross recursive. This is the reason why it is impossible to
1656   - put a form within a form. In order to construct a web page, you essentially have to
1657   - produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
1658   -
1659   - In practice, you don't have to worry so much about these two types, because elements
1660   - which may be put anywhere are constructed for both types by functions with the same
1661   - name and the same arguments. Hence, for both types, you just write the same thing. You
1662   - are warned by the compiler only when you try to put an element at a place it is not
1663   - allowed.
1664   -
1665   -
1666   -
1667   - *** (3.3) Defining your own style.
1668   -
1669   - We provide generic tools for constructing HTML elements. However, your web site needs
1670   - to have a ``style''.
1671   -
1672   - To that end, you need to write down a set of ``styling functions'', using the tools
1673   - defined here. These styling functions allow the introduction of your colors and other
1674   - visual characteristics into the constructed elements once and for all. For example, you
1675   - may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
1676   - some 'text_color'. You may write something like this:
1677   -
1678   - define RGB text_color = rgb(10,40,40).
1679   -
1680   - define HTML_Off_Form
1681   - text
1682   - (
1683   - String the_text
1684   - ) =
1685   - text([font("helvetica"),size(14),color(text_color)],
1686   - the_text).
1687   -
1688   - (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
1689   - page, you just write:
1690   -
1691   - text("... some text ...")
1692   -
1693   - and you don't have to provide the font, size and color for each text. If you want to
1694   - have several styles of text presentation, you just write several sets of such
1695   - convenience functions. This also suggests a trick. You may want for example different
1696   - colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
1697   - defining two functions as above, with the same name and same argument type, but
1698   - returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
1699   -
1700   - If this preliminary work is well done, you will not waste your time later when you
1701   - concentrate on the actual informational content of your pages.
1702   -
1703   - This general principle should be applied to all sorts of elements. This is the best
1704   - thing to do in order to separate the functions defining the visual style from the
1705   - functions defining the informational content itself, so that changing the style without
1706   - changing the content becomes easy. This is also the best way for having a clean and
1707   - easily readable source for your web site.
1708   -
1709   -
1710   -
1711   -
1712   -
1713   - *** (3.4) Actioners and forms.
1714   -
1715   - We have gathered several notions from HTML into that of an 'actioner'. An actioner is
1716   - an HTML element which opens a connection to our server when clicked upon. Actioners may
1717   - have different visual aspects. They may look like hypertext links or like buttons
1718   - (rollovers), or even like selectors (with immediate action). In any case, their
1719   - behavior is the same: they open a connection to our server, and send a set of 'web
1720   - arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
1721   - the action to be performed, and the others should be considered as operands for this
1722   - action. Actually, the precise behavior of the actioner has several variants.
1723   -
1724   - The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
1725   - type 'Actioner_Connection' above.
1726   -
1727   - You must also choose where the answer must be rendered. This may be in the same window
1728   - or in another window (or frame). If it is in another window, the name of that window
1729   - must be given. If the window does not exist, the browser will create it. Optionally,
1730   - you may give the dimensions of the new window and other characteristics. See the type
1731   - 'Actioner_Target' above.
1732   -
1733   - The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
1734   - case of a rollover button, you provide the URLs of two images (of the same size)
1735   - representing the button:
1736   -
1737   - url_off: to be used when the mouse is not over the button,
1738   - url_on: to be used when the mouse is over the button.
1739   -
1740   - You can also create rollover buttons without creating images. Just use the second
1741   - alternative named 'button'. The server creates the images automatically.
1742   -
1743   - The purpose of forms is just to give operands to actioners. If the actioner is placed
1744   - within a form, all the input elements which are within this form provide operands to
1745   - the actioner (except sometimes when they are not set by the client). If it is not put
1746   - within a form, the actioner gets no operand, except if the name of a form is explicitly
1747   - given, in which case the actioner gets all the inputs from that form as
1748   - operands. Furthermore, you may want to give extra operands to the actioner. This may be
1749   - useful for separating families of actioners with the same action name. Extra operands
1750   - 'name=value' must be given in the form of pairs '(name,value)'.
1751   -
1752   - Notice that the name of a form may be used by an actioner which is off the form, so as
1753   - to get the operands provided by this form. Also notice that several actioners may refer
1754   - to the same form, being either in the form, or referring to the form from the
1755   - outside. These actioners simply get the same set of operands, even if they correspond
1756   - to distinct actions.
1757   -
1758   - Input elements may be put only within a form.
1759   -
1760   -
1761   -
1762   - *** (3.5) Local popup.
1763   -
1764   - This element looks like a link or a rollover button. When this button is clicked upon,
1765   - a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
1766   - page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
1767   - particular, clicking on the button does not open any connection. This is why it is
1768   - called 'local'. The arguments have the following roles:
1769   -
1770   - Actioner_Aspect aspect of the button (same semantics as for actioners)
1771   - content content of the popup window
1772   - x, y, position of the popup window on the HTML page (not relative
1773   - to the button but to the page itself)
1774   - title title of the popup window
1775   - color color of the title bar and close button in the popup window.
1776   - A lightened version of this color is used for the background
1777   - of the popup window.
1778   - width width of the title bar
1779   -
1780   -
1781   -
1782   -
1783   -
1784   - --- That's all for the public part ! --------------------------------------------------
1785   -
1786   -
1787   -
1788   -
1789   -
1790   - ----------------------------------- Table of Contents ---------------------------------
1791   -
1792   - *** [1] States.
1793   - *** [1.1] Saving and retrieving states.
1794   - *** [1.2] Deleting out of date states.
1795   -
1796   - *** [2] Tools.
1797   - *** [2.1] Directories.
1798   - *** [2.2] Secondary documents.
1799   -
1800   - *** [3] Managing web arguments.
1801   - *** [3.1] Prefixing web arguments names.
1802   - *** [3.2] Separating web arguments.
1803   - *** [3.3] Applying an action.
1804   -
1805   - *** [4] Web site descriptions and the 'awp handlers'.
1806   - *** [4.1] The type 'Web_Site'.
1807   - *** [4.2] Making a web site description.
1808   - *** [4.3] Starting the servers.
1809   -
1810   - *** [5] HTML Formating.
1811   - *** [5.1] The type 'HTML_Any($T)'.
1812   - *** [5.2] Formating a color.
1813   - *** [5.3] Creating buttons.
1814   - *** [5.4] Formating an actioner.
1815   - *** [5.5] Formating a private download link.
1816   - *** [5.6] Formating rows and cells in a table.
1817   - *** [5.7] Formating elements which may be put anywhere.
1818   - *** [5.8] Formating 'in form' elements.
1819   - *** [5.9] Formating 'off form' elements.
1820   - *** [5.10] Formating meta-tags.
1821   -
1822   - ---------------------------------------------------------------------------------------
1823   -
1824   -
1825   -
1826   -
1827   -public define String
1828   - doctype_w3c_header
1829   - =
1830   - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "+
1831   - "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".
1832   -
1833   -public define String
1834   - html_header
1835   - =
1836   - "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n".
1837   -
1838   -
1839   - *** [1] States.
1840   -
1841   - We have to define functions for saving a state, retrieving a state, deleting out of
1842   - date states. We need one such function per web site. The types of the first two
1843   - functions depend on the parameter $State. This is not the case of the third one. The
1844   - fact that the instance of $State is variable from one web sites to the other implies
1845   - rather subtle manipulations using full functionality.
1846   -
1847   -
1848   - *** [1.1] Saving and retrieving states.
1849   -
1850   - Each state is saved into a file on the server's disk (in the directory represented by
1851   - the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
1852   - state is saved together with a time stamp whose value is obtained by adding the current
1853   - time to the given timeout for states. The state receives a name obtained by hashing
1854   - (using sha1) the content of the file itself, and then encoding the hash with
1855   - 'web_arg_encode'. The name of the file into which the state is saved is the
1856   - concatenation of "s" and the name of the state.
1857   -
1858   -read CXM_web_arg_encode.anubis
1859   -
1860   - The tool below constructs the function which is able to save a state on the server's
1861   - disk.
1862   -
1863   -define ($State s) -> String // the function constructed returns the name of the state
1864   - make_save_state_function
1865   - (
1866   - Int timeout,
1867   - String state_directory
1868   - ) =
1869   - ($State s) |->
1870   - with time_stamp = now+timeout,
1871   - to_be_saved = (time_stamp,s),
1872   - state_name = web_arg_encode(sha1(s)),
1873   - if save(to_be_saved,state_directory+"/s"+state_name) is ok
1874   - then state_name
1875   - else (print("Cannot create state file in '"+state_directory+"'.\n"); "").
1876   -
1877   -
1878   - When a request arrives, we need to retrieve the previous state from the server's
1879   - disk. We receive the name of that state. If the state is out of date, the state file is
1880   - kept 3 days, and then deleted.
1881   -
1882   -type PreviousState($State):
1883   - not_found, // cannot retrieve the previous state
1884   - out_of_date($State), // the previous state is out of date
1885   - still_valid($State). // the previous state is still valid
1886   -
1887   -define (String state_name) -> PreviousState($State)
1888   - make_retrieve_state_function
1889   - (
1890   - String state_directory
1891   - ) =
1892   - (String state_name) |->
1893   - with file_path = state_directory+"/s"+state_name,
1894   - if (RetrieveResult((Int,$State)))retrieve(file_path) is ok(d)
1895   - then (
1896   - if d is (time_stamp,s) then
1897   - if time_stamp < now
1898   - then (
1899   - forget(remove(file_path));
1900   - out_of_date(s)
1901   - )
1902   - else still_valid(s) // state has been successfully retrieved
1903   - )
1904   - else not_found.
1905   -
1906   -
1907   -
1908   - *** [1.2] Deleting out of date states.
1909   -
1910   - We also need to delete states which are out of date and which will never be deleted by
1911   - the above method. This may be performed by a machine doing this periodically (say once
1912   - per states life time period).
1913   -
1914   -define (List(String) file_names) -> One
1915   - make_delete_out_of_date_states_function
1916   - (
1917   - Maybe($State) dummy,
1918   - String state_directory
1919   - ) =
1920   - (List(String) file_names) |-df->
1921   - if file_names is
1922   - {
1923   - [ ] then unique,
1924   - [h . t] then
1925   - with file_path = state_directory+"/"+h,
1926   - if (RetrieveResult((Int,$State)))retrieve(file_path) is ok(d)
1927   - then (
1928   - if d is (time_stamp,data) then
1929   - if time_stamp < now
1930   - then (forget(remove(file_path)); df(t))
1931   - else df(t)
1932   - )
1933   - else (forget(remove(file_path)); df(t))
1934   - }.
1935   -
1936   -
1937   - The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
1938   -
1939   - Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
1940   - function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
1941   - does not accept a parameter in the body of a function (here the parameter is required
1942   - by the use of 'retrieve') if this parameter does not appear in the type of the
1943   - function. This is because this would create ambiguities that no explicit typing may
1944   - ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
1945   - and if you compile this file, you will get a message like this one:
1946   -
1947   - Error in 'making_a_web_site.anubis', line 1300, column 7:
1948   - A definition may not contain parameters which are not present
1949   - in the declaration part (hidden parameters):
1950   - $State
1951   -
1952   - The type of the function constructed by 'make_delete_out_of_date_states_function' is
1953   - independant of the parameter $State. This is important because this allows to create
1954   - the list of such functions for all web sites. From this list, it is possible to call
1955   - the functions one after the other, so deleting out of date states for all web
1956   - sites. Actually, the next function receives a list of pairs (state_directory,function),
1957   - one for each web site.
1958   -
1959   -define One
1960   - delete_out_of_date_states // for all web sites
1961   - (
1962   - List((String, List(String) -> One)) directories_and_functions
1963   - ) =
1964   - if directories_and_functions is
1965   - {
1966   - [ ] then unique,
1967   - [h . t] then if h is (state_directory,function) then
1968   - function(directory_list(state_directory,"s*"));
1969   - delete_out_of_date_states(t)
1970   - }.
1971   -
1972   -
1973   - The above function must be called periodically in a separate virtual machine. The
1974   - period we have choosen is (rather logically) the life time of states itself. This may
1975   - be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
1976   - be really infinite, because the servers may be shutdown. Hence, our loop must test
1977   - (rather frequently; say every second) if the servers are down. If they are, the loop
1978   - must be exited.
1979   -
1980   -define One
1981   - delete_states_loop
1982   - (
1983   - List((String,List(String) -> One)) directories_and_functions,
1984   - Int timeout,
1985   - Int next_time,
1986   - Server http_server,
1987   - Server https_server,
1988   - (One) -> Bool shutdown_required
1989   - ) =
1990   - if shutdown_required(unique)
1991   - then (shutdown(http_server); shutdown(https_server))
1992   - else unique;
1993   - if (is_down(http_server) & is_down(https_server))
1994   - then unique
1995   - else if now > next_time
1996   - then
1997   - (
1998   - delete_out_of_date_states(directories_and_functions);
1999   - delete_states_loop(directories_and_functions,
2000   - timeout,
2001   - now+timeout,
2002   - http_server,
2003   - https_server,
2004   - shutdown_required)
2005   - )
2006   - else
2007   - (
2008   - sleep(1000); // sleep just one second and try again
2009   - delete_states_loop(directories_and_functions,
2010   - timeout,
2011   - next_time,
2012   - http_server,
2013   - https_server,
2014   - shutdown_required)
2015   - ).
2016   -
2017   - The above loop must be run in a separate virtual machine. This will be done just after
2018   - the two servers are started.
2019   -
2020   -
2021   -
2022   -
2023   -
2024   - *** [2] Tools.
2025   -
2026   - *** [2.1] Directories.
2027   -
2028   - We need a tool for creating directories (if needed).
2029   -
2030   - (This tool has been moved to 'tools/basis.anubis').
2031   -
2032   -
2033   -
2034   -
2035   - *** [2.2] Secondary documents.
2036   -
2037   - Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
2038   - from the current document, but only through an URL. For this reason, we implement a
2039   - mecanism for creating secondary documents on the fly. To that end we use the 'private
2040   - download' mecanism.
2041   -
2042   - A secondary document is formated by the same functions as the main document itself. The
2043   - next function takes an 'off form' element, creates the file containing the secondary
2044   - document in HTML format, and returns the URL at which the document will be available.
2045   -
2046   -define String
2047   - create_secondary_document
2048   - (
2049   - String sd, // site directory
2050   - String as, // authorization_secret
2051   - String sn, // state name
2052   - $T -> Printable_tree format_element,
2053   - $T content,
2054   - HTML_Size width
2055   - ) =
2056   - with private_download_directory = sd+"/private_download",
2057   - hash = web_arg_encode(sha1(content)),
2058   - file_content = (Printable_tree)
2059   - [doctype_w3c_header,
2060   - "<html><body><table ",
2061   - if width is
2062   - {
2063   - absolute(w) then ["width=\"",w-25],
2064   - percentage(w) then ["width=\"95%\""]
2065   - },"\"><tbody><tr><td align=right>",
2066   - format_element(content),
2067   - "</td></tr></tbody></table></body></html>"
2068   - ],
2069   - file_name = "sd"+hash+".html",
2070   - file_path = private_download_directory+"/"+file_name,
2071   - if write_to_file(file_path,file_content) is
2072   - {
2073   - cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
2074   - write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
2075   - ok then file_name+"?zauth="+
2076   - make_authorization(sd,as,private_download_directory+"/"+file_name)
2077   - }.
2078   -
2079   -
2080   -
2081   -
2082   - *** [2.3] Generating unique ids.
2083   -
2084   - In order to uniquely name object for JavaScript we generate unique ids from a counter.
2085   -
2086   -define Int
2087   - new_idnum
2088   - (
2089   - Var(Int) ic_v // 'idnum' counter variable
2090   - ) =
2091   - protect
2092   - with result = *ic_v+1,
2093   - ic_v <- result;
2094   - result.
2095   -
2096   -
2097   -
2098   -
2099   -
2100   -
2101   - *** [3] Managing web arguments.
2102   -
2103   - Web arguments are those pairs 'name=value' which are transmitted through the HTTP
2104   - protocol. We need precise naming conventions for these web arguments.
2105   -
2106   -
2107   -
2108   - *** [3.1] Prefixing web arguments names.
2109   -
2110   - CR 2008-07-08 Prefixing Removed
2111   -
2112   -
2113   -
2114   - *** [3.2] Separating web arguments.
2115   -
2116   - When a new request arrives, we need to separate the web arguments, that is to say:
2117   -
2118   - - find the value of "s", and recover the corresponding state,
2119   - - find the value of "a", which is the name of the action to be performed,
2120   - - get the list of all the remaining web arguments (operands of the action).
2121   -
2122   - We must also determine if the previous state may be recovered. If it is not the case
2123   - (either because the previous state name is invalid, or the previous state is out of
2124   - date), we must check if there is an action name. Indeed, the presence of an action name
2125   - indicates that the user has clicked on one of our buttons or links. If on the contrary
2126   - there is no action name the user has just entered our address in his browser. In this
2127   - last case, we must send the first page of our site (maybe a 'login' page), but if there
2128   - is an action, we must send a page just saying that the session ticket has expired. If
2129   - the previous state is recovered and there is no action, the new state is the same as
2130   - the previous state.
2131   -
2132   - The result of the separation of the web arguments is of type:
2133   -
2134   -type Separated_Web_Args($State):
2135   - swa(PreviousState($State) previous_state,
2136   - Maybe(String) action_name,
2137   - List(Web_arg) operands).
2138   -
2139   -
2140   -
2141   - The next function constructs the function which separates the web arguments.
2142   -
2143   -define (List(Web_arg) lwa, HTTP_Info info) -> Separated_Web_Args($State)
2144   - make_separate_web_args_function
2145   - (
2146   - String state_directory,
2147   - String -> PreviousState($State) retrieve_state,
2148   - String website_name
2149   - ) =
2150   - (List(Web_arg) lwa, HTTP_Info info) |-swaf->
2151   - if lwa is
2152   - {
2153   - [ ] then
2154   - //
2155   - // no web arg found => no previous state and no action
2156   - //
2157   - if find_cookie("state_"+website_name, server_get_cookies(http_headers(info))) is
2158   - {
2159   - failure then swa(not_found,failure,[]),
2160   - success(cookie) then swa(retrieve_state(value(cookie)),failure,[])
2161   - },
2162   -
2163   - //swa(failure,failure,[]),
2164   -
2165   - [wa_1 . wa_others] then
2166   - //
2167   - // at least one web arg =>
2168   - // separate other web args, and insert the first one as needed
2169   - //
2170   - if (Separated_Web_Args($State))swaf(wa_others, info) is
2171   - {
2172   - swa(ps1, // possible previous state
2173   - an1, // maybe an action name
2174   - op1) // operands so far
2175   - then
2176   - if wa_1 is
2177   - {
2178   - web_arg(n, value) then
2179   - with name = if substr(n,0,4) = "amp;" then substr(n, 4, length(n) - 4) else n,
2180   - if name = "a" then
2181   - swa(ps1,success(value),op1)
2182   - else if name = "t" then
2183   - swa(ps1,an1,[web_arg("target",value) . op1])
2184   - else
2185   - swa(ps1,an1,[web_arg(name,value) . op1]),
2186   -
2187   - upload(n,v,t) then
2188   - swa(ps1,an1,[wa_1 . op1])
2189   - }
2190   - }
2191   - }.
2192   -
2193   -
2194   -
2195   -
2196   -
2197   -
2198   - *** [3.3] Applying an action.
2199   -
2200   - When the web arguments are separated (and their names cleaned up from prefixes), we may
2201   - apply the action to the operands and the current state. We search for the action to be
2202   - applied in the list of actions. If no action is found, the new state is the same as
2203   - the previous state. Also, we deny the application of an HTTP action if the request
2204   - arrives through the HTTPS channel and conversely.
2205   -
2206   -
2207   -define ($State previous,
2208   - String action_name,
2209   - HTTP_Info http_info,
2210   - List(Web_arg) lwa,
2211   - Bool is_https) -> $State
2212   - make_apply_action_function
2213   - (
2214   - List(Web_Action($State)) actions_list
2215   - ) =
2216   - with f =
2217   - ($State previous,
2218   - String action_name,
2219   - HTTP_Info http_info,
2220   - List(Web_arg) lwa,
2221   - Bool is_https,
2222   - List(Web_Action($State)) actions) |-f->
2223   - //println("Looking for action ["+action_name+"]");
2224   - if actions is
2225   - {
2226   - [ ] then print("action '"+action_name+
2227   - "' not found.\n"); previous,
2228   - [ac1 . others] then if ac1 is
2229   - {
2230   - http_action(an,allow,do_it) then
2231   - if an = action_name
2232   - then if is_https
2233   - then (print("HTTP action '"+an+
2234   - "' called through HTTPS (denied).\n");
2235   - previous)
2236   - else if allow(previous)
2237   - then do_it(http_info,lwa,previous)
2238   - else previous
2239   - else f(previous,action_name,http_info,lwa,is_https,others),
2240   -
2241   - https_action(an,allow,do_it) then
2242   - if an = action_name
2243   - then if is_https
2244   - then if allow(previous)
2245   - then do_it(http_info,lwa,previous)
2246   - else previous
2247   - else (print("HTTPS action '"+an+
2248   - "' called through HTTP (denied).\n");
2249   - previous)
2250   - else f(previous,action_name,http_info,lwa,is_https,others),
2251   -
2252   - http_https_action(an,allow,do_it) then
2253   - if an = action_name
2254   - then
2255   - //println("Action ["+action_name+"] found");
2256   - if allow(previous)
2257   - then do_it(http_info,lwa,previous)
2258   - else previous
2259   - else f(previous,action_name,http_info,lwa,is_https,others),
2260   -
2261   - }
2262   - },
2263   - ($State previous,
2264   - String action_name,
2265   - HTTP_Info http_info,
2266   - List(Web_arg) lwa,
2267   - Bool is_https) |->
2268   - f(previous, action_name, http_info, lwa, is_https, actions_list).
2269   -
2270   -
2271   -
2272   -
2273   -
2274   -
2275   -
2276   -
2277   - *** [4] Web site descriptions and the 'awp handlers'.
2278   -
2279   - *** [4.1] The type 'Web_Site'.
2280   -
2281   - The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
2282   - need another one, because, we have some extra informations to record for each site.
2283   -
2284   -public type Web_Site:
2285   - web_site((Word32,Word32) -> Web_Site_Description description,
2286   - List(String) -> One delete_out_of_date).
2287   -
2288   -
2289   -
2290   -
2291   - *** [4.2] Making a web site description.
2292   -
2293   - Below is the function which creates a web site description. It first creates (if
2294   - needed) the directories for the site, then constructs the tool functions for the site,
2295   - and the site handler. Finally, it constructs the web site description.
2296   -
2297   - We gather common (constant) informations in the following type:
2298   -
2299   -public type CommonInfo:
2300   - info(String common_name,
2301   - Word32 http_port,
2302   - Word32 https_port,
2303   - String site_directory,
2304   - String authorization_secret
2305   - ).
2306   -
2307   - We need a forward declaration.
2308   -
2309   -public define Printable_tree
2310   - format
2311   - (
2312   - CommonInfo cinfo,
2313   - String state_name,
2314   - List(HTTP_header) headers,
2315   - HTTP_Answer page,
2316   - Bool is_https,
2317   - String charset
2318   - ).
2319   -
2320   -
2321   -define Printable_tree
2322   - format
2323   - (
2324   - HTML_Size s
2325   - ) =
2326   - if s is
2327   - {
2328   - absolute(x) then ["\"",x,"\""],
2329   - percentage(x) then ["\"",x,"%\""]
2330   - }.
2331   -
2332   -define List(HTTP_header)
2333   - make_state_cookie_headers
2334   - (
2335   - String website_name,
2336   - String state_name
2337   - )
2338   - =
2339   - [
2340   - http_header("Set-Cookie", "state_"+website_name+"="+state_name)
2341   - ]
2342   - .
2343   -
2344   - public define Web_Site
2345   - make_web_site_description
2346   - (
2347   - List(String) common_names, // for example: ["www.our-business.com",
2348   - // "192.168.0.1"]
2349   - // the second one is just for testing
2350   - String site_directory, // where 'public' and other directories are
2351   - // located (should NOT end with '/')
2352   - One -> One init,
2353   - (HTTP_Info,
2354   - List(Web_arg),
2355   - Bool is_https) -> $State initial_state,
2356   - ($State expired,
2357   - HTTP_Info,
2358   - List(Web_arg),
2359   - Bool is_https) -> $State ticket_expired_state,
2360   - (HTTP_Info,
2361   - List(Web_arg),
2362   - Bool is_https) -> $State ticket_lost_state,
2363   - List(Web_Action($State)) actions,
2364   - $State -> HTTP_Answer compute_page,
2365   - $State -> List(HTTP_header) additional_headers,
2366   - Int timeout, // seconds (todo: minutes)
2367   - Redirections redirections,
2368   - String charset,
2369   - List(String) journal_extensions,
2370   - List(String) journal_headers,
2371   - String authorization_secret,
2372   - List(MIME) known_mime_types,
2373   - (String action_name,
2374   - List(Web_arg) args)-> One before_send_file
2375   - ).
2376   -
2377   -
2378   -public define Web_Site
2379   - make_web_site_description
2380   - (
2381   - String website_name,
2382   - List(String) common_names, // for example: ["www.our-business.com"]
2383   - String site_directory,
2384   - String state_directory,
2385   - One -> One init,
2386   - (HTTP_Info,
2387   - List(Web_arg),
2388   - Bool is_https) -> $State initial_state,
2389   - ($State expired,
2390   - Maybe(String),
2391   - HTTP_Info,
2392   - List(Web_arg),
2393   - Bool is_https) -> $State ticket_expired_state,
2394   - (Maybe(String),
2395   - HTTP_Info,
2396   - List(Web_arg),
2397   - Bool is_https) -> $State ticket_lost_state,
2398   - List(Web_Action($State)) actions,
2399   - $State -> HTTP_Answer compute_page,
2400   - $State -> List(HTTP_header) additional_headers,
2401   - Int timeout,
2402   - Redirections redirections,
2403   - String charset,
2404   - List(String) journal_extensions,
2405   - List(String) journal_headers,
2406   - String secret,
2407   - List(MIME) known_mime_types,
2408   - (String action_name,
2409   - List(Web_arg) args) -> One before_send_file
2410   - //Bool using_state_cookies
2411   - ) =
2412   - init(unique);
2413   -
2414   -
2415   - //
2416   - // make required directories (if needed)
2417   - //
2418   - with //web_sites_directory = (String) make_directory(my_anubis_directory+"/web_sites"),
2419   - base_directory = (String) make_directory(site_directory),
2420   - // state_directory = make_directory(site_directory+"/states"),
2421   - forget((String)make_directory(site_directory+"/public"));
2422   - //
2423   - // construct tool functions
2424   - // TODO 'make_separate_web_args_function' must retrieve state_cookies before parsing web_args if using_state_cookies is true
2425   - with save_state = make_save_state_function(timeout, state_directory),
2426   - retrieve_state = make_retrieve_state_function(state_directory),
2427   - separate_web_args = make_separate_web_args_function(state_directory, retrieve_state, website_name),
2428   - apply_action = make_apply_action_function(actions),
2429   - //
2430   - // construct the site handler
2431   - //
2432   - site_handler = (Word32 http_port, Word32 https_port) |->
2433   - ((String host_name,
2434   - HTTP_Info http_info,
2435   - List(Web_arg) lwa,
2436   - Bool is_https) |->
2437   - //(Printable_tree)
2438   - if separate_web_args(lwa, http_info) is
2439   - {
2440   - swa(mb_previous_state, mb_action_name, operands) then
2441   - with new_state = if mb_previous_state is
2442   - {
2443   - not_found then
2444   - //println("previous state not found");
2445   - if mb_action_name is
2446   - {
2447   - failure then initial_state(http_info, operands, is_https),
2448   - success(_) then
2449   - ticket_lost_state(mb_action_name, http_info,operands,is_https)
2450   - },
2451   -
2452   - out_of_date(state) then
2453   - ticket_expired_state(state, mb_action_name, http_info,operands,is_https),
2454   -
2455   - still_valid(state) then
2456   - if mb_action_name is
2457   - {
2458   - failure then state,
2459   - success(action_name) then
2460   - apply_action(state,action_name,http_info,operands,is_https)
2461   - }
2462   - },
2463   - //if state_and_headers is (session_ticket, mb_new_state, headers) then
2464   - with state_name = save_state(new_state),
2465   - //println("Cookie new STATE NAME "+state_name);
2466   - with cookie_headers = if mb_action_name is
2467   - {
2468   - failure then [],
2469   - success(action_name) then
2470   - if substr(action_name, 0, 5)="ajax_" then
2471   - []
2472   - else
2473   - make_state_cookie_headers(website_name, state_name)
2474   - },
2475   -
2476   - format(info(host_name, http_port, https_port, site_directory, secret),
2477   - state_name,
2478   - additional_headers(new_state) + cookie_headers,
2479   - compute_page(new_state),
2480   - is_https,
2481   - charset)
2482   - }),
2483   - //
2484   - // make the delete_out_of_date function
2485   - //
2486   - delete_out_of_date =
2487   - make_delete_out_of_date_states_function((Maybe($State))failure,
2488   - site_directory+"/states"),
2489   - //
2490   - // construct the web site description
2491   - //
2492   - web_site((Word32 http_port, Word32 https_port) |->
2493   - web_site_description(common_names,
2494   - site_directory,
2495   - redirections,
2496   - charset,
2497   - journal_extensions,
2498   - journal_headers,
2499   - secret,
2500   - known_mime_types,
2501   - site_handler(http_port,https_port),
2502   - (HTTP_Info http_info, List(Web_arg) lwa) |-> if separate_web_args(lwa, http_info ) is
2503   - swa(mb_previous_state,mb_action_name,operands) then
2504   - if mb_action_name is
2505   - {
2506   - failure then unique
2507   - success(an) then before_send_file(an,operands)
2508   - }
2509   - //using_state_cookies
2510   - ),
2511   - delete_out_of_date).
2512   -
2513   -define String
2514   - _random_string
2515   - (
2516   - Int size,
2517   - String current
2518   - )=
2519   - if size = 0 then
2520   - current
2521   - else
2522   - _random_string( size - 1, current + random(9))
2523   - .
2524   -
2525   -public define String
2526   - generate_random_string
2527   - (
2528   - Int size
2529   - ) =
2530   - to_string(extract(base64_encode(to_byte_array(_random_string(size, ""))), 0, size)).
2531   -
2532   -
2533   -define String
2534   - get_site_uid
2535   - (
2536   - String site_path
2537   - )=
2538   - with file_name = site_path+"/site_UID",
2539   - if read_from_file(file_name) is
2540   - {
2541   - cannot_find_file then
2542   - println("site UID doesn't exists. Generating it now");
2543   - with uid = generate_random_string(9),
2544   - if write_to_file(file_name, to_byte_array(uid)) is
2545   - {
2546   - cannot_open_file then println("Can't create site UID here "+file_name);"",
2547   - write_error(_) then println("Can't write site UID here "+file_name);"",
2548   - ok then uid
2549   - }
2550   - read_error(_) then
2551   - println("cannot read site UID from "+file_name);""
2552   - ok(ba) then to_string(ba)
2553   - }
2554   - .
2555   -
2556   -// Compatibility function for older websites
2557   -public define Web_Site
2558   - make_web_site_description
2559   - (
2560   - List(String) common_names, // for example: ["www.our-business.com"]
2561   - String site_directory,
2562   - String state_directory,
2563   - One -> One init,
2564   - (HTTP_Info,
2565   - List(Web_arg),
2566   - Bool is_https) -> $State initial_state,
2567   - ($State expired,
2568   - Maybe(String),
2569   - HTTP_Info,
2570   - List(Web_arg),
2571   - Bool is_https) -> $State ticket_expired_state,
2572   - (Maybe(String),
2573   - HTTP_Info,
2574   - List(Web_arg),
2575   - Bool is_https) -> $State ticket_lost_state,
2576   - List(Web_Action($State)) actions,
2577   - $State -> HTTP_Answer compute_page,
2578   - $State -> List(HTTP_header) additional_headers,
2579   - Int timeout,
2580   - Redirections redirections,
2581   - String charset,
2582   - List(String) journal_extensions,
2583   - List(String) journal_headers,
2584   - String secret,
2585   - List(MIME) known_mime_types,
2586   - (String action_name,
2587   - List(Web_arg) args) -> One before_send_file
2588   - //Bool using_state_cookies
2589   - )
2590   - =
2591   - //generate an unique ID if doesn't exist in root of site_directory
2592   - with site_UID = get_site_uid(site_directory),
2593   - println("site_UID : "+site_UID);
2594   - make_web_site_description(site_UID, common_names, site_directory, state_directory, init, initial_state, ticket_expired_state, ticket_lost_state, actions, compute_page, additional_headers, timeout, redirections, charset, journal_extensions, journal_headers, secret, known_mime_types, before_send_file)
2595   - .
2596   -
2597   -
2598   -
2599   - *** [4.3] Starting the servers.
2600   -
2601   -public define Start_Web_Sites_Result
2602   - start_web_sites
2603   - (
2604   - Word32 ip_address, // the IP address shared by the web sites
2605   - Word32 http_port, // usually: 80
2606   - Word32 https_port, // usually: 443
2607   - String ssl_certificate_common_name,
2608   - List(Web_Site) web_sites, // web sites to be started
2609   - (One) -> Bool shutdown_required
2610   - ) =
2611   - with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
2612   - with http_server_r =
2613   - start_http_server(ip_address,http_port,
2614   - map(get_description,web_sites),
2615   - load_denial_of_service_info),
2616   - with https_server_r =
2617   - start_https_server(ip_address,https_port,
2618   - ssl_certificate_common_name,
2619   - map(get_description,web_sites),
2620   - load_denial_of_service_info),
2621   - if http_server_r is ok(http_server)
2622   - then
2623   - (
2624   - if https_server_r is ok(https_server)
2625   - then
2626   - (
2627   - start_http_servers_tasks(map(get_description,web_sites),
2628   - [http_server,https_server],
2629   - 600); // period of 10 minutes
2630   - delegate
2631   - delete_states_loop(
2632   - map((Web_Site ws) |->
2633   - (site_directory(description(ws)(http_port,https_port))+
2634   - "/states",delete_out_of_date(ws)),
2635   - web_sites),
2636   - 3600*24*3, // keep out of date states 3 days
2637   - now,
2638   - http_server,
2639   - https_server,
2640   - shutdown_required),
2641   - ok(http_server,https_server)
2642   - )
2643   - else cannot_bind_to_port(https_port)
2644   - )
2645   - else
2646   - (
2647   - if https_server_r is ok(https_server)
2648   - then cannot_bind_to_port(http_port)
2649   - else cannot_bind_to_port(http_port,https_port)
2650   - ).
2651   -
2652   -
2653   -public define One
2654   - start_web_sites
2655   - (
2656   - Word32 ip_address, // the IP address shared by the web sites
2657   - Word32 http_port, // usually: 80
2658   - Word32 https_port, // usually: 443
2659   - String ssl_certificate_common_name,
2660   - List(Web_Site) web_sites, // web sites to be started
2661   - (One) -> Bool shutdown_required,
2662   - Logger log
2663   - ) =
2664   - if (Start_Web_Sites_Result)start_web_sites(ip_address,
2665   - http_port,
2666   - https_port,
2667   - ssl_certificate_common_name,
2668   - web_sites,
2669   - shutdown_required) is
2670   - {
2671   - cannot_bind_to_port(n) then logError(log, "Cannot bind to port: "+n),
2672   - cannot_bind_to_port(n,m) then logError(log, "Cannot bind to ports: "+n+", "+m),
2673   - ok(s1,s2) then logInfo(log, "Servers started on ports "+http_port+" (HTTP) and "+https_port+" (HTTPS).")
2674   - }.
2675   -
2676   -
2677   -
2678   -
2679   -
2680   - *** [5] HTML Formating.
2681   -
2682   - We need to translate HTML elements as defined above into actual HTML text.
2683   -
2684   - Actioners require special informations, which must be transmitted when needed by the
2685   - 'format' functions:
2686   -
2687   - - the 'common name', which is used for URLs,
2688   - - the HTTP/HTTPS port number,
2689   - - the 'state name', which must be transmitted when the actioner is clicked upon,
2690   - - the 'form name' (if any) to which the actioner refers.
2691   -
2692   - If the actioner is off form, and if it refers to a form, the name of that form is
2693   - already known by the actioner. On the contrary, if the actioner is 'in form', it refers
2694   - implicitly to the form containing it. The name of that form is transmitted to the
2695   - 'format' functions called from within the formating of that form.
2696   -
2697   -
2698   -
2699   -
2700   - *** [5.1] The type 'HTML_Any($T)'.
2701   -
2702   - The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
2703   - parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
2704   -
2705   -type HTML_Any($T):
2706   - any_text (List(Text_Option), String the_text),
2707   - any_preformated (List(Text_Option), String),
2708   - any_paragraph (List(Text_Option), $T content),
2709   - any_image (List(CoreAttrs), String url, String alternate),
2710   - any_image (List(CoreAttrs), String url, String alternate, Int width, Int height),
2711   - any_table (List(Table_Option), HTML_Header_Row($T), List(HTML_Row($T)), HTML_Footer_Row($T)),
2712   - any_center ($T),
2713   - any_mail_to (String email, $T element),
2714   - any_scroller (Int width, Int height,
2715   - Int content_width, Int content_height,
2716   - $T content),
2717   - any_fixed_size (HTML_Size width, HTML_Size height, $T content),
2718   - any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
2719   - any_actioner (Actioner_Connection,
2720   - Actioner_Target,
2721   - Actioner_Aspect,
2722   - String action_name,
2723   - List((String,String)) extra_ops,
2724   - List(Actioner_Local_Action),
2725   - Maybe(String) form_name),
2726   - any_foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
2727   - any_foreign_link (List(Text_Option), String url),
2728   - any_foreign_link (List(Text_Option), String url, String name),
2729   - any_private_download (String abs_path, String name, String extra_ext,
2730   - Maybe((String,List((String,String))))),
2731   - any_div (List(CoreAttrs), $T element),
2732   - any_div_empty (List(CoreAttrs)),
2733   - any_coreattrs (List(CoreAttrs)).
2734   -
2735   -
2736   -
2737   - *** [5.2] Formating a color.
2738   -
2739   - RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
2740   - hexadecimal values.
2741   -
2742   -define String
2743   - html_format
2744   - (
2745   - RGB color
2746   - ) =
2747   - if color is rgb(r,g,b) then
2748   - "#" + to_hexa(r)
2749   - + to_hexa(g)
2750   - + to_hexa(b).
2751   -
2752   -
2753   - The following is a very arbitrary definition of the opposite color. The thing which is
2754   - important is that it is far from the original, so that characters in 'opposite' color
2755   - are clearly visible over the original.
2756   -
2757   -define RGB
2758   - opposite
2759   - (
2760   - RGB color
2761   - ) =
2762   - if color is rgb(r,g,b) then
2763   - rgb(255-r,
2764   - 255-g,
2765   - 255-b).
2766   -
2767   -
2768   -
2769   -
2770   - *** [5.3] Creating buttons.
2771   -
2772   - We want to be able to create buttons in the form of a pair of images (rollovers)
2773   - automatically. We use the JPEG interface, because for the time being Anubis cannot
2774   - handle other kinds of images.
2775   -
2776   -
2777   - Computing printed text length.
2778   -
2779   - define Int
2780   - printed_text_width
2781   - (
2782   - Word8 -> Int char_size,
2783   - List(Word8) l
2784   - ) =
2785   - if l is
2786   - {
2787   - [] then (Int) 0,
2788   - [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
2789   - }.
2790   -
2791   - define Int
2792   - printed_text_width
2793   - (
2794   - SystemFont font,
2795   - String s
2796   - ) =
2797   - printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
2798   - explode(s)).
2799   -
2800   -
2801   -
2802   - Converting RGB to RGBA.
2803   -
2804   -define RGBA
2805   - to_rgba
2806   - (
2807   - RGB color
2808   - ) =
2809   - if color is rgb(r,g,b) then rgba(r,g,b,255).
2810   -
2811   -
2812   - Drawing a 'relief'.
2813   -
2814   - define One
2815   - draw_relief
2816   - (
2817   - RGBAImage dest,
2818   - RGBA color,
2819   - Int contrast,
2820   - Int x,
2821   - Int y,
2822   - Int width,
2823   - Int height
2824   - ) =
2825   - with l = lighten(color,contrast),
2826   - d = darken(color,contrast),
2827   - draw_rectangle(dest,rect(x,y,x+width,y+1),l);
2828   - draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
2829   - draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
2830   - draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
2831   -
2832   -
2833   - Creating a button background.
2834   -
2835   - define RGBAImage
2836   - create_button_background
2837   - (
2838   - RGBA color,
2839   - Int width,
2840   - Int height
2841   - ) =
2842   - with result = create_rgba_image(width,height,color),
2843   - draw_relief(result,color,100,0,0,width,height);
2844   - draw_relief(result,color,70,1,1,width-2,height-2);
2845   - draw_relief(result,color,55,2,2,width-4,height-4);
2846   - draw_relief(result,color,35,3,3,width-6,height-6);
2847   - draw_relief(result,color,20,4,4,width-8,height-8);
2848   - draw_relief(result,color,10,5,5,width-10,height-10);
2849   - draw_relief(result,color,5,6,6,width-12,height-12);
2850   - result.
2851   -
2852   -
2853   - Drawing the text over the background.
2854   -
2855   - define One
2856   - draw_button_text
2857   - (
2858   - RGBAImage image,
2859   - String text,
2860   - Int text_index,
2861   - Int pixel_x,
2862   - Int y,
2863   - Rectangle clip,
2864   - RGBA color,
2865   - SystemFont font,
2866   - ) =
2867   - if nth(text_index,text) is
2868   - {
2869   - failure then unique,
2870   - success(c) then
2871   - with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
2872   - draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
2873   - }.
2874   -
2875   - define One
2876   - draw_button_text
2877   - (
2878   - RGBAImage image,
2879   - String text,
2880   - Int text_width,
2881   - RGBA light_color,
2882   - RGBA dark_color,
2883   - SystemFont font
2884   - ) =
2885   - with image_width = width(image),
2886   - image_height = height(image),
2887   - x_pos = (image_width-text_width)>>1,
2888   - clip = rect(0,0,image_width,image_height),
2889   - new_light_color = lighten(light_color,150),
2890   - new_dark_color = darken(dark_color,40),
2891   - draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
2892   - draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
2893   -
2894   -
2895   - The next function creates the two images for a button. The information given is the
2896   - main color of the button, the text of the button and the minimal width (in pixels) of
2897   - the button. The function does not create the button if the images already exist. The
2898   - two images are stored in the directory 'site_directory/buttons'. The names of the files
2899   - are of the form:
2900   -
2901   - bxxxx_off.jpg
2902   - bxxxx_on.jpg
2903   -
2904   - where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
2905   - 'rm'), and where 'xxxx' is created from the given informations by the formula:
2906   -
2907   - xxxx = web_arg_encode(sha1((color,text,width)))
2908   -
2909   - Hence, distinct informations give distinct file names.
2910   -
2911   -
2912   - define String // returns xxxx
2913   - create_button_images
2914   - (
2915   - String site_directory,
2916   - RGBA color,
2917   - String text,
2918   - Int width,
2919   - SystemFont font
2920   - ) =
2921   - with xxxx = web_arg_encode(sha1((color,text,width))),
2922   - buttons_dir = site_directory+"/public/buttons",
2923   - off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
2924   - on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
2925   - if file_exists(on_filepath)
2926   - then xxxx
2927   - else with
2928   - text_width = printed_text_width(font,text),
2929   - button_width = max(width,text_width+12),
2930   - button_height = (Int)20,
2931   - light_color = lighten(color,60),
2932   - very_light_color = lighten(light_color,30),
2933   - dark_color = darken(color,40),
2934   - background_off =
2935   - create_button_background(color,button_width,button_height),
2936   - background_on =
2937   - create_button_background(light_color,button_width,button_height),
2938   -
2939   - draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
2940   - draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
2941   - forget(write_image_to_JPEG_file(to_JPEG(background_off),
2942   - off_filepath,
2943   - 100));
2944   - forget(write_image_to_JPEG_file(to_JPEG(background_on),
2945   - on_filepath,
2946   - 100));
2947   - xxxx.
2948   -
2949   -
2950   -
2951   -
2952   -
2953   - *** [5.4] Formating an actioner.
2954   -
2955   - An actioner works as follows. Assume first that it refers to a form. When it is clicked
2956   - upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
2957   - form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
2958   - actioner does not refer to a form, it fires the URL directly via 'href', because in
2959   - that case, the actioner is always an <a> tag.
2960   -
2961   - The URL itself is composed using the connection sort (same, http or https), the common
2962   - name and port number (if needed), the state name, the action name, and the extra
2963   - operands, which are put into a query string. It may look like this:
2964   -
2965   - http://common_name:port/?s=state_name&a=action_name&oname=value...
2966   -
2967   - Each extra operand is a pair of strings: (name,value). It is formated as:
2968   -
2969   - &oname=value
2970   -
2971   -
2972   -public define String
2973   - format_extra_operands
2974   - (
2975   - List((String,String)) l
2976   - ) =
2977   - if l is
2978   - {
2979   - [ ] then "",
2980   - [h . t] then if h is (n,v) then
2981   - "&amp;"+n+"="+v+format_extra_operands(t)
2982   - }.
2983   -
2984   -
2985   - It seams that the standard requires "&amp;" instead of "&" !
2986   -
2987   - In case the target is another window, we need to format the options for this window.
2988   -
2989   -define String
2990   - format
2991   - (
2992   - List(Other_Window_Option) l
2993   - ) =
2994   - if l is
2995   - {
2996   - [ ] then "",
2997   - [h . t] then if h is
2998   - {
2999   - resizable then "resizable",
3000   - scrollbars then "scrollbars",
3001   - width(w) then "width="+w,
3002   - height(h2) then "height="+h2
3003   - } + if t is [ ] then "" else (","+format(t))
3004   - }.
3005   -
3006   -
3007   -
3008   - Formating choices for a <select> tag.
3009   -
3010   -public define Printable_tree
3011   - format_choices
3012   - (
3013   - List(String) l
3014   - ) =
3015   - if l is
3016   - {
3017   - [ ] then [ ],
3018   - [h . t] then ["<option>", h, "</option>\n" . format_choices(t)]
3019   - }.
3020   -
3021   -
3022   -public define Printable_tree
3023   - format_choices
3024   - (
3025   - List(String) l,
3026   - InitialValue selected
3027   - ) =
3028   - if l is
3029   - {
3030   - [ ] then [ ],
3031   - [h . t] then if h = selected.value
3032   - then ["<option selected=\"selected\">", h, "</option>\n" . format_choices(t)]
3033   - else ["<option>", h, "</option>\n" . format_choices(t,selected)]
3034   - }.
3035   -
3036   -
3037   -public define Printable_tree
3038   - format_choices
3039   - (
3040   - List((WebArgValue,String)) l
3041   - ) =
3042   - if l is
3043   - {
3044   - [ ] then [ ],
3045   - [h . t] then
3046   - if h is (val,item)
3047   - then ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t)]
3048   - }.
3049   -
3050   -public define Printable_tree
3051   - format_choices
3052   - (
3053   - List((WebArgValue,String)) l,
3054   - InitialValue selected
3055   - ) =
3056   - if l is
3057   - {
3058   - [ ] then [ ],
3059   - [h . t] then
3060   - if h is (val,item) then
3061   - if val.value = selected.value
3062   - then ["<option value=\""+val.value+"\" selected=\"selected\">", item, "</option>\n" . format_choices(t)]
3063   - else ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t,selected)]
3064   - }.
3065   -
3066   -
3067   -
3068   - Note: this counter is private to the virtual machine, hence there is one counter by
3069   - client and by page : it seems that a new VM (simply a delegate) is started for each request.
3070   -
3071   -define Int
3072   - new_count
3073   - (
3074   - Var(Int) count
3075   - )
3076   - =
3077   - count <- *count+1;
3078   - *count.
3079   -
3080   -
3081   - The next function composes the URL. It is a JavaScript URL when the target is another
3082   - window.
3083   -
3084   -define String
3085   - make_actioner_url
3086   - (
3087   - CommonInfo cinfo,
3088   - Actioner_Connection connection,
3089   - Actioner_Target target,
3090   - String state_name,
3091   - String action_name,
3092   - List((String,String)) extra_ops,
3093   - Bool is_https
3094   - ) =
3095   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3096   - with strict_url =
3097   - if connection is
3098   - {
3099   - same then "/",
3100   - http then "http://"+common_name+":"+http_port+"/",
3101   - https then "https://"+common_name+":"+https_port+"/",
3102   - } +
3103   - "?a=" + action_name +
3104   - format_extra_operands(extra_ops),
3105   - if target is
3106   - {
3107   - same then strict_url,
3108   - same(label) then strict_url+"#"+label,
3109   - other(wn,ops) then
3110   -
3111   - "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
3112   - "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
3113   - }.
3114   -
3115   -define String
3116   - make_foreign_link_url
3117   - (
3118   - Actioner_Target target,
3119   - String url
3120   - ) =
3121   - if target is
3122   - {
3123   - same then "/",
3124   - same(label) then "/#"+label,
3125   - other(wn,ops) then
3126   -
3127   - "javascript:void window.open('"+url+"','"+
3128   - "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
3129   - }.
3130   -
3131   -
3132   - Depending on the fact that the actioner refers to a form or not, the URL is used in two
3133   - different ways. If the actioner does not refer to a form, it is realized by a '<a>'
3134   - tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
3135   - tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
3136   - event handler. The handler calls a JavaScript function which puts the URL as the value
3137   - of the 'action' attribute of the form, and submits the form.
3138   -
3139   -type URL_or_JavaScript:
3140   - url (String),
3141   - javascript (Printable_tree script, Printable_tree handler).
3142   -
3143   -
3144   - It was as shown below, in order to allow submission of a form from outside the form,
3145   - but this makes problems:
3146   -
3147   -define URL_or_JavaScript
3148   - format_action
3149   - (
3150   - String the_url,
3151   - Actioner_Aspect aspect,
3152   - Maybe(String) mb_id,
3153   - Maybe(String) mb_form_name,
3154   - Var(Int) action_count
3155   - ) =
3156   - if mb_form_name is
3157   - {
3158   - failure then
3159   - url(the_url),
3160   - success(form_name) then
3161   - with n = new_count(action_count),
3162   - fn_name = "pfu_" + if mb_id is
3163   - {
3164   - failure then form_name + "_" + n,
3165   - success(id) then id,
3166   - },
3167   - javascript(
3168   - [
3169   - "<script type=\"text/javascript\" language =\"JavaScript\">\n",
3170   - "function ", fn_name, "() {\n",
3171   - " var fa = document.forms.",form_name,";\n",
3172   - " var u = \"",the_url,"\";\n",
3173   - " fa.action = u;",
3174   - if aspect is submit(_, _) then " }\n"
3175   - else "\n fa.submit(); }\n",
3176   - "</script>\n"
3177   - ],
3178   - [fn_name,"();"]
3179   - )
3180   - }.
3181   -
3182   -
3183   -
3184   - Now, we format the actioner according to its aspect.
3185   -
3186   -define List(Text_Option)
3187   - get_text_options
3188   - (
3189   - List(Text_Option) l
3190   - )
3191   - =
3192   - if l is
3193   - {
3194   - [] then [],
3195   - [h . t ] then
3196   - if h is class(_) then
3197   - get_text_options(t)
3198   - else if h is id(_) then
3199   - get_text_options(t)
3200   - else if h is style(_) then
3201   - get_text_options(t)
3202   - else if h is title(_) then
3203   - get_text_options(t)
3204   - else
3205   - [ h . get_text_options(t) ]
3206   -
3207   - }.
3208   -
3209   - /**
3210   - * Extract the CSS class list from the list of Text_Option
3211   - */
3212   -define List(CoreAttrs)
3213   - get_css_class
3214   - (
3215   - List(Text_Option) l
3216   - )
3217   - =
3218   - if l is
3219   - {
3220   - [] then [],
3221   - [h . t ] then
3222   -
3223   -
3224   - if h is class(name) then
3225   - [ class(name) . get_css_class(t) ]
3226   - else if h is id(name) then
3227   - [ id(name) . get_css_class(t) ]
3228   - else if h is style(the_style) then
3229   - [ style(the_style) . get_css_class(t) ]
3230   - else if h is title(name) then
3231   - [ tooltip(name) . get_css_class(t) ]
3232   - else if h is attr(name, value) then
3233   - [ attr(name, value) . get_css_class(t) ]
3234   - else
3235   - get_css_class(t)
3236   - }
3237   - .
3238   -
3239   -define Printable_tree
3240   - format
3241   - (
3242   - List(Actioner_Local_Action) l
3243   - ) =
3244   - if l is
3245   - {
3246   - [ ] then [ ],
3247   - [h . t] then [if h is
3248   - {
3249   - close_window then [" window.close(); "]
3250   - }
3251   - . format(t)]
3252   - }.
3253   -
3254   -public define String
3255   - format_attrs
3256   - (
3257   - List(CoreAttrs) attributs
3258   - )=
3259   - if attributs is
3260   - {
3261   - [] then "",
3262   - [h . t] then
3263   - with current = if h is
3264   - {
3265   - id(id_name) then
3266   - " id=\"" + id_name + "\"",
3267   - class(class_name) then
3268   - " class=\"" + class_name + "\"",
3269   - style(style_string) then
3270   - " style=\"" + style_string + "\"",
3271   -
3272   - title(title_string) then
3273   - " title=\"" + title_string + "\"",
3274   -
3275   - lang(lang) then
3276   - " xml:lang=" + lang,
3277   - dir(reading_Way) then
3278   - if reading_Way is
3279   - {
3280   - ltr then " dir=ltr",
3281   - rtl then " dir=rtl"
3282   - },
3283   -
3284   - accesskey(key) then
3285   - " accesskey=\"" + key + "\"",
3286   - tabindex(index) then
3287   - " tabindex=\"" + index + "\"",
3288   -
3289   - attr(name, value) then
3290   - " " + name + "=\"" + value + "\"",
3291   -
3292   - event(e, value) then
3293   - " " + event_name(e) + "=\"" + value + "\"",
3294   - },
3295   - current + format_attrs(t)
3296   - }.
3297   -
3298   -define String
3299   - format_attrs
3300   - (
3301   - List(InputAttrs) attributs
3302   - )=
3303   - if attributs is
3304   - {
3305   - [] then "",
3306   - [h . t] then
3307   - with current = if h is
3308   - {
3309   - class(class_name) then
3310   - " class=\"" + class_name + "\"",
3311   - style(style_string) then
3312   - " style=\"" + style_string + "\"",
3313   -
3314   - title(title_string) then
3315   - " title=\"" + title_string + "\"",
3316   -
3317   - lang(lang) then
3318   - " xml:lang=" + lang,
3319   - dir(reading_Way) then
3320   - if reading_Way is
3321   - {
3322   - ltr then " dir=ltr",
3323   - rtl then " dir=rtl"
3324   - },
3325   -
3326   - accesskey(key) then
3327   - " accesskey=\"" + key + "\"",
3328   - tabindex(index) then
3329   - " tabindex=\"" + index + "\"",
3330   -
3331   - attr(name, value) then
3332   - " " + name + "=\"" + value + "\"",
3333   -
3334   - event(e, value) then
3335   - " " + event_name(e) + "=\"" + value + "\"",
3336   - },
3337   - current + format_attrs(t)
3338   - }.
3339   -
3340   -define String
3341   - format_text_options
3342   - (
3343   - List(Text_Option) l
3344   - )
3345   - =
3346   - with text_options = get_text_options(l),
3347   - css_classes = get_css_class(l),
3348   - if text_options is
3349   - {
3350   - [] then "",
3351   - [_ . _] then " style=\"" + format(text_options) + "\" "
3352   - }
3353   - +
3354   - if css_classes is
3355   - {
3356   - [] then "",
3357   - [_ . _] then format_attrs(css_classes)
3358   - }.
3359   -
3360   -//define String
3361   -// _format
3362   -// (
3363   -// List(CoreAttrs) opt
3364   -// )=
3365   -// if opt is
3366   -// {
3367   -// [] then "",
3368   -// [h .t] then
3369   -// with current = if h is
3370   -// {
3371   -// id(id_name) then
3372   -// " id=\"" + id_name + "\"",
3373   -// class(class_name) then
3374   -// " class=\"" + class_name + "\"",
3375   -// style(style_string) then
3376   -// " style=\"" + style_string + "\"",
3377   -//
3378   -// title(title_string) then
3379   -// " title=\"" + title_string + "\"",
3380   -// lang(lang) then
3381   -// " xml:lang=" + lang,
3382   -// dir(reading_Way) then
3383   -// if reading_Way is
3384   -// {
3385   -// ltr then " dir=ltr",
3386   -// rtl then " dir=rtl"
3387   -// },
3388   -// attr(name, value) then
3389   -// " " + name + "=\"" + value + "\"",
3390   -// event(e, value) then
3391   -// " " + event_name(e) + "=\"" + value + "\""
3392   -// },
3393   -// current + _format(t)
3394   -// }
3395   -// .
3396   -
3397   -define Printable_tree
3398   - format_div_option
3399   - (
3400   - List(CoreAttrs) options
3401   - )=
3402   - ["<div" + format_attrs(options) + ">"] .
3403   -
3404   -
3405   -define Maybe(String)
3406   - extract_id
3407   - (
3408   - List(CoreAttrs) attrbs
3409   - ) =
3410   - if attrbs is
3411   - {
3412   - [] then failure,
3413   - [h . t] then
3414   - if h is id(value) then success(value)
3415   - else extract_id(t)
3416   - }.
3417   -
3418   -define Maybe(String)
3419   - extract_id
3420   - (
3421   - Actioner_Aspect aspect
3422   - ) =
3423   - if aspect is
3424   - {
3425   - link(_,_,_) then failure,
3426   - img_link(_,_) then failure,
3427   - push_button(opts,_) then extract_id(opts),
3428   - button(_,_) then failure,
3429   - button(_,_,_,_) then failure,
3430   - submit(opts,_) then extract_id(opts),
3431   - immediate_selector(_,_,_,_) then failure,
3432   - immediate_selector(_,_,_,_,_) then failure,
3433   - }.
3434   -
3435   -to do Complete format_foreign_link
3436   -//TODO Complete format_foreign_link
3437   -define Printable_tree
3438   - format_foreign_link
3439   - (
3440   -// CommonInfo cinfo,
3441   - Actioner_Target target,
3442   - Actioner_Aspect aspect,
3443   - String url,
3444   - Bool is_https,
3445   - ) =
3446   - //if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
3447   - with full_url = make_foreign_link_url(target,url),
3448   -// with action = format_action(full_url, aspect, extract_id(aspect), mb_form_name),
3449   - if aspect is
3450   - {
3451   -
3452   - link(opt, text, mb_name) then
3453   - with name_tag = if mb_name is success(n) then " name=\"" + n + "\"" else "",
3454   - [
3455   - "<a href=\"", full_url, "\"", format_text_options(opt), name_tag, ">",
3456   - text,
3457   - "</a>"
3458   - ],
3459   - img_link(img,alt_text) then
3460   - [
3461   - "<a href=\"", full_url ,"\"><img src=\"", img,
3462   - "\" alt=\"" + alt_text + "\" border=\"0\"></a>"
3463   - ],
3464   - push_button(options, text) then
3465   - ["<a href=\"",full_url,"\"", format_attrs(options), ">",
3466   - text, "</a>"
3467   - ],
3468   - button(url_off,url_on) then
3469   - [ ],
3470   -
3471   - button(url_off,url_on,w,h) then
3472   - [ ],
3473   -
3474   - submit(options, text) then
3475   - [ ],
3476   -
3477   - immediate_selector(opt,name,size,choices) then
3478   - [ ],
3479   - immediate_selector(opt,name,size,choices,selected) then
3480   - [ ],
3481   - }.
3482   -
3483   -define Printable_tree
3484   - format_actioner
3485   - (
3486   - CommonInfo cinfo,
3487   - String state_name,
3488   - Actioner_Connection connection,
3489   - Actioner_Target target,
3490   - Actioner_Aspect aspect,
3491   - String action_name,
3492   - List((String,String)) extra_ops,
3493   - List(Actioner_Local_Action) local_actions,
3494   - Maybe(String) mb_form_name,
3495   - Bool is_https,
3496   - Var(Int) action_count
3497   - ) =
3498   - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
3499   - with url = make_actioner_url(cinfo,connection,target,
3500   - state_name,action_name,extra_ops,is_https),
3501   - with action = format_action(url, aspect, extract_id(aspect), mb_form_name, action_count),
3502   - if aspect is
3503   - {
3504   -
3505   - link(opt, text, mb_name) then
3506   - with name_tag = if mb_name is success(n) then " name=\"" + n + "\"" else "",
3507   - [
3508   - "<a href=\"", url, "\"", format_text_options(opt), name_tag, ">",
3509   - text,
3510   - "</a>"
3511   - ],
3512   - img_link(img, alt_text) then
3513   - [
3514   - "<a href=\"", url ,"\"><img src=\"", img,
3515   - "\" alt=\"" + alt_text + "\" border=\"0\"></a>"
3516   - ],
3517   - push_button(options, text) then
3518   - [
3519   - if action is
3520   - {
3521   - url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
3522   - text, "</a>"
3523   - ],
3524   - javascript(s,h) then
3525   - if action_name = "" then
3526   - ["<input type=\"button\" value=\"",text,"\"", format_attrs(options), " />"]
3527   - else
3528   - [s,"<input type=\"button\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
3529   - }
3530   - ],
3531   - button(url_off,url_on) then
3532   - [ if action is
3533   - {
3534   - url(u) then ["<a href=\"",u],
3535   - javascript(s,h) then [s,"<a onMouseDown=\"",h]
3536   - },
3537   - "\" style=\"text-decoration:none\">",
3538   - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
3539   - " onMouseOver=\"this.src='",url_on,"'\" ",
3540   - " onMouseOut=\"this.src='",url_off,"'\">",
3541   - "</a>"
3542   - ],
3543   -
3544   - button(url_off,url_on,w,h) then
3545   - [ if action is
3546   - {
3547   - url(u) then ["<a href=\"",u],
3548   - javascript(s,h2) then [s,"<a onMouseDown=\"",h2]
3549   - },
3550   - "\" style=\"text-decoration:none\">",
3551   - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
3552   - " onMouseOver=\"this.src='",url_on,"'\" ",
3553   - " onMouseOut=\"this.src='",url_off,"'\">",
3554   - "</a>"
3555   - ],
3556   -
3557   - submit(options, text) then
3558   - [
3559   - if action is
3560   - {
3561   - url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
3562   - text, "</a>"
3563   - ],
3564   - javascript(s,h) then
3565   - if action_name = "" then // special case where URL (so the action_name) is provided by the form itself
3566   - // so javascript isn't needed. This is the standard HMTL way.
3567   - ["<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " />"]
3568   - else
3569   - [s,"<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
3570   - }
3571   - ],
3572   -
3573   - immediate_selector(options,name,size,choices) then
3574   - [ if action is
3575   - {
3576   - url(u) then ["<select href=\"",u]
3577   - javascript(s,h) then [s,"<select onchange=\"",h]
3578   - },
3579   - "\" name=\"",name,"\" size=\"",size,"\"", format_attrs(options), ">",
3580   - format_choices(choices),"</select>"
3581   - ],
3582   - immediate_selector(options,name,size,choices,selected) then
3583   - [ if action is
3584   - {
3585   - url(u) then ["<select href=\"",u]
3586   - javascript(s,h) then [s,"<select onchange=\"",h]
3587   - },
3588   - "\" name=\"",name,"\" size=\"",size,"\"", format_attrs(options), ">",
3589   - format_choices(choices, selected),"</select>"
3590   - ],
3591   - }.
3592   -
3593   -
3594   -
3595   -
3596   -
3597   -
3598   - *** [5.5] Formating a private download link.
3599   -
3600   - We get the absolute path of the file to be downloaded, and the name under which it
3601   - should appear to the client. The function 'format_private_download' creates an
3602   - hypertext link for downloading the file. The secured mecanism of private download is
3603   - used. This function is called by the function which formats HTML_Any($T) elements.
3604   -
3605   -
3606   -define Printable_tree
3607   - format_private_download
3608   - (
3609   - CommonInfo cinfo,
3610   - String sn, // state name
3611   - String abs_path, // absolute file path on server
3612   - String name, // name of file as it appears in the browser
3613   - String extra, // extra extension
3614   - Maybe((String,List((String,String)))) action
3615   -
3616   - ) =
3617   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3618   - with private_download_directory = site_directory+"/private_download",
3619   - with auth = make_authorization(site_directory,secret,abs_path),
3620   - [
3621   - "<a href=\"",name,extra,"?zauth=",auth,
3622   - if action is
3623   - {
3624   - failure then [ ]
3625   - success(a) then if a is (an,args) then
3626   - ["&amp;a=",an,format_extra_operands(args)]
3627   - },
3628   - "\">",
3629   - name,
3630   - "</a>"
3631   - ].
3632   -
3633   -
3634   -
3635   -
3636   - *** [5.6] Formating rows and cells in a table.
3637   -
3638   -define Int
3639   - percent
3640   - (
3641   - Int p
3642   - ) =
3643   - if p < 0 then 0 else if p > 100 then 100 else p.
3644   -
3645   -
3646   -
3647   -
3648   - Formating cell options.
3649   -
3650   -
3651   -
3652   -define String
3653   - format
3654   - (
3655   - BackgroundOption o
3656   - ) =
3657   - if o is
3658   - {
3659   - repeat then "",
3660   - repeat_horizontal then "; background-repeat: repeat-x",
3661   - repeat_vertical then "; background-repeat: repeat-y",
3662   - no_repeat then "; background-repeat: no-repeat",
3663   - center then "; background-position: center top"
3664   - }.
3665   -
3666   -define String
3667   - format
3668   - (
3669   - List(BackgroundOption) l
3670   - ) =
3671   - if l is
3672   - {
3673   - [ ] then "",
3674   - [h . t] then format(h)+format(t)
3675   - }.
3676   -
3677   -
3678   -define String
3679   - format
3680   - (
3681   - List(Cell_Option) options
3682   - ) =
3683   - if options is
3684   - {
3685   - [ ] then "",
3686   - [h . t] then
3687   - if h is
3688   - {
3689   - core_attrs(core_attr_list) then format_attrs(core_attr_list),
3690   - left then " align=\"left\"",
3691   - h_center then " align=\"center\"",
3692   - right then " align=\"right\"",
3693   - top then " valign=\"top\"",
3694   - v_center then " valign=\"middle\"",
3695   - bottom then " valign=\"bottom\"",
3696   - base_line then " valign=\"baseline\"",
3697   - background_color(c) then " bgcolor=\""+html_format(c)+"\"",
3698   - background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
3699   - width(w) then " width=\""+w+"\"",
3700   - percentage_width(n) then " width=\""+percent(n)+"%\"",
3701   - height(h2) then " height=\""+h2+"\"",
3702   - columns(n) then " colspan=\""+n+"\"",
3703   - rows(n) then " rowspan=\""+n+"\"",
3704   - nowrap then " nowrap",
3705   - class(class_name) then " class=\""+class_name+"\""
3706   - }
3707   - + format(t)
3708   - }.
3709   -
3710   -
3711   - Normalizing a list of cell options (horizontal position must be specified; the default
3712   - is 'left').
3713   -
3714   - Formating cells in a row.
3715   -
3716   -define Printable_tree
3717   - format
3718   - (
3719   - List(HTML_Cell($T)) cells,
3720   - $T -> Printable_tree format_element
3721   - ) =
3722   - if cells is
3723   - {
3724   - [ ] then [ ],
3725   - [h . t] then if h is cell(options,element) then
3726   - ["<td",format(reverse(options)),">",
3727   - format_element(element),
3728   - "</td>"
3729   - . format(t,format_element)]
3730   - }.
3731   -
3732   -define Printable_tree
3733   - format
3734   - (
3735   - List(HTML_Header_Cell($T)) cells,
3736   - $T -> Printable_tree format_element
3737   - ) =
3738   - if cells is
3739   - {
3740   - [ ] then [ ],
3741   - [h . t] then if h is header_cell(options,element) then
3742   - ["<th ",format(reverse(options)),">",
3743   - format_element(element),
3744   - "</th>"
3745   - . format(t,format_element)]
3746   - }.
3747   -
3748   - Formating the rows in a table.
3749   -
3750   -define Printable_tree
3751   - format
3752   - (
3753   - List(HTML_Row($T)) rows,
3754   - $T -> Printable_tree format_element,
3755   - ) =
3756   - if rows is
3757   - {
3758   - [ ] then [ ],
3759   - [h . t] then if h is row(options,cells) then
3760   - ["<tr",format(reverse(options)),">",
3761   - format(cells,format_element),
3762   - "</tr>\n"
3763   - . format(t,format_element)]
3764   - }.
3765   -
3766   -define Printable_tree
3767   - format
3768   - (
3769   - HTML_Header_Row($T) row,
3770   - $T -> Printable_tree format_element
3771   - ) =
3772   - if row is
3773   - {
3774   - empty then [ ],
3775   - header_row(options,cells) then
3776   - ["<thead> <tr",format(reverse(options)),">",
3777   - format(cells,format_element),
3778   - "</tr> </thead>\n"
3779   - ]
3780   - }.
3781   -
3782   -define Printable_tree
3783   - format
3784   - (
3785   - HTML_Footer_Row($T) row,
3786   - $T -> Printable_tree format_element
3787   - ) =
3788   - if row is
3789   - {
3790   - empty then [ ],
3791   - footer_row(options,cells) then
3792   - ["<tfoot> <tr",format(reverse(options)),">",
3793   - format(cells,format_element),
3794   - "</tr> </tfoot>\n"
3795   - ]
3796   - }.
3797   -
3798   -define Printable_tree
3799   - format1
3800   - (
3801   - List(TextAreaOption) l
3802   - ) =
3803   - if l is
3804   - {
3805   - [] then [],
3806   - [h . t] then if h is
3807   - {
3808   - input_attrs(input_attr_list) then [format_attrs(input_attr_list) . format1(t)],
3809   - disabled then [" disabled=\"disabled\" " . format1(t)]
3810   - read_only then [" readonly=\"readonly\" " . format1(t)]
3811   - wrap_lines then [" wrap=\"wrap\" " . format1(t)]
3812   - }
3813   - }.
3814   -
3815   -define Printable_tree
3816   - format
3817   - (
3818   - List(TextAreaOption) l
3819   - ) =
3820   - if member(l,wrap_lines)
3821   - then format1(l)
3822   - else [" wrap=\"off\" " . format1(l)].
3823   -
3824   -
3825   - *** [5.7] Formating elements which may be put anywhere.
3826   -
3827   - The function below involves the parameter $T which is later instantiated as
3828   - 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
3829   - for these two types, and because formating of tables requires recursive calls of such
3830   - functions, it is necessary to provide the 'format' function to be called recursively as
3831   - an argument. Putting naively a call to 'format' will not work, because the compiler
3832   - will look for a function able to format data of type $T (which is at that time distinct
3833   - from any other type, including our two types). Such a function does not exist. Hence
3834   - the function to be called for formating elements must be passed as a functional
3835   - argument (called 'format_element' below). Actually, what we pass is a function taking
3836   - a unique argument of type $T. Other informations (like the name of the state) are
3837   - already in the function by way of full functionality.
3838   -
3839   -
3840   - Formating text options. They are formated in CSS syntax, to be used within a
3841   - 'style=...'.
3842   -
3843   -define String
3844   - format
3845   - (
3846   - List(Text_Option) l
3847   - ) =
3848   - if l is
3849   - {
3850   - [ ] then "",
3851   - [h . t] then if h is
3852   - {
3853   - size(n) then "font-size:"+n+"pt",
3854   - font(fn) then "font-family:"+fn,
3855   - color(c) then if c is rgb(r,g,b) then
3856   - "color:rgb("+to_decimal(r)+","+to_decimal(g)+","+to_decimal(b)+")",
3857   - italic then "font-style:italic",
3858   - oblique then "font-style:oblique",
3859   - small_capitals then "font-variant:small-caps",
3860   - bold then "font-weight:bold",
3861   - underlined then "text-decoration:underline",
3862   - left_justified then "text-align:left",
3863   - right_justified then "text-align:right",
3864   - justified then "text-align:justify",
3865   - line_through then "text-decoration:line-through",
3866   - nowrap then "white-space:nowrap",
3867   - class(class_name)then " class=\"" + class_name +"\"",
3868   - id(id_name) then " id=\"" + id_name +"\"",
3869   - style(style_string) then " style=\"" + style_string + "\"",
3870   - title(s) then " title=\"" + s +"\""
3871   - attr(n,v) then " " + n + "=\"" + v +"\""
3872   - } + if t is [ ] then "" else ("; "+format(t))
3873   - }.
3874   -
3875   -
3876   -
3877   - Formating table options.
3878   -
3879   -define String
3880   - format
3881   - (
3882   - List(Table_Option) l,
3883   - Bool border_seen
3884   - ) =
3885   - if l is
3886   - {
3887   - [ ] then if border_seen then "" else " border=\"0\" cellspacing=\"0\" cellpadding=\"0\"",
3888   - [h . t] then if h is
3889   - {
3890   - background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
3891   - background_image(url) then " background="+url+format(t,border_seen),
3892   - border(o,top,i,c) then " border=\""+o+"\" cellspacing=\""+top+"\" cellpadding=\""+i+"\""+
3893   - //" bordercolor="+format(c)+
3894   - format(t,true),
3895   - width(w) then " width=\""+w+"\""+format(t,border_seen),
3896   - percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
3897   - attr(name, value) then " " + name + "=\"" + value + "\"" +format(t,border_seen),
3898   - class(name) then " class=\""+name+"\""
3899   - }
3900   - }.
3901   -
3902   -
3903   -
3904   -
3905   -
3906   -define Printable_tree
3907   - format_scroller
3908   - (
3909   - String sn,
3910   - Int width,
3911   - Int height,
3912   - Int content_width,
3913   - Int content_height,
3914   - Int idnum, // identifying the scroller
3915   - $T content,
3916   - $T -> Printable_tree format_element
3917   - ) =
3918   - [
3919   - "<script type = \"text/javascript\" language=\"JavaScript\">",
3920   - "function doscroll_",idnum,"(dx,dy) {\n",
3921   - " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
3922   - " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
3923   - idnum,"').style\"); } else\n",
3924   - " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
3925   - " var x_",idnum," = parseInt(c_",idnum,".left);\n",
3926   - " var y_",idnum," = parseInt(c_",idnum,".top);\n",
3927   - " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
3928   - " { x_",idnum," += dx; }\n",
3929   - " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
3930   - " { y_",idnum," += dy; }\n",
3931   - " c_",idnum,".left = x_",idnum,";\n",
3932   - " c_",idnum,".top = y_",idnum,";\n",
3933   - " }\n",
3934   - "</script>\n",
3935   - "<table>",
3936   - "<tbody>",
3937   - "<tr>",
3938   - "<td align=left valign=top",
3939   - " width=",width,
3940   - " height=",height,
3941   - ">",
3942   - "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
3943   - " clip:rect(0px ",width,"px ",height,"px 0px)\">",
3944   - "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
3945   - format_element(content),
3946   - "</div>",
3947   - "</div>",
3948   - "</td>",
3949   - "<td valign=bottom>",
3950   - "<table>",
3951   - "<tbody>",
3952   - "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onmousedown=\"doscroll_", idnum,"(0,20);\"></td></tr>",
3953   - "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onmousedown=\"doscroll_", idnum,"(0,-20);\"></td></tr>",
3954   - "</tbody>",
3955   - "</table>",
3956   - "</td>",
3957   - "</tr>",
3958   - (if content_width > width then
3959   - [
3960   - "<tr>",
3961   - "<td align=right>",
3962   - "<table>",
3963   - "<tbody>",
3964   - "<tr>",
3965   - "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onmousedown=\"doscroll_", idnum,"(20,0);\"></td>",
3966   - "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onmousedown=\"doscroll_", idnum,"(-20,0);\"></td>",
3967   - "</tr>",
3968   - "</tbody>",
3969   - "</table>",
3970   - "</td>",
3971   - "</tr>",
3972   - ] else [ ]),
3973   - "</tbody>",
3974   - "</table>"
3975   - ].
3976   -
3977   -
3978   -
3979   -define Printable_tree
3980   - format
3981   - (
3982   - Body_Option o
3983   - ) =
3984   - if o is
3985   - {
3986   - core_attrs(core_attr_list) then [" " + format_attrs(core_attr_list)],
3987   - background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
3988   - background_image(n) then [" background=", n],
3989   - background_image(n,o2) then [" style=\"background: url(",n,")",format(o2),"\""]
3990   -
3991   - }.
3992   -
3993   -
3994   -
3995   -define Printable_tree
3996   - format
3997   - (
3998   - List(Body_Option) l
3999   - ) =
4000   - if l is
4001   - {
4002   - [ ] then [ ],
4003   - [h . t] then [format(h) . format(t)]
4004   - }.
4005   -
4006   -define Printable_tree
4007   - add_css_files
4008   - (
4009   - List(CSS_File) l
4010   - ) =
4011   - if l is
4012   - {
4013   - [ ] then [ ],
4014   - [h . t] then
4015   - [ ["<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file_name(h) + "\" />\n" ]
4016   - . add_css_files(t)]
4017   - }.
4018   -
4019   -define Printable_tree
4020   - add_js_files_attributes
4021   - (
4022   - List(JS_Attribute) l
4023   - ) =
4024   - if l is
4025   - {
4026   - [ ] then [ ],
4027   - [h . t] then
4028   - if h is attr(name, value) then
4029   - [ " " + name + "=\"" + value + "\""
4030   - . add_js_files_attributes(t)]
4031   - }.
4032   -
4033   -
4034   -define Printable_tree
4035   - add_js_files
4036   - (
4037   - List(JS_File) l
4038   - ) =
4039   - if l is
4040   - {
4041   - [ ] then [ ],
4042   - [h . t] then
4043   - [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\""
4044   - . add_js_files_attributes(attributes(h)) ]
4045   - . ["></script>\n"
4046   - . add_js_files(t)] ]
4047   - }.
4048   -
4049   -define Printable_tree
4050   - add_script
4051   - (
4052   - List(Script) l
4053   - ) =
4054   - if l is
4055   - {
4056   - [ ] then [ ],
4057   - [h . t] then [ "<script type=\""+ script_type(h) +"\">\n"+ content(h) + "\n</script>\n" . add_script(t) ]
4058   - }.
4059   -
4060   -define Printable_tree
4061   - add_css_styles
4062   - (
4063   - List(CSS_Style) css_styles
4064   - ) =
4065   -
4066   - if css_styles is
4067   - {
4068   - [] then [],
4069   - [_ . _] then [ "<style type=\"text/css\"><!--\n",
4070   - format_css_styles(css_styles),
4071   - " --></style>\n"
4072   - ]
4073   - }.
4074   -
4075   -
4076   - // The function below formats a datum of type 'HTML_Any($T)'.
4077   -
4078   -define Printable_tree
4079   - format
4080   - (
4081   - CommonInfo cinfo,
4082   - String sn, // state_name
4083   - Var(Int) ic_v,
4084   - HTML_Any($T) element,
4085   - $T -> Printable_tree format_element, // able to format a datum of type $T
4086   - Bool is_https,
4087   - Var(Int) action_count
4088   - ) =
4089   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
4090   - if element is
4091   - {
4092   - any_text(opts , t) then
4093   - if opts is
4094   - {
4095   - [] then [t],
4096   - [_ . _] then ["<span", format_text_options(opts), ">",t,"</span>"]
4097   - },
4098   - any_preformated(opts,s) then
4099   - ["<span", format_text_options(opts), "><pre>",s,"</pre></span>"],
4100   - //["<pre>",s,"</pre>"],
4101   - any_paragraph(opts,e) then
4102   - ["<p", format_text_options(opts), ">",format_element(e),"</p>\n"],
4103   - any_image(opts, url, alt) then
4104   - ["<img alt=\"",alt,"\" src=\"",url,"\" ", format_attrs(opts)," />"],
4105   - any_image(opts, url, alt, w, h) then
4106   - ["<img alt=\"",alt,"\" src=\"",url,"\" width=\"",w,"\" height=\"",h,"\" ", format_attrs(opts)," />"],
4107   - any_table(opts,h_row, rows, f_row) then
4108   - ["<table",format(reverse(opts),false),">",
4109   - format(h_row, format_element),
4110   - "<tbody>",format(rows,format_element),"</tbody>",
4111   - format(f_row, format_element),
4112   - "</table>\n"],
4113   - any_center(e) then
4114   - ["<center>",format_element(e),"</center>"],
4115   - any_mail_to(email,elem) then
4116   - ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
4117   - any_scroller(w,h,cw,ch,c) then
4118   - format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
4119   - any_fixed_size(w,h,c) then
4120   - with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
4121   - ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
4122   - "secondary document",
4123   - "</object>"],
4124   - any_fixed_size_2(w,h,fn) then
4125   - with url = fn+"?zauth="+make_authorization(site_directory,secret,
4126   - fn),
4127   - ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
4128   - "secondary document",
4129   - "</object>"],
4130   - any_actioner(c,t,a,an,eo,ja,fn) then
4131   - format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https, action_count),
4132   -
4133   - any_foreign_link_new(target, aspect, url) then
4134   - format_foreign_link(target, aspect, url, is_https),
4135   -
4136   - any_foreign_link(options,url) then
4137   - ["<a href=\"",url,"\" ", format_text_options(options), "></a>"], // IE7 doesn't support the form <a href="..." />
4138   - any_foreign_link(options,url,name) then
4139   - ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
4140   - any_private_download(url,name,extra_ext,action) then
4141   - format_private_download(cinfo,sn,url,name,extra_ext,action),
4142   - any_div(options, e) then
4143   - [format_div_option(options), format_element(e),"</div>\n"],
4144   - any_div_empty(options) then
4145   - [format_div_option(options), "</div>\n"],
4146   - any_coreattrs(attributs) then
4147   - [format_attrs(attributs)]
4148   - }.
4149   -
4150   -
4151   -
4152   -
4153   - // *** [5.8] Formating 'in form' elements.
4154   -
4155   -
4156   -define Printable_tree
4157   - maybe_label
4158   - (
4159   - String label_text,
4160   - HtmlId id
4161   - ) =
4162   - if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"]
4163   - else [].
4164   -
4165   -
4166   -define Printable_tree
4167   - format
4168   - (
4169   - CommonInfo cinfo,
4170   - String fn, // form_name
4171   - String sn, // state_name
4172   - Var(Int) ic_v, // idnum counter variable
4173   - HTML_In_Form element,
4174   - Bool is_https,
4175   - Var(Int) action_count
4176   - ) =
4177   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
4178   - with format_element = (HTML_In_Form e) |-> format(cinfo, fn, sn, ic_v, e, is_https, action_count),
4179   - if element is
4180   - {
4181   - literal_pt(t) then t,
4182   - literal(t) then [t],
4183   - sequence(l) then flat(map(format_element,l))
4184   - text(opts,t) then
4185   - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https, action_count),
4186   - preformated(o,s) then
4187   - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https, action_count),
4188   - paragraph(opts,t) then
4189   - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https, action_count),
4190   - image(opts, url, alt) then
4191   - format(cinfo,sn,ic_v,any_image(opts, url, alt),format_element,is_https, action_count),
4192   - image(opts, url, alt, w, h) then
4193   - format(cinfo,sn,ic_v,any_image(opts, url, alt, w, h),format_element,is_https, action_count),
4194   - table(opts,header_row,rows,footer_row) then
4195   - format(cinfo,sn,ic_v,any_table(opts, header_row, rows, footer_row),format_element,is_https, action_count),
4196   - center(e) then
4197   - format(cinfo,sn,ic_v,any_center(e),format_element,is_https, action_count),
4198   - mail_to(a,e) then
4199   - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https, action_count),
4200   - scroller(w,h,cw,ch,c) then
4201   - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https, action_count),
4202   - actioner(c,t,a,an,eo,ja) then
4203   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https, action_count),
4204   -
4205   - foreign_link_new(target, aspect, url) then
4206   - format(cinfo,sn,ic_v,any_foreign_link_new(target,aspect,url),format_element,is_https, action_count),
4207   -
4208   - foreign_link(options,url) then
4209   - format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https, action_count),
4210   - foreign_link(options,url,name) then
4211   - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https, action_count),
4212   - private_download(url,name,extra,action) then
4213   - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https, action_count),
4214   - text_input(options, label_text, id, name, i, w) then
4215   - [ maybe_label(label_text, id),
4216   - "<input type=\"text\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
4217   - text_input_ro(options, label_text, id, name,i,w) then
4218   - [ maybe_label(label_text, id),
4219   - "<input readonly=\"readonly\" type=\"text\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
4220   - password_input(options, label_text, id, name,i,w) then
4221   - [ maybe_label(label_text, id),
4222   - "<input type=\"password\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
4223   - text_area(opts,label_text, id, n,i,w,h) then
4224   - [ maybe_label(label_text, id), "<textarea ",format(opts)," id=\"",id,"\" name=\"",n,"\" cols=\"",w,"\" rows=\"",h,"\">",i,"</textarea>"],
4225   - file_upload(options, label, id, n, w) then
4226   - [ maybe_label(label, id),
4227   - "<input type=\"file\" id=\"",id,"\" size=\"",w,"\" name=\"",n,"\"",format_attrs(options)," />"],
4228   - selector(opts, label, id, n,s,cs) then
4229   - [ maybe_label(label, id),
4230   - "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
4231   - selector(opts, label, id, n,s,cs,sd) then
4232   - [ maybe_label(label, id),
4233   - "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
4234   - selector_c(opts, label, id, n,s,cs) then
4235   - [ maybe_label(label, id),
4236   - "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
4237   - selector_c(opts, label, id, n,s,cs,sd) then
4238   - [ maybe_label(label, id),
4239   - "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
4240   -
4241   - radio_button(options, label_text, id, n, v, c) then
4242   - [ maybe_label(label_text, id),
4243   - "<input type=\"radio\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />"],
4244   - radio_button_r(options, label_text, id, n, v, c) then
4245   - [ "<input type=\"radio\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />",
4246   - maybe_label(label_text, id)],
4247   - check_box(options, label_text, id, n, v, c) then
4248   - [ maybe_label(label_text, id),
4249   - "<input type=\"checkbox\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />"]
4250   - check_box_r(options, label_text, id, n, v, c) then
4251   - [ "<input type=\"checkbox\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />",
4252   - maybe_label(label_text, id)]
4253   - div(options, e) then
4254   - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https, action_count),
4255   - div_empty(options) then
4256   - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https, action_count),
4257   - hidden(id, name, value) then
4258   - ["<input type=\"hidden\" id=\"",id,"\" name=\"",name,"\" value=\"",value,"\" />"],
4259   -
4260   - }.
4261   -
4262   -
4263   -
4264   -
4265   -
4266   -
4267   - *** [5.9] Formating 'off form' elements.
4268   -
4269   - The encryption type 'multipart/form-data' is required for a form containing an upload.
4270   -
4271   -
4272   -define Bool
4273   - contains_an_upload
4274   - (
4275   - HTML_In_Form form_content
4276   - ).
4277   -
4278   -define Bool
4279   - contains_an_upload
4280   - (
4281   - HTML_Row(HTML_In_Form) row
4282   - ) =
4283   - mapor(contains_an_upload,
4284   - map(content,cells(row))).
4285   -
4286   -define Bool
4287   - contains_an_upload
4288   - (
4289   - HTML_Footer_Row(HTML_In_Form) row
4290   - ) =
4291   - if row is
4292   - {
4293   - empty then false,
4294   - footer_row(_, cells) then
4295   - mapor(contains_an_upload,
4296   - map(content,cells))
4297   - }.
4298   -
4299   -define Bool
4300   - contains_an_upload
4301   - (
4302   - HTML_In_Form form_content
4303   - ) =
4304   - if form_content is
4305   - {
4306   - literal_pt(t) then false,
4307   - literal(t) then false,
4308   - sequence(l) then mapor(contains_an_upload,l)
4309   - text(o,t) then false,
4310   - preformated(o,s) then false,
4311   - paragraph(o,t) then false,
4312   - image(o,u,a) then false,
4313   - image(o,u,a,w,h) then false,
4314   - table(o,_,rows,f_row) then if mapor(contains_an_upload,rows) then true
4315   - else contains_an_upload(f_row),
4316   - center(e) then contains_an_upload(e),
4317   - mail_to(m,e) then false, // 'e' may but should not contain an upload
4318   - scroller(w,h,cw,ch,e) then contains_an_upload(e),
4319   - actioner(c,t,a,an,eo,ja) then false,
4320   - foreign_link_new(_,_,_) then false,
4321   - foreign_link(o,u) then false,
4322   - foreign_link(o,u,n) then false,
4323   - private_download(p,n,e,a) then false,
4324   - text_input(o,lt,l,n,i,w) then false,
4325   - text_input_ro(o,lt,l,n,i,w) then false,
4326   - password_input(o,lt,l,n,i,w) then false,
4327   - text_area(o,lt,id,n,i,w,h) then false,
4328   - file_upload(o,l,i,n,w) then true,
4329   - selector(o,l,i,n,s,c) then false,
4330   - selector(o,l,i,n,s,c,p) then false,
4331   - selector_c(o,l,i,n,s,c) then false,
4332   - selector_c(o,l,i,n,s,c,p) then false,
4333   - radio_button(o,_,_,n,v,c) then false,
4334   - radio_button_r(o,_,_,n,v,c) then false,
4335   - check_box(o,_,_,n,v,c) then false,
4336   - check_box_r(o,_,_,n,v,c) then false,
4337   - div(o,c) then contains_an_upload(c),
4338   - div_empty(o) then false,
4339   - hidden(_,_,_) then false
4340   - }.
4341   -
4342   -define String
4343   - enctype
4344   - (
4345   - HTML_In_Form form_content
4346   - ) =
4347   - if contains_an_upload(form_content)
4348   - then " enctype=\"multipart/form-data\""
4349   - else "".
4350   -
4351   -
4352   -
4353   -define Printable_tree
4354   - format
4355   - (
4356   - CommonInfo cinfo,
4357   - String sn, // state_name
4358   - Var(Int) ic_v, // 'idnum' counter variable
4359   - HTML_Off_Form element,
4360   - Bool is_https,
4361   - Var(Int) action_count
4362   - ) =
4363   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
4364   - with format_element = (HTML_Off_Form e) |-> format(cinfo, sn, ic_v, e, is_https, action_count),
4365   - if element is
4366   - {
4367   - literal_pt(t) then t,
4368   - literal(t) then [t],
4369   - sequence(l) then flat(map(format_element,l)),
4370   - text(opts,t) then
4371   - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https, action_count),
4372   - preformated(o,s) then
4373   - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https, action_count),
4374   - paragraph(opts,t) then
4375   - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https, action_count),
4376   - image(opts,url,alt) then
4377   - format(cinfo,sn,ic_v,any_image(opts,url,alt),format_element,is_https, action_count),
4378   - image(opts,url,alt,w,h) then
4379   - format(cinfo,sn,ic_v,any_image(opts,url,alt,w,h),format_element,is_https, action_count),
4380   - table(opts,header_row, rows, footer_row) then
4381   - format(cinfo,sn,ic_v,any_table(opts,header_row, rows, footer_row),format_element,is_https, action_count),
4382   - center(e) then
4383   - format(cinfo,sn,ic_v,any_center(e),format_element,is_https, action_count),
4384   - mail_to(a,e) then
4385   - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https, action_count),
4386   - scroller(w,h,cw,ch,c) then
4387   - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https, action_count),
4388   - fixed_size(w,h,c) then
4389   - format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https, action_count),
4390   - fixed_size_2(w,h,fn) then
4391   - format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https, action_count),
4392   - actioner(c,t,a,an,eo,ja) then
4393   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https, action_count),
4394   - actioner(c,t,a,an,eo,ja,fn) then
4395   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https, action_count),
4396   - foreign_link_new(target, aspect, url) then
4397   - format(cinfo,sn,ic_v,any_foreign_link_new(target,aspect,url),format_element,is_https, action_count),
4398   - foreign_link(options,url) then
4399   - format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https, action_count),
4400   - foreign_link(options,url,name) then
4401   - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https, action_count),
4402   - private_download(url,name,extra,action) then
4403   - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https, action_count),
4404   - label(n) then ["<a name=\"",n,"\">"],
4405   - form(fn,attributs, c) then
4406   - [
4407   - "<form id=\"",fn,"\"",
4408   - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https, action_count),
4409   - " method=\"post\"",
4410   - enctype(c),
4411   - " action=\"http",
4412   - if is_https then "s" else "",
4413   - "://",common_name,":",to_decimal(http_port),"/\">",
4414   - // action is set dynamically by
4415   - // the actioner using JavaScript
4416   - if fn is htmlId(id) then
4417   - format(cinfo,id,sn,ic_v,c,is_https, action_count),
4418   - "</form>"
4419   - ]
4420   - form(fn,attributs, action_name, extra_ops, c) then
4421   - with url = make_actioner_url(cinfo, same, same,
4422   - sn, action_name, extra_ops, is_https),
4423   - [
4424   - "<form id=\"",fn,"\"",
4425   - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https, action_count),
4426   - " method=\"post\"",
4427   - enctype(c),
4428   - " action=\"" + url + "\">",
4429   -// " action=\"http",
4430   -// if is_https then "s" else "",
4431   -// "://",common_name,":",http_port,"/\">",
4432   - // action is set dynamically by
4433   - // the actioner using JavaScript
4434   - if fn is htmlId(id) then
4435   - format(cinfo,id,sn,ic_v,c,is_https, action_count),
4436   - "</form>"
4437   - ]
4438   - div(options, e) then
4439   - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https, action_count),
4440   - div_empty(options) then
4441   - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https, action_count),
4442   - iframe(options, css_styles, css_files, js_files, body) then
4443   - if body is body(body_options,element) then
4444   - [ "<iframe", format_attrs(options), ">\n",
4445   - "<html style=\"margin: 0pt; padding: 0pt;\">\n",
4446   - "<head>\n",
4447   - add_css_styles(css_styles),
4448   - add_css_files(css_files),
4449   - add_js_files(js_files),
4450   - "</head>\n",
4451   - "<body ", format(body_options), ">\n", // format body options
4452   - format(cinfo,sn,ic_v,element,is_https, action_count),
4453   - "</body>\n",
4454   - "</html>\n",
4455   - "</iframe>\n",
4456   - ],
4457   -
4458   - }.
4459   -
4460   -
4461   -
4462   -
4463   - *** [5.10] Formating meta-tags.
4464   -
4465   -define Printable_tree
4466   - format_keywords
4467   - (
4468   - List(String) l
4469   - ) =
4470   - if l is
4471   - {
4472   - [] then [ ],
4473   - [h . t] then if t is []
4474   - then [h]
4475   - else [h , ", " . format_keywords(t)]
4476   - }.
4477   -
4478   -
4479   -define Printable_tree
4480   - format
4481   - (
4482   - CommonInfo cinfo,
4483   - String state_name,
4484   - HTML_Meta m,
4485   - Bool is_https
4486   - ) =
4487   - if m is
4488   - {
4489   - keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],
4490   - refresh(co,ta,an,delay) then
4491   - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
4492   - make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\" />\n"],
4493   - refresh(url,delay) then
4494   - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",url,"\" />\n"],
4495   - meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],
4496   - http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],
4497   - generic_meta(l) then ["<meta ",
4498   - flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
4499   - l)),
4500   - " />\n"],
4501   - literal(s) then [s]
4502   - }.
4503   -
4504   -define Printable_tree // c'est le nรดtre et c'est meilleur
4505   - format
4506   - (
4507   - HTML_Meta m,
4508   - ) =
4509   - if m is
4510   - {
4511   - keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],
4512   - refresh(co,ta,an,delay) then
4513   - ["<!-- TODO refresh(co,ta,an,delay) --> "], // TODO
4514   - refresh(url,delay) then
4515   - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",url,"\" />\n"],
4516   - meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],
4517   - http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],
4518   - generic_meta(l) then ["<meta ",
4519   - flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
4520   - l)),
4521   - " />\n"],
4522   - literal(s) then [s]
4523   - }.
4524   -
4525   -
4526   -define Printable_tree
4527   - format
4528   - (
4529   - CommonInfo cinfo,
4530   - String state_name,
4531   - List(HTML_Meta) metas,
4532   - Bool is_https,
4533   - String charset
4534   - ) =
4535   - if metas is
4536   - {
4537   - [] then [format(cinfo,state_name,http_equiv("content-type",
4538   - "text/html; charset="+charset),is_https)],
4539   - [h . t] then [format(cinfo,state_name,h,is_https)
4540   - . format(cinfo,state_name,t,is_https,charset)]
4541   - }.
4542   -
4543   -define Printable_tree
4544   - _format_html_head
4545   - (
4546   - List(HTML_Head_Tag) lwf,
4547   - Printable_tree so_far
4548   - )
4549   - =
4550   - if lwf is
4551   - {
4552   - [] then reverse(so_far),
4553   - [ h . t] then
4554   - if h is
4555   - {
4556   - meta(meta) then
4557   - [format(meta) . _format_html_head(t, so_far)],
4558   - title(title) then
4559   - ["<title>"+title+"</title>\n" . _format_html_head(t, so_far)],
4560   - js(jsf) then
4561   - [add_js_files([jsf]) . _format_html_head(t, so_far)],
4562   - js_inline(jsi) then
4563   - ["<script>"+content(jsi)+"</script>\n" . _format_html_head(t, so_far)],
4564   - css(cssf) then
4565   - ["<link rel=\"stylesheet\" type=\"text/css\" href=\""+file_name(cssf)+"\" />\n" . _format_html_head(t, so_far)],
4566   - css_inline(cssi) then
4567   - ["<style>"+cssi+"</style>\n" . _format_html_head(t, so_far)]
4568   - }
4569   - }
4570   - .
4571   -
4572   -public define Printable_tree
4573   - format_html_head
4574   - (
4575   - List(HTML_Head_Tag) lwf,
4576   - String charset
4577   - )
4578   - =
4579   - with lwf = (List(HTML_Head_Tag))[ meta(http_equiv("content-type", "text/html; charset="+charset)) . lwf],
4580   - if lwf is
4581   - {
4582   - [] then
4583   - [],
4584   - [ h . t] then
4585   - _format_html_head(lwf, [])
4586   - }
4587   - .
4588   -
4589   -define Printable_tree
4590   - format
4591   - (
4592   - CommonInfo cinfo,
4593   - String state_name,
4594   - List(HTTP_header) additional_headers,
4595   - HTTP_Answer page,
4596   - Bool is_https,
4597   - String charset
4598   - ) =
4599   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
4600   - with ic_v = var((Int)0),
4601   - if page is
4602   - {
4603   - html_page(status, head_tags, body) then
4604   - if body is body(options,element) then
4605   - if format(status) is (status_string, status_headers) then
4606   - with answer_body =
4607   - [doctype_w3c_header,
4608   - html_header,
4609   -
4610   - "<head>\n", format_html_head(head_tags, charset),"</head>\n",
4611   -
4612   - "<body ", format(options), ">", // format body options
4613   - format(cinfo,state_name,ic_v,element,is_https, var(0)),
4614   - "</body>\n",
4615   -
4616   - "</html>"],
4617   - [ "HTTP/1.1 " + status_string, crlf,
4618   - format_headers(standard_headers),
4619   - format_headers(standard_headers_for("text/html", length(answer_body), success(charset))),
4620   - format_headers(status_headers),
4621   - format_headers(additional_headers),
4622   - crlf
4623   - . answer_body
4624   - ]
4625   -
4626   - html_page(status, title, metas, css_styles, css_files, js_files, script, body) then
4627   - if body is body(options,element) then
4628   - if format(status) is (status_string, status_headers) then
4629   - with answer_body =
4630   - [ doctype_w3c_header,
4631   - html_header,
4632   - "<head>\n",
4633   - add_css_styles(css_styles),
4634   - add_css_files(css_files),
4635   - add_js_files(js_files),
4636   - add_script(script),
4637   - "<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n",
4638   - "<script type = \"text/javascript\" language=\"JavaScript\">",
4639   - " function show_local_popup(divname,stvname) {",
4640   - " if (document.layers) { var d = eval(document.divname); } else\n",
4641   - " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
4642   - " if (document.all) { var d = eval(document.all.divname.style)};\n",
4643   - // " alert(typeof(eval(stvname))); ",
4644   - " var s = eval(stvname); ",
4645   - " if (s[0]==0) ",
4646   - " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
4647   - " { s[0]=0; d.style.visibility = 'hidden'; }; }",
4648   - "</script>\n",
4649   - "<title>",title,"</title>\n", // put title
4650   - format(cinfo,state_name,metas,is_https,charset), // format the metas
4651   - "</head>\n",
4652   - "<body ", format(options), ">", // format body options
4653   - //"<center>",
4654   - format(cinfo,state_name,ic_v,element,is_https, var(0)),
4655   - //"</center>",
4656   - "</body>\n",
4657   - "</html>"
4658   - ],
4659   - [ "HTTP/1.1 " + status_string, crlf,
4660   - format_headers(standard_headers),
4661   - format_headers(standard_headers_for("text/html", length(answer_body), success(charset))),
4662   - format_headers(status_headers),
4663   - format_headers(additional_headers),
4664   - crlf
4665   - . answer_body
4666   - ],
4667   -
4668   - plain_text (HTTP_Status status, String text) then
4669   - if format(status) is (status_string, status_headers) then
4670   - [ "HTTP/1.1 " + status_string, crlf,
4671   - format_headers(standard_headers),
4672   - format_headers(standard_headers_for("text/plain", length(text), success(charset))),
4673   - format_headers(status_headers),
4674   - format_headers(additional_headers),
4675   - crlf,
4676   - text . (Printable_tree)[]
4677   - ],
4678   -
4679   - custom_text(HTTP_Status status, String mime_type, String content) then
4680   - if format(status) is (status_string, status_headers) then
4681   - [ "HTTP/1.1 " + status_string, crlf,
4682   - format_headers(standard_headers),
4683   - format_headers(standard_headers_for(mime_type, length(content), success(charset))),
4684   - format_headers(status_headers),
4685   - format_headers(additional_headers),
4686   - crlf,
4687   - content . (Printable_tree)[]
4688   - ],
4689   - custom_binary(HTTP_Status status, String mime_type, ByteArray content, List(HTTP_header) other_headers) then
4690   - if format(status) is (status_string, status_headers) then
4691   - [ "HTTP/1.1 " + status_string, crlf,
4692   - format_headers(status_headers),
4693   - format_headers(standard_headers_for(mime_type, length(content), failure)),
4694   - format_headers(status_headers),
4695   - format_headers(other_headers),
4696   - format_headers(additional_headers),
4697   - crlf,
4698   - content . (Printable_tree)[]
4699   - ],
4700   -
4701   - custom_tree(HTTP_Status status, String mime_type, Printable_tree content) then
4702   - if format(status) is (status_string, status_headers) then
4703   - [ "HTTP/1.1 " + status_string, crlf,
4704   - format_headers(standard_headers),
4705   - format_headers(standard_headers_for(mime_type, length(content), success(charset))),
4706   - format_headers(status_headers),
4707   - format_headers(additional_headers),
4708   - crlf .
4709   - content
4710   - ],
4711   -
4712   - http_raw(Printable_tree content) then content,
4713   -
4714   - html_content(HTTP_Status status, HTML_Off_Form content_HTML) then
4715   - if format(status) is (status_string, status_headers) then
4716   - with content = format(cinfo, state_name, ic_v, content_HTML, is_https, var(0)),
4717   - [ "HTTP/1.1 " + status_string, crlf,
4718   - format_headers(standard_headers),
4719   - format_headers(standard_headers_for("text/html", length(content), success(charset))),
4720   - format_headers(status_headers),
4721   - format_headers(additional_headers),
4722   - crlf .
4723   - content
4724   - ],
4725   - }.
4726   -
  1 +๏ปฟ
  2 +
  3 + *Project* Anubis
  4 +
  5 + *Title* Making interactive Web sites.
  6 +
  7 + *Copyright* Copyright (c) Alain Proutรฉ 2004-2005.
  8 + Copyright (c) Calexium 2007.
  9 +
  10 +
  11 + *Authors* Alain Proutรฉ
  12 + David Renรฉ
  13 +
  14 + *Revised* May 2007
  15 +
  16 +
  17 + *Overview*
  18 +
  19 + In this file we propose simple tools for making well structured interactive and secured
  20 + web sites.
  21 +
  22 +
  23 + ----------------------------------- Table of Contents ---------------------------------
  24 +
  25 + * (1) Structure of a web site.
  26 + ** (1.1) Three sorts of data.
  27 + ** (1.2) How requests are handled.
  28 + ** (1.3) What web pages are made of.
  29 + ** (1.4) Actions.
  30 + ** (1.5) States.
  31 +
  32 + * (2) Carrying on.
  33 + ** (2.1) Describing your web sites.
  34 + ** (2.2) Directories on the server's disk.
  35 + ** (2.3) Starting your web sites.
  36 +
  37 + * (3) The HTML interface.
  38 + ** (3.1) Types used by the HTML interface.
  39 + ** (3.2) ``in form'' versus ``off form''.
  40 + ** (3.3) Defining your own style.
  41 + ** (3.4) Actioners and forms.
  42 + ** (3.5) Local popup.
  43 +
  44 + ---------------------------------------------------------------------------------------
  45 +
  46 +
  47 +read tools/basis.anubis
  48 +read tools/printable_tree.anubis
  49 +read tools/base64.anubis
  50 +read system/string.anubis
  51 +read system/logger.anubis
  52 +read CXM_common.anubis
  53 +read CXM_multihost_http_server.anubis
  54 +read web/mime.anubis
  55 +read CXM_cookies.anubis
  56 +
  57 +
  58 + * (1) Structure of a web site.
  59 +
  60 + First of all we need to explain what a web site should be made of. Ideally, the
  61 + visitor (also called the 'client') should see the web site working as any other
  62 + interactive computer software. So, it should be clear that a 'session' (i.e. a visit
  63 + to the web site, including the consultation of several pages) is some kind of
  64 + conversation between the visitor and the web site, and that the web site should
  65 + maintain a 'current state' of this conversation. At each new request (click) from the
  66 + visitor, this state must be updated. This whole conversation is called a 'session' and
  67 + should not be confused with a single request.
  68 +
  69 +
  70 +
  71 + ** (1.1) Three sorts of data.
  72 +
  73 + All the data needed for putting a web site at work may be dispatched into three
  74 + categories:
  75 +
  76 + 1. Constant data (data that never change). These data may be hard coded into the
  77 + Anubis source files of the web site.
  78 +
  79 + 2. Permanent data (data which always exist independantly of the users connected to
  80 + the web site). These data are normally recorded into data bases.
  81 +
  82 + 3. Session data (data which depend on a particular visitor and which exist only
  83 + during the time he visits the web site). These data are stored into so-called
  84 + 'states'.
  85 +
  86 +
  87 + It is important to determine which data belongs to which category. This is part of your
  88 + design decisions.
  89 +
  90 +
  91 +
  92 + ** (1.2) How requests are handled.
  93 +
  94 + We want to separate the following two functionalities (which are used at each request
  95 + (click) during a single session):
  96 +
  97 + - computing the new state from the previous state and from the client request, and
  98 + updating the data base,
  99 +
  100 + - computing the page to be sent to the client from the new current state and from
  101 + the informations in the data base.
  102 +
  103 +
  104 + The next picture shows the structure we have in mind:
  105 +
  106 +
  107 + request +---------+ HTML page (with a hidden state name)
  108 + .-------------------| client |<--------------.
  109 + | .-----------------| | |
  110 + | | previous state +---------+ |
  111 + | | name (if any) |
  112 + | | | client side
  113 + ............................................................................
  114 + | | | server side
  115 + | | |
  116 + | | .-------------------. |
  117 + | | | previous state | |
  118 + V V V | |
  119 + +---------------+ +---------------+ +--------------+
  120 + | compute state | | server's disk | | compute page |
  121 + +---------------+ +---------------+ +--------------+
  122 + ^ | | ^ ^ ^ ^ ^
  123 + | | | | | | | |
  124 + | | `--------------------+--------------------' | |
  125 + | | new state | | |
  126 + read | `------------------------+--------------------' |
  127 + write | new state name |
  128 + update V |
  129 + +-----------+ |
  130 + | data base |--------------------------------------------'
  131 + +-----------+ read only
  132 +
  133 +
  134 + When the client begins a session, there is no previous state. In this case, a default
  135 + 'initial state' is used instead.
  136 +
  137 + The data base may be updated by 'compute state' box, but should not be update by the
  138 + 'compute page' box. The 'compute page' box should be allowed only to read the data
  139 + base.
  140 +
  141 + In this file, all the above stuff is defined, except the 'compute state' and 'compute
  142 + page' boxes. You just have to provide the function for computing a new state (compute
  143 + state) and the function for computing the page (compute page) from the new state. You
  144 + don't have to worry about state names, saving and retrieving states and the like.
  145 +
  146 +
  147 +
  148 +
  149 +
  150 + ** (1.3) What web pages are made of.
  151 +
  152 + What the client can see in his browser's window may be called a 'page'. Within a page,
  153 + we have several sorts of components:
  154 +
  155 + - 'local' components, i.e. all components which do not open a connection, like
  156 + texts, images, etc... possibly using JavaScript programmation,
  157 +
  158 + - 'actioners', which, when clicked upon, open a connection with our web site; they
  159 + may appear as links or buttons, etc...
  160 +
  161 + - 'foreign links', which when clicked upon, open a connection with another web site
  162 + (or ours eventually).
  163 +
  164 + Of course, what an actioner does is just ask our web site to perform an action. To that
  165 + end, the actioner essentially sends the name of the action to be performed. However, it
  166 + may be necessary to provide additional informations which may be seen as 'operands' of
  167 + the action. In order to attach operands to an action, HTML provides the notion of
  168 + 'form'. Indeed, a form contains essentially a set of input fields into which the client
  169 + may put values for the required operands of the action, and a submit button, which is
  170 + the actioner itself. Notice that a single form may contain several submit buttons,
  171 + which simply means that there are several distincts actions taking the same set of
  172 + operands.
  173 +
  174 + Restrictions must be put on the use of all theses gadgets. Indeed, for example,
  175 + putting a form within another form is officially meaningless in HTML, and the client's
  176 + browser may be seriously disturbed by this. In this file, we propose an interface to
  177 + the HTML language, which forbids such meaningless things, simply by imposing a strict
  178 + typing of HTML concepts.
  179 +
  180 + Each web site may be accessible through two communication channels:
  181 +
  182 + - a non secured channel (HTTP),
  183 + - a secured channel (HTTPS).
  184 +
  185 + Nevertheless, the whole thing should be considered as a single web site. For example,
  186 + you may have a secured page, obtained through HTTPS, containing public images obtained
  187 + through HTTP. An actioner in a non secured page may open a secured connection, and
  188 + conversely.
  189 +
  190 + Summarizing, a web page is made of local elements, foreign links and actioners.
  191 + Actioners receive operands from forms, and they also choose to communicate through the
  192 + non secured or through the secured channel.
  193 +
  194 +
  195 +
  196 + +-------------------+
  197 + | page |
  198 + | | +---------------+
  199 + | +--------------+ | | next page |
  200 + | | form | | | (non secured) |
  201 + | | +----------+ | | HTTP | |
  202 + | | | actioner |---------------------------->| |
  203 + | | +----------+ | | +---------------+
  204 + | | | |
  205 + | | +----------+ | | +---------------+
  206 + | | | actioner |---------------------------->| next page |
  207 + | | +----------+ | | HTTPS | (secured) |
  208 + | | | | | |
  209 + | +--------------+ | | |
  210 + | | +---------------+
  211 + | |
  212 + +-------------------+
  213 +
  214 +
  215 + Notice that actioners need no be necessarily put into forms. In that case, they work as
  216 + ordinary links, but they still may receive operands as we shall see.
  217 +
  218 +
  219 +
  220 +
  221 + ** (1.4) Actions.
  222 +
  223 + The client opens a new connection with our web site whenever he clicks on an
  224 + actioner. The result is that a request is sent, essentially made of a list of 'web
  225 + arguments'. Each web argument is a pair (name,value). One of these web arguments, the
  226 + 'action' web argument (whose name is "a"), determines the action to be performed. The
  227 + other web arguments (not including "s", used to identify the state) are the operands
  228 + for this action.
  229 +
  230 + Hence, the 'compute state' box in the picture above, splits naturally into as many
  231 + sub-boxes as there are actions. For this reason, we define the following type for
  232 + representing actions (where '$State' is the type representing session informations):
  233 +
  234 +public type Web_Action($State):
  235 + http_action (String name, // name of action
  236 + ($State) -> Bool allow, // true if action allowed
  237 + (HTTP_Info http_info,
  238 + List(Web_arg) web_args, // actually only 'operands' web arguments
  239 + $State state) -> $State do_it),
  240 + https_action (String name, // name of action
  241 + ($State) -> Bool allow, // true if action allowed
  242 + (HTTP_Info http_info,
  243 + List(Web_arg) web_args, // actually only 'operands' web arguments
  244 + $State state) -> $State do_it),
  245 + http_https_action (String name,
  246 + ($State) -> Bool allow,
  247 + (HTTP_Info http_info,
  248 + List(Web_arg) web_args,
  249 + $State state) -> $State do_it).
  250 +
  251 + 'http_action's are executed only under HTTP, and 'https_action's are executed only
  252 + under HTTPS. 'http_https_action's may be executed under both types of connections.
  253 +
  254 + Each action has a name, which is used to identify the action. Each action also has a
  255 + function 'allow' whose job is to verify that the action is allowed in the current
  256 + state, and a function 'do_it' for performing the action. The function 'do_it' receives
  257 + a lot of informations:
  258 +
  259 + - 'HTTP informations':
  260 + - the IP address of the client,
  261 + - the URI requested by the client (after redirection),
  262 + - the list of HTTP headers generated by the client's browser,
  263 + - the list of web arguments sent by the client (except "s" and "a"),
  264 + - the previous state (or the 'initial' or 'ticket expired' state if no previous
  265 + state can be found).
  266 +
  267 + In most cases, HTTP informations are not used. This is the reason why they are gathered
  268 + for simplicity into a unique datum of type 'HTTP_Info'.
  269 +
  270 +// For your convenience, we introduce the following simpler variants:
  271 +//
  272 +//public define Web_Action($State)
  273 +// http_action
  274 +// (
  275 +// String name,
  276 +// $State -> Bool allow,
  277 +// (List(Web_arg),$State) -> $State do_it
  278 +// ) =
  279 +// http_action(name,
  280 +// (Maybe($State) ms) |-> if ms is
  281 +// {
  282 +// failure then true,
  283 +// success(s) then allow(s)
  284 +// },
  285 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  286 +// {
  287 +// failure then (failure, []),
  288 +// success(s2) then (success(do_it(l,s2)), [])
  289 +// }).
  290 +//
  291 +//public define Web_Action($State)
  292 +// https_action
  293 +// (
  294 +// String name,
  295 +// $State -> Bool allow,
  296 +// (List(Web_arg),$State) -> $State do_it
  297 +// ) =
  298 +// https_action(name,
  299 +// (Maybe($State) ms) |-> if ms is
  300 +// {
  301 +// failure then true,
  302 +// success(s) then allow(s)
  303 +// },
  304 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  305 +// {
  306 +// failure then (failure, []),
  307 +// success(s2) then (success(do_it(l,s2)), [])
  308 +// }).
  309 +//
  310 +//public define Web_Action($State)
  311 +// http_https_action
  312 +// (
  313 +// String name,
  314 +// $State -> Bool allow,
  315 +// (List(Web_arg),$State) -> $State do_it
  316 +// ) =
  317 +// http_https_action(name,
  318 +// (Maybe($State) ms) |-> if ms is
  319 +// {
  320 +// failure then true,
  321 +// success(s) then allow(s)
  322 +// },
  323 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  324 +// {
  325 +// failure then (failure, []),
  326 +// success(s2) then (success(do_it(l,s2)), [])
  327 +// }).
  328 +
  329 +
  330 +
  331 + When you define your web site, you must provide the list of all the actions of the
  332 + site. When a new state has been computed, a graphical representation of this state
  333 + must be sent to the client. To that end, you must provide a function (named below
  334 + 'compute_page') of type:
  335 +
  336 + $State -> HTTP_Answer
  337 +
  338 + where the type 'HTTP_Answer' (defined below in this file) abstractly represents HTML
  339 + pages.
  340 +
  341 +public type HTTP_Answer:...
  342 +
  343 + It should be clear that states and pages are deeply linked together. Indeed, we really
  344 + understand the page shown to the client as a representation of the current state of the
  345 + conversation between the client and the web site, but also containing informations
  346 + taken from the data bases.
  347 +
  348 +
  349 +
  350 +
  351 +
  352 + ** (1.5) States.
  353 +
  354 + Now, we explain how you can define the type (say 'State') to be used as an instance of
  355 + the type parameter '$State'. The following is just a suggestion.
  356 +
  357 + Each state determines a page (since 'compute_page' computes a page from a
  358 + state). However, some components of the state may be independant of the page. It may be
  359 + the case for example for the indication of the natural language used by the
  360 + client. Hence, a state should be made of (at least) two parts:
  361 +
  362 + - informations which are the same for all pages,
  363 + - informations which are particular to each page.
  364 +
  365 + For example, you could define:
  366 +
  367 + type Page: // one alternative per page, with particular informations
  368 + login(...), // in the components
  369 + main_page(...),
  370 + ...etc...
  371 +
  372 + Now, the type 'State' could be defined as follows:
  373 +
  374 + type State:
  375 + state(Language, // informations valid for all pages
  376 + ...,
  377 + Page). // informations particular to a page
  378 +
  379 + However, if you are making a secured web site within which clients should be identified
  380 + (by id and password), it may be a good idea to have two sorts of states, one for non
  381 + identified clients and one for identified clients. In this case, define the type
  382 + 'State' as follows (this is just a suggestion):
  383 +
  384 + type State:
  385 + non_identified(Language),
  386 + identified(String id,
  387 + Language,
  388 + Page).
  389 +
  390 + When a request arrives, check if the previous state is 'identified(...)' or
  391 + 'non_identified(...)', and don't provide access to certain pages to non identified
  392 + clients. This is required for security.
  393 +
  394 + Some more words on security. If your site needs to identify clients, define the
  395 + initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
  396 + password of the client. If the id and password are correct, then change the state of
  397 + the client to 'identified(...)'. No other action should be able to do that. Now, be
  398 + confident that clients cannot forge states. The only information they have is the name
  399 + of a state, not the state itself which is never sent over the network, but only stored
  400 + on the server's disk. The name of the state is constructed using strong cryptographical
  401 + methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
  402 + state names cannot be seen by a third party. So, if the system retrieves a previous
  403 + state of the form 'identified(...)', you can be confident that your client is well
  404 + identified, and you can send him confidential informations.
  405 +
  406 + States have a limited life time. It may happen that a client clicks on a button at a
  407 + time its state is out of date. In this case, this system considers that the new state
  408 + is a special state named 'ticket expired'. You must provide a function producing this
  409 + state when you describe your web site. The page corresponding to this state must just
  410 + inform the client that he/she waited a too long time before clicking on a button, and
  411 + has to restart (a new conversation) from the begining.
  412 +
  413 +
  414 +
  415 + * (2) Carrying on.
  416 +
  417 + ** (2.1) Describing your web sites.
  418 +
  419 + Before you may start your web site, you must describe it, i.e. produce a datum of the
  420 + opaque type 'Web_Site'.
  421 +
  422 +public type Web_Site:...
  423 +
  424 + Producing such a datum may be performed by:
  425 +
  426 +public define Web_Site
  427 + make_web_site_description
  428 + (
  429 + List(String) common_names, // for example: ["www.our-business.com",
  430 + // "192.168.0.1"]
  431 + // the second one is just for testing
  432 + String site_directory, // where 'public' and other directories are
  433 + // located (should NOT end with '/')
  434 + String state_directory,
  435 + One -> One init,
  436 + (HTTP_Info,
  437 + List(Web_arg),
  438 + Bool is_https) -> $State initial_state,
  439 + ($State expired,
  440 + Maybe(String),
  441 + HTTP_Info,
  442 + List(Web_arg),
  443 + Bool is_https) -> $State ticket_expired_state,
  444 + (Maybe(String),
  445 + HTTP_Info,
  446 + List(Web_arg),
  447 + Bool is_https) -> $State ticket_lost_state,
  448 + List(Web_Action($State)) actions,
  449 + $State -> HTTP_Answer compute_page,
  450 + $State -> List(HTTP_header) additional_headers,
  451 + Int timeout, // seconds (todo: minutes)
  452 + Redirections redirections,
  453 + String charset,
  454 + List(String) journal_extensions,
  455 + List(String) journal_headers,
  456 + String authorization_secret,
  457 + List(MIME) known_mime_types,
  458 + (String action_name,
  459 + List(Web_arg) args)-> One before_send_file
  460 + ).
  461 +
  462 +
  463 + Explanations:
  464 +
  465 + 'common_names' is the list of names of the site (the name the browser must send as the
  466 + value of the 'Host' HTTP header in order to access the site must be in that list). Such
  467 + a name generally looks like this:
  468 +
  469 + www.somewhere.com
  470 +
  471 + If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
  472 + the site must be exactly the same as the name on the certificate (which is precisely
  473 + called the 'common name' in the X.509 jargon). If the two names do not match, the site
  474 + will still work, but the transaction will not be transparent to the client. His browser
  475 + will complain that the name of the certificate does not match the name of the site, and
  476 + he will have to accept the certificate manually.
  477 +
  478 + 'site_directory' is the absolute path to the directory where the files needed by the
  479 + site are located. Usually this directory looks like:
  480 +
  481 + my_anubis/web_sites/www.somewhere.com
  482 +
  483 + However, this information is not computed from 'common_name', so that you can change
  484 + the common name (for example temporarily, for networking reasons) without loosing
  485 + access to the files.
  486 +
  487 + 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
  488 + whose graphical representation is a page explaining to the user that its 'ticket' (or
  489 + 'session information') has expired, and that he/she must close all popup windows and
  490 + start a new session. The arguments of the function contain the previous (expired)
  491 + state and all current informations concerning the user. This arguments may be useful
  492 + for example for producing the expiration message in the language chosen by the user.
  493 + You can also (and this may be much smarter) send a 'ticket prolongation page'
  494 + (including a new login for example), and resume the same conversation, since you have
  495 + all the pertinent informations at hand. In the case the ticket is definitely lost, the
  496 + second fonction 'ticket_lost_state' is used.
  497 +
  498 + Notice that despite the fact that the parameter $State is involved in the arguments of
  499 + the above function, the type 'Web_Site' does not depend on this parameter. This allows
  500 + to produce lists of web site descriptions, where each description may be constructed
  501 + with a different instance of $State. This is required because distinct sites must have
  502 + distinct types of session informations. This is made possible by the fact that the
  503 + type is obscure, and the constructor replaced by a function which assembles
  504 + 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
  505 + entity not depending on $State. You should have a look to the private part of this file
  506 + if you want more precisions about this programming technique.
  507 +
  508 + 'charset' is a string which will determine the character encoding to be used by the
  509 + browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
  510 + etc...
  511 +
  512 + 'before_send_file' is a function which is executed just before the HTTP server sends a
  513 + file. It gets an action name and the web arguments received with the request for that
  514 + file. Notice that this action name and these web arguments may be put into a
  515 + 'private_download' element, and will come back to the server at the time of the
  516 + download.
  517 +
  518 +
  519 +
  520 + ** (2.2) Directories on the server's disk.
  521 +
  522 + The description of your site contains the name of the directory within which the
  523 + required files are located. This may be for example:
  524 +
  525 + my_anubis/web_sites/www.our-business.com/
  526 +
  527 + This is called the 'site directory' (for the given site). Within the site directory,
  528 + the following directories are created by this program:
  529 +
  530 + states
  531 + public
  532 + journal
  533 + private_download
  534 + upload_temporary
  535 +
  536 + The directory 'states' is used for storing states (session informations). Out of date
  537 + states are automatically removed after some time.
  538 +
  539 + The tree rooted at 'public' contains files that the server is allowed to send to the
  540 + clients. For security reasons, the server never sends a file which is not within the
  541 + tree whose root is this 'public' directory (except for the 'private download' mecanism;
  542 + see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
  543 + must have been recognized before the file may be sent.
  544 +
  545 + The directory 'journal' contains the jounal files. The roles of the remaining
  546 + directories 'private_download' and 'upload_temporary' is explained in
  547 + 'multihost_http_server.anubis', where you will also find further informations on
  548 + 'public' and 'journal'.
  549 +
  550 +
  551 +
  552 +
  553 + ** (2.3) Web servers parameters.
  554 +
  555 + The web servers have several parameters useful for administration. They are described
  556 + as follows:
  557 +
  558 + public type WebServersParameters:
  559 + wsparms(Var(Bool) shutdown_required,
  560 +
  561 +
  562 +
  563 +
  564 + ** (2.4) Starting your web sites.
  565 +
  566 + When you have described all your web sites (you may want to have several web sites, and
  567 + they are distinguished by their 'common name'), you may start them all together using
  568 + 'start_web_sites' below. This function returns a result of the following type:
  569 +
  570 +public type Start_Web_Sites_Result:
  571 + cannot_bind_to_port(Word32),
  572 + cannot_bind_to_port(Word32,Word32),
  573 + ok(Server http_server,
  574 + Server https_server).
  575 +
  576 + Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
  577 + ports (or to both). The main reason is that another server is already listening on that
  578 + port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
  579 + the 's' bit has not been set for 'user' and 'group' (there is not such problem under
  580 + Windows). Also notice that the Linux kernel may need a rather long time (up to several
  581 + minutes) before liberating a listening port. Now, if the system can bind to the two
  582 + ports, the pair of the two servers is returned. Two tools are useful for manipulating
  583 + servers:
  584 +
  585 + shutdown of type Server -> One
  586 + is_down of type Server -> Bool
  587 +
  588 + They are defined in 'predefined.anubis' (together with the type 'Server').
  589 +
  590 +
  591 +public define Start_Web_Sites_Result
  592 + start_web_sites
  593 + (
  594 + Word32 ip_address, // the IP address shared by the web sites
  595 + Word32 http_port, // usually: 80
  596 + Word32 https_port, // usually: 443
  597 + String ssl_certificate_common_name,
  598 + List(Web_Site) web_sites, // web sites to be started
  599 + (One) -> Bool shutdown_required
  600 + ).
  601 +
  602 + 'ip_address' is the IP address on which the two servers listen. If you put 0, the
  603 + servers listen on all the IP addresses of the machine. This may be useful if the
  604 + machine has several network interfaces.
  605 +
  606 + 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
  607 + loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
  608 + certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
  609 + and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
  610 +
  611 + Notice that the number of servers is always 2, regardless of the number of web sites
  612 + you are starting.
  613 +
  614 + The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
  615 + servers from within the web site (typically the administration part). The servers will
  616 + shutdown as soon as this variable contains 'true'. So you must provide a variable
  617 + containing 'false' otherwise your servers will not run. You may also use the primitive
  618 + 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
  619 +
  620 +
  621 +
  622 +
  623 +
  624 +
  625 + * (3) The HTML interface.
  626 +
  627 + We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
  628 + of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
  629 + reactive and attractive on the client side.
  630 +
  631 +
  632 + ** (3.1) Types used by the HTML interface.
  633 +
  634 + For easy reference, we gather below the definitions of all the types used by the HTML
  635 + interface, and we comment them immediately.
  636 +
  637 +
  638 +public type HTML_Size:
  639 + absolute(Int), // in pixels
  640 + percentage(Int).
  641 +
  642 +
  643 +public type Text_Option:
  644 + size(Int), // size of character font to use
  645 + font(String), // name of character font to use (such as "helvetica",...)
  646 + color(RGB), // color to be used for characters
  647 + italic,
  648 + oblique,
  649 + small_capitals,
  650 + bold,
  651 + underlined,
  652 + left_justified,
  653 + right_justified,
  654 + justified, // justified on both sides
  655 + line_through,
  656 + nowrap,
  657 + class(String), //CSS class
  658 + id(String),
  659 + style(String),
  660 + title(String),
  661 + attr(String, String).
  662 +
  663 +public define Text_Option
  664 + tooltip(String s) = title(s).
  665 +
  666 + A list of 'Text_Option' must be given with each text you want to put in your page.
  667 +
  668 + This indicate the way of reading text.
  669 +public type Reading_Way:
  670 + ltr, //the text is readable from "Left To Right" like english
  671 + rtl. //the text is readable from "Right To Left" like arabic
  672 +
  673 +
  674 +// Following types are define to prohib any arguments order error
  675 +
  676 +/**
  677 + * String value for 'id' attribut
  678 + */
  679 +public type HtmlId:
  680 + htmlId(String id).
  681 +
  682 +/**
  683 + * String value for 'class' attribut
  684 + */
  685 +public type HtmlClass:
  686 + htmlClass(String class).
  687 +
  688 +/**
  689 + * Name used in arguments list when form is submitted.
  690 + */
  691 +public type WebArgName:
  692 + wan(String name).
  693 +
  694 +/**
  695 + * Value passed to arguments list when form is submitted (for RadioButton and CheckBox).
  696 + */
  697 +public type WebArgValue:
  698 + wav(String value).
  699 +
  700 +/**
  701 + * Initial value of an input field.
  702 + */
  703 +public type InitialValue:
  704 + init(String value).
  705 +
  706 +public define Printable_tree [HtmlId x . Printable_tree y] = str_pt(x.id, y).
  707 +public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y).
  708 +public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y).
  709 +public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y).
  710 +public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y).
  711 +
  712 +
  713 +public type HtmlEvents:
  714 + // Form element events
  715 + onchange,
  716 + onsubmit,
  717 + onreset,
  718 + onselect,
  719 + onblur,
  720 + onfocus,
  721 + // Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
  722 + onkeydown,
  723 + onkeypress,
  724 + onkeyup,
  725 + // Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
  726 + onclick,
  727 + ondblclick,
  728 + onmousedown,
  729 + onmousemove,
  730 + onmouseout,
  731 + onmouseover,
  732 + onmouseup.
  733 +
  734 +public type CoreAttrs:
  735 + id (String),
  736 + class (String),
  737 + style (String),
  738 + title (String),
  739 + lang (String),
  740 + dir (Reading_Way),
  741 + accesskey (Word8),
  742 + tabindex (Int),
  743 + attr (String, String),
  744 + event (HtmlEvents, String).
  745 +
  746 +public define CoreAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false").
  747 +
  748 +public type InputAttrs:
  749 + class (String),
  750 + style (String),
  751 + title (String),
  752 + lang (String),
  753 + dir (Reading_Way),
  754 + accesskey (Word8),
  755 + tabindex (Int),
  756 + attr (String, String),
  757 + event (HtmlEvents, String).
  758 +
  759 +public define InputAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false").
  760 +
  761 +public define CoreAttrs
  762 + tooltip(String s) = title(s).
  763 +public define InputAttrs
  764 + tooltip(String s) = title(s).
  765 +
  766 +public type I18n:
  767 + lang (String),
  768 + dir (Reading_Way).
  769 +
  770 +//public type DIV_Option:
  771 +// id (String),
  772 +// class (String),
  773 +// style (String),
  774 +// tooltip (String),
  775 +// lang (String),
  776 +// dir (Reading_Way),
  777 +// attr (String, String),
  778 +// event (HtmlEvents, String).
  779 +//
  780 +
  781 + A list of 'DIV_Option' must be given with each DIV you want to put in your page.
  782 +
  783 +define String event_name
  784 + (
  785 + HtmlEvents e
  786 + ) =
  787 + if e is
  788 + {
  789 + // Form element events
  790 + onchange then "onchange",
  791 + onsubmit then "onsubmit",
  792 + onreset then "onreset",
  793 + onselect then "onselect",
  794 + onblur then "onblur",
  795 + onfocus then "onfocus",
  796 + // Keyboard events
  797 + onkeydown then "onkeydown",
  798 + onkeypress then "onkeypress",
  799 + onkeyup then "onkeyup",
  800 + // Mouse events
  801 + onclick then "onclick",
  802 + ondblclick then "ondblclick",
  803 + onmousedown then "onmousedown",
  804 + onmousemove then "onmousemove",
  805 + onmouseout then "onmouseout",
  806 + onmouseover then "onmouseover",
  807 + onmouseup then "onmouseup"
  808 + }.
  809 +
  810 +public type Table_Option:
  811 + background_color(RGB), // applied to all cells in the table
  812 + background_image(String url),
  813 + border(Int width_of_outer_edge, // if not present, all values are 0
  814 + Int width_of_top_of_relief,
  815 + Int width_of_inner_edge,
  816 + RGB border_color),
  817 + width(Int), // sets a minimal width for the table
  818 + percentage_width(Int),
  819 + attr(String name, String value),
  820 + class(String class_name).
  821 +
  822 +
  823 +public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
  824 +
  825 +
  826 + A list of 'Table_Option' must be given with each table.
  827 +
  828 +
  829 +public type BackgroundOption:
  830 + repeat, // repeat the background in both directions
  831 + repeat_horizontal, // repeat the background only horizontally
  832 + repeat_vertical, // repeat the background only verticall
  833 + no_repeat, // don't repeat the background
  834 + center.
  835 +
  836 +
  837 +public type Cell_Option:
  838 + core_attrs(List(CoreAttrs)),
  839 + left, // put the content of the cell on the left
  840 + h_center, // center the content of the cell horizontally
  841 + right, // put the content of the cell on the right
  842 + top, // put the content of the cell upwards
  843 + v_center, // center the content of tye cell vertically,
  844 + bottom, // put the content of the cell downwards
  845 + base_line, // align the content vertically according to base lines
  846 + background_color(RGB),
  847 + background_image(String url, BackgroundOption),
  848 + width(Int), // sets a minimal width for the cell
  849 + percentage_width(Int),
  850 + height(Int), // sets a minimal height for the cell
  851 + columns(Int), // lets the cell span over several columns
  852 + rows(Int), // lets the cell span over several rows
  853 + nowrap, // do not allow text wrapping within the cell
  854 + class(String class_name).
  855 +
  856 + A list of 'Cell_Option' must be given with each cell and each row in a table. Options
  857 + given with a row apply to all the cells in the row, but are superseded by options given
  858 + with cells, which apply only to the cell they are given with.
  859 +
  860 +
  861 +public type HTML_Cell($T):
  862 + cell(List(Cell_Option) options, $T content).
  863 +
  864 +public type HTML_Header_Cell($T):
  865 + header_cell(List(Cell_Option) options, $T content).
  866 +
  867 + The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
  868 + depending on where you put your table (within a form or not within a form). For your
  869 + convenience, we define the following particular case:
  870 +
  871 +public define HTML_Cell($T)
  872 + cell
  873 + (
  874 + $T content
  875 + ) =
  876 + cell([],content).
  877 +
  878 +public define HTML_Header_Cell($T)
  879 + header_cell
  880 + (
  881 + $T content
  882 + ) =
  883 + header_cell([],content).
  884 +
  885 +
  886 +public type HTML_Row($T):
  887 + row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
  888 +
  889 +public type HTML_Header_Row($T):
  890 + empty,
  891 + header_row(List(Cell_Option) options, List(HTML_Header_Cell($T)) cells).
  892 +
  893 +public type HTML_Footer_Row($T):
  894 + empty,
  895 + footer_row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
  896 +
  897 + Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
  898 +
  899 +public define HTML_Row($T)
  900 + row
  901 + (
  902 + List(HTML_Cell($T)) cells
  903 + ) =
  904 + row([],cells).
  905 +
  906 +public define HTML_Row($T)
  907 + row
  908 + (
  909 + HTML_Cell($T) cell
  910 + ) =
  911 + row([],[cell]).
  912 +
  913 +public define HTML_Header_Row($T)
  914 + header_row
  915 + (
  916 + List(HTML_Header_Cell($T)) cells
  917 + ) =
  918 + header_row([],cells).
  919 +
  920 +public define HTML_Header_Row($T)
  921 + header_row
  922 + (
  923 + HTML_Header_Cell($T) cell
  924 + ) =
  925 + header_row([],[cell]).
  926 +
  927 +public type Actioner_Connection:
  928 + same, // use same type of connection as current page
  929 + http, // use non secured connection
  930 + https. // use secured connection
  931 +
  932 +public type Other_Window_Option:
  933 + resizable, // the new window may be resized by the client
  934 + scrollbars, // the new window has scrollbars
  935 + width(Int), // the new window has the specified width
  936 + height(Int). // the new window has the specified height
  937 +
  938 +public type Actioner_Target:
  939 + same,
  940 + same (String label),
  941 + other(String window_name, List(Other_Window_Option)).
  942 +
  943 +public type Actioner_Aspect:
  944 + link (List(Text_Option), String text, Maybe(String) name),// hypertext link
  945 + img_link (String img_url, String alt_text),
  946 + push_button (List(CoreAttrs),String text),
  947 + button (String url_off, String url_on), // rollover button
  948 + button (String url_off, String url_on, Int w, Int h), // idem with size
  949 + submit (List(CoreAttrs),String text),
  950 + immediate_selector (List(CoreAttrs),WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices),
  951 + immediate_selector (List(CoreAttrs),WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices, InitialValue selected).
  952 +
  953 +
  954 +
  955 +public type Actioner_Local_Action:
  956 + close_window.
  957 +
  958 +
  959 +public define Actioner_Aspect
  960 + link
  961 + (
  962 + List(Text_Option) options,
  963 + String text
  964 + ) =
  965 + link(options, text, failure).
  966 +
  967 +public define Actioner_Aspect
  968 + link
  969 + (
  970 + String text
  971 + ) =
  972 + link([], text, failure).
  973 +
  974 +
  975 +public define Actioner_Aspect
  976 + link
  977 + (
  978 + List(Text_Option) options,
  979 + Int i
  980 + ) =
  981 + link(options,to_decimal(i), failure).
  982 +
  983 +public define Actioner_Aspect
  984 + link
  985 + (
  986 + Int i
  987 + ) =
  988 + link([], i).
  989 +
  990 +public define Actioner_Aspect
  991 + button
  992 + (
  993 + String url_img
  994 + ) =
  995 + button(url_img,url_img).
  996 +
  997 + Actioners are explained in details below.
  998 +
  999 +
  1000 +public type TextAreaOption:
  1001 + input_attrs(List(InputAttrs)),
  1002 + disabled,
  1003 + read_only,
  1004 + wrap_lines.
  1005 +
  1006 +public type CSS_Style:
  1007 + text_options(List(Text_Option)).
  1008 +
  1009 +public type CSS_File:
  1010 + css_file(String file_name).
  1011 +
  1012 +public type JS_Attribute:
  1013 + attr (String, String).
  1014 +
  1015 +public type JS_File:
  1016 + js_file(String file_name,
  1017 + List(JS_Attribute) attributes).
  1018 +
  1019 +public type Script:
  1020 + script( String script_type,
  1021 + String content).
  1022 +
  1023 +public define JS_File
  1024 + js_file
  1025 + (
  1026 + String file_name
  1027 + ) =
  1028 + js_file(file_name, []).
  1029 +
  1030 +public type HTML_Meta:...
  1031 +
  1032 +public type HTML_Off_Form:...
  1033 +
  1034 +public type HTML_Head_Tag:
  1035 + meta(HTML_Meta),
  1036 + title(String),
  1037 + js(JS_File),
  1038 + js_inline(Script),
  1039 + css(CSS_File),
  1040 + css_inline(String css_styles) /* Note: <style> and </style> tags are not necessaries */
  1041 + .
  1042 +
  1043 + /* It contains
  1044 + * - a List of HTML_Head_Tag representing necessaries head tags for the content. ex: jquery js files, js script, css specific styles, ...
  1045 + * - a HTML_Off_Form representing the HTML code for the part of the page we want to display
  1046 + */
  1047 +public type HTML_Partial_Content:
  1048 + partial_content
  1049 + (
  1050 + List(HTML_Head_Tag),
  1051 + HTML_Off_Form
  1052 + )
  1053 + .
  1054 +
  1055 +
  1056 +public define List(HTML_Head_Tag)
  1057 + to_HTML_Head_Tag
  1058 + (
  1059 + List(CSS_File) css_file_list
  1060 + )=
  1061 + map((CSS_File css_f) |-> css(css_f), css_file_list).
  1062 +
  1063 +public define List(HTML_Head_Tag)
  1064 + to_HTML_Head_Tag
  1065 + (
  1066 + List(JS_File) js_file_list
  1067 + )=
  1068 + map((JS_File js_f) |-> js(js_f), js_file_list).
  1069 +
  1070 +public type HTML_In_Form:
  1071 + literal_pt (Printable_tree),
  1072 + literal (String),
  1073 + sequence (List(HTML_In_Form) items),
  1074 + text (List(Text_Option), String the_text),
  1075 + preformated (List(Text_Option), String),
  1076 + paragraph (List(Text_Option), HTML_In_Form content),
  1077 + image (List(CoreAttrs), String url, String alternate),
  1078 + image (List(CoreAttrs), String url, String alternate, Int width, Int height),
  1079 + table (List(Table_Option), HTML_Header_Row(HTML_In_Form), List(HTML_Row(HTML_In_Form)), HTML_Footer_Row(HTML_In_Form)),
  1080 + center (HTML_In_Form),
  1081 + mail_to (String email, HTML_In_Form element),
  1082 + scroller (Int width, Int height,
  1083 + Int content_width, Int content_height,
  1084 + HTML_In_Form content),
  1085 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  1086 + String action_name, List((String,String)) extra_ops,
  1087 + List(Actioner_Local_Action)),
  1088 + foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
  1089 + foreign_link (List(Text_Option), String url),
  1090 + foreign_link (List(Text_Option), String url, String name),
  1091 + private_download (String abs_path, String name, String extra_ext,
  1092 + Maybe((String,List((String,String)))) action),
  1093 + text_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
  1094 + text_input_ro (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
  1095 + password_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width),
  1096 + text_area (List(TextAreaOption), String label_text, HtmlId id, WebArgName name, InitialValue init, Int width, Int height),
  1097 + file_upload (List(InputAttrs), String label, HtmlId id, WebArgName name, Int width),
  1098 + selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List(String) choices),
  1099 + selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List(String) choices, InitialValue selected),
  1100 + // List((String,String)) = List((code,name)) where :
  1101 + // code is the web-arg value
  1102 + // name appears in selector
  1103 + selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices),
  1104 + selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices, InitialValue selected),
  1105 + radio_button (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1106 + radio_button_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1107 + check_box (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1108 + check_box_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1109 + div (List(CoreAttrs), HTML_In_Form content),
  1110 + div_empty (List(CoreAttrs)),
  1111 + hidden (HtmlId id, WebArgName name, InitialValue init).
  1112 +
  1113 +
  1114 + 'HTML_In_Form' defines all the elements you may put within a form. We define a
  1115 + convenience function:
  1116 +
  1117 +public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
  1118 +
  1119 +public define HTML_In_Form image(String url) = image([], url, url).
  1120 +
  1121 +public define HTML_In_Form image(String url, Int width, Int height) = image([], url, url, width, height).
  1122 +
  1123 +public define HTML_In_Form
  1124 + foreign_link
  1125 + (
  1126 + Int tsize,
  1127 + String url,
  1128 + String name
  1129 + ) =
  1130 + foreign_link([size(tsize)],url,name).
  1131 +
  1132 +public define HTML_In_Form
  1133 + actioner
  1134 + (
  1135 + Actioner_Connection conn,
  1136 + Actioner_Target targ,
  1137 + Actioner_Aspect asp,
  1138 + String action_name,
  1139 + List((String,String)) extra_ops
  1140 + ) =
  1141 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1142 +
  1143 +public define HTML_In_Form
  1144 + actioner
  1145 + (
  1146 + Actioner_Connection conn,
  1147 + Actioner_Target targ,
  1148 + Actioner_Aspect asp,
  1149 + ) =
  1150 + actioner(conn, targ, asp, "", [], []).
  1151 +
  1152 +
  1153 +public define HTML_In_Form
  1154 + text_area
  1155 + (
  1156 + WebArgName name,
  1157 + InitialValue init,
  1158 + Int width,
  1159 + Int height
  1160 + ) =
  1161 + text_area([], "", htmlId(""), name,init,width,height).
  1162 +
  1163 +public define HTML_In_Form
  1164 + text_area
  1165 + (
  1166 + List(TextAreaOption) options,
  1167 + WebArgName name,
  1168 + InitialValue init,
  1169 + Int width,
  1170 + Int height
  1171 + ) =
  1172 + text_area(options, "", htmlId(""), name,init,width,height).
  1173 +
  1174 +public define HTML_In_Form
  1175 + table
  1176 + (
  1177 + List(HTML_Row(HTML_In_Form)) rows
  1178 + ) =
  1179 + table([],empty,rows,empty).
  1180 +
  1181 +public define HTML_In_Form
  1182 + table
  1183 + (
  1184 + List(Table_Option) options,
  1185 + List(HTML_Row(HTML_In_Form)) rows
  1186 + ) =
  1187 + table(options,empty,rows,empty).
  1188 +
  1189 +public define HTML_In_Form
  1190 + table
  1191 + (
  1192 + HTML_Header_Row(HTML_In_Form) h_row,
  1193 + List(HTML_Row(HTML_In_Form)) rows
  1194 + ) =
  1195 + table([],h_row,rows,empty).
  1196 +
  1197 +public define HTML_In_Form
  1198 + table
  1199 + (
  1200 + List(Table_Option) options,
  1201 + HTML_Header_Row(HTML_In_Form) h_row,
  1202 + List(HTML_Row(HTML_In_Form)) rows
  1203 + ) =
  1204 + table(options,h_row,rows,empty).
  1205 +
  1206 +public define HTML_In_Form
  1207 + private_download
  1208 + (
  1209 + String abs_path,
  1210 + String name,
  1211 + String extra_ext
  1212 + ) =
  1213 + private_download(abs_path,name,extra_ext,failure).
  1214 +
  1215 +public define HTML_In_Form
  1216 + private_download
  1217 + (
  1218 + String abs_path,
  1219 + String name,
  1220 + String extra_ext,
  1221 + String action_name,
  1222 + List((String,String)) args
  1223 + ) =
  1224 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1225 +
  1226 +public define HTML_In_Form
  1227 + text
  1228 + (
  1229 + String s
  1230 + ) =
  1231 + text([],s).
  1232 +
  1233 +public define HTML_In_Form
  1234 + radio_button (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1235 + = radio_button (options, label_text, htmlId(n.name), n, value, checked).
  1236 +
  1237 +public define HTML_In_Form
  1238 + radio_button_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1239 + = radio_button_r (options, label_text, htmlId(n.name), n, value, checked).
  1240 +
  1241 +public define HTML_In_Form
  1242 + check_box (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1243 + = check_box (options, label_text, htmlId(n.name), n, value, checked).
  1244 +
  1245 +public define HTML_In_Form
  1246 + check_box_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1247 + = check_box_r (options, label_text, htmlId(n.name), n, value, checked).
  1248 +
  1249 +public define HTML_In_Form
  1250 + file_upload (WebArgName name, Int width)
  1251 + = file_upload([], "", htmlId(""), name, width).
  1252 +
  1253 +public define HTML_In_Form
  1254 + hidden (WebArgName name, InitialValue init)
  1255 + = if name is wan(web_name) then
  1256 + hidden(htmlId(web_name), name, init).
  1257 +
  1258 +public type HTML_Body:...
  1259 +
  1260 +public type HTML_Off_Form:
  1261 + literal_pt (Printable_tree),
  1262 + literal (String),
  1263 + sequence (List(HTML_Off_Form) items),
  1264 + text (List(Text_Option), String the_text),
  1265 + preformated (List(Text_Option), String),
  1266 + paragraph (List(Text_Option), HTML_Off_Form content),
  1267 + image (List(CoreAttrs), String url, String alternate),
  1268 + image (List(CoreAttrs), String url, String alternate, Int width, Int height),
  1269 + table (List(Table_Option), HTML_Header_Row(HTML_Off_Form), List(HTML_Row(HTML_Off_Form)), HTML_Footer_Row(HTML_Off_Form)),
  1270 + center (HTML_Off_Form),
  1271 + mail_to (String email, HTML_Off_Form element),
  1272 + scroller (Int width, Int height,
  1273 + Int content_width, Int content_height,
  1274 + HTML_Off_Form content),
  1275 + fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
  1276 + fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  1277 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  1278 + String action_name, List((String,String)) extra_ops,
  1279 + List(Actioner_Local_Action)),
  1280 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  1281 + String action_name, List((String,String)) extra_ops,
  1282 + List(Actioner_Local_Action), String form_name),
  1283 + foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
  1284 + foreign_link (List(Text_Option), String url),
  1285 + foreign_link (List(Text_Option), String url, String name),
  1286 + private_download (String abs_path, String name, String extra_ext,
  1287 + Maybe((String,List((String,String)))) action),
  1288 + label (String name),
  1289 + form (HtmlId form_id, List(CoreAttrs), HTML_In_Form content),
  1290 + form (HtmlId form_id, List(CoreAttrs),
  1291 + String action_name, List((String,String)) extra_ops,
  1292 + HTML_In_Form content),
  1293 + div (List(CoreAttrs), HTML_Off_Form content),
  1294 + div_empty (List(CoreAttrs)),
  1295 + iframe (List(CoreAttrs),
  1296 + List(CSS_Style) /*styles*/,
  1297 + List(CSS_File) /*css_files*/,
  1298 + List(JS_File) /*js_files*/,
  1299 + HTML_Body /*body*/).
  1300 +
  1301 + 'HTML_Off_Form' defines all the elements you may put outside any form.
  1302 +
  1303 +
  1304 +public define HTML_Off_Form image(String url) = image([], url, url).
  1305 +
  1306 +public define HTML_Off_Form image(String url, Int width, Int height) = image([], url, url, width, height).
  1307 +
  1308 +public define HTML_Off_Form
  1309 + form
  1310 + (
  1311 + String form_name,
  1312 + List(CoreAttrs) options,
  1313 + HTML_In_Form content
  1314 + ) =
  1315 + form(htmlId(form_name), options, content).
  1316 +
  1317 +public define HTML_Off_Form
  1318 + form
  1319 + (
  1320 + String form_name,
  1321 + List(CoreAttrs) options,
  1322 + String action_name,
  1323 + List((String,String)) extra_ops,
  1324 + HTML_In_Form content
  1325 + ) =
  1326 + form(htmlId(form_name), options, action_name, extra_ops, content).
  1327 +
  1328 +
  1329 +public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
  1330 +public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
  1331 + = fixed_size_2(width,height,name_of_HTML_file).
  1332 +
  1333 +
  1334 +public define HTML_Off_Form
  1335 + foreign_link
  1336 + (
  1337 + Int tsize,
  1338 + String url,
  1339 + String name
  1340 + ) =
  1341 + foreign_link([size(tsize)],url,name).
  1342 +
  1343 +
  1344 +public define HTML_Off_Form
  1345 + actioner
  1346 + (
  1347 + Actioner_Connection conn,
  1348 + Actioner_Target targ,
  1349 + Actioner_Aspect asp,
  1350 + String action_name,
  1351 + List((String,String)) extra_ops
  1352 + ) =
  1353 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1354 +
  1355 +public define HTML_Off_Form
  1356 + table
  1357 + (
  1358 + List(HTML_Row(HTML_Off_Form)) rows
  1359 + ) =
  1360 + table([],empty,rows,empty).
  1361 +
  1362 +public define HTML_Off_Form
  1363 + table
  1364 + (
  1365 + List(Table_Option) options,
  1366 + List(HTML_Row(HTML_Off_Form)) rows
  1367 + ) =
  1368 + table(options,empty,rows,empty).
  1369 +
  1370 +public define HTML_Off_Form
  1371 + table
  1372 + (
  1373 + HTML_Header_Row(HTML_Off_Form) h_row,
  1374 + List(HTML_Row(HTML_Off_Form)) rows
  1375 + ) =
  1376 + table([],h_row,rows,empty).
  1377 +
  1378 +public define HTML_Off_Form
  1379 + table
  1380 + (
  1381 + List(Table_Option) options,
  1382 + HTML_Header_Row(HTML_Off_Form) h_row,
  1383 + List(HTML_Row(HTML_Off_Form)) rows
  1384 + ) =
  1385 + table(options,h_row,rows,empty).
  1386 +
  1387 + We add two convenience functions for 'row'. The reason why we add two functions, one
  1388 + for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
  1389 + arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
  1390 + the arguments of the function do not refer to any of the types defined here.
  1391 +
  1392 +public define HTML_Row(HTML_In_Form)
  1393 + row
  1394 + (
  1395 + HTML_In_Form content
  1396 + ) =
  1397 + row([],[cell([],content)]).
  1398 +
  1399 +public define HTML_Row(HTML_Off_Form)
  1400 + row
  1401 + (
  1402 + HTML_Off_Form content
  1403 + ) =
  1404 + row([],[cell([],content)]).
  1405 +
  1406 +
  1407 +public define HTML_Off_Form
  1408 + private_download
  1409 + (
  1410 + String abs_path,
  1411 + String name,
  1412 + String extra_ext
  1413 + ) =
  1414 + private_download(abs_path,name,extra_ext,failure).
  1415 +
  1416 +
  1417 +public define HTML_Off_Form
  1418 + private_download
  1419 + (
  1420 + String abs_path,
  1421 + String name,
  1422 + String extra_ext,
  1423 + String action_name,
  1424 + List((String,String)) args
  1425 + ) =
  1426 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1427 +
  1428 +public define HTML_Off_Form
  1429 + text
  1430 + (
  1431 + List(Text_Option) lto,
  1432 + Int i
  1433 + ) =
  1434 + text(lto,to_decimal(i)).
  1435 +
  1436 +
  1437 +public define HTML_Off_Form
  1438 + text
  1439 + (
  1440 + Int i
  1441 + ) =
  1442 + text([],i).
  1443 +
  1444 +public define HTML_Off_Form
  1445 + text
  1446 + (
  1447 + String s
  1448 + ) =
  1449 + text([],s).
  1450 +
  1451 + - Cell a gap between two other cells :
  1452 +
  1453 +public define HTML_Cell(HTML_Off_Form)
  1454 + width_gap
  1455 + (
  1456 + Int w
  1457 + ) =
  1458 + cell([width(w)],text([],"")).
  1459 +
  1460 +public define HTML_Cell(HTML_In_Form)
  1461 + width_gap
  1462 + (
  1463 + Int w
  1464 + ) =
  1465 + cell([width(w)],text([],"")).
  1466 +
  1467 +
  1468 + - Row a gap between two other rows :
  1469 +
  1470 +public define HTML_Row(HTML_Off_Form)
  1471 + height_gap
  1472 + (
  1473 + Int h
  1474 + ) =
  1475 + row([],[cell([height(h)],text([],""))]).
  1476 +
  1477 +public define HTML_Row(HTML_In_Form)
  1478 + height_gap
  1479 + (
  1480 + Int h
  1481 + ) =
  1482 + row([],[cell([height(h)],text([],""))]).
  1483 +
  1484 +
  1485 + Notice that the two types have alternatives in common (same name, same arguments types,
  1486 + up to the value of the parameter $T), which correspond to elements which may be put
  1487 + anywhere in the page.
  1488 +
  1489 +
  1490 +public define HTML_Partial_Content
  1491 + partial_content
  1492 + (
  1493 + HTML_Off_Form content
  1494 + )=
  1495 + partial_content([], content).
  1496 +
  1497 +public define HTML_Partial_Content
  1498 + partial_content
  1499 + (
  1500 + List(HTML_Head_Tag) tags,
  1501 + HTML_Partial_Content p_content
  1502 + )=
  1503 + if p_content is partial_content(ptags, html) then
  1504 + partial_content(tags + ptags, html).
  1505 +
  1506 +define HTML_Partial_Content
  1507 + list_to_pcontent
  1508 + (
  1509 + List(HTML_Partial_Content) l_p_content,
  1510 + List(HTML_Head_Tag) so_far_tags,
  1511 + List(HTML_Off_Form) so_far_html
  1512 + )=
  1513 + if l_p_content is
  1514 + {
  1515 + [] then partial_content(so_far_tags, sequence(reverse(so_far_html))),
  1516 + [h . t] then
  1517 + //extract resurgent type and symbols of h
  1518 + if h is partial_content(tags, html) then
  1519 + list_to_pcontent(t, so_far_tags + tags, [html . so_far_html])
  1520 + }.
  1521 +
  1522 +public define HTML_Partial_Content
  1523 + partial_content
  1524 + (
  1525 + List(HTML_Partial_Content) l_p_content
  1526 + )=
  1527 + list_to_pcontent(l_p_content, [], []).
  1528 +
  1529 +
  1530 +
  1531 +define String
  1532 + format
  1533 + (
  1534 + List(Text_Option) l
  1535 + ).
  1536 +
  1537 +
  1538 +
  1539 +
  1540 +define Printable_tree
  1541 + format_css_styles
  1542 + (
  1543 + List(CSS_Style) l
  1544 + ) =
  1545 + if l is
  1546 + {
  1547 + [ ] then [ ],
  1548 + [h . t] then
  1549 + [ if h is
  1550 + {
  1551 + text_options(tos) then
  1552 + [" body, span, p { ", format(tos), " }\n" ]
  1553 + }
  1554 + . format_css_styles(t)]
  1555 + }.
  1556 +
  1557 +
  1558 +
  1559 +public type HTML_Meta:
  1560 + keywords (List(String)),
  1561 + refresh (Actioner_Connection connection,
  1562 + Actioner_Target target,
  1563 + String action_name,
  1564 + Int delay), // in seconds
  1565 + refresh (String url, Int delay), // in seconds
  1566 + meta (String name, String content),
  1567 + http_equiv (String name, String content),
  1568 + generic_meta (List((String,String))),
  1569 + literal (String).
  1570 +
  1571 + Meta tags are put in the 'head' of the HTML page.
  1572 +
  1573 +
  1574 +public type Body_Option:
  1575 + core_attrs(List(CoreAttrs)),
  1576 + background_color (RGB),
  1577 + background_image (String url),
  1578 + background_image (String url, List(BackgroundOption)).
  1579 +
  1580 +public type HTML_Body:
  1581 + body(List(Body_Option) options, HTML_Off_Form content).
  1582 +
  1583 +
  1584 +public type HTTP_Answer:
  1585 + html_page (HTTP_Status /*http_status*/,
  1586 + List(HTML_Head_Tag) /*html <head> tags*/,
  1587 + HTML_Body /*body*/),
  1588 + html_page (HTTP_Status /*http_status*/,
  1589 + String /*title*/,
  1590 + List(HTML_Meta) /*meta_tags*/,
  1591 + List(CSS_Style) /*styles*/,
  1592 + List(CSS_File) /*css_files*/,
  1593 + List(JS_File) /*js_files*/,
  1594 + List(Script) /*script*/,
  1595 + HTML_Body /*body*/),
  1596 + plain_text (HTTP_Status /*http_status*/,
  1597 + String /*text*/),
  1598 + custom_text (HTTP_Status /*http_status*/,
  1599 + String /*mime_type*/,
  1600 + String /*content*/),
  1601 + custom_binary(HTTP_Status /*http_status*/,
  1602 + String /*mime_type*/,
  1603 + ByteArray /*content*/,
  1604 + List(HTTP_header)/*other headers*/),
  1605 + custom_tree (HTTP_Status /*http_status*/,
  1606 + String /*mime_type*/,
  1607 + Printable_tree /*content*/),
  1608 + http_raw (Printable_tree),
  1609 + html_content (HTTP_Status /*http_status*/,
  1610 + HTML_Off_Form).
  1611 +
  1612 +
  1613 +public define HTTP_Answer
  1614 + html_page
  1615 + (
  1616 + String title,
  1617 + List(HTML_Meta) metas,
  1618 + HTML_Body body
  1619 + ) =
  1620 + html_page(http_ok, title,metas,[],[],[],[],body).
  1621 +
  1622 +public define HTTP_Answer
  1623 + html_page
  1624 + (
  1625 + String title,
  1626 + List(HTML_Meta) metas,
  1627 + List(CSS_Style) styles,
  1628 + HTML_Body body
  1629 + ) =
  1630 + html_page(http_ok, title, metas, styles, [], [], [], body).
  1631 +
  1632 + 'HTTP_Answer' represents the final product of the construction of a web page.
  1633 +
  1634 +
  1635 +
  1636 +
  1637 + *** (3.2) ``in form'' versus ``off form''.
  1638 +
  1639 + There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
  1640 + of them may have a content, which is yet another HTML element (or several). Hence, it
  1641 + is meaningful to say that an element is 'within' another one. Now, putting any element
  1642 + within any other one may be meaningless. For example, an input element must be put
  1643 + within a form (otherwise, it is useless), and a from within another form has no precise
  1644 + meaning (and is forbidden by the HTML specification).
  1645 +
  1646 + Actually, the main criterium is ``within a form or not within a form''. So, HTML
  1647 + elements in a given page are separated into two categories: those who are within a
  1648 + form, and the others. Nevertheless, there are elements which may belong to both
  1649 + categories, like images and texts. We want to make use of the strong typing mecanism of
  1650 + Anubis in order to forbid non meaningful placement of elements.
  1651 +
  1652 + The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
  1653 + 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
  1654 + and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
  1655 + but the two types are not cross recursive. This is the reason why it is impossible to
  1656 + put a form within a form. In order to construct a web page, you essentially have to
  1657 + produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
  1658 +
  1659 + In practice, you don't have to worry so much about these two types, because elements
  1660 + which may be put anywhere are constructed for both types by functions with the same
  1661 + name and the same arguments. Hence, for both types, you just write the same thing. You
  1662 + are warned by the compiler only when you try to put an element at a place it is not
  1663 + allowed.
  1664 +
  1665 +
  1666 +
  1667 + *** (3.3) Defining your own style.
  1668 +
  1669 + We provide generic tools for constructing HTML elements. However, your web site needs
  1670 + to have a ``style''.
  1671 +
  1672 + To that end, you need to write down a set of ``styling functions'', using the tools
  1673 + defined here. These styling functions allow the introduction of your colors and other
  1674 + visual characteristics into the constructed elements once and for all. For example, you
  1675 + may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
  1676 + some 'text_color'. You may write something like this:
  1677 +
  1678 + define RGB text_color = rgb(10,40,40).
  1679 +
  1680 + define HTML_Off_Form
  1681 + text
  1682 + (
  1683 + String the_text
  1684 + ) =
  1685 + text([font("helvetica"),size(14),color(text_color)],
  1686 + the_text).
  1687 +
  1688 + (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
  1689 + page, you just write:
  1690 +
  1691 + text("... some text ...")
  1692 +
  1693 + and you don't have to provide the font, size and color for each text. If you want to
  1694 + have several styles of text presentation, you just write several sets of such
  1695 + convenience functions. This also suggests a trick. You may want for example different
  1696 + colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
  1697 + defining two functions as above, with the same name and same argument type, but
  1698 + returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
  1699 +
  1700 + If this preliminary work is well done, you will not waste your time later when you
  1701 + concentrate on the actual informational content of your pages.
  1702 +
  1703 + This general principle should be applied to all sorts of elements. This is the best
  1704 + thing to do in order to separate the functions defining the visual style from the
  1705 + functions defining the informational content itself, so that changing the style without
  1706 + changing the content becomes easy. This is also the best way for having a clean and
  1707 + easily readable source for your web site.
  1708 +
  1709 +
  1710 +
  1711 +
  1712 +
  1713 + *** (3.4) Actioners and forms.
  1714 +
  1715 + We have gathered several notions from HTML into that of an 'actioner'. An actioner is
  1716 + an HTML element which opens a connection to our server when clicked upon. Actioners may
  1717 + have different visual aspects. They may look like hypertext links or like buttons
  1718 + (rollovers), or even like selectors (with immediate action). In any case, their
  1719 + behavior is the same: they open a connection to our server, and send a set of 'web
  1720 + arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
  1721 + the action to be performed, and the others should be considered as operands for this
  1722 + action. Actually, the precise behavior of the actioner has several variants.
  1723 +
  1724 + The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
  1725 + type 'Actioner_Connection' above.
  1726 +
  1727 + You must also choose where the answer must be rendered. This may be in the same window
  1728 + or in another window (or frame). If it is in another window, the name of that window
  1729 + must be given. If the window does not exist, the browser will create it. Optionally,
  1730 + you may give the dimensions of the new window and other characteristics. See the type
  1731 + 'Actioner_Target' above.
  1732 +
  1733 + The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
  1734 + case of a rollover button, you provide the URLs of two images (of the same size)
  1735 + representing the button:
  1736 +
  1737 + url_off: to be used when the mouse is not over the button,
  1738 + url_on: to be used when the mouse is over the button.
  1739 +
  1740 + You can also create rollover buttons without creating images. Just use the second
  1741 + alternative named 'button'. The server creates the images automatically.
  1742 +
  1743 + The purpose of forms is just to give operands to actioners. If the actioner is placed
  1744 + within a form, all the input elements which are within this form provide operands to
  1745 + the actioner (except sometimes when they are not set by the client). If it is not put
  1746 + within a form, the actioner gets no operand, except if the name of a form is explicitly
  1747 + given, in which case the actioner gets all the inputs from that form as
  1748 + operands. Furthermore, you may want to give extra operands to the actioner. This may be
  1749 + useful for separating families of actioners with the same action name. Extra operands
  1750 + 'name=value' must be given in the form of pairs '(name,value)'.
  1751 +
  1752 + Notice that the name of a form may be used by an actioner which is off the form, so as
  1753 + to get the operands provided by this form. Also notice that several actioners may refer
  1754 + to the same form, being either in the form, or referring to the form from the
  1755 + outside. These actioners simply get the same set of operands, even if they correspond
  1756 + to distinct actions.
  1757 +
  1758 + Input elements may be put only within a form.
  1759 +
  1760 +
  1761 +
  1762 + *** (3.5) Local popup.
  1763 +
  1764 + This element looks like a link or a rollover button. When this button is clicked upon,
  1765 + a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
  1766 + page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
  1767 + particular, clicking on the button does not open any connection. This is why it is
  1768 + called 'local'. The arguments have the following roles:
  1769 +
  1770 + Actioner_Aspect aspect of the button (same semantics as for actioners)
  1771 + content content of the popup window
  1772 + x, y, position of the popup window on the HTML page (not relative
  1773 + to the button but to the page itself)
  1774 + title title of the popup window
  1775 + color color of the title bar and close button in the popup window.
  1776 + A lightened version of this color is used for the background
  1777 + of the popup window.
  1778 + width width of the title bar
  1779 +
  1780 +
  1781 +
  1782 +
  1783 +
  1784 + --- That's all for the public part ! --------------------------------------------------
  1785 +
  1786 +
  1787 +
  1788 +
  1789 +
  1790 + ----------------------------------- Table of Contents ---------------------------------
  1791 +
  1792 + *** [1] States.
  1793 + *** [1.1] Saving and retrieving states.
  1794 + *** [1.2] Deleting out of date states.
  1795 +
  1796 + *** [2] Tools.
  1797 + *** [2.1] Directories.
  1798 + *** [2.2] Secondary documents.
  1799 +
  1800 + *** [3] Managing web arguments.
  1801 + *** [3.1] Prefixing web arguments names.
  1802 + *** [3.2] Separating web arguments.
  1803 + *** [3.3] Applying an action.
  1804 +
  1805 + *** [4] Web site descriptions and the 'awp handlers'.
  1806 + *** [4.1] The type 'Web_Site'.
  1807 + *** [4.2] Making a web site description.
  1808 + *** [4.3] Starting the servers.
  1809 +
  1810 + *** [5] HTML Formating.
  1811 + *** [5.1] The type 'HTML_Any($T)'.
  1812 + *** [5.2] Formating a color.
  1813 + *** [5.3] Creating buttons.
  1814 + *** [5.4] Formating an actioner.
  1815 + *** [5.5] Formating a private download link.
  1816 + *** [5.6] Formating rows and cells in a table.
  1817 + *** [5.7] Formating elements which may be put anywhere.
  1818 + *** [5.8] Formating 'in form' elements.
  1819 + *** [5.9] Formating 'off form' elements.
  1820 + *** [5.10] Formating meta-tags.
  1821 +
  1822 + ---------------------------------------------------------------------------------------
  1823 +
  1824 +
  1825 +
  1826 +
  1827 +public define String
  1828 + doctype_w3c_header
  1829 + =
  1830 + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "+
  1831 + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".
  1832 +
  1833 +public define String
  1834 + html_header
  1835 + =
  1836 + "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n".
  1837 +
  1838 +
  1839 + *** [1] States.
  1840 +
  1841 + We have to define functions for saving a state, retrieving a state, deleting out of
  1842 + date states. We need one such function per web site. The types of the first two
  1843 + functions depend on the parameter $State. This is not the case of the third one. The
  1844 + fact that the instance of $State is variable from one web sites to the other implies
  1845 + rather subtle manipulations using full functionality.
  1846 +
  1847 +
  1848 + *** [1.1] Saving and retrieving states.
  1849 +
  1850 + Each state is saved into a file on the server's disk (in the directory represented by
  1851 + the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
  1852 + state is saved together with a time stamp whose value is obtained by adding the current
  1853 + time to the given timeout for states. The state receives a name obtained by hashing
  1854 + (using sha1) the content of the file itself, and then encoding the hash with
  1855 + 'web_arg_encode'. The name of the file into which the state is saved is the
  1856 + concatenation of "s" and the name of the state.
  1857 +
  1858 +read CXM_web_arg_encode.anubis
  1859 +
  1860 + The tool below constructs the function which is able to save a state on the server's
  1861 + disk.
  1862 +
  1863 +define ($State s) -> String // the function constructed returns the name of the state
  1864 + make_save_state_function
  1865 + (
  1866 + Int timeout,
  1867 + String state_directory
  1868 + ) =
  1869 + ($State s) |->
  1870 + with time_stamp = now+timeout,
  1871 + to_be_saved = (time_stamp,s),
  1872 + state_name = web_arg_encode(sha1(s)),
  1873 + if save(to_be_saved,state_directory+"/s"+state_name) is ok
  1874 + then state_name
  1875 + else (print("Cannot create state file in '"+state_directory+"'.\n"); "").
  1876 +
  1877 +
  1878 + When a request arrives, we need to retrieve the previous state from the server's
  1879 + disk. We receive the name of that state. If the state is out of date, the state file is
  1880 + kept 3 days, and then deleted.
  1881 +
  1882 +type PreviousState($State):
  1883 + not_found, // cannot retrieve the previous state
  1884 + out_of_date($State), // the previous state is out of date
  1885 + still_valid($State). // the previous state is still valid
  1886 +
  1887 +define (String state_name) -> PreviousState($State)
  1888 + make_retrieve_state_function
  1889 + (
  1890 + String state_directory
  1891 + ) =
  1892 + (String state_name) |->
  1893 + with file_path = state_directory+"/s"+state_name,
  1894 + if (RetrieveResult((Int,$State)))retrieve(file_path) is ok(d)
  1895 + then (
  1896 + if d is (time_stamp,s) then
  1897 + if time_stamp < now
  1898 + then (
  1899 + forget(remove(file_path));
  1900 + out_of_date(s)
  1901 + )
  1902 + else still_valid(s) // state has been successfully retrieved
  1903 + )
  1904 + else not_found.
  1905 +
  1906 +
  1907 +
  1908 + *** [1.2] Deleting out of date states.
  1909 +
  1910 + We also need to delete states which are out of date and which will never be deleted by
  1911 + the above method. This may be performed by a machine doing this periodically (say once
  1912 + per states life time period).
  1913 +
  1914 +define (List(String) file_names) -> One
  1915 + make_delete_out_of_date_states_function
  1916 + (
  1917 + Maybe($State) dummy,
  1918 + String state_directory
  1919 + ) =
  1920 + (List(String) file_names) |-df->
  1921 + if file_names is
  1922 + {
  1923 + [ ] then unique,
  1924 + [h . t] then
  1925 + with file_path = state_directory+"/"+h,
  1926 + if (RetrieveResult((Int,$State)))retrieve(file_path) is ok(d)
  1927 + then (
  1928 + if d is (time_stamp,data) then
  1929 + if time_stamp < now
  1930 + then (forget(remove(file_path)); df(t))
  1931 + else df(t)
  1932 + )
  1933 + else (forget(remove(file_path)); df(t))
  1934 + }.
  1935 +
  1936 +
  1937 + The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
  1938 +
  1939 + Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
  1940 + function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
  1941 + does not accept a parameter in the body of a function (here the parameter is required
  1942 + by the use of 'retrieve') if this parameter does not appear in the type of the
  1943 + function. This is because this would create ambiguities that no explicit typing may
  1944 + ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
  1945 + and if you compile this file, you will get a message like this one:
  1946 +
  1947 + Error in 'making_a_web_site.anubis', line 1300, column 7:
  1948 + A definition may not contain parameters which are not present
  1949 + in the declaration part (hidden parameters):
  1950 + $State
  1951 +
  1952 + The type of the function constructed by 'make_delete_out_of_date_states_function' is
  1953 + independant of the parameter $State. This is important because this allows to create
  1954 + the list of such functions for all web sites. From this list, it is possible to call
  1955 + the functions one after the other, so deleting out of date states for all web
  1956 + sites. Actually, the next function receives a list of pairs (state_directory,function),
  1957 + one for each web site.
  1958 +
  1959 +define One
  1960 + delete_out_of_date_states // for all web sites
  1961 + (
  1962 + List((String, List(String) -> One)) directories_and_functions
  1963 + ) =
  1964 + if directories_and_functions is
  1965 + {
  1966 + [ ] then unique,
  1967 + [h . t] then if h is (state_directory,function) then
  1968 + function(directory_list(state_directory,"s*"));
  1969 + delete_out_of_date_states(t)
  1970 + }.
  1971 +
  1972 +
  1973 + The above function must be called periodically in a separate virtual machine. The
  1974 + period we have choosen is (rather logically) the life time of states itself. This may
  1975 + be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
  1976 + be really infinite, because the servers may be shutdown. Hence, our loop must test
  1977 + (rather frequently; say every second) if the servers are down. If they are, the loop
  1978 + must be exited.
  1979 +
  1980 +define One
  1981 + delete_states_loop
  1982 + (
  1983 + List((String,List(String) -> One)) directories_and_functions,
  1984 + Int timeout,
  1985 + Int next_time,
  1986 + Server http_server,
  1987 + Server https_server,
  1988 + (One) -> Bool shutdown_required
  1989 + ) =
  1990 + if shutdown_required(unique)
  1991 + then (shutdown(http_server); shutdown(https_server))
  1992 + else unique;
  1993 + if (is_down(http_server) & is_down(https_server))
  1994 + then unique
  1995 + else if now > next_time
  1996 + then
  1997 + (
  1998 + delete_out_of_date_states(directories_and_functions);
  1999 + delete_states_loop(directories_and_functions,
  2000 + timeout,
  2001 + now+timeout,
  2002 + http_server,
  2003 + https_server,
  2004 + shutdown_required)
  2005 + )
  2006 + else
  2007 + (
  2008 + sleep(1000); // sleep just one second and try again
  2009 + delete_states_loop(directories_and_functions,
  2010 + timeout,
  2011 + next_time,
  2012 + http_server,
  2013 + https_server,
  2014 + shutdown_required)
  2015 + ).
  2016 +
  2017 + The above loop must be run in a separate virtual machine. This will be done just after
  2018 + the two servers are started.
  2019 +
  2020 +
  2021 +
  2022 +
  2023 +
  2024 + *** [2] Tools.
  2025 +
  2026 + *** [2.1] Directories.
  2027 +
  2028 + We need a tool for creating directories (if needed).
  2029 +
  2030 + (This tool has been moved to 'tools/basis.anubis').
  2031 +
  2032 +
  2033 +
  2034 +
  2035 + *** [2.2] Secondary documents.
  2036 +
  2037 + Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
  2038 + from the current document, but only through an URL. For this reason, we implement a
  2039 + mecanism for creating secondary documents on the fly. To that end we use the 'private
  2040 + download' mecanism.
  2041 +
  2042 + A secondary document is formated by the same functions as the main document itself. The
  2043 + next function takes an 'off form' element, creates the file containing the secondary
  2044 + document in HTML format, and returns the URL at which the document will be available.
  2045 +
  2046 +define String
  2047 + create_secondary_document
  2048 + (
  2049 + String sd, // site directory
  2050 + String as, // authorization_secret
  2051 + String sn, // state name
  2052 + $T -> Printable_tree format_element,
  2053 + $T content,
  2054 + HTML_Size width
  2055 + ) =
  2056 + with private_download_directory = sd+"/private_download",
  2057 + hash = web_arg_encode(sha1(content)),
  2058 + file_content = (Printable_tree)
  2059 + [doctype_w3c_header,
  2060 + "<html><body><table ",
  2061 + if width is
  2062 + {
  2063 + absolute(w) then ["width=\"",w-25],
  2064 + percentage(w) then ["width=\"95%\""]
  2065 + },"\"><tbody><tr><td align=right>",
  2066 + format_element(content),
  2067 + "</td></tr></tbody></table></body></html>"
  2068 + ],
  2069 + file_name = "sd"+hash+".html",
  2070 + file_path = private_download_directory+"/"+file_name,
  2071 + if write_to_file(file_path,file_content) is
  2072 + {
  2073 + cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
  2074 + write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
  2075 + ok then file_name+"?zauth="+
  2076 + make_authorization(sd,as,private_download_directory+"/"+file_name)
  2077 + }.
  2078 +
  2079 +
  2080 +
  2081 +
  2082 + *** [2.3] Generating unique ids.
  2083 +
  2084 + In order to uniquely name object for JavaScript we generate unique ids from a counter.
  2085 +
  2086 +define Int
  2087 + new_idnum
  2088 + (
  2089 + Var(Int) ic_v // 'idnum' counter variable
  2090 + ) =
  2091 + protect
  2092 + with result = *ic_v+1,
  2093 + ic_v <- result;
  2094 + result.
  2095 +
  2096 +
  2097 +
  2098 +
  2099 +
  2100 +
  2101 + *** [3] Managing web arguments.
  2102 +
  2103 + Web arguments are those pairs 'name=value' which are transmitted through the HTTP
  2104 + protocol. We need precise naming conventions for these web arguments.
  2105 +
  2106 +
  2107 +
  2108 + *** [3.1] Prefixing web arguments names.
  2109 +
  2110 + CR 2008-07-08 Prefixing Removed
  2111 +
  2112 +
  2113 +
  2114 + *** [3.2] Separating web arguments.
  2115 +
  2116 + When a new request arrives, we need to separate the web arguments, that is to say:
  2117 +
  2118 + - find the value of "s", and recover the corresponding state,
  2119 + - find the value of "a", which is the name of the action to be performed,
  2120 + - get the list of all the remaining web arguments (operands of the action).
  2121 +
  2122 + We must also determine if the previous state may be recovered. If it is not the case
  2123 + (either because the previous state name is invalid, or the previous state is out of
  2124 + date), we must check if there is an action name. Indeed, the presence of an action name
  2125 + indicates that the user has clicked on one of our buttons or links. If on the contrary
  2126 + there is no action name the user has just entered our address in his browser. In this
  2127 + last case, we must send the first page of our site (maybe a 'login' page), but if there
  2128 + is an action, we must send a page just saying that the session ticket has expired. If
  2129 + the previous state is recovered and there is no action, the new state is the same as
  2130 + the previous state.
  2131 +
  2132 + The result of the separation of the web arguments is of type:
  2133 +
  2134 +type Separated_Web_Args($State):
  2135 + swa(PreviousState($State) previous_state,
  2136 + Maybe(String) action_name,
  2137 + List(Web_arg) operands).
  2138 +
  2139 +
  2140 +
  2141 + The next function constructs the function which separates the web arguments.
  2142 +
  2143 +define (List(Web_arg) lwa, HTTP_Info info) -> Separated_Web_Args($State)
  2144 + make_separate_web_args_function
  2145 + (
  2146 + String state_directory,
  2147 + String -> PreviousState($State) retrieve_state,
  2148 + String website_name
  2149 + ) =
  2150 + (List(Web_arg) lwa, HTTP_Info info) |-swaf->
  2151 + if lwa is
  2152 + {
  2153 + [ ] then
  2154 + //
  2155 + // no web arg found => no previous state and no action
  2156 + //
  2157 + if find_cookie("state_"+website_name, server_get_cookies(http_headers(info))) is
  2158 + {
  2159 + failure then swa(not_found,failure,[]),
  2160 + success(cookie) then swa(retrieve_state(value(cookie)),failure,[])
  2161 + },
  2162 +
  2163 + //swa(failure,failure,[]),
  2164 +
  2165 + [wa_1 . wa_others] then
  2166 + //
  2167 + // at least one web arg =>
  2168 + // separate other web args, and insert the first one as needed
  2169 + //
  2170 + if (Separated_Web_Args($State))swaf(wa_others, info) is
  2171 + {
  2172 + swa(ps1, // possible previous state
  2173 + an1, // maybe an action name
  2174 + op1) // operands so far
  2175 + then
  2176 + if wa_1 is
  2177 + {
  2178 + web_arg(n, value) then
  2179 + with name = if substr(n,0,4) = "amp;" then substr(n, 4, length(n) - 4) else n,
  2180 + if name = "a" then
  2181 + swa(ps1,success(value),op1)
  2182 + else if name = "t" then
  2183 + swa(ps1,an1,[web_arg("target",value) . op1])
  2184 + else
  2185 + swa(ps1,an1,[web_arg(name,value) . op1]),
  2186 +
  2187 + upload(n,v,t) then
  2188 + swa(ps1,an1,[wa_1 . op1])
  2189 + }
  2190 + }
  2191 + }.
  2192 +
  2193 +
  2194 +
  2195 +
  2196 +
  2197 +
  2198 + *** [3.3] Applying an action.
  2199 +
  2200 + When the web arguments are separated (and their names cleaned up from prefixes), we may
  2201 + apply the action to the operands and the current state. We search for the action to be
  2202 + applied in the list of actions. If no action is found, the new state is the same as
  2203 + the previous state. Also, we deny the application of an HTTP action if the request
  2204 + arrives through the HTTPS channel and conversely.
  2205 +
  2206 +
  2207 +define ($State previous,
  2208 + String action_name,
  2209 + HTTP_Info http_info,
  2210 + List(Web_arg) lwa,
  2211 + Bool is_https) -> $State
  2212 + make_apply_action_function
  2213 + (
  2214 + List(Web_Action($State)) actions_list
  2215 + ) =
  2216 + with f =
  2217 + ($State previous,
  2218 + String action_name,
  2219 + HTTP_Info http_info,
  2220 + List(Web_arg) lwa,
  2221 + Bool is_https,
  2222 + List(Web_Action($State)) actions) |-f->
  2223 + //println("Looking for action ["+action_name+"]");
  2224 + if actions is
  2225 + {
  2226 + [ ] then print("action '"+action_name+
  2227 + "' not found.\n"); previous,
  2228 + [ac1 . others] then if ac1 is
  2229 + {
  2230 + http_action(an,allow,do_it) then
  2231 + if an = action_name
  2232 + then if is_https
  2233 + then (print("HTTP action '"+an+
  2234 + "' called through HTTPS (denied).\n");
  2235 + previous)
  2236 + else if allow(previous)
  2237 + then do_it(http_info,lwa,previous)
  2238 + else previous
  2239 + else f(previous,action_name,http_info,lwa,is_https,others),
  2240 +
  2241 + https_action(an,allow,do_it) then
  2242 + if an = action_name
  2243 + then if is_https
  2244 + then if allow(previous)
  2245 + then do_it(http_info,lwa,previous)
  2246 + else previous
  2247 + else (print("HTTPS action '"+an+
  2248 + "' called through HTTP (denied).\n");
  2249 + previous)
  2250 + else f(previous,action_name,http_info,lwa,is_https,others),
  2251 +
  2252 + http_https_action(an,allow,do_it) then
  2253 + if an = action_name
  2254 + then
  2255 + //println("Action ["+action_name+"] found");
  2256 + if allow(previous)
  2257 + then do_it(http_info,lwa,previous)
  2258 + else previous
  2259 + else f(previous,action_name,http_info,lwa,is_https,others),
  2260 +
  2261 + }
  2262 + },
  2263 + ($State previous,
  2264 + String action_name,
  2265 + HTTP_Info http_info,
  2266 + List(Web_arg) lwa,
  2267 + Bool is_https) |->
  2268 + f(previous, action_name, http_info, lwa, is_https, actions_list).
  2269 +
  2270 +
  2271 +
  2272 +
  2273 +
  2274 +
  2275 +
  2276 +
  2277 + *** [4] Web site descriptions and the 'awp handlers'.
  2278 +
  2279 + *** [4.1] The type 'Web_Site'.
  2280 +
  2281 + The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
  2282 + need another one, because, we have some extra informations to record for each site.
  2283 +
  2284 +public type Web_Site:
  2285 + web_site((Word32,Word32) -> Web_Site_Description description,
  2286 + List(String) -> One delete_out_of_date).
  2287 +
  2288 +
  2289 +
  2290 +
  2291 + *** [4.2] Making a web site description.
  2292 +
  2293 + Below is the function which creates a web site description. It first creates (if
  2294 + needed) the directories for the site, then constructs the tool functions for the site,
  2295 + and the site handler. Finally, it constructs the web site description.
  2296 +
  2297 + We gather common (constant) informations in the following type:
  2298 +
  2299 +public type CommonInfo:
  2300 + info(String common_name,
  2301 + Word32 http_port,
  2302 + Word32 https_port,
  2303 + String site_directory,
  2304 + String authorization_secret
  2305 + ).
  2306 +
  2307 + We need a forward declaration.
  2308 +
  2309 +public define Printable_tree
  2310 + format
  2311 + (
  2312 + CommonInfo cinfo,
  2313 + String state_name,
  2314 + List(HTTP_header) headers,
  2315 + HTTP_Answer page,
  2316 + Bool is_https,
  2317 + String charset
  2318 + ).
  2319 +
  2320 +
  2321 +define Printable_tree
  2322 + format
  2323 + (
  2324 + HTML_Size s
  2325 + ) =
  2326 + if s is
  2327 + {
  2328 + absolute(x) then ["\"",x,"\""],
  2329 + percentage(x) then ["\"",x,"%\""]
  2330 + }.
  2331 +
  2332 +define List(HTTP_header)
  2333 + make_state_cookie_headers
  2334 + (
  2335 + String website_name,
  2336 + String state_name
  2337 + )
  2338 + =
  2339 + [
  2340 + http_header("Set-Cookie", "state_"+website_name+"="+state_name)
  2341 + ]
  2342 + .
  2343 +
  2344 + public define Web_Site
  2345 + make_web_site_description
  2346 + (
  2347 + List(String) common_names, // for example: ["www.our-business.com",
  2348 + // "192.168.0.1"]
  2349 + // the second one is just for testing
  2350 + String site_directory, // where 'public' and other directories are
  2351 + // located (should NOT end with '/')
  2352 + One -> One init,
  2353 + (HTTP_Info,
  2354 + List(Web_arg),
  2355 + Bool is_https) -> $State initial_state,
  2356 + ($State expired,
  2357 + HTTP_Info,
  2358 + List(Web_arg),
  2359 + Bool is_https) -> $State ticket_expired_state,
  2360 + (HTTP_Info,
  2361 + List(Web_arg),
  2362 + Bool is_https) -> $State ticket_lost_state,
  2363 + List(Web_Action($State)) actions,
  2364 + $State -> HTTP_Answer compute_page,
  2365 + $State -> List(HTTP_header) additional_headers,
  2366 + Int timeout, // seconds (todo: minutes)
  2367 + Redirections redirections,
  2368 + String charset,
  2369 + List(String) journal_extensions,
  2370 + List(String) journal_headers,
  2371 + String authorization_secret,
  2372 + List(MIME) known_mime_types,
  2373 + (String action_name,
  2374 + List(Web_arg) args)-> One before_send_file
  2375 + ).
  2376 +
  2377 +
  2378 +public define Web_Site
  2379 + make_web_site_description
  2380 + (
  2381 + String website_name,
  2382 + List(String) common_names, // for example: ["www.our-business.com"]
  2383 + String site_directory,
  2384 + String state_directory,
  2385 + One -> One init,
  2386 + (HTTP_Info,
  2387 + List(Web_arg),
  2388 + Bool is_https) -> $State initial_state,
  2389 + ($State expired,
  2390 + Maybe(String),
  2391 + HTTP_Info,
  2392 + List(Web_arg),
  2393 + Bool is_https) -> $State ticket_expired_state,
  2394 + (Maybe(String),
  2395 + HTTP_Info,
  2396 + List(Web_arg),
  2397 + Bool is_https) -> $State ticket_lost_state,
  2398 + List(Web_Action($State)) actions,
  2399 + $State -> HTTP_Answer compute_page,
  2400 + $State -> List(HTTP_header) additional_headers,
  2401 + Int timeout,
  2402 + Redirections redirections,
  2403 + String charset,
  2404 + List(String) journal_extensions,
  2405 + List(String) journal_headers,
  2406 + String secret,
  2407 + List(MIME) known_mime_types,
  2408 + (String action_name,
  2409 + List(Web_arg) args) -> One before_send_file
  2410 + //Bool using_state_cookies
  2411 + ) =
  2412 + init(unique);
  2413 +
  2414 +
  2415 + //
  2416 + // make required directories (if needed)
  2417 + //
  2418 + with //web_sites_directory = (String) make_directory(my_anubis_directory+"/web_sites"),
  2419 + base_directory = (String) make_directory(site_directory),
  2420 + // state_directory = make_directory(site_directory+"/states"),
  2421 + forget((String)make_directory(site_directory+"/public"));
  2422 + //
  2423 + // construct tool functions
  2424 + // TODO 'make_separate_web_args_function' must retrieve state_cookies before parsing web_args if using_state_cookies is true
  2425 + with save_state = make_save_state_function(timeout, state_directory),
  2426 + retrieve_state = make_retrieve_state_function(state_directory),
  2427 + separate_web_args = make_separate_web_args_function(state_directory, retrieve_state, website_name),
  2428 + apply_action = make_apply_action_function(actions),
  2429 + //
  2430 + // construct the site handler
  2431 + //
  2432 + site_handler = (Word32 http_port, Word32 https_port) |->
  2433 + ((String host_name,
  2434 + HTTP_Info http_info,
  2435 + List(Web_arg) lwa,
  2436 + Bool is_https) |->
  2437 + //(Printable_tree)
  2438 + if separate_web_args(lwa, http_info) is
  2439 + {
  2440 + swa(mb_previous_state, mb_action_name, operands) then
  2441 + with new_state = if mb_previous_state is
  2442 + {
  2443 + not_found then
  2444 + //println("previous state not found");
  2445 + if mb_action_name is
  2446 + {
  2447 + failure then initial_state(http_info, operands, is_https),
  2448 + success(_) then
  2449 + ticket_lost_state(mb_action_name, http_info,operands,is_https)
  2450 + },
  2451 +
  2452 + out_of_date(state) then
  2453 + ticket_expired_state(state, mb_action_name, http_info,operands,is_https),
  2454 +
  2455 + still_valid(state) then
  2456 + if mb_action_name is
  2457 + {
  2458 + failure then state,
  2459 + success(action_name) then
  2460 + apply_action(state,action_name,http_info,operands,is_https)
  2461 + }
  2462 + },
  2463 + //if state_and_headers is (session_ticket, mb_new_state, headers) then
  2464 + with state_name = save_state(new_state),
  2465 + //println("Cookie new STATE NAME "+state_name);
  2466 + with cookie_headers = if mb_action_name is
  2467 + {
  2468 + failure then [],
  2469 + success(action_name) then
  2470 + if substr(action_name, 0, 5)="ajax_" then
  2471 + []
  2472 + else
  2473 + make_state_cookie_headers(website_name, state_name)
  2474 + },
  2475 +
  2476 + format(info(host_name, http_port, https_port, site_directory, secret),
  2477 + state_name,
  2478 + additional_headers(new_state) + cookie_headers,
  2479 + compute_page(new_state),
  2480 + is_https,
  2481 + charset)
  2482 + }),
  2483 + //
  2484 + // make the delete_out_of_date function
  2485 + //
  2486 + delete_out_of_date =
  2487 + make_delete_out_of_date_states_function((Maybe($State))failure,
  2488 + site_directory+"/states"),
  2489 + //
  2490 + // construct the web site description
  2491 + //
  2492 + web_site((Word32 http_port, Word32 https_port) |->
  2493 + web_site_description(common_names,
  2494 + site_directory,
  2495 + redirections,
  2496 + charset,
  2497 + journal_extensions,
  2498 + journal_headers,
  2499 + secret,
  2500 + known_mime_types,
  2501 + site_handler(http_port,https_port),
  2502 + (HTTP_Info http_info, List(Web_arg) lwa) |-> if separate_web_args(lwa, http_info ) is
  2503 + swa(mb_previous_state,mb_action_name,operands) then
  2504 + if mb_action_name is
  2505 + {
  2506 + failure then unique
  2507 + success(an) then before_send_file(an,operands)
  2508 + }
  2509 + //using_state_cookies
  2510 + ),
  2511 + delete_out_of_date).
  2512 +
  2513 +define String
  2514 + _random_string
  2515 + (
  2516 + Int size,
  2517 + String current
  2518 + )=
  2519 + if size = 0 then
  2520 + current
  2521 + else
  2522 + _random_string( size - 1, current + random(9))
  2523 + .
  2524 +
  2525 +public define String
  2526 + generate_random_string
  2527 + (
  2528 + Int size
  2529 + ) =
  2530 + to_string(extract(base64_encode(to_byte_array(_random_string(size, ""))), 0, size)).
  2531 +
  2532 +
  2533 +define String
  2534 + get_site_uid
  2535 + (
  2536 + String site_path
  2537 + )=
  2538 + with file_name = site_path+"/site_UID",
  2539 + if read_from_file(file_name) is
  2540 + {
  2541 + cannot_find_file then
  2542 + println("site UID doesn't exists. Generating it now");
  2543 + with uid = generate_random_string(9),
  2544 + if write_to_file(file_name, to_byte_array(uid)) is
  2545 + {
  2546 + cannot_open_file then println("Can't create site UID here "+file_name);"",
  2547 + write_error(_) then println("Can't write site UID here "+file_name);"",
  2548 + ok then uid
  2549 + }
  2550 + read_error(_) then
  2551 + println("cannot read site UID from "+file_name);""
  2552 + ok(ba) then to_string(ba)
  2553 + }
  2554 + .
  2555 +
  2556 +// Compatibility function for older websites
  2557 +public define Web_Site
  2558 + make_web_site_description
  2559 + (
  2560 + List(String) common_names, // for example: ["www.our-business.com"]
  2561 + String site_directory,
  2562 + String state_directory,
  2563 + One -> One init,
  2564 + (HTTP_Info,
  2565 + List(Web_arg),
  2566 + Bool is_https) -> $State initial_state,
  2567 + ($State expired,
  2568 + Maybe(String),
  2569 + HTTP_Info,
  2570 + List(Web_arg),
  2571 + Bool is_https) -> $State ticket_expired_state,
  2572 + (Maybe(String),
  2573 + HTTP_Info,
  2574 + List(Web_arg),
  2575 + Bool is_https) -> $State ticket_lost_state,
  2576 + List(Web_Action($State)) actions,
  2577 + $State -> HTTP_Answer compute_page,
  2578 + $State -> List(HTTP_header) additional_headers,
  2579 + Int timeout,
  2580 + Redirections redirections,
  2581 + String charset,
  2582 + List(String) journal_extensions,
  2583 + List(String) journal_headers,
  2584 + String secret,
  2585 + List(MIME) known_mime_types,
  2586 + (String action_name,
  2587 + List(Web_arg) args) -> One before_send_file
  2588 + //Bool using_state_cookies
  2589 + )
  2590 + =
  2591 + //generate an unique ID if doesn't exist in root of site_directory
  2592 + with site_UID = get_site_uid(site_directory),
  2593 + println("site_UID : "+site_UID);
  2594 + make_web_site_description(site_UID, common_names, site_directory, state_directory, init, initial_state, ticket_expired_state, ticket_lost_state, actions, compute_page, additional_headers, timeout, redirections, charset, journal_extensions, journal_headers, secret, known_mime_types, before_send_file)
  2595 + .
  2596 +
  2597 +
  2598 +
  2599 + *** [4.3] Starting the servers.
  2600 +
  2601 +public define Start_Web_Sites_Result
  2602 + start_web_sites
  2603 + (
  2604 + Word32 ip_address, // the IP address shared by the web sites
  2605 + Word32 http_port, // usually: 80
  2606 + Word32 https_port, // usually: 443
  2607 + String ssl_certificate_common_name,
  2608 + List(Web_Site) web_sites, // web sites to be started
  2609 + (One) -> Bool shutdown_required
  2610 + ) =
  2611 + with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
  2612 + with http_server_r =
  2613 + start_http_server(ip_address,http_port,
  2614 + map(get_description,web_sites),
  2615 + load_denial_of_service_info),
  2616 + with https_server_r =
  2617 + start_https_server(ip_address,https_port,
  2618 + ssl_certificate_common_name,
  2619 + map(get_description,web_sites),
  2620 + load_denial_of_service_info),
  2621 + if http_server_r is ok(http_server)
  2622 + then
  2623 + (
  2624 + if https_server_r is ok(https_server)
  2625 + then
  2626 + (
  2627 + start_http_servers_tasks(map(get_description,web_sites),
  2628 + [http_server,https_server],
  2629 + 600); // period of 10 minutes
  2630 + delegate
  2631 + delete_states_loop(
  2632 + map((Web_Site ws) |->
  2633 + (site_directory(description(ws)(http_port,https_port))+
  2634 + "/states",delete_out_of_date(ws)),
  2635 + web_sites),
  2636 + 3600*24*3, // keep out of date states 3 days
  2637 + now,
  2638 + http_server,
  2639 + https_server,
  2640 + shutdown_required),
  2641 + ok(http_server,https_server)
  2642 + )
  2643 + else cannot_bind_to_port(https_port)
  2644 + )
  2645 + else
  2646 + (
  2647 + if https_server_r is ok(https_server)
  2648 + then cannot_bind_to_port(http_port)
  2649 + else cannot_bind_to_port(http_port,https_port)
  2650 + ).
  2651 +
  2652 +
  2653 +public define One
  2654 + start_web_sites
  2655 + (
  2656 + Word32 ip_address, // the IP address shared by the web sites
  2657 + Word32 http_port, // usually: 80
  2658 + Word32 https_port, // usually: 443
  2659 + String ssl_certificate_common_name,
  2660 + List(Web_Site) web_sites, // web sites to be started
  2661 + (One) -> Bool shutdown_required,
  2662 + Logger log
  2663 + ) =
  2664 + if (Start_Web_Sites_Result)start_web_sites(ip_address,
  2665 + http_port,
  2666 + https_port,
  2667 + ssl_certificate_common_name,
  2668 + web_sites,
  2669 + shutdown_required) is
  2670 + {
  2671 + cannot_bind_to_port(n) then logError(log, "Cannot bind to port: "+n),
  2672 + cannot_bind_to_port(n,m) then logError(log, "Cannot bind to ports: "+n+", "+m),
  2673 + ok(s1,s2) then logInfo(log, "Servers started on ports "+http_port+" (HTTP) and "+https_port+" (HTTPS).")
  2674 + }.
  2675 +
  2676 +
  2677 +
  2678 +
  2679 +
  2680 + *** [5] HTML Formating.
  2681 +
  2682 + We need to translate HTML elements as defined above into actual HTML text.
  2683 +
  2684 + Actioners require special informations, which must be transmitted when needed by the
  2685 + 'format' functions:
  2686 +
  2687 + - the 'common name', which is used for URLs,
  2688 + - the HTTP/HTTPS port number,
  2689 + - the 'state name', which must be transmitted when the actioner is clicked upon,
  2690 + - the 'form name' (if any) to which the actioner refers.
  2691 +
  2692 + If the actioner is off form, and if it refers to a form, the name of that form is
  2693 + already known by the actioner. On the contrary, if the actioner is 'in form', it refers
  2694 + implicitly to the form containing it. The name of that form is transmitted to the
  2695 + 'format' functions called from within the formating of that form.
  2696 +
  2697 +
  2698 +
  2699 +
  2700 + *** [5.1] The type 'HTML_Any($T)'.
  2701 +
  2702 + The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
  2703 + parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
  2704 +
  2705 +type HTML_Any($T):
  2706 + any_text (List(Text_Option), String the_text),
  2707 + any_preformated (List(Text_Option), String),
  2708 + any_paragraph (List(Text_Option), $T content),
  2709 + any_image (List(CoreAttrs), String url, String alternate),
  2710 + any_image (List(CoreAttrs), String url, String alternate, Int width, Int height),
  2711 + any_table (List(Table_Option), HTML_Header_Row($T), List(HTML_Row($T)), HTML_Footer_Row($T)),
  2712 + any_center ($T),
  2713 + any_mail_to (String email, $T element),
  2714 + any_scroller (Int width, Int height,
  2715 + Int content_width, Int content_height,
  2716 + $T content),
  2717 + any_fixed_size (HTML_Size width, HTML_Size height, $T content),
  2718 + any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  2719 + any_actioner (Actioner_Connection,
  2720 + Actioner_Target,
  2721 + Actioner_Aspect,
  2722 + String action_name,
  2723 + List((String,String)) extra_ops,
  2724 + List(Actioner_Local_Action),
  2725 + Maybe(String) form_name),
  2726 + any_foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
  2727 + any_foreign_link (List(Text_Option), String url),
  2728 + any_foreign_link (List(Text_Option), String url, String name),
  2729 + any_private_download (String abs_path, String name, String extra_ext,
  2730 + Maybe((String,List((String,String))))),
  2731 + any_div (List(CoreAttrs), $T element),
  2732 + any_div_empty (List(CoreAttrs)),
  2733 + any_coreattrs (List(CoreAttrs)).
  2734 +
  2735 +
  2736 +
  2737 + *** [5.2] Formating a color.
  2738 +
  2739 + RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
  2740 + hexadecimal values.
  2741 +
  2742 +define String
  2743 + html_format
  2744 + (
  2745 + RGB color
  2746 + ) =
  2747 + if color is rgb(r,g,b) then
  2748 + "#" + to_hexa(r)
  2749 + + to_hexa(g)
  2750 + + to_hexa(b).
  2751 +
  2752 +
  2753 + The following is a very arbitrary definition of the opposite color. The thing which is
  2754 + important is that it is far from the original, so that characters in 'opposite' color
  2755 + are clearly visible over the original.
  2756 +
  2757 +define RGB
  2758 + opposite
  2759 + (
  2760 + RGB color
  2761 + ) =
  2762 + if color is rgb(r,g,b) then
  2763 + rgb(255-r,
  2764 + 255-g,
  2765 + 255-b).
  2766 +
  2767 +
  2768 +
  2769 +
  2770 + *** [5.3] Creating buttons.
  2771 +
  2772 + We want to be able to create buttons in the form of a pair of images (rollovers)
  2773 + automatically. We use the JPEG interface, because for the time being Anubis cannot
  2774 + handle other kinds of images.
  2775 +
  2776 +
  2777 + Computing printed text length.
  2778 +
  2779 + define Int
  2780 + printed_text_width
  2781 + (
  2782 + Word8 -> Int char_size,
  2783 + List(Word8) l
  2784 + ) =
  2785 + if l is
  2786 + {
  2787 + [] then (Int) 0,
  2788 + [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
  2789 + }.
  2790 +
  2791 + define Int
  2792 + printed_text_width
  2793 + (
  2794 + SystemFont font,
  2795 + String s
  2796 + ) =
  2797 + printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
  2798 + explode(s)).
  2799 +
  2800 +
  2801 +
  2802 + Converting RGB to RGBA.
  2803 +
  2804 +define RGBA
  2805 + to_rgba
  2806 + (
  2807 + RGB color
  2808 + ) =
  2809 + if color is rgb(r,g,b) then rgba(r,g,b,255).
  2810 +
  2811 +
  2812 + Drawing a 'relief'.
  2813 +
  2814 + define One
  2815 + draw_relief
  2816 + (
  2817 + RGBAImage dest,
  2818 + RGBA color,
  2819 + Int contrast,
  2820 + Int x,
  2821 + Int y,
  2822 + Int width,
  2823 + Int height
  2824 + ) =
  2825 + with l = lighten(color,contrast),
  2826 + d = darken(color,contrast),
  2827 + draw_rectangle(dest,rect(x,y,x+width,y+1),l);
  2828 + draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
  2829 + draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
  2830 + draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
  2831 +
  2832 +
  2833 + Creating a button background.
  2834 +
  2835 + define RGBAImage
  2836 + create_button_background
  2837 + (
  2838 + RGBA color,
  2839 + Int width,
  2840 + Int height
  2841 + ) =
  2842 + with result = create_rgba_image(width,height,color),
  2843 + draw_relief(result,color,100,0,0,width,height);
  2844 + draw_relief(result,color,70,1,1,width-2,height-2);
  2845 + draw_relief(result,color,55,2,2,width-4,height-4);
  2846 + draw_relief(result,color,35,3,3,width-6,height-6);
  2847 + draw_relief(result,color,20,4,4,width-8,height-8);
  2848 + draw_relief(result,color,10,5,5,width-10,height-10);
  2849 + draw_relief(result,color,5,6,6,width-12,height-12);
  2850 + result.
  2851 +
  2852 +
  2853 + Drawing the text over the background.
  2854 +
  2855 + define One
  2856 + draw_button_text
  2857 + (
  2858 + RGBAImage image,
  2859 + String text,
  2860 + Int text_index,
  2861 + Int pixel_x,
  2862 + Int y,
  2863 + Rectangle clip,
  2864 + RGBA color,
  2865 + SystemFont font,
  2866 + ) =
  2867 + if nth(text_index,text) is
  2868 + {
  2869 + failure then unique,
  2870 + success(c) then
  2871 + with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
  2872 + draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
  2873 + }.
  2874 +
  2875 + define One
  2876 + draw_button_text
  2877 + (
  2878 + RGBAImage image,
  2879 + String text,
  2880 + Int text_width,
  2881 + RGBA light_color,
  2882 + RGBA dark_color,
  2883 + SystemFont font
  2884 + ) =
  2885 + with image_width = width(image),
  2886 + image_height = height(image),
  2887 + x_pos = (image_width-text_width)>>1,
  2888 + clip = rect(0,0,image_width,image_height),
  2889 + new_light_color = lighten(light_color,150),
  2890 + new_dark_color = darken(dark_color,40),
  2891 + draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
  2892 + draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
  2893 +
  2894 +
  2895 + The next function creates the two images for a button. The information given is the
  2896 + main color of the button, the text of the button and the minimal width (in pixels) of
  2897 + the button. The function does not create the button if the images already exist. The
  2898 + two images are stored in the directory 'site_directory/buttons'. The names of the files
  2899 + are of the form:
  2900 +
  2901 + bxxxx_off.jpg
  2902 + bxxxx_on.jpg
  2903 +
  2904 + where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
  2905 + 'rm'), and where 'xxxx' is created from the given informations by the formula:
  2906 +
  2907 + xxxx = web_arg_encode(sha1((color,text,width)))
  2908 +
  2909 + Hence, distinct informations give distinct file names.
  2910 +
  2911 +
  2912 + define String // returns xxxx
  2913 + create_button_images
  2914 + (
  2915 + String site_directory,
  2916 + RGBA color,
  2917 + String text,
  2918 + Int width,
  2919 + SystemFont font
  2920 + ) =
  2921 + with xxxx = web_arg_encode(sha1((color,text,width))),
  2922 + buttons_dir = site_directory+"/public/buttons",
  2923 + off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
  2924 + on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
  2925 + if file_exists(on_filepath)
  2926 + then xxxx
  2927 + else with
  2928 + text_width = printed_text_width(font,text),
  2929 + button_width = max(width,text_width+12),
  2930 + button_height = (Int)20,
  2931 + light_color = lighten(color,60),
  2932 + very_light_color = lighten(light_color,30),
  2933 + dark_color = darken(color,40),
  2934 + background_off =
  2935 + create_button_background(color,button_width,button_height),
  2936 + background_on =
  2937 + create_button_background(light_color,button_width,button_height),
  2938 +
  2939 + draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
  2940 + draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
  2941 + forget(write_image_to_JPEG_file(to_JPEG(background_off),
  2942 + off_filepath,
  2943 + 100));
  2944 + forget(write_image_to_JPEG_file(to_JPEG(background_on),
  2945 + on_filepath,
  2946 + 100));
  2947 + xxxx.
  2948 +
  2949 +
  2950 +
  2951 +
  2952 +
  2953 + *** [5.4] Formating an actioner.
  2954 +
  2955 + An actioner works as follows. Assume first that it refers to a form. When it is clicked
  2956 + upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
  2957 + form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
  2958 + actioner does not refer to a form, it fires the URL directly via 'href', because in
  2959 + that case, the actioner is always an <a> tag.
  2960 +
  2961 + The URL itself is composed using the connection sort (same, http or https), the common
  2962 + name and port number (if needed), the state name, the action name, and the extra
  2963 + operands, which are put into a query string. It may look like this:
  2964 +
  2965 + http://common_name:port/?s=state_name&a=action_name&oname=value...
  2966 +
  2967 + Each extra operand is a pair of strings: (name,value). It is formated as:
  2968 +
  2969 + &oname=value
  2970 +
  2971 +public define String
  2972 + format_attrs
  2973 + (
  2974 + List(CoreAttrs) attributs
  2975 + ).
  2976 +
  2977 +
  2978 +public define String
  2979 + format_extra_operands
  2980 + (
  2981 + List((String,String)) l
  2982 + ) =
  2983 + if l is
  2984 + {
  2985 + [ ] then "",
  2986 + [h . t] then if h is (n,v) then
  2987 + "&amp;"+n+"="+v+format_extra_operands(t)
  2988 + }.
  2989 +
  2990 +
  2991 + It seams that the standard requires "&amp;" instead of "&" !
  2992 +
  2993 + In case the target is another window, we need to format the options for this window.
  2994 +
  2995 +define String
  2996 + format
  2997 + (
  2998 + List(Other_Window_Option) l
  2999 + ) =
  3000 + if l is
  3001 + {
  3002 + [ ] then "",
  3003 + [h . t] then if h is
  3004 + {
  3005 + resizable then "resizable",
  3006 + scrollbars then "scrollbars",
  3007 + width(w) then "width="+w,
  3008 + height(h2) then "height="+h2
  3009 + } + if t is [ ] then "" else (","+format(t))
  3010 + }.
  3011 +
  3012 +
  3013 +
  3014 + Formating choices for a <select> tag.
  3015 +
  3016 +public define Printable_tree
  3017 + format_choices
  3018 + (
  3019 + List(String) l
  3020 + ) =
  3021 + if l is
  3022 + {
  3023 + [ ] then [ ],
  3024 + [h . t] then ["<option>", h, "</option>\n" . format_choices(t)]
  3025 + }.
  3026 +
  3027 +
  3028 +public define Printable_tree
  3029 + format_choices
  3030 + (
  3031 + List(String) l,
  3032 + InitialValue selected
  3033 + ) =
  3034 + if l is
  3035 + {
  3036 + [ ] then [ ],
  3037 + [h . t] then if h = selected.value
  3038 + then ["<option selected=\"selected\">", h, "</option>\n" . format_choices(t)]
  3039 + else ["<option>", h, "</option>\n" . format_choices(t,selected)]
  3040 + }.
  3041 +
  3042 +
  3043 +public define Printable_tree
  3044 + format_choices
  3045 + (
  3046 + List((List(CoreAttrs),WebArgValue,String)) l
  3047 + ) =
  3048 + if l is
  3049 + {
  3050 + [ ] then [ ],
  3051 + [h . t] then
  3052 + if h is (attrs,val,item)
  3053 + then ["<option value=\""+val.value+"\" "+format_attrs(attrs)+">", item, "</option>\n" . format_choices(t)]
  3054 + }.
  3055 +
  3056 +public define Printable_tree
  3057 + format_choices
  3058 + (
  3059 + List((List(CoreAttrs),WebArgValue,String)) l,
  3060 + InitialValue selected
  3061 + ) =
  3062 + if l is
  3063 + {
  3064 + [ ] then [ ],
  3065 + [h . t] then
  3066 + if h is (attrs,val,item) then
  3067 + if val.value = selected.value
  3068 + then ["<option value=\""+val.value+"\" "+format_attrs(attrs)+" selected=\"selected\">", item, "</option>\n" . format_choices(t)]
  3069 + else ["<option value=\""+val.value+"\" "+format_attrs(attrs)+" >", item, "</option>\n" . format_choices(t,selected)]
  3070 + }.
  3071 +
  3072 +
  3073 +
  3074 + Note: this counter is private to the virtual machine, hence there is one counter by
  3075 + client and by page : it seems that a new VM (simply a delegate) is started for each request.
  3076 +
  3077 +define Int
  3078 + new_count
  3079 + (
  3080 + Var(Int) count
  3081 + )
  3082 + =
  3083 + count <- *count+1;
  3084 + *count.
  3085 +
  3086 +
  3087 + The next function composes the URL. It is a JavaScript URL when the target is another
  3088 + window.
  3089 +
  3090 +define String
  3091 + make_actioner_url
  3092 + (
  3093 + CommonInfo cinfo,
  3094 + Actioner_Connection connection,
  3095 + Actioner_Target target,
  3096 + String state_name,
  3097 + String action_name,
  3098 + List((String,String)) extra_ops,
  3099 + Bool is_https
  3100 + ) =
  3101 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3102 + with strict_url =
  3103 + if connection is
  3104 + {
  3105 + same then "/",
  3106 + http then "http://"+common_name+":"+http_port+"/",
  3107 + https then "https://"+common_name+":"+https_port+"/",
  3108 + } +
  3109 + "?a=" + action_name +
  3110 + format_extra_operands(extra_ops),
  3111 + if target is
  3112 + {
  3113 + same then strict_url,
  3114 + same(label) then strict_url+"#"+label,
  3115 + other(wn,ops) then
  3116 +
  3117 + "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
  3118 + "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
  3119 + }.
  3120 +
  3121 +define String
  3122 + make_foreign_link_url
  3123 + (
  3124 + Actioner_Target target,
  3125 + String url
  3126 + ) =
  3127 + if target is
  3128 + {
  3129 + same then "/",
  3130 + same(label) then "/#"+label,
  3131 + other(wn,ops) then
  3132 +
  3133 + "javascript:void window.open('"+url+"','"+
  3134 + "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
  3135 + }.
  3136 +
  3137 +
  3138 + Depending on the fact that the actioner refers to a form or not, the URL is used in two
  3139 + different ways. If the actioner does not refer to a form, it is realized by a '<a>'
  3140 + tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
  3141 + tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
  3142 + event handler. The handler calls a JavaScript function which puts the URL as the value
  3143 + of the 'action' attribute of the form, and submits the form.
  3144 +
  3145 +type URL_or_JavaScript:
  3146 + url (String),
  3147 + javascript (Printable_tree script, Printable_tree handler).
  3148 +
  3149 +
  3150 + It was as shown below, in order to allow submission of a form from outside the form,
  3151 + but this makes problems:
  3152 +
  3153 +define URL_or_JavaScript
  3154 + format_action
  3155 + (
  3156 + String the_url,
  3157 + Actioner_Aspect aspect,
  3158 + Maybe(String) mb_id,
  3159 + Maybe(String) mb_form_name,
  3160 + Var(Int) action_count
  3161 + ) =
  3162 + if mb_form_name is
  3163 + {
  3164 + failure then
  3165 + url(the_url),
  3166 + success(form_name) then
  3167 + with n = new_count(action_count),
  3168 + fn_name = "pfu_" + if mb_id is
  3169 + {
  3170 + failure then form_name + "_" + n,
  3171 + success(id) then id,
  3172 + },
  3173 + javascript(
  3174 + [
  3175 + "<script type=\"text/javascript\" language =\"JavaScript\">\n",
  3176 + "function ", fn_name, "() {\n",
  3177 + " var fa = document.forms.",form_name,";\n",
  3178 + " var u = \"",the_url,"\";\n",
  3179 + " fa.action = u;",
  3180 + if aspect is submit(_, _) then " }\n"
  3181 + else "\n fa.submit(); }\n",
  3182 + "</script>\n"
  3183 + ],
  3184 + [fn_name,"();"]
  3185 + )
  3186 + }.
  3187 +
  3188 +
  3189 +
  3190 + Now, we format the actioner according to its aspect.
  3191 +
  3192 +define List(Text_Option)
  3193 + get_text_options
  3194 + (
  3195 + List(Text_Option) l
  3196 + )
  3197 + =
  3198 + if l is
  3199 + {
  3200 + [] then [],
  3201 + [h . t ] then
  3202 + if h is class(_) then
  3203 + get_text_options(t)
  3204 + else if h is id(_) then
  3205 + get_text_options(t)
  3206 + else if h is style(_) then
  3207 + get_text_options(t)
  3208 + else if h is title(_) then
  3209 + get_text_options(t)
  3210 + else
  3211 + [ h . get_text_options(t) ]
  3212 +
  3213 + }.
  3214 +
  3215 + /**
  3216 + * Extract the CSS class list from the list of Text_Option
  3217 + */
  3218 +define List(CoreAttrs)
  3219 + get_css_class
  3220 + (
  3221 + List(Text_Option) l
  3222 + )
  3223 + =
  3224 + if l is
  3225 + {
  3226 + [] then [],
  3227 + [h . t ] then
  3228 +
  3229 +
  3230 + if h is class(name) then
  3231 + [ class(name) . get_css_class(t) ]
  3232 + else if h is id(name) then
  3233 + [ id(name) . get_css_class(t) ]
  3234 + else if h is style(the_style) then
  3235 + [ style(the_style) . get_css_class(t) ]
  3236 + else if h is title(name) then
  3237 + [ tooltip(name) . get_css_class(t) ]
  3238 + else if h is attr(name, value) then
  3239 + [ attr(name, value) . get_css_class(t) ]
  3240 + else
  3241 + get_css_class(t)
  3242 + }
  3243 + .
  3244 +
  3245 +define Printable_tree
  3246 + format
  3247 + (
  3248 + List(Actioner_Local_Action) l
  3249 + ) =
  3250 + if l is
  3251 + {
  3252 + [ ] then [ ],
  3253 + [h . t] then [if h is
  3254 + {
  3255 + close_window then [" window.close(); "]
  3256 + }
  3257 + . format(t)]
  3258 + }.
  3259 +
  3260 +public define String
  3261 + format_attrs
  3262 + (
  3263 + List(CoreAttrs) attributs
  3264 + )=
  3265 + if attributs is
  3266 + {
  3267 + [] then "",
  3268 + [h . t] then
  3269 + with current = if h is
  3270 + {
  3271 + id(id_name) then
  3272 + " id=\"" + id_name + "\"",
  3273 + class(class_name) then
  3274 + " class=\"" + class_name + "\"",
  3275 + style(style_string) then
  3276 + " style=\"" + style_string + "\"",
  3277 +
  3278 + title(title_string) then
  3279 + " title=\"" + title_string + "\"",
  3280 +
  3281 + lang(lang) then
  3282 + " xml:lang=" + lang,
  3283 + dir(reading_Way) then
  3284 + if reading_Way is
  3285 + {
  3286 + ltr then " dir=ltr",
  3287 + rtl then " dir=rtl"
  3288 + },
  3289 +
  3290 + accesskey(key) then
  3291 + " accesskey=\"" + key + "\"",
  3292 + tabindex(index) then
  3293 + " tabindex=\"" + index + "\"",
  3294 +
  3295 + attr(name, value) then
  3296 + " " + name + "=\"" + value + "\"",
  3297 +
  3298 + event(e, value) then
  3299 + " " + event_name(e) + "=\"" + value + "\"",
  3300 + },
  3301 + current + format_attrs(t)
  3302 + }.
  3303 +
  3304 +define String
  3305 + format_attrs
  3306 + (
  3307 + List(InputAttrs) attributs
  3308 + )=
  3309 + if attributs is
  3310 + {
  3311 + [] then "",
  3312 + [h . t] then
  3313 + with current = if h is
  3314 + {
  3315 + class(class_name) then
  3316 + " class=\"" + class_name + "\"",
  3317 + style(style_string) then
  3318 + " style=\"" + style_string + "\"",
  3319 +
  3320 + title(title_string) then
  3321 + " title=\"" + title_string + "\"",
  3322 +
  3323 + lang(lang) then
  3324 + " xml:lang=" + lang,
  3325 + dir(reading_Way) then
  3326 + if reading_Way is
  3327 + {
  3328 + ltr then " dir=ltr",
  3329 + rtl then " dir=rtl"
  3330 + },
  3331 +
  3332 + accesskey(key) then
  3333 + " accesskey=\"" + key + "\"",
  3334 + tabindex(index) then
  3335 + " tabindex=\"" + index + "\"",
  3336 +
  3337 + attr(name, value) then
  3338 + " " + name + "=\"" + value + "\"",
  3339 +
  3340 + event(e, value) then
  3341 + " " + event_name(e) + "=\"" + value + "\"",
  3342 + },
  3343 + current + format_attrs(t)
  3344 + }.
  3345 +
  3346 +define String
  3347 + format_text_options
  3348 + (
  3349 + List(Text_Option) l
  3350 + )
  3351 + =
  3352 + with text_options = get_text_options(l),
  3353 + css_classes = get_css_class(l),
  3354 + if text_options is
  3355 + {
  3356 + [] then "",
  3357 + [_ . _] then " style=\"" + format(text_options) + "\" "
  3358 + }
  3359 + +
  3360 + if css_classes is
  3361 + {
  3362 + [] then "",
  3363 + [_ . _] then format_attrs(css_classes)
  3364 + }.
  3365 +
  3366 +//define String
  3367 +// _format
  3368 +// (
  3369 +// List(CoreAttrs) opt
  3370 +// )=
  3371 +// if opt is
  3372 +// {
  3373 +// [] then "",
  3374 +// [h .t] then
  3375 +// with current = if h is
  3376 +// {
  3377 +// id(id_name) then
  3378 +// " id=\"" + id_name + "\"",
  3379 +// class(class_name) then
  3380 +// " class=\"" + class_name + "\"",
  3381 +// style(style_string) then
  3382 +// " style=\"" + style_string + "\"",
  3383 +//
  3384 +// title(title_string) then
  3385 +// " title=\"" + title_string + "\"",
  3386 +// lang(lang) then
  3387 +// " xml:lang=" + lang,
  3388 +// dir(reading_Way) then
  3389 +// if reading_Way is
  3390 +// {
  3391 +// ltr then " dir=ltr",
  3392 +// rtl then " dir=rtl"
  3393 +// },
  3394 +// attr(name, value) then
  3395 +// " " + name + "=\"" + value + "\"",
  3396 +// event(e, value) then
  3397 +// " " + event_name(e) + "=\"" + value + "\""
  3398 +// },
  3399 +// current + _format(t)
  3400 +// }
  3401 +// .
  3402 +
  3403 +define Printable_tree
  3404 + format_div_option
  3405 + (
  3406 + List(CoreAttrs) options
  3407 + )=
  3408 + ["<div" + format_attrs(options) + ">"] .
  3409 +
  3410 +
  3411 +define Maybe(String)
  3412 + extract_id
  3413 + (
  3414 + List(CoreAttrs) attrbs
  3415 + ) =
  3416 + if attrbs is
  3417 + {
  3418 + [] then failure,
  3419 + [h . t] then
  3420 + if h is id(value) then success(value)
  3421 + else extract_id(t)
  3422 + }.
  3423 +
  3424 +define Maybe(String)
  3425 + extract_id
  3426 + (
  3427 + Actioner_Aspect aspect
  3428 + ) =
  3429 + if aspect is
  3430 + {
  3431 + link(_,_,_) then failure,
  3432 + img_link(_,_) then failure,
  3433 + push_button(opts,_) then extract_id(opts),
  3434 + button(_,_) then failure,
  3435 + button(_,_,_,_) then failure,
  3436 + submit(opts,_) then extract_id(opts),
  3437 + immediate_selector(_,_,_,_) then failure,
  3438 + immediate_selector(_,_,_,_,_) then failure,
  3439 + }.
  3440 +
  3441 +to do Complete format_foreign_link
  3442 +//TODO Complete format_foreign_link
  3443 +define Printable_tree
  3444 + format_foreign_link
  3445 + (
  3446 +// CommonInfo cinfo,
  3447 + Actioner_Target target,
  3448 + Actioner_Aspect aspect,
  3449 + String url,
  3450 + Bool is_https,
  3451 + ) =
  3452 + //if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  3453 + with full_url = make_foreign_link_url(target,url),
  3454 +// with action = format_action(full_url, aspect, extract_id(aspect), mb_form_name),
  3455 + if aspect is
  3456 + {
  3457 +
  3458 + link(opt, text, mb_name) then
  3459 + with name_tag = if mb_name is success(n) then " name=\"" + n + "\"" else "",
  3460 + [
  3461 + "<a href=\"", full_url, "\"", format_text_options(opt), name_tag, ">",
  3462 + text,
  3463 + "</a>"
  3464 + ],
  3465 + img_link(img,alt_text) then
  3466 + [
  3467 + "<a href=\"", full_url ,"\"><img src=\"", img,
  3468 + "\" alt=\"" + alt_text + "\" border=\"0\"></a>"
  3469 + ],
  3470 + push_button(options, text) then
  3471 + ["<a href=\"",full_url,"\"", format_attrs(options), ">",
  3472 + text, "</a>"
  3473 + ],
  3474 + button(url_off,url_on) then
  3475 + [ ],
  3476 +
  3477 + button(url_off,url_on,w,h) then
  3478 + [ ],
  3479 +
  3480 + submit(options, text) then
  3481 + [ ],
  3482 +
  3483 + immediate_selector(opt,name,size,choices) then
  3484 + [ ],
  3485 + immediate_selector(opt,name,size,choices,selected) then
  3486 + [ ],
  3487 + }.
  3488 +
  3489 +define Printable_tree
  3490 + format_actioner
  3491 + (
  3492 + CommonInfo cinfo,
  3493 + String state_name,
  3494 + Actioner_Connection connection,
  3495 + Actioner_Target target,
  3496 + Actioner_Aspect aspect,
  3497 + String action_name,
  3498 + List((String,String)) extra_ops,
  3499 + List(Actioner_Local_Action) local_actions,
  3500 + Maybe(String) mb_form_name,
  3501 + Bool is_https,
  3502 + Var(Int) action_count
  3503 + ) =
  3504 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  3505 + with url = make_actioner_url(cinfo,connection,target,
  3506 + state_name,action_name,extra_ops,is_https),
  3507 + with action = format_action(url, aspect, extract_id(aspect), mb_form_name, action_count),
  3508 + if aspect is
  3509 + {
  3510 +
  3511 + link(opt, text, mb_name) then
  3512 + with name_tag = if mb_name is success(n) then " name=\"" + n + "\"" else "",
  3513 + [
  3514 + "<a href=\"", url, "\"", format_text_options(opt), name_tag, ">",
  3515 + text,
  3516 + "</a>"
  3517 + ],
  3518 + img_link(img, alt_text) then
  3519 + [
  3520 + "<a href=\"", url ,"\"><img src=\"", img,
  3521 + "\" alt=\"" + alt_text + "\" border=\"0\"></a>"
  3522 + ],
  3523 + push_button(options, text) then
  3524 + [
  3525 + if action is
  3526 + {
  3527 + url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
  3528 + text, "</a>"
  3529 + ],
  3530 + javascript(s,h) then
  3531 + if action_name = "" then
  3532 + ["<input type=\"button\" value=\"",text,"\"", format_attrs(options), " />"]
  3533 + else
  3534 + [s,"<input type=\"button\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
  3535 + }
  3536 + ],
  3537 + button(url_off,url_on) then
  3538 + [ if action is
  3539 + {
  3540 + url(u) then ["<a href=\"",u],
  3541 + javascript(s,h) then [s,"<a onMouseDown=\"",h]
  3542 + },
  3543 + "\" style=\"text-decoration:none\">",
  3544 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  3545 + " onMouseOver=\"this.src='",url_on,"'\" ",
  3546 + " onMouseOut=\"this.src='",url_off,"'\">",
  3547 + "</a>"
  3548 + ],
  3549 +
  3550 + button(url_off,url_on,w,h) then
  3551 + [ if action is
  3552 + {
  3553 + url(u) then ["<a href=\"",u],
  3554 + javascript(s,h2) then [s,"<a onMouseDown=\"",h2]
  3555 + },
  3556 + "\" style=\"text-decoration:none\">",
  3557 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  3558 + " onMouseOver=\"this.src='",url_on,"'\" ",
  3559 + " onMouseOut=\"this.src='",url_off,"'\">",
  3560 + "</a>"
  3561 + ],
  3562 +
  3563 + submit(options, text) then
  3564 + [
  3565 + if action is
  3566 + {
  3567 + url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
  3568 + text, "</a>"
  3569 + ],
  3570 + javascript(s,h) then
  3571 + if action_name = "" then // special case where URL (so the action_name) is provided by the form itself
  3572 + // so javascript isn't needed. This is the standard HMTL way.
  3573 + ["<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " />"]
  3574 + else
  3575 + [s,"<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
  3576 + }
  3577 + ],
  3578 +
  3579 + immediate_selector(options,name,size,choices) then
  3580 + [ if action is
  3581 + {
  3582 + url(u) then ["<select href=\"",u]
  3583 + javascript(s,h) then [s,"<select onchange=\"",h]
  3584 + },
  3585 + "\" name=\"",name,"\" size=\"",size,"\"", format_attrs(options), ">",
  3586 + format_choices(choices),"</select>"
  3587 + ],
  3588 + immediate_selector(options,name,size,choices,selected) then
  3589 + [ if action is
  3590 + {
  3591 + url(u) then ["<select href=\"",u]
  3592 + javascript(s,h) then [s,"<select onchange=\"",h]
  3593 + },
  3594 + "\" name=\"",name,"\" size=\"",size,"\"", format_attrs(options), ">",
  3595 + format_choices(choices, selected),"</select>"
  3596 + ],
  3597 + }.
  3598 +
  3599 +
  3600 +
  3601 +
  3602 +
  3603 +
  3604 + *** [5.5] Formating a private download link.
  3605 +
  3606 + We get the absolute path of the file to be downloaded, and the name under which it
  3607 + should appear to the client. The function 'format_private_download' creates an
  3608 + hypertext link for downloading the file. The secured mecanism of private download is
  3609 + used. This function is called by the function which formats HTML_Any($T) elements.
  3610 +
  3611 +
  3612 +define Printable_tree
  3613 + format_private_download
  3614 + (
  3615 + CommonInfo cinfo,
  3616 + String sn, // state name
  3617 + String abs_path, // absolute file path on server
  3618 + String name, // name of file as it appears in the browser
  3619 + String extra, // extra extension
  3620 + Maybe((String,List((String,String)))) action
  3621 +
  3622 + ) =
  3623 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3624 + with private_download_directory = site_directory+"/private_download",
  3625 + with auth = make_authorization(site_directory,secret,abs_path),
  3626 + [
  3627 + "<a href=\"",name,extra,"?zauth=",auth,
  3628 + if action is
  3629 + {
  3630 + failure then [ ]
  3631 + success(a) then if a is (an,args) then
  3632 + ["&amp;a=",an,format_extra_operands(args)]
  3633 + },
  3634 + "\">",
  3635 + name,
  3636 + "</a>"
  3637 + ].
  3638 +
  3639 +
  3640 +
  3641 +
  3642 + *** [5.6] Formating rows and cells in a table.
  3643 +
  3644 +define Int
  3645 + percent
  3646 + (
  3647 + Int p
  3648 + ) =
  3649 + if p < 0 then 0 else if p > 100 then 100 else p.
  3650 +
  3651 +
  3652 +
  3653 +
  3654 + Formating cell options.
  3655 +
  3656 +
  3657 +
  3658 +define String
  3659 + format
  3660 + (
  3661 + BackgroundOption o
  3662 + ) =
  3663 + if o is
  3664 + {
  3665 + repeat then "",
  3666 + repeat_horizontal then "; background-repeat: repeat-x",
  3667 + repeat_vertical then "; background-repeat: repeat-y",
  3668 + no_repeat then "; background-repeat: no-repeat",
  3669 + center then "; background-position: center top"
  3670 + }.
  3671 +
  3672 +define String
  3673 + format
  3674 + (
  3675 + List(BackgroundOption) l
  3676 + ) =
  3677 + if l is
  3678 + {
  3679 + [ ] then "",
  3680 + [h . t] then format(h)+format(t)
  3681 + }.
  3682 +
  3683 +
  3684 +define String
  3685 + format
  3686 + (
  3687 + List(Cell_Option) options
  3688 + ) =
  3689 + if options is
  3690 + {
  3691 + [ ] then "",
  3692 + [h . t] then
  3693 + if h is
  3694 + {
  3695 + core_attrs(core_attr_list) then format_attrs(core_attr_list),
  3696 + left then " align=\"left\"",
  3697 + h_center then " align=\"center\"",
  3698 + right then " align=\"right\"",
  3699 + top then " valign=\"top\"",
  3700 + v_center then " valign=\"middle\"",
  3701 + bottom then " valign=\"bottom\"",
  3702 + base_line then " valign=\"baseline\"",
  3703 + background_color(c) then " bgcolor=\""+html_format(c)+"\"",
  3704 + background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
  3705 + width(w) then " width=\""+w+"\"",
  3706 + percentage_width(n) then " width=\""+percent(n)+"%\"",
  3707 + height(h2) then " height=\""+h2+"\"",
  3708 + columns(n) then " colspan=\""+n+"\"",
  3709 + rows(n) then " rowspan=\""+n+"\"",
  3710 + nowrap then " nowrap",
  3711 + class(class_name) then " class=\""+class_name+"\""
  3712 + }
  3713 + + format(t)
  3714 + }.
  3715 +
  3716 +
  3717 + Normalizing a list of cell options (horizontal position must be specified; the default
  3718 + is 'left').
  3719 +
  3720 + Formating cells in a row.
  3721 +
  3722 +define Printable_tree
  3723 + format
  3724 + (
  3725 + List(HTML_Cell($T)) cells,
  3726 + $T -> Printable_tree format_element
  3727 + ) =
  3728 + if cells is
  3729 + {
  3730 + [ ] then [ ],
  3731 + [h . t] then if h is cell(options,element) then
  3732 + ["<td",format(reverse(options)),">",
  3733 + format_element(element),
  3734 + "</td>"
  3735 + . format(t,format_element)]
  3736 + }.
  3737 +
  3738 +define Printable_tree
  3739 + format
  3740 + (
  3741 + List(HTML_Header_Cell($T)) cells,
  3742 + $T -> Printable_tree format_element
  3743 + ) =
  3744 + if cells is
  3745 + {
  3746 + [ ] then [ ],
  3747 + [h . t] then if h is header_cell(options,element) then
  3748 + ["<th ",format(reverse(options)),">",
  3749 + format_element(element),
  3750 + "</th>"
  3751 + . format(t,format_element)]
  3752 + }.
  3753 +
  3754 + Formating the rows in a table.
  3755 +
  3756 +define Printable_tree
  3757 + format
  3758 + (
  3759 + List(HTML_Row($T)) rows,
  3760 + $T -> Printable_tree format_element,
  3761 + ) =
  3762 + if rows is
  3763 + {
  3764 + [ ] then [ ],
  3765 + [h . t] then if h is row(options,cells) then
  3766 + ["<tr",format(reverse(options)),">",
  3767 + format(cells,format_element),
  3768 + "</tr>\n"
  3769 + . format(t,format_element)]
  3770 + }.
  3771 +
  3772 +define Printable_tree
  3773 + format
  3774 + (
  3775 + HTML_Header_Row($T) row,
  3776 + $T -> Printable_tree format_element
  3777 + ) =
  3778 + if row is
  3779 + {
  3780 + empty then [ ],
  3781 + header_row(options,cells) then
  3782 + ["<thead> <tr",format(reverse(options)),">",
  3783 + format(cells,format_element),
  3784 + "</tr> </thead>\n"
  3785 + ]
  3786 + }.
  3787 +
  3788 +define Printable_tree
  3789 + format
  3790 + (
  3791 + HTML_Footer_Row($T) row,
  3792 + $T -> Printable_tree format_element
  3793 + ) =
  3794 + if row is
  3795 + {
  3796 + empty then [ ],
  3797 + footer_row(options,cells) then
  3798 + ["<tfoot> <tr",format(reverse(options)),">",
  3799 + format(cells,format_element),
  3800 + "</tr> </tfoot>\n"
  3801 + ]
  3802 + }.
  3803 +
  3804 +define Printable_tree
  3805 + format1
  3806 + (
  3807 + List(TextAreaOption) l
  3808 + ) =
  3809 + if l is
  3810 + {
  3811 + [] then [],
  3812 + [h . t] then if h is
  3813 + {
  3814 + input_attrs(input_attr_list) then [format_attrs(input_attr_list) . format1(t)],
  3815 + disabled then [" disabled=\"disabled\" " . format1(t)]
  3816 + read_only then [" readonly=\"readonly\" " . format1(t)]
  3817 + wrap_lines then [" wrap=\"wrap\" " . format1(t)]
  3818 + }
  3819 + }.
  3820 +
  3821 +define Printable_tree
  3822 + format
  3823 + (
  3824 + List(TextAreaOption) l
  3825 + ) =
  3826 + if member(l,wrap_lines)
  3827 + then format1(l)
  3828 + else [" wrap=\"off\" " . format1(l)].
  3829 +
  3830 +
  3831 + *** [5.7] Formating elements which may be put anywhere.
  3832 +
  3833 + The function below involves the parameter $T which is later instantiated as
  3834 + 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
  3835 + for these two types, and because formating of tables requires recursive calls of such
  3836 + functions, it is necessary to provide the 'format' function to be called recursively as
  3837 + an argument. Putting naively a call to 'format' will not work, because the compiler
  3838 + will look for a function able to format data of type $T (which is at that time distinct
  3839 + from any other type, including our two types). Such a function does not exist. Hence
  3840 + the function to be called for formating elements must be passed as a functional
  3841 + argument (called 'format_element' below). Actually, what we pass is a function taking
  3842 + a unique argument of type $T. Other informations (like the name of the state) are
  3843 + already in the function by way of full functionality.
  3844 +
  3845 +
  3846 + Formating text options. They are formated in CSS syntax, to be used within a
  3847 + 'style=...'.
  3848 +
  3849 +define String
  3850 + format
  3851 + (
  3852 + List(Text_Option) l
  3853 + ) =
  3854 + if l is
  3855 + {
  3856 + [ ] then "",
  3857 + [h . t] then if h is
  3858 + {
  3859 + size(n) then "font-size:"+n+"pt",
  3860 + font(fn) then "font-family:"+fn,
  3861 + color(c) then if c is rgb(r,g,b) then
  3862 + "color:rgb("+to_decimal(r)+","+to_decimal(g)+","+to_decimal(b)+")",
  3863 + italic then "font-style:italic",
  3864 + oblique then "font-style:oblique",
  3865 + small_capitals then "font-variant:small-caps",
  3866 + bold then "font-weight:bold",
  3867 + underlined then "text-decoration:underline",
  3868 + left_justified then "text-align:left",
  3869 + right_justified then "text-align:right",
  3870 + justified then "text-align:justify",
  3871 + line_through then "text-decoration:line-through",
  3872 + nowrap then "white-space:nowrap",
  3873 + class(class_name)then " class=\"" + class_name +"\"",
  3874 + id(id_name) then " id=\"" + id_name +"\"",
  3875 + style(style_string) then " style=\"" + style_string + "\"",
  3876 + title(s) then " title=\"" + s +"\""
  3877 + attr(n,v) then " " + n + "=\"" + v +"\""
  3878 + } + if t is [ ] then "" else ("; "+format(t))
  3879 + }.
  3880 +
  3881 +
  3882 +
  3883 + Formating table options.
  3884 +
  3885 +define String
  3886 + format
  3887 + (
  3888 + List(Table_Option) l,
  3889 + Bool border_seen
  3890 + ) =
  3891 + if l is
  3892 + {
  3893 + [ ] then if border_seen then "" else " border=\"0\" cellspacing=\"0\" cellpadding=\"0\"",
  3894 + [h . t] then if h is
  3895 + {
  3896 + background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
  3897 + background_image(url) then " background="+url+format(t,border_seen),
  3898 + border(o,top,i,c) then " border=\""+o+"\" cellspacing=\""+top+"\" cellpadding=\""+i+"\""+
  3899 + //" bordercolor="+format(c)+
  3900 + format(t,true),
  3901 + width(w) then " width=\""+w+"\""+format(t,border_seen),
  3902 + percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
  3903 + attr(name, value) then " " + name + "=\"" + value + "\"" +format(t,border_seen),
  3904 + class(name) then " class=\""+name+"\""
  3905 + }
  3906 + }.
  3907 +
  3908 +
  3909 +
  3910 +
  3911 +
  3912 +define Printable_tree
  3913 + format_scroller
  3914 + (
  3915 + String sn,
  3916 + Int width,
  3917 + Int height,
  3918 + Int content_width,
  3919 + Int content_height,
  3920 + Int idnum, // identifying the scroller
  3921 + $T content,
  3922 + $T -> Printable_tree format_element
  3923 + ) =
  3924 + [
  3925 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3926 + "function doscroll_",idnum,"(dx,dy) {\n",
  3927 + " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
  3928 + " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
  3929 + idnum,"').style\"); } else\n",
  3930 + " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
  3931 + " var x_",idnum," = parseInt(c_",idnum,".left);\n",
  3932 + " var y_",idnum," = parseInt(c_",idnum,".top);\n",
  3933 + " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
  3934 + " { x_",idnum," += dx; }\n",
  3935 + " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
  3936 + " { y_",idnum," += dy; }\n",
  3937 + " c_",idnum,".left = x_",idnum,";\n",
  3938 + " c_",idnum,".top = y_",idnum,";\n",
  3939 + " }\n",
  3940 + "</script>\n",
  3941 + "<table>",
  3942 + "<tbody>",
  3943 + "<tr>",
  3944 + "<td align=left valign=top",
  3945 + " width=",width,
  3946 + " height=",height,
  3947 + ">",
  3948 + "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
  3949 + " clip:rect(0px ",width,"px ",height,"px 0px)\">",
  3950 + "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
  3951 + format_element(content),
  3952 + "</div>",
  3953 + "</div>",
  3954 + "</td>",
  3955 + "<td valign=bottom>",
  3956 + "<table>",
  3957 + "<tbody>",
  3958 + "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onmousedown=\"doscroll_", idnum,"(0,20);\"></td></tr>",
  3959 + "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onmousedown=\"doscroll_", idnum,"(0,-20);\"></td></tr>",
  3960 + "</tbody>",
  3961 + "</table>",
  3962 + "</td>",
  3963 + "</tr>",
  3964 + (if content_width > width then
  3965 + [
  3966 + "<tr>",
  3967 + "<td align=right>",
  3968 + "<table>",
  3969 + "<tbody>",
  3970 + "<tr>",
  3971 + "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onmousedown=\"doscroll_", idnum,"(20,0);\"></td>",
  3972 + "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onmousedown=\"doscroll_", idnum,"(-20,0);\"></td>",
  3973 + "</tr>",
  3974 + "</tbody>",
  3975 + "</table>",
  3976 + "</td>",
  3977 + "</tr>",
  3978 + ] else [ ]),
  3979 + "</tbody>",
  3980 + "</table>"
  3981 + ].
  3982 +
  3983 +
  3984 +
  3985 +define Printable_tree
  3986 + format
  3987 + (
  3988 + Body_Option o
  3989 + ) =
  3990 + if o is
  3991 + {
  3992 + core_attrs(core_attr_list) then [" " + format_attrs(core_attr_list)],
  3993 + background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
  3994 + background_image(n) then [" background=", n],
  3995 + background_image(n,o2) then [" style=\"background: url(",n,")",format(o2),"\""]
  3996 +
  3997 + }.
  3998 +
  3999 +
  4000 +
  4001 +define Printable_tree
  4002 + format
  4003 + (
  4004 + List(Body_Option) l
  4005 + ) =
  4006 + if l is
  4007 + {
  4008 + [ ] then [ ],
  4009 + [h . t] then [format(h) . format(t)]
  4010 + }.
  4011 +
  4012 +define Printable_tree
  4013 + add_css_files
  4014 + (
  4015 + List(CSS_File) l
  4016 + ) =
  4017 + if l is
  4018 + {
  4019 + [ ] then [ ],
  4020 + [h . t] then
  4021 + [ ["<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file_name(h) + "\" />\n" ]
  4022 + . add_css_files(t)]
  4023 + }.
  4024 +
  4025 +define Printable_tree
  4026 + add_js_files_attributes
  4027 + (
  4028 + List(JS_Attribute) l
  4029 + ) =
  4030 + if l is
  4031 + {
  4032 + [ ] then [ ],
  4033 + [h . t] then
  4034 + if h is attr(name, value) then
  4035 + [ " " + name + "=\"" + value + "\""
  4036 + . add_js_files_attributes(t)]
  4037 + }.
  4038 +
  4039 +
  4040 +define Printable_tree
  4041 + add_js_files
  4042 + (
  4043 + List(JS_File) l
  4044 + ) =
  4045 + if l is
  4046 + {
  4047 + [ ] then [ ],
  4048 + [h . t] then
  4049 + [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\""
  4050 + . add_js_files_attributes(attributes(h)) ]
  4051 + . ["></script>\n"
  4052 + . add_js_files(t)] ]
  4053 + }.
  4054 +
  4055 +define Printable_tree
  4056 + add_script
  4057 + (
  4058 + List(Script) l
  4059 + ) =
  4060 + if l is
  4061 + {
  4062 + [ ] then [ ],
  4063 + [h . t] then [ "<script type=\""+ script_type(h) +"\">\n"+ content(h) + "\n</script>\n" . add_script(t) ]
  4064 + }.
  4065 +
  4066 +define Printable_tree
  4067 + add_css_styles
  4068 + (
  4069 + List(CSS_Style) css_styles
  4070 + ) =
  4071 +
  4072 + if css_styles is
  4073 + {
  4074 + [] then [],
  4075 + [_ . _] then [ "<style type=\"text/css\"><!--\n",
  4076 + format_css_styles(css_styles),
  4077 + " --></style>\n"
  4078 + ]
  4079 + }.
  4080 +
  4081 +
  4082 + // The function below formats a datum of type 'HTML_Any($T)'.
  4083 +
  4084 +define Printable_tree
  4085 + format
  4086 + (
  4087 + CommonInfo cinfo,
  4088 + String sn, // state_name
  4089 + Var(Int) ic_v,
  4090 + HTML_Any($T) element,
  4091 + $T -> Printable_tree format_element, // able to format a datum of type $T
  4092 + Bool is_https,
  4093 + Var(Int) action_count
  4094 + ) =
  4095 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  4096 + if element is
  4097 + {
  4098 + any_text(opts , t) then
  4099 + if opts is
  4100 + {
  4101 + [] then [t],
  4102 + [_ . _] then ["<span", format_text_options(opts), ">",t,"</span>"]
  4103 + },
  4104 + any_preformated(opts,s) then
  4105 + ["<span", format_text_options(opts), "><pre>",s,"</pre></span>"],
  4106 + //["<pre>",s,"</pre>"],
  4107 + any_paragraph(opts,e) then
  4108 + ["<p", format_text_options(opts), ">",format_element(e),"</p>\n"],
  4109 + any_image(opts, url, alt) then
  4110 + ["<img alt=\"",alt,"\" src=\"",url,"\" ", format_attrs(opts)," />"],
  4111 + any_image(opts, url, alt, w, h) then
  4112 + ["<img alt=\"",alt,"\" src=\"",url,"\" width=\"",w,"\" height=\"",h,"\" ", format_attrs(opts)," />"],
  4113 + any_table(opts,h_row, rows, f_row) then
  4114 + ["<table",format(reverse(opts),false),">",
  4115 + format(h_row, format_element),
  4116 + "<tbody>",format(rows,format_element),"</tbody>",
  4117 + format(f_row, format_element),
  4118 + "</table>\n"],
  4119 + any_center(e) then
  4120 + ["<center>",format_element(e),"</center>"],
  4121 + any_mail_to(email,elem) then
  4122 + ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
  4123 + any_scroller(w,h,cw,ch,c) then
  4124 + format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
  4125 + any_fixed_size(w,h,c) then
  4126 + with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
  4127 + ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
  4128 + "secondary document",
  4129 + "</object>"],
  4130 + any_fixed_size_2(w,h,fn) then
  4131 + with url = fn+"?zauth="+make_authorization(site_directory,secret,
  4132 + fn),
  4133 + ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
  4134 + "secondary document",
  4135 + "</object>"],
  4136 + any_actioner(c,t,a,an,eo,ja,fn) then
  4137 + format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https, action_count),
  4138 +
  4139 + any_foreign_link_new(target, aspect, url) then
  4140 + format_foreign_link(target, aspect, url, is_https),
  4141 +
  4142 + any_foreign_link(options,url) then
  4143 + ["<a href=\"",url,"\" ", format_text_options(options), "></a>"], // IE7 doesn't support the form <a href="..." />
  4144 + any_foreign_link(options,url,name) then
  4145 + ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
  4146 + any_private_download(url,name,extra_ext,action) then
  4147 + format_private_download(cinfo,sn,url,name,extra_ext,action),
  4148 + any_div(options, e) then
  4149 + [format_div_option(options), format_element(e),"</div>\n"],
  4150 + any_div_empty(options) then
  4151 + [format_div_option(options), "</div>\n"],
  4152 + any_coreattrs(attributs) then
  4153 + [format_attrs(attributs)]
  4154 + }.
  4155 +
  4156 +
  4157 +
  4158 +
  4159 + // *** [5.8] Formating 'in form' elements.
  4160 +
  4161 +
  4162 +define Printable_tree
  4163 + maybe_label
  4164 + (
  4165 + String label_text,
  4166 + HtmlId id
  4167 + ) =
  4168 + if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"]
  4169 + else [].
  4170 +
  4171 +
  4172 +define Printable_tree
  4173 + format
  4174 + (
  4175 + CommonInfo cinfo,
  4176 + String fn, // form_name
  4177 + String sn, // state_name
  4178 + Var(Int) ic_v, // idnum counter variable
  4179 + HTML_In_Form element,
  4180 + Bool is_https,
  4181 + Var(Int) action_count
  4182 + ) =
  4183 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  4184 + with format_element = (HTML_In_Form e) |-> format(cinfo, fn, sn, ic_v, e, is_https, action_count),
  4185 + if element is
  4186 + {
  4187 + literal_pt(t) then t,
  4188 + literal(t) then [t],
  4189 + sequence(l) then flat(map(format_element,l))
  4190 + text(opts,t) then
  4191 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https, action_count),
  4192 + preformated(o,s) then
  4193 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https, action_count),
  4194 + paragraph(opts,t) then
  4195 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https, action_count),
  4196 + image(opts, url, alt) then
  4197 + format(cinfo,sn,ic_v,any_image(opts, url, alt),format_element,is_https, action_count),
  4198 + image(opts, url, alt, w, h) then
  4199 + format(cinfo,sn,ic_v,any_image(opts, url, alt, w, h),format_element,is_https, action_count),
  4200 + table(opts,header_row,rows,footer_row) then
  4201 + format(cinfo,sn,ic_v,any_table(opts, header_row, rows, footer_row),format_element,is_https, action_count),
  4202 + center(e) then
  4203 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https, action_count),
  4204 + mail_to(a,e) then
  4205 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https, action_count),
  4206 + scroller(w,h,cw,ch,c) then
  4207 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https, action_count),
  4208 + actioner(c,t,a,an,eo,ja) then
  4209 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https, action_count),
  4210 +
  4211 + foreign_link_new(target, aspect, url) then
  4212 + format(cinfo,sn,ic_v,any_foreign_link_new(target,aspect,url),format_element,is_https, action_count),
  4213 +
  4214 + foreign_link(options,url) then
  4215 + format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https, action_count),
  4216 + foreign_link(options,url,name) then
  4217 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https, action_count),
  4218 + private_download(url,name,extra,action) then
  4219 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https, action_count),
  4220 + text_input(options, label_text, id, name, i, w) then
  4221 + [ maybe_label(label_text, id),
  4222 + "<input type=\"text\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  4223 + text_input_ro(options, label_text, id, name,i,w) then
  4224 + [ maybe_label(label_text, id),
  4225 + "<input readonly=\"readonly\" type=\"text\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  4226 + password_input(options, label_text, id, name,i,w) then
  4227 + [ maybe_label(label_text, id),
  4228 + "<input type=\"password\" name=\"",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  4229 + text_area(opts,label_text, id, n,i,w,h) then
  4230 + [ maybe_label(label_text, id), "<textarea ",format(opts)," id=\"",id,"\" name=\"",n,"\" cols=\"",w,"\" rows=\"",h,"\">",i,"</textarea>"],
  4231 + file_upload(options, label, id, n, w) then
  4232 + [ maybe_label(label, id),
  4233 + "<input type=\"file\" id=\"",id,"\" size=\"",w,"\" name=\"",n,"\"",format_attrs(options)," />"],
  4234 + selector(opts, label, id, n,s,cs) then
  4235 + [ maybe_label(label, id),
  4236 + "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
  4237 + selector(opts, label, id, n,s,cs,sd) then
  4238 + [ maybe_label(label, id),
  4239 + "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
  4240 + selector_c(opts, label, id, n,s,cs) then
  4241 + [ maybe_label(label, id),
  4242 + "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
  4243 + selector_c(opts, label, id, n,s,cs,sd) then
  4244 + [ maybe_label(label, id),
  4245 + "<select id=\"",id,"\" name=\"",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
  4246 +
  4247 + radio_button(options, label_text, id, n, v, c) then
  4248 + [ maybe_label(label_text, id),
  4249 + "<input type=\"radio\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />"],
  4250 + radio_button_r(options, label_text, id, n, v, c) then
  4251 + [ "<input type=\"radio\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />",
  4252 + maybe_label(label_text, id)],
  4253 + check_box(options, label_text, id, n, v, c) then
  4254 + [ maybe_label(label_text, id),
  4255 + "<input type=\"checkbox\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />"]
  4256 + check_box_r(options, label_text, id, n, v, c) then
  4257 + [ "<input type=\"checkbox\" name=\"",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />",
  4258 + maybe_label(label_text, id)]
  4259 + div(options, e) then
  4260 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https, action_count),
  4261 + div_empty(options) then
  4262 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https, action_count),
  4263 + hidden(id, name, value) then
  4264 + ["<input type=\"hidden\" id=\"",id,"\" name=\"",name,"\" value=\"",value,"\" />"],
  4265 +
  4266 + }.
  4267 +
  4268 +
  4269 +
  4270 +
  4271 +
  4272 +
  4273 + *** [5.9] Formating 'off form' elements.
  4274 +
  4275 + The encryption type 'multipart/form-data' is required for a form containing an upload.
  4276 +
  4277 +
  4278 +define Bool
  4279 + contains_an_upload
  4280 + (
  4281 + HTML_In_Form form_content
  4282 + ).
  4283 +
  4284 +define Bool
  4285 + contains_an_upload
  4286 + (
  4287 + HTML_Row(HTML_In_Form) row
  4288 + ) =
  4289 + mapor(contains_an_upload,
  4290 + map(content,cells(row))).
  4291 +
  4292 +define Bool
  4293 + contains_an_upload
  4294 + (
  4295 + HTML_Footer_Row(HTML_In_Form) row
  4296 + ) =
  4297 + if row is
  4298 + {
  4299 + empty then false,
  4300 + footer_row(_, cells) then
  4301 + mapor(contains_an_upload,
  4302 + map(content,cells))
  4303 + }.
  4304 +
  4305 +define Bool
  4306 + contains_an_upload
  4307 + (
  4308 + HTML_In_Form form_content
  4309 + ) =
  4310 + if form_content is
  4311 + {
  4312 + literal_pt(t) then false,
  4313 + literal(t) then false,
  4314 + sequence(l) then mapor(contains_an_upload,l)
  4315 + text(o,t) then false,
  4316 + preformated(o,s) then false,
  4317 + paragraph(o,t) then false,
  4318 + image(o,u,a) then false,
  4319 + image(o,u,a,w,h) then false,
  4320 + table(o,_,rows,f_row) then if mapor(contains_an_upload,rows) then true
  4321 + else contains_an_upload(f_row),
  4322 + center(e) then contains_an_upload(e),
  4323 + mail_to(m,e) then false, // 'e' may but should not contain an upload
  4324 + scroller(w,h,cw,ch,e) then contains_an_upload(e),
  4325 + actioner(c,t,a,an,eo,ja) then false,
  4326 + foreign_link_new(_,_,_) then false,
  4327 + foreign_link(o,u) then false,
  4328 + foreign_link(o,u,n) then false,
  4329 + private_download(p,n,e,a) then false,
  4330 + text_input(o,lt,l,n,i,w) then false,
  4331 + text_input_ro(o,lt,l,n,i,w) then false,
  4332 + password_input(o,lt,l,n,i,w) then false,
  4333 + text_area(o,lt,id,n,i,w,h) then false,
  4334 + file_upload(o,l,i,n,w) then true,
  4335 + selector(o,l,i,n,s,c) then false,
  4336 + selector(o,l,i,n,s,c,p) then false,
  4337 + selector_c(o,l,i,n,s,c) then false,
  4338 + selector_c(o,l,i,n,s,c,p) then false,
  4339 + radio_button(o,_,_,n,v,c) then false,
  4340 + radio_button_r(o,_,_,n,v,c) then false,
  4341 + check_box(o,_,_,n,v,c) then false,
  4342 + check_box_r(o,_,_,n,v,c) then false,
  4343 + div(o,c) then contains_an_upload(c),
  4344 + div_empty(o) then false,
  4345 + hidden(_,_,_) then false
  4346 + }.
  4347 +
  4348 +define String
  4349 + enctype
  4350 + (
  4351 + HTML_In_Form form_content
  4352 + ) =
  4353 + if contains_an_upload(form_content)
  4354 + then " enctype=\"multipart/form-data\""
  4355 + else "".
  4356 +
  4357 +
  4358 +
  4359 +define Printable_tree
  4360 + format
  4361 + (
  4362 + CommonInfo cinfo,
  4363 + String sn, // state_name
  4364 + Var(Int) ic_v, // 'idnum' counter variable
  4365 + HTML_Off_Form element,
  4366 + Bool is_https,
  4367 + Var(Int) action_count
  4368 + ) =
  4369 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  4370 + with format_element = (HTML_Off_Form e) |-> format(cinfo, sn, ic_v, e, is_https, action_count),
  4371 + if element is
  4372 + {
  4373 + literal_pt(t) then t,
  4374 + literal(t) then [t],
  4375 + sequence(l) then flat(map(format_element,l)),
  4376 + text(opts,t) then
  4377 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https, action_count),
  4378 + preformated(o,s) then
  4379 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https, action_count),
  4380 + paragraph(opts,t) then
  4381 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https, action_count),
  4382 + image(opts,url,alt) then
  4383 + format(cinfo,sn,ic_v,any_image(opts,url,alt),format_element,is_https, action_count),
  4384 + image(opts,url,alt,w,h) then
  4385 + format(cinfo,sn,ic_v,any_image(opts,url,alt,w,h),format_element,is_https, action_count),
  4386 + table(opts,header_row, rows, footer_row) then
  4387 + format(cinfo,sn,ic_v,any_table(opts,header_row, rows, footer_row),format_element,is_https, action_count),
  4388 + center(e) then
  4389 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https, action_count),
  4390 + mail_to(a,e) then
  4391 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https, action_count),
  4392 + scroller(w,h,cw,ch,c) then
  4393 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https, action_count),
  4394 + fixed_size(w,h,c) then
  4395 + format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https, action_count),
  4396 + fixed_size_2(w,h,fn) then
  4397 + format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https, action_count),
  4398 + actioner(c,t,a,an,eo,ja) then
  4399 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https, action_count),
  4400 + actioner(c,t,a,an,eo,ja,fn) then
  4401 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https, action_count),
  4402 + foreign_link_new(target, aspect, url) then
  4403 + format(cinfo,sn,ic_v,any_foreign_link_new(target,aspect,url),format_element,is_https, action_count),
  4404 + foreign_link(options,url) then
  4405 + format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https, action_count),
  4406 + foreign_link(options,url,name) then
  4407 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https, action_count),
  4408 + private_download(url,name,extra,action) then
  4409 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https, action_count),
  4410 + label(n) then ["<a name=\"",n,"\">"],
  4411 + form(fn,attributs, c) then
  4412 + [
  4413 + "<form id=\"",fn,"\"",
  4414 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https, action_count),
  4415 + " method=\"post\"",
  4416 + enctype(c),
  4417 + " action=\"http",
  4418 + if is_https then "s" else "",
  4419 + "://",common_name,":",to_decimal(http_port),"/\">",
  4420 + // action is set dynamically by
  4421 + // the actioner using JavaScript
  4422 + if fn is htmlId(id) then
  4423 + format(cinfo,id,sn,ic_v,c,is_https, action_count),
  4424 + "</form>"
  4425 + ]
  4426 + form(fn,attributs, action_name, extra_ops, c) then
  4427 + with url = make_actioner_url(cinfo, same, same,
  4428 + sn, action_name, extra_ops, is_https),
  4429 + [
  4430 + "<form id=\"",fn,"\"",
  4431 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https, action_count),
  4432 + " method=\"post\"",
  4433 + enctype(c),
  4434 + " action=\"" + url + "\">",
  4435 +// " action=\"http",
  4436 +// if is_https then "s" else "",
  4437 +// "://",common_name,":",http_port,"/\">",
  4438 + // action is set dynamically by
  4439 + // the actioner using JavaScript
  4440 + if fn is htmlId(id) then
  4441 + format(cinfo,id,sn,ic_v,c,is_https, action_count),
  4442 + "</form>"
  4443 + ]
  4444 + div(options, e) then
  4445 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https, action_count),
  4446 + div_empty(options) then
  4447 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https, action_count),
  4448 + iframe(options, css_styles, css_files, js_files, body) then
  4449 + if body is body(body_options,element) then
  4450 + [ "<iframe", format_attrs(options), ">\n",
  4451 + "<html style=\"margin: 0pt; padding: 0pt;\">\n",
  4452 + "<head>\n",
  4453 + add_css_styles(css_styles),
  4454 + add_css_files(css_files),
  4455 + add_js_files(js_files),
  4456 + "</head>\n",
  4457 + "<body ", format(body_options), ">\n", // format body options
  4458 + format(cinfo,sn,ic_v,element,is_https, action_count),
  4459 + "</body>\n",
  4460 + "</html>\n",
  4461 + "</iframe>\n",
  4462 + ],
  4463 +
  4464 + }.
  4465 +
  4466 +
  4467 +
  4468 +
  4469 + *** [5.10] Formating meta-tags.
  4470 +
  4471 +define Printable_tree
  4472 + format_keywords
  4473 + (
  4474 + List(String) l
  4475 + ) =
  4476 + if l is
  4477 + {
  4478 + [] then [ ],
  4479 + [h . t] then if t is []
  4480 + then [h]
  4481 + else [h , ", " . format_keywords(t)]
  4482 + }.
  4483 +
  4484 +
  4485 +define Printable_tree
  4486 + format
  4487 + (
  4488 + CommonInfo cinfo,
  4489 + String state_name,
  4490 + HTML_Meta m,
  4491 + Bool is_https
  4492 + ) =
  4493 + if m is
  4494 + {
  4495 + keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],
  4496 + refresh(co,ta,an,delay) then
  4497 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
  4498 + make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\" />\n"],
  4499 + refresh(url,delay) then
  4500 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",url,"\" />\n"],
  4501 + meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],
  4502 + http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],
  4503 + generic_meta(l) then ["<meta ",
  4504 + flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
  4505 + l)),
  4506 + " />\n"],
  4507 + literal(s) then [s]
  4508 + }.
  4509 +
  4510 +define Printable_tree // c'est le nรดtre et c'est meilleur
  4511 + format
  4512 + (
  4513 + HTML_Meta m,
  4514 + ) =
  4515 + if m is
  4516 + {
  4517 + keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],
  4518 + refresh(co,ta,an,delay) then
  4519 + ["<!-- TODO refresh(co,ta,an,delay) --> "], // TODO
  4520 + refresh(url,delay) then
  4521 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",url,"\" />\n"],
  4522 + meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],
  4523 + http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],
  4524 + generic_meta(l) then ["<meta ",
  4525 + flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
  4526 + l)),
  4527 + " />\n"],
  4528 + literal(s) then [s]
  4529 + }.
  4530 +
  4531 +
  4532 +define Printable_tree
  4533 + format
  4534 + (
  4535 + CommonInfo cinfo,
  4536 + String state_name,
  4537 + List(HTML_Meta) metas,
  4538 + Bool is_https,
  4539 + String charset
  4540 + ) =
  4541 + if metas is
  4542 + {
  4543 + [] then [format(cinfo,state_name,http_equiv("content-type",
  4544 + "text/html; charset="+charset),is_https)],
  4545 + [h . t] then [format(cinfo,state_name,h,is_https)
  4546 + . format(cinfo,state_name,t,is_https,charset)]
  4547 + }.
  4548 +
  4549 +define Printable_tree
  4550 + _format_html_head
  4551 + (
  4552 + List(HTML_Head_Tag) lwf,
  4553 + Printable_tree so_far
  4554 + )
  4555 + =
  4556 + if lwf is
  4557 + {
  4558 + [] then reverse(so_far),
  4559 + [ h . t] then
  4560 + if h is
  4561 + {
  4562 + meta(meta) then
  4563 + [format(meta) . _format_html_head(t, so_far)],
  4564 + title(title) then
  4565 + ["<title>"+title+"</title>\n" . _format_html_head(t, so_far)],
  4566 + js(jsf) then
  4567 + [add_js_files([jsf]) . _format_html_head(t, so_far)],
  4568 + js_inline(jsi) then
  4569 + ["<script>"+content(jsi)+"</script>\n" . _format_html_head(t, so_far)],
  4570 + css(cssf) then
  4571 + ["<link rel=\"stylesheet\" type=\"text/css\" href=\""+file_name(cssf)+"\" />\n" . _format_html_head(t, so_far)],
  4572 + css_inline(cssi) then
  4573 + ["<style>"+cssi+"</style>\n" . _format_html_head(t, so_far)]
  4574 + }
  4575 + }
  4576 + .
  4577 +
  4578 +public define Printable_tree
  4579 + format_html_head
  4580 + (
  4581 + List(HTML_Head_Tag) lwf,
  4582 + String charset
  4583 + )
  4584 + =
  4585 + with lwf = (List(HTML_Head_Tag))[ meta(http_equiv("content-type", "text/html; charset="+charset)) . lwf],
  4586 + if lwf is
  4587 + {
  4588 + [] then
  4589 + [],
  4590 + [ h . t] then
  4591 + _format_html_head(lwf, [])
  4592 + }
  4593 + .
  4594 +
  4595 +define Printable_tree
  4596 + format
  4597 + (
  4598 + CommonInfo cinfo,
  4599 + String state_name,
  4600 + List(HTTP_header) additional_headers,
  4601 + HTTP_Answer page,
  4602 + Bool is_https,
  4603 + String charset
  4604 + ) =
  4605 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  4606 + with ic_v = var((Int)0),
  4607 + if page is
  4608 + {
  4609 + html_page(status, head_tags, body) then
  4610 + if body is body(options,element) then
  4611 + if format(status) is (status_string, status_headers) then
  4612 + with answer_body =
  4613 + [doctype_w3c_header,
  4614 + html_header,
  4615 +
  4616 + "<head>\n", format_html_head(head_tags, charset),"</head>\n",
  4617 +
  4618 + "<body ", format(options), ">", // format body options
  4619 + format(cinfo,state_name,ic_v,element,is_https, var(0)),
  4620 + "</body>\n",
  4621 +
  4622 + "</html>"],
  4623 + [ "HTTP/1.1 " + status_string, crlf,
  4624 + format_headers(standard_headers),
  4625 + format_headers(standard_headers_for("text/html", length(answer_body), success(charset))),
  4626 + format_headers(status_headers),
  4627 + format_headers(additional_headers),
  4628 + crlf
  4629 + . answer_body
  4630 + ]
  4631 +
  4632 + html_page(status, title, metas, css_styles, css_files, js_files, script, body) then
  4633 + if body is body(options,element) then
  4634 + if format(status) is (status_string, status_headers) then
  4635 + with answer_body =
  4636 + [ doctype_w3c_header,
  4637 + html_header,
  4638 + "<head>\n",
  4639 + add_css_styles(css_styles),
  4640 + add_css_files(css_files),
  4641 + add_js_files(js_files),
  4642 + add_script(script),
  4643 + "<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n",
  4644 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  4645 + " function show_local_popup(divname,stvname) {",
  4646 + " if (document.layers) { var d = eval(document.divname); } else\n",
  4647 + " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
  4648 + " if (document.all) { var d = eval(document.all.divname.style)};\n",
  4649 + // " alert(typeof(eval(stvname))); ",
  4650 + " var s = eval(stvname); ",
  4651 + " if (s[0]==0) ",
  4652 + " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
  4653 + " { s[0]=0; d.style.visibility = 'hidden'; }; }",
  4654 + "</script>\n",
  4655 + "<title>",title,"</title>\n", // put title
  4656 + format(cinfo,state_name,metas,is_https,charset), // format the metas
  4657 + "</head>\n",
  4658 + "<body ", format(options), ">", // format body options
  4659 + //"<center>",
  4660 + format(cinfo,state_name,ic_v,element,is_https, var(0)),
  4661 + //"</center>",
  4662 + "</body>\n",
  4663 + "</html>"
  4664 + ],
  4665 + [ "HTTP/1.1 " + status_string, crlf,
  4666 + format_headers(standard_headers),
  4667 + format_headers(standard_headers_for("text/html", length(answer_body), success(charset))),
  4668 + format_headers(status_headers),
  4669 + format_headers(additional_headers),
  4670 + crlf
  4671 + . answer_body
  4672 + ],
  4673 +
  4674 + plain_text (HTTP_Status status, String text) then
  4675 + if format(status) is (status_string, status_headers) then
  4676 + [ "HTTP/1.1 " + status_string, crlf,
  4677 + format_headers(standard_headers),
  4678 + format_headers(standard_headers_for("text/plain", length(text), success(charset))),
  4679 + format_headers(status_headers),
  4680 + format_headers(additional_headers),
  4681 + crlf,
  4682 + text . (Printable_tree)[]
  4683 + ],
  4684 +
  4685 + custom_text(HTTP_Status status, String mime_type, String content) then
  4686 + if format(status) is (status_string, status_headers) then
  4687 + [ "HTTP/1.1 " + status_string, crlf,
  4688 + format_headers(standard_headers),
  4689 + format_headers(standard_headers_for(mime_type, length(content), success(charset))),
  4690 + format_headers(status_headers),
  4691 + format_headers(additional_headers),
  4692 + crlf,
  4693 + content . (Printable_tree)[]
  4694 + ],
  4695 + custom_binary(HTTP_Status status, String mime_type, ByteArray content, List(HTTP_header) other_headers) then
  4696 + if format(status) is (status_string, status_headers) then
  4697 + [ "HTTP/1.1 " + status_string, crlf,
  4698 + format_headers(status_headers),
  4699 + format_headers(standard_headers_for(mime_type, length(content), failure)),
  4700 + format_headers(status_headers),
  4701 + format_headers(other_headers),
  4702 + format_headers(additional_headers),
  4703 + crlf,
  4704 + content . (Printable_tree)[]
  4705 + ],
  4706 +
  4707 + custom_tree(HTTP_Status status, String mime_type, Printable_tree content) then
  4708 + if format(status) is (status_string, status_headers) then
  4709 + [ "HTTP/1.1 " + status_string, crlf,
  4710 + format_headers(standard_headers),
  4711 + format_headers(standard_headers_for(mime_type, length(content), success(charset))),
  4712 + format_headers(status_headers),
  4713 + format_headers(additional_headers),
  4714 + crlf .
  4715 + content
  4716 + ],
  4717 +
  4718 + http_raw(Printable_tree content) then content,
  4719 +
  4720 + html_content(HTTP_Status status, HTML_Off_Form content_HTML) then
  4721 + if format(status) is (status_string, status_headers) then
  4722 + with content = format(cinfo, state_name, ic_v, content_HTML, is_https, var(0)),
  4723 + [ "HTTP/1.1 " + status_string, crlf,
  4724 + format_headers(standard_headers),
  4725 + format_headers(standard_headers_for("text/html", length(content), success(charset))),
  4726 + format_headers(status_headers),
  4727 + format_headers(additional_headers),
  4728 + crlf .
  4729 + content
  4730 + ],
  4731 + }.
  4732 +
... ...