Commit 9026a8dbc8b960090bac999a3663c706e19c56f2

Authored by David RENE
1 parent 38d4c6fc

add the management of js_file in html header

Showing 1 changed file with 3740 additions and 3722 deletions   Show diff stats
calexium_lib/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   -
9   -
10   - *Author* Alain Prouté
11   -
12   - *Revised* January 2005.
13   -
14   -
15   - *Overview*
16   -
17   - In this file we propose simple tools for making well structured interactive and secured
18   - web sites.
19   -
20   -
21   - ----------------------------------- Table of Contents ---------------------------------
22   -
23   - * (1) Structure of a web site.
24   - ** (1.1) Three sorts of data.
25   - ** (1.2) How requests are handled.
26   - ** (1.3) What web pages are made of.
27   - ** (1.4) Actions.
28   - ** (1.5) States.
29   -
30   - * (2) Carrying on.
31   - ** (2.1) Describing your web sites.
32   - ** (2.2) Directories on the server's disk.
33   - ** (2.3) Starting your web sites.
34   -
35   - * (3) The HTML interface.
36   - ** (3.1) Types used by the HTML interface.
37   - ** (3.2) ``in form'' versus ``off form''.
38   - ** (3.3) Defining your own style.
39   - ** (3.4) Actioners and forms.
40   - ** (3.5) Local popup.
41   -
42   - ---------------------------------------------------------------------------------------
43   -
44   -
45   -read tools/basis.anubis
46   -read CXM_common.anubis
47   -read CXM_multihost_http_server.anubis
48   -read CXM_mime.anubis
49   -
50   -
51   -
52   - * (1) Structure of a web site.
53   -
54   - First of all we need to explain what a web site should be made of. Ideally, the
55   - visitor (also called the 'client') should see the web site working as any other
56   - interactive computer software. So, it should be clear that a 'session' (i.e. a visit
57   - to the web site, including the consultation of several pages) is some kind of
58   - conversation between the visitor and the web site, and that the web site should
59   - maintain a 'current state' of this conversation. At each new request (click) from the
60   - visitor, this state must be updated. This whole conversation is called a 'session' and
61   - should not be confused with a single request.
62   -
63   -
64   -
65   - ** (1.1) Three sorts of data.
66   -
67   - All the data needed for putting a web site at work may be dispatched into three
68   - categories:
69   -
70   - 1. Constant data (data that never change). These data may be hard coded into the
71   - Anubis source files of the web site.
72   -
73   - 2. Permanent data (data which always exist independantly of the users connected to
74   - the web site). These data are normally recorded into data bases.
75   -
76   - 3. Session data (data which depend on a particular visitor and which exist only
77   - during the time he visits the web site). These data are stored into so-called
78   - 'states'.
79   -
80   -
81   - It is important to determine which data belongs to which category. This is part of your
82   - design decisions.
83   -
84   -
85   -
86   - ** (1.2) How requests are handled.
87   -
88   - We want to separate the following two functionalities (which are used at each request
89   - (click) during a single session):
90   -
91   - - computing the new state from the previous state and from the client request, and
92   - updating the data base,
93   -
94   - - computing the page to be sent to the client from the new current state and from
95   - the informations in the data base.
96   -
97   -
98   - The next picture shows the structure we have in mind:
99   -
100   -
101   - request +---------+ HTML page (with a hidden state name)
102   - .-------------------| client |<--------------.
103   - | .-----------------| | |
104   - | | previous state +---------+ |
105   - | | name (if any) |
106   - | | | client side
107   - ............................................................................
108   - | | | server side
109   - | | |
110   - | | .-------------------. |
111   - | | | previous state | |
112   - V V V | |
113   - +---------------+ +---------------+ +--------------+
114   - | compute state | | server's disk | | compute page |
115   - +---------------+ +---------------+ +--------------+
116   - ^ | | ^ ^ ^ ^ ^
117   - | | | | | | | |
118   - | | `--------------------+--------------------' | |
119   - | | new state | | |
120   - read | `------------------------+--------------------' |
121   - write | new state name |
122   - update V |
123   - +-----------+ |
124   - | data base |--------------------------------------------'
125   - +-----------+ read only
126   -
127   -
128   - When the client begins a session, there is no previous state. In this case, a default
129   - 'initial state' is used instead.
130   -
131   - The data base may be updated by 'compute state' box, but should not be update by the
132   - 'compute page' box. The 'compute page' box should be allowed only to read the data
133   - base.
134   -
135   - In this file, all the above stuff is defined, except the 'compute state' and 'compute
136   - page' boxes. You just have to provide the function for computing a new state (compute
137   - state) and the function for computing the page (compute page) from the new state. You
138   - don't have to worry about state names, saving and retrieving states and the like.
139   -
140   -
141   -
142   -
143   -
144   - ** (1.3) What web pages are made of.
145   -
146   - What the client can see in his browser's window may be called a 'page'. Within a page,
147   - we have several sorts of components:
148   -
149   - - 'local' components, i.e. all components which do not open a connection, like
150   - texts, images, etc... possibly using JavaScript programmation,
151   -
152   - - 'actioners', which, when clicked upon, open a connection with our web site; they
153   - may appear as links or buttons, etc...
154   -
155   - - 'foreign links', which when clicked upon, open a connection with another web site
156   - (or ours eventually).
157   -
158   - Of course, what an actioner does is just ask our web site to perform an action. To that
159   - end, the actioner essentially sends the name of the action to be performed. However, it
160   - may be necessary to provide additional informations which may be seen as 'operands' of
161   - the action. In order to attach operands to an action, HTML provides the notion of
162   - 'form'. Indeed, a form contains essentially a set of input fields into which the client
163   - may put values for the required operands of the action, and a submit button, which is
164   - the actioner itself. Notice that a single form may contain several submit buttons,
165   - which simply means that there are several distincts actions taking the same set of
166   - operands.
167   -
168   - Restrictions must be put on the use of all theses gadgets. Indeed, for example,
169   - putting a form within another form is officially meaningless in HTML, and the client's
170   - browser may be seriously disturbed by this. In this file, we propose an interface to
171   - the HTML language, which forbids such meaningless things, simply by imposing a strict
172   - typing of HTML concepts.
173   -
174   - Each web site may be accessible through two communication channels:
175   -
176   - - a non secured channel (HTTP),
177   - - a secured channel (HTTPS).
178   -
179   - Nevertheless, the whole thing should be considered as a single web site. For example,
180   - you may have a secured page, obtained through HTTPS, containing public images obtained
181   - through HTTP. An actioner in a non secured page may open a secured connection, and
182   - conversely.
183   -
184   - Summarizing, a web page is made of local elements, foreign links and actioners.
185   - Actioners receive operands from forms, and they also choose to communicate through the
186   - non secured or through the secured channel.
187   -
188   -
189   -
190   - +-------------------+
191   - | page |
192   - | | +---------------+
193   - | +--------------+ | | next page |
194   - | | form | | | (non secured) |
195   - | | +----------+ | | HTTP | |
196   - | | | actioner |---------------------------->| |
197   - | | +----------+ | | +---------------+
198   - | | | |
199   - | | +----------+ | | +---------------+
200   - | | | actioner |---------------------------->| next page |
201   - | | +----------+ | | HTTPS | (secured) |
202   - | | | | | |
203   - | +--------------+ | | |
204   - | | +---------------+
205   - | |
206   - +-------------------+
207   -
208   -
209   - Notice that actioners need no be necessarily put into forms. In that case, they work as
210   - ordinary links, but they still may receive operands as we shall see.
211   -
212   -
213   -
214   -
215   - ** (1.4) Actions.
216   -
217   - The client opens a new connection with our web site whenever he clicks on an
218   - actioner. The result is that a request is sent, essentially made of a list of 'web
219   - arguments'. Each web argument is a pair (name,value). One of these web arguments, the
220   - 'action' web argument (whose name is "a"), determines the action to be performed. The
221   - other web arguments (not including "s", used to identify the state) are the operands
222   - for this action.
223   -
224   - Hence, the 'compute state' box in the picture above, splits naturally into as many
225   - sub-boxes as there are actions. For this reason, we define the following type for
226   - representing actions (where '$State' is the type representing session informations):
227   -
228   -public type Web_Action($SessionTicket, $State):
229   - http_action (String name, // name of action
230   - (Maybe($State)) -> Bool allow, // true if action allowed
231   - (HTTP_Info http_info,
232   - List(Web_arg) web_args, // actually only 'operands' web arguments
233   - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
234   - https_action (String name, // name of action
235   - (Maybe($State)) -> Bool allow, // true if action allowed
236   - (HTTP_Info http_info,
237   - List(Web_arg) web_args, // actually only 'operands' web arguments
238   - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
239   - http_https_action (String name,
240   - (Maybe($State)) -> Bool allow, // true if action allowed
241   - (HTTP_Info http_info,
242   - List(Web_arg) web_args,
243   - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).
244   -
245   - 'http_action's are executed only under HTTP, and 'https_action's are executed only
246   - under HTTPS. 'http_https_action's may be executed under both types of connections.
247   -
248   - Each action has a name, which is used to identify the action. Each action also has a
249   - function 'allow' whose job is to verify that the action is allowed in the current
250   - state, and a function 'do_it' for performing the action. The function 'do_it' receives
251   - a lot of informations:
252   -
253   - - 'HTTP informations':
254   - - the IP address of the client,
255   - - the URI requested by the client (after redirection),
256   - - the list of HTTP headers generated by the client's browser,
257   - - the list of web arguments sent by the client (except "s" and "a"),
258   - - the previous state (or the 'initial' or 'ticket expired' state if no previous
259   - state can be found).
260   -
261   - In most cases, HTTP informations are not used. This is the reason why they are gathered
262   - for simplicity into a unique datum of type 'HTTP_Info'.
263   -
264   -// For your convenience, we introduce the following simpler variants:
265   -//
266   -//public define Web_Action($State)
267   -// http_action
268   -// (
269   -// String name,
270   -// $State -> Bool allow,
271   -// (List(Web_arg),$State) -> $State do_it
272   -// ) =
273   -// http_action(name,
274   -// (Maybe($State) ms) |-> if ms is
275   -// {
276   -// failure then true,
277   -// success(s) then allow(s)
278   -// },
279   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
280   -// {
281   -// failure then (failure, []),
282   -// success(s2) then (success(do_it(l,s2)), [])
283   -// }).
284   -//
285   -//public define Web_Action($State)
286   -// https_action
287   -// (
288   -// String name,
289   -// $State -> Bool allow,
290   -// (List(Web_arg),$State) -> $State do_it
291   -// ) =
292   -// https_action(name,
293   -// (Maybe($State) ms) |-> if ms is
294   -// {
295   -// failure then true,
296   -// success(s) then allow(s)
297   -// },
298   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
299   -// {
300   -// failure then (failure, []),
301   -// success(s2) then (success(do_it(l,s2)), [])
302   -// }).
303   -//
304   -//public define Web_Action($State)
305   -// http_https_action
306   -// (
307   -// String name,
308   -// $State -> Bool allow,
309   -// (List(Web_arg),$State) -> $State do_it
310   -// ) =
311   -// http_https_action(name,
312   -// (Maybe($State) ms) |-> if ms is
313   -// {
314   -// failure then true,
315   -// success(s) then allow(s)
316   -// },
317   -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
318   -// {
319   -// failure then (failure, []),
320   -// success(s2) then (success(do_it(l,s2)), [])
321   -// }).
322   -
323   -
324   -
325   - When you define your web site, you must provide the list of all the actions of the
326   - site. When a new state has been computed, a graphical representation of this state
327   - must be sent to the client. To that end, you must provide a function (named below
328   - 'compute_page') of type:
329   -
330   - $State -> HTML_Page
331   -
332   - where the type 'HTML_Page' (defined below in this file) abstractly represents HTML
333   - pages.
334   -
335   -public type HTML_Page:...
336   -
337   - It should be clear that states and pages are deeply linked together. Indeed, we really
338   - understand the page shown to the client as a representation of the current state of the
339   - conversation between the client and the web site, but also containing informations
340   - taken from the data bases.
341   -
342   -
343   -
344   -
345   -
346   - ** (1.5) States.
347   -
348   - Now, we explain how you can define the type (say 'State') to be used as an instance of
349   - the type parameter '$State'. The following is just a suggestion.
350   -
351   - Each state determines a page (since 'compute_page' computes a page from a
352   - state). However, some components of the state may be independant of the page. It may be
353   - the case for example for the indication of the natural language used by the
354   - client. Hence, a state should be made of (at least) two parts:
355   -
356   - - informations which are the same for all pages,
357   - - informations which are particular to each page.
358   -
359   - For example, you could define:
360   -
361   - type Page: // one alternative per page, with particular informations
362   - login(...), // in the components
363   - main_page(...),
364   - ...etc...
365   -
366   - Now, the type 'State' could be defined as follows:
367   -
368   - type State:
369   - state(Language, // informations valid for all pages
370   - ...,
371   - Page). // informations particular to a page
372   -
373   - However, if you are making a secured web site within which clients should be identified
374   - (by id and password), it may be a good idea to have two sorts of states, one for non
375   - identified clients and one for identified clients. In this case, define the type
376   - 'State' as follows (this is just a suggestion):
377   -
378   - type State:
379   - non_identified(Language),
380   - identified(String id,
381   - Language,
382   - Page).
383   -
384   - When a request arrives, check if the previous state is 'identified(...)' or
385   - 'non_identified(...)', and don't provide access to certain pages to non identified
386   - clients. This is required for security.
387   -
388   - Some more words on security. If your site needs to identify clients, define the
389   - initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
390   - password of the client. If the id and password are correct, then change the state of
391   - the client to 'identified(...)'. No other action should be able to do that. Now, be
392   - confident that clients cannot forge states. The only information they have is the name
393   - of a state, not the state itself which is never sent over the network, but only stored
394   - on the server's disk. The name of the state is constructed using strong cryptographical
395   - methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
396   - state names cannot be seen by a third party. So, if the system retrieves a previous
397   - state of the form 'identified(...)', you can be confident that your client is well
398   - identified, and you can send him confidential informations.
399   -
400   - States have a limited life time. It may happen that a client clicks on a button at a
401   - time its state is out of date. In this case, this system considers that the new state
402   - is a special state named 'ticket expired'. You must provide a function producing this
403   - state when you describe your web site. The page corresponding to this state must just
404   - inform the client that he/she waited a too long time before clicking on a button, and
405   - has to restart (a new conversation) from the begining.
406   -
407   -
408   -
409   - * (2) Carrying on.
410   -
411   - ** (2.1) Describing your web sites.
412   -
413   - Before you may start your web site, you must describe it, i.e. produce a datum of the
414   - opaque type 'Web_Site'.
415   -
416   -public type Web_Site:...
417   -
418   - Producing such a datum may be performed by:
419   -
420   -public define Web_Site
421   - make_web_site_description
422   - (
423   - List(String) common_names, // for example: ["www.our-business.com",
424   - // "192.168.0.1"]
425   - // the second one is just for testing
426   - String site_directory, // where 'public' and other directories are
427   - // located (should NOT end with '/')
428   - One -> One init,
429   - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
430   - ($State expired,
431   - HTTP_Info,
432   - List(Web_arg),
433   - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
434   - (HTTP_Info,
435   - List(Web_arg),
436   - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
437   - List(Web_Action($SessionTicket, $State)) actions,
438   - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
439   - Int32 timeout, // seconds (todo: minutes)
440   - List(Redirection) redirections,
441   - String charset,
442   - List(String) journal_extensions,
443   - List(String) journal_headers,
444   - String authorization_secret,
445   - List(MIME) known_mime_types,
446   - (String action_name,
447   - List(Web_arg) args)-> One before_send_file
448   - ).
449   -
450   -
451   - Explanations:
452   -
453   - 'common_names' is the list of names of the site (the name the browser must send as the
454   - value of the 'Host' HTTP header in order to access the site must be in that list). Such
455   - a name generally looks like this:
456   -
457   - www.somewhere.com
458   -
459   - If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
460   - the site must be exactly the same as the name on the certificate (which is precisely
461   - called the 'common name' in the X.509 jargon). If the two names do not match, the site
462   - will still work, but the transaction will not be transparent to the client. His browser
463   - will complain that the name of the certificate does not match the name of the site, and
464   - he will have to accept the certificate manually.
465   -
466   - 'site_directory' is the absolute path to the directory where the files needed by the
467   - site are located. Usually this directory looks like:
468   -
469   - my_anubis/web_sites/www.somewhere.com
470   -
471   - However, this information is not computed from 'common_name', so that you can change
472   - the common name (for example temporarily, for networking reasons) without loosing
473   - access to the files.
474   -
475   - 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
476   - whose graphical representation is a page explaining to the user that its 'ticket' (or
477   - 'session information') has expired, and that he/she must close all popup windows and
478   - start a new session. The arguments of the function contain the previous (expired)
479   - state and all current informations concerning the user. This arguments may be useful
480   - for example for producing the expiration message in the language chosen by the user.
481   - You can also (and this may be much smarter) send a 'ticket prolongation page'
482   - (including a new login for example), and resume the same conversation, since you have
483   - all the pertinent informations at hand. In the case the ticket is definitely lost, the
484   - second fonction 'ticket_lost_state' is used.
485   -
486   - Notice that despite the fact that the parameter $State is involved in the arguments of
487   - the above function, the type 'Web_Site' does not depend on this parameter. This allows
488   - to produce lists of web site descriptions, where each description may be constructed
489   - with a different instance of $State. This is required because distinct sites must have
490   - distinct types of session informations. This is made possible by the fact that the
491   - type is obscure, and the constructor replaced by a function which assembles
492   - 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
493   - entity not depending on $State. You should have a look to the private part of this file
494   - if you want more precisions about this programming technique.
495   -
496   - 'charset' is a string which will determine the character encoding to be used by the
497   - browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
498   - etc...
499   -
500   - 'before_send_file' is a function which is executed just before the HTTP server sends a
501   - file. It gets an action name and the web arguments received with the request for that
502   - file. Notice that this action name and these web arguments may be put into a
503   - 'private_download' element, and will come back to the server at the time of the
504   - download.
505   -
506   -
507   -
508   - ** (2.2) Directories on the server's disk.
509   -
510   - The description of you site contains the name of the directory within which the
511   - required files are located. This may be for example:
512   -
513   - my_anubis/web_sites/www.our-business.com/
514   -
515   - This is called the 'site directory' (for the given site). Within the site directory,
516   - the following directories are created by this program:
517   -
518   - states
519   - public
520   - journal
521   - private_download
522   - upload_temporary
523   -
524   - The directory 'states' is used for storing states (session informations). Out of date
525   - states are automatically removed after some time.
526   -
527   - The tree rooted at 'public' contains files that the server is allowed to send to the
528   - clients. For security reasons, the server never sends a file which is not within the
529   - tree whose root is this 'public' directory (except for the 'private download' mecanism;
530   - see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
531   - must have been recognized before the file may be sent.
532   -
533   - The directory 'journal' contains the jounal files. The roles of the remaining
534   - directories 'private_download' and 'upload_temporary' is explained in
535   - 'multihost_http_server.anubis', where you will also find further informations on
536   - 'public' and 'journal'.
537   -
538   -
539   -
540   -
541   - ** (2.3) Web servers parameters.
542   -
543   - The web servers have several parameters useful for administration. They are described
544   - as follows:
545   -
546   - public type WebServersParameters:
547   - wsparms(Var(Bool) shutdown_required,
548   -
549   -
550   -
551   -
552   - ** (2.4) Starting your web sites.
553   -
554   - When you have described all your web sites (you may want to have several web sites, and
555   - they are distinguished by their 'common name'), you may start them all together using
556   - 'start_web_sites' below. This function returns a result of the following type:
557   -
558   -public type Start_Web_Sites_Result:
559   - cannot_bind_to_port(Int32),
560   - cannot_bind_to_port(Int32,Int32),
561   - ok(Server http_server,
562   - Server https_server).
563   -
564   - Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
565   - ports (or to both). The main reason is that another server is already listening on that
566   - port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
567   - the 's' bit has not been set for 'user' and 'group' (there is not such problem under
568   - Windows). Also notice that the Linux kernel may need a rather long time (up to several
569   - minutes) before liberating a listening port. Now, if the system can bind to the two
570   - ports, the pair of the two servers is returned. Two tools are useful for manipulating
571   - servers:
572   -
573   - shutdown of type Server -> One
574   - is_down of type Server -> Bool
575   -
576   - They are defined in 'predefined.anubis' (together with the type 'Server').
577   -
578   -
579   -public define Start_Web_Sites_Result
580   - start_web_sites
581   - (
582   - Int32 ip_address, // the IP address shared by the web sites
583   - Int32 http_port, // usually: 80
584   - Int32 https_port, // usually: 443
585   - String ssl_certificate_common_name,
586   - List(Web_Site) web_sites, // web sites to be started
587   - Var(Bool) shutdown_required
588   - ).
589   -
590   - 'ip_address' is the IP address on which the two servers listen. If you put 0, the
591   - servers listen on all the IP addresses of the machine. This may be useful if the
592   - machine has several network interfaces.
593   -
594   - 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
595   - loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
596   - certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
597   - and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
598   -
599   - Notice that the number of servers is always 2, regardless of the number of web sites
600   - you are starting.
601   -
602   - The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
603   - servers from within the web site (typically the administration part). The servers will
604   - shutdown as soon as this variable contains 'true'. So you must provide a variable
605   - containing 'false' otherwise your servers will not run. You may also use the primitive
606   - 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
607   -
608   -
609   -
610   -
611   -
612   -
613   - * (3) The HTML interface.
614   -
615   - We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
616   - of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
617   - reactive and attractive on the client side.
618   -
619   -
620   - ** (3.1) Types used by the HTML interface.
621   -
622   - For easy reference, we gather below the definitions of all the types used by the HTML
623   - interface, and we comment them immediately.
624   -
625   -
626   -public type HTML_Size:
627   - absolute(Int32), // in pixels
628   - percentage(Int32).
629   -
630   -
631   -public type Text_Option:
632   - size(Int32), // size of character font to use
633   - font(String), // name of character font to use (such as "helvetica",...)
634   - color(RGB), // color to be used for characters
635   - italic,
636   - oblique,
637   - small_capitals,
638   - bold,
639   - underlined,
640   - left_justified,
641   - right_justified,
642   - justified, // justified on both sides
643   - line_through,
644   - nowrap,
645   - class(String). //CSS class
646   -
647   - A list of 'Text_Option' must be given with each text you want to put in your page.
648   -
649   - This indicate the way of reading text.
650   -public type Reading_Way:
651   - ltr, //the text is readable from "Left To Right" like english
652   - rtl. //the text is readable from "Right To Left" like arabic
653   -
654   -
655   -
656   -public type CoreAttrs:
657   - id (String),
658   - class (String),
659   - style (String),
660   - title (String).
661   -
662   -public type I18n:
663   - lang (String),
664   - dir (Reading_Way).
665   -
666   -public type DIV_Option:
667   - id (String),
668   - class (String),
669   - style (String),
670   - title (String),
671   - lang (String),
672   - dir (Reading_Way).
673   -
674   -
675   - A list of 'DIV_Option' must be given with each DIV you want to put in your page.
676   -
677   -
678   -public type Table_Option:
679   - background_color(RGB), // applied to all cells in the table
680   - background_image(String url),
681   - border(Int32 width_of_outer_edge, // if not present, all values are 0
682   - Int32 width_of_top_of_relief,
683   - Int32 width_of_inner_edge,
684   - RGB border_color),
685   - width(Int32), // sets a minimal width for the table
686   - percentage_width(Int32).
687   -
688   -
689   -public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
690   -
691   -
692   - A list of 'Table_Option' must be given with each table.
693   -
694   -
695   -public type BackgroundOption:
696   - repeat, // repeat the background in both directions
697   - repeat_horizontal, // repeat the background only horizontally
698   - repeat_vertical, // repeat the background only verticall
699   - no_repeat, // don't repeat the background
700   - center.
701   -
702   -
703   -public type Cell_Option:
704   - left, // put the content of the cell on the left
705   - h_center, // center the content of the cell horizontally
706   - right, // put the content of the cell on the right
707   - top, // put the content of the cell upwards
708   - v_center, // center the content of tye cell vertically,
709   - bottom, // put the content of the cell downwards
710   - base_line, // align the content vertically according to base lines
711   - background_color(RGB),
712   - background_image(String url, BackgroundOption),
713   - width(Int32), // sets a minimal width for the cell
714   - percentage_width(Int32),
715   - height(Int32), // sets a minimal height for the cell
716   - columns(Int32), // lets the cell span over several columns
717   - rows(Int32), // lets the cell span over several rows
718   - nowrap. // do not allow text wrapping within the cell
719   -
720   - A list of 'Cell_Option' must be given with each cell and each row in a table. Options
721   - given with a row apply to all the cells in the row, but are superseded by options given
722   - with cells, which apply only to the cell they are given with.
723   -
724   -
725   -public type HTML_Cell($T):
726   - cell(List(Cell_Option) options, $T content).
727   -
728   - The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
729   - depending on where you put your table (within a form or not within a form). For your
730   - convenience, we define the following particular case:
731   -
732   -public define HTML_Cell($T)
733   - cell
734   - (
735   - $T content
736   - ) =
737   - cell([],content).
738   -
739   -
740   -
741   -public type HTML_Row($T):
742   - row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
743   -
744   - Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
745   -
746   -public define HTML_Row($T)
747   - row
748   - (
749   - List(HTML_Cell($T)) cells
750   - ) =
751   - row([],cells).
752   -
753   -public define HTML_Row($T)
754   - row
755   - (
756   - HTML_Cell($T) cell
757   - ) =
758   - row([],[cell]).
759   -
760   -public type Actioner_Connection:
761   - same, // use same type of connection as current page
762   - http, // use non secured connection
763   - https. // use secured connection
764   -
765   -public type Other_Window_Option:
766   - resizable, // the new window may be resized by the client
767   - scrollbars, // the new window has scrollbars
768   - width(Int32), // the new window has the specified width
769   - height(Int32). // the new window has the specified height
770   -
771   -public type Actioner_Target:
772   - same,
773   - same (String label),
774   - other(String window_name, List(Other_Window_Option)).
775   -
776   -public type Actioner_Aspect:
777   - link (List(Text_Option),String text), // hypertext link
778   - push_button (List(CoreAttrs),String text),
779   - button (String url_off, String url_on), // rollover button
780   - button (String url_off, String url_on, Int32 w, Int32 h), // idem with size
781   - immediate_selector (String name, Int32 size, List(String) choices).
782   -
783   -
784   -public type Actioner_Local_Action:
785   - close_window.
786   -
787   -
788   -public define Actioner_Aspect
789   - link
790   - (
791   - String text
792   - ) =
793   - link([],text).
794   -
795   -
796   -public define Actioner_Aspect
797   - link
798   - (
799   - List(Text_Option) options,
800   - Int32 i
801   - ) =
802   - link(options,integer_to_string(i)).
803   -
804   -public define Actioner_Aspect
805   - link
806   - (
807   - Int32 i
808   - ) =
809   - link([],i).
810   -
811   -public define Actioner_Aspect
812   - button
813   - (
814   - String url_img
815   - ) =
816   - button(url_img,url_img).
817   -
818   -
819   -
820   - Actioners are explained in details below.
821   -
822   -
823   -public type TextAreaOption:
824   - disabled,
825   - read_only,
826   - wrap_lines.
827   -
828   -public type HTML_In_Form:
829   - literal_pt (Printable_tree),
830   - literal (String),
831   - sequence (List(HTML_In_Form) items),
832   - text (List(Text_Option), String the_text),
833   - preformated (List(Text_Option), String),
834   - paragraph (List(Text_Option), String the_text),
835   - image (String url),
836   - image (String url, Int32 width, Int32 height),
837   - table (List(Table_Option), List(HTML_Row(HTML_In_Form))),
838   - center (HTML_In_Form),
839   - mail_to (String email, HTML_In_Form element),
840   - scroller (Int32 width, Int32 height,
841   - Int32 content_width, Int32 content_height,
842   - HTML_In_Form content),
843   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
844   - String action_name, List((String,String)) extra_ops,
845   - List(Actioner_Local_Action)),
846   - foreign_link (List(Text_Option), String url, String name),
847   - private_download (String abs_path, String name, String extra_ext,
848   - Maybe((String,List((String,String)))) action),
849   - text_input (String label_text, String label, String name, String init, Int32 width),
850   - password_input (String label_text, String label, String name, Int32 width),
851   - text_area (List(TextAreaOption), String name, String init, Int32 width, Int32 height),
852   - file_upload (String name, Int32 width),
853   - selector (String name, Int32 size, List(String) choices),
854   - selector (String name, Int32 size, List(String) choices, String selected),
855   - // List((String,String)) = List((code,name)) where :
856   - // name appears in selector
857   - // code is the web-arg value
858   - selector_c (String name, Int32 size, List((String,String)) choices),
859   - selector_c (String name, Int32 size, List((String,String)) choices, String selected),
860   - radio_button (String label_text, String label, String name, String value, Bool checked),
861   - check_box (String label_text, String label, String name, Bool checked),
862   - div (List(DIV_Option), HTML_In_Form content),
863   - div_empty (List(DIV_Option)),
864   - hidden (String name, String value).
865   -
866   -
867   - 'HTML_In_Form' defines all the elements you may put within a form. We define a
868   - convenience function:
869   -
870   -public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
871   -
872   -public define HTML_In_Form
873   - foreign_link
874   - (
875   - Int32 tsize,
876   - String url,
877   - String name
878   - ) =
879   - foreign_link([size(tsize)],url,name).
880   -
881   -public define HTML_In_Form
882   - actioner
883   - (
884   - Actioner_Connection conn,
885   - Actioner_Target targ,
886   - Actioner_Aspect asp,
887   - String action_name,
888   - List((String,String)) extra_ops
889   - ) =
890   - actioner(conn,targ,asp,action_name,extra_ops,[]).
891   -
892   -
893   -
894   -public define HTML_In_Form
895   - text_area
896   - (
897   - String name,
898   - String init,
899   - Int32 width,
900   - Int32 height
901   - ) =
902   - text_area([],name,init,width,height).
903   -
904   -public define HTML_In_Form
905   - table
906   - (
907   - List(HTML_Row(HTML_In_Form)) rows
908   - ) =
909   - table([],rows).
910   -
911   -
912   -public define HTML_In_Form
913   - private_download
914   - (
915   - String abs_path,
916   - String name,
917   - String extra_ext
918   - ) =
919   - private_download(abs_path,name,extra_ext,failure).
920   -
921   -public define HTML_In_Form
922   - private_download
923   - (
924   - String abs_path,
925   - String name,
926   - String extra_ext,
927   - String action_name,
928   - List((String,String)) args
929   - ) =
930   - private_download(abs_path,name,extra_ext,success((action_name,args))).
931   -
932   -public define HTML_In_Form
933   - text
934   - (
935   - String s
936   - ) =
937   - text([],s).
938   -
939   -
940   -
941   -
942   -public type HTML_Off_Form:
943   - literal_pt (Printable_tree),
944   - literal (String),
945   - sequence (List(HTML_Off_Form) items),
946   - text (List(Text_Option), String the_text),
947   - preformated (List(Text_Option), String),
948   - paragraph (List(Text_Option), String the_text),
949   - image (String url),
950   - image (String url, Int32 width, Int32 height),
951   - table (List(Table_Option), List(HTML_Row(HTML_Off_Form))),
952   - center (HTML_Off_Form),
953   - mail_to (String email, HTML_Off_Form element),
954   - scroller (Int32 width, Int32 height,
955   - Int32 content_width, Int32 content_height,
956   - HTML_Off_Form content),
957   - fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
958   - fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
959   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
960   - String action_name, List((String,String)) extra_ops,
961   - List(Actioner_Local_Action)),
962   - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
963   - String action_name, List((String,String)) extra_ops,
964   - List(Actioner_Local_Action), String form_name),
965   - foreign_link (List(Text_Option), String url, String name),
966   - private_download (String abs_path, String name, String extra_ext,
967   - Maybe((String,List((String,String)))) action),
968   - label (String name),
969   - form (String form_name, List(CoreAttrs), HTML_In_Form content),
970   - div (List(DIV_Option), HTML_Off_Form content),
971   - div_empty (List(DIV_Option)).
972   -
973   - 'HTML_Off_Form' defines all the elements you may put outside any form.
974   -
975   -
976   -public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
977   -public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
978   - = fixed_size_2(width,height,name_of_HTML_file).
979   -
980   -
981   -public define HTML_Off_Form
982   - foreign_link
983   - (
984   - Int32 tsize,
985   - String url,
986   - String name
987   - ) =
988   - foreign_link([size(tsize)],url,name).
989   -
990   -
991   -public define HTML_Off_Form
992   - actioner
993   - (
994   - Actioner_Connection conn,
995   - Actioner_Target targ,
996   - Actioner_Aspect asp,
997   - String action_name,
998   - List((String,String)) extra_ops
999   - ) =
1000   - actioner(conn,targ,asp,action_name,extra_ops,[]).
1001   -
1002   -public define HTML_Off_Form
1003   - table
1004   - (
1005   - List(HTML_Row(HTML_Off_Form)) rows
1006   - ) =
1007   - table([],rows).
1008   -
1009   -
1010   -
1011   - We add two convenience functions for 'row'. The reason why we add two functions, one
1012   - for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
1013   - arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
1014   - the arguments of the function do not refer to any of the types defined here.
1015   -
1016   -public define HTML_Row(HTML_In_Form)
1017   - row
1018   - (
1019   - HTML_In_Form content
1020   - ) =
1021   - row([],[cell([],content)]).
1022   -
1023   -public define HTML_Row(HTML_Off_Form)
1024   - row
1025   - (
1026   - HTML_Off_Form content
1027   - ) =
1028   - row([],[cell([],content)]).
1029   -
1030   -
1031   -public define HTML_Off_Form
1032   - private_download
1033   - (
1034   - String abs_path,
1035   - String name,
1036   - String extra_ext
1037   - ) =
1038   - private_download(abs_path,name,extra_ext,failure).
1039   -
1040   -
1041   -public define HTML_Off_Form
1042   - private_download
1043   - (
1044   - String abs_path,
1045   - String name,
1046   - String extra_ext,
1047   - String action_name,
1048   - List((String,String)) args
1049   - ) =
1050   - private_download(abs_path,name,extra_ext,success((action_name,args))).
1051   -
1052   -public define HTML_Off_Form
1053   - text
1054   - (
1055   - List(Text_Option) lto,
1056   - Int32 i
1057   - ) =
1058   - text(lto,integer_to_string(i)).
1059   -
1060   -
1061   -public define HTML_Off_Form
1062   - text
1063   - (
1064   - Int32 i
1065   - ) =
1066   - text([],i).
1067   -
1068   -public define HTML_Off_Form
1069   - text
1070   - (
1071   - String s
1072   - ) =
1073   - text([],s).
1074   -
1075   - - Cell a gap between two other cells :
1076   -
1077   -public define HTML_Cell(HTML_Off_Form)
1078   - width_gap
1079   - (
1080   - Int32 w
1081   - ) =
1082   - cell([width(w)],text([],"")).
1083   -
1084   -public define HTML_Cell(HTML_In_Form)
1085   - width_gap
1086   - (
1087   - Int32 w
1088   - ) =
1089   - cell([width(w)],text([],"")).
1090   -
1091   -
1092   - - Row a gap between two other rows :
1093   -
1094   -public define HTML_Row(HTML_Off_Form)
1095   - height_gap
1096   - (
1097   - Int32 h
1098   - ) =
1099   - row([],[cell([height(h)],text([],""))]).
1100   -
1101   -public define HTML_Row(HTML_In_Form)
1102   - height_gap
1103   - (
1104   - Int32 h
1105   - ) =
1106   - row([],[cell([height(h)],text([],""))]).
1107   -
1108   -
1109   - Notice that the two types have alternatives in common (same name, same arguments types,
1110   - up to the value of the parameter $T), which correspond to elements which may be put
1111   - anywhere in the page.
1112   -
1113   -
1114   -
1115   -public type CSS_Style:
1116   - text_options(List(Text_Option)).
1117   -
1118   -public type CSS_File:
1119   - css_file(String file_name).
1120   -
1121   -define String
1122   - format
1123   - (
1124   - List(Text_Option) l
1125   - ).
1126   -
1127   -
1128   -
1129   -
1130   -define Printable_tree
1131   - format_css_styles
1132   - (
1133   - List(CSS_Style) l
1134   - ) =
1135   - if l is
1136   - {
1137   - [ ] then [ ],
1138   - [h . t] then
1139   - [ if h is
1140   - {
1141   - text_options(tos) then
1142   - [" body, span, p { ", format(tos), " }\n" ]
1143   - }
1144   - . format_css_styles(t)]
1145   - }.
1146   -
1147   -
1148   -
1149   -public type HTML_Meta:
1150   - keywords (List(String)),
1151   - refresh (Actioner_Connection connection,
1152   - Actioner_Target target,
1153   - String action_name,
1154   - Int32 delay), // in seconds
1155   - meta (String name, String content),
1156   - http_equiv (String name, String content),
1157   - generic_meta (List((String,String))),
1158   - literal (String).
1159   -
1160   - Meta tags are put in the 'head' of the HTML page.
1161   -
1162   -
1163   -public type Body_Option:
1164   - background_color (RGB),
1165   - background_image (String url),
1166   - background_image (String url, List(BackgroundOption)).
1167   -
1168   -public type HTML_Body:
1169   - body(List(Body_Option) options, HTML_Off_Form content).
1170   -
1171   -
1172   -public type HTML_Page:
1173   - html_page(String title,
1174   - List(HTML_Meta) meta_tags,
1175   - List(CSS_Style) styles,
1176   - List(CSS_File) css_files,
1177   - HTML_Body body).
1178   -
1179   -public define HTML_Page
1180   - html_page
1181   - (
1182   - String title,
1183   - List(HTML_Meta) metas,
1184   - HTML_Body body
1185   - ) =
1186   - html_page(title,metas,[],[],body).
1187   -
1188   -public define HTML_Page
1189   - html_page
1190   - (
1191   - String title,
1192   - List(HTML_Meta) metas,
1193   - List(CSS_Style) styles,
1194   - HTML_Body body
1195   - ) =
1196   - html_page(title, metas, styles, [], body).
1197   -
1198   - 'HTML_Page' represents the final product of the construction of a web page.
1199   -
1200   -
1201   -
1202   -
1203   - *** (3.2) ``in form'' versus ``off form''.
1204   -
1205   - There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
1206   - of them may have a content, which is yet another HTML element (or several). Hence, it
1207   - is meaningful to say that an element is 'within' another one. Now, putting any element
1208   - within any other one may be meaningless. For example, an input element must be put
1209   - within a form (otherwise, it is useless), and a from within another form has no precise
1210   - meaning (and is forbidden by the HTML specification).
1211   -
1212   - Actually, the main criterium is ``within a form or not within a form''. So, HTML
1213   - elements in a given page are separated into two categories: those who are within a
1214   - form, and the others. Nevertheless, there are elements which may belong to both
1215   - categories, like images and texts. We want to make use of the strong typing mecanism of
1216   - Anubis in order to forbid non meaningful placement of elements.
1217   -
1218   - The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
1219   - 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
1220   - and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
1221   - but the two types are not cross recursive. This is the reason why it is impossible to
1222   - put a form within a form. In order to construct a web page, you essentially have to
1223   - produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
1224   -
1225   - In practice, you don't have to worry so much about these two types, because elements
1226   - which may be put anywhere are constructed for both types by functions with the same
1227   - name and the same arguments. Hence, for both types, you just write the same thing. You
1228   - are warned by the compiler only when you try to put an element at a place it is not
1229   - allowed.
1230   -
1231   -
1232   -
1233   - *** (3.3) Defining your own style.
1234   -
1235   - We provide generic tools for constructing HTML elements. However, your web site needs
1236   - to have a ``style''.
1237   -
1238   - To that end, you need to write down a set of ``styling functions'', using the tools
1239   - defined here. These styling functions allow the introduction of your colors and other
1240   - visual characteristics into the constructed elements once and for all. For example, you
1241   - may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
1242   - some 'text_color'. You may write something like this:
1243   -
1244   - define RGB text_color = rgb(10,40,40).
1245   -
1246   - define HTML_Off_Form
1247   - text
1248   - (
1249   - String the_text
1250   - ) =
1251   - text([font("helvetica"),size(14),color(text_color)],
1252   - the_text).
1253   -
1254   - (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
1255   - page, you just write:
1256   -
1257   - text("... some text ...")
1258   -
1259   - and you don't have to provide the font, size and color for each text. If you want to
1260   - have several styles of text presentation, you just write several sets of such
1261   - convenience functions. This also suggests a trick. You may want for example different
1262   - colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
1263   - defining two functions as above, with the same name and same argument type, but
1264   - returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
1265   -
1266   - If this preliminary work is well done, you will not waste your time later when you
1267   - concentrate on the actual informational content of your pages.
1268   -
1269   - This general principle should be applied to all sorts of elements. This is the best
1270   - thing to do in order to separate the functions defining the visual style from the
1271   - functions defining the informational content itself, so that changing the style without
1272   - changing the content becomes easy. This is also the best way for having a clean and
1273   - easily readable source for your web site.
1274   -
1275   -
1276   -
1277   -
1278   -
1279   - *** (3.4) Actioners and forms.
1280   -
1281   - We have gathered several notions from HTML into that of an 'actioner'. An actioner is
1282   - an HTML element which opens a connection to our server when clicked upon. Actioners may
1283   - have different visual aspects. They may look like hypertext links or like buttons
1284   - (rollovers), or even like selectors (with immediate action). In any case, their
1285   - behavior is the same: they open a connection to our server, and send a set of 'web
1286   - arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
1287   - the action to be performed, and the others should be considered as operands for this
1288   - action. Actually, the precise behavior of the actioner has several variants.
1289   -
1290   - The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
1291   - type 'Actioner_Connection' above.
1292   -
1293   - You must also choose where the answer must be rendered. This may be in the same window
1294   - or in another window (or frame). If it is in another window, the name of that window
1295   - must be given. If the window does not exist, the browser will create it. Optionally,
1296   - you may give the dimensions of the new window and other characteristics. See the type
1297   - 'Actioner_Target' above.
1298   -
1299   - The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
1300   - case of a rollover button, you provide the URLs of two images (of the same size)
1301   - representing the button:
1302   -
1303   - url_off: to be used when the mouse is not over the button,
1304   - url_on: to be used when the mouse is over the button.
1305   -
1306   - You can also create rollover buttons without creating images. Just use the second
1307   - alternative named 'button'. The server creates the images automatically.
1308   -
1309   - The purpose of forms is just to give operands to actioners. If the actioner is placed
1310   - within a form, all the input elements which are within this form provide operands to
1311   - the actioner (except sometimes when they are not set by the client). If it is not put
1312   - within a form, the actioner gets no operand, except if the name of a form is explicitly
1313   - given, in which case the actioner gets all the inputs from that form as
1314   - operands. Furthermore, you may want to give extra operands to the actioner. This may be
1315   - useful for separating families of actioners with the same action name. Extra operands
1316   - 'name=value' must be given in the form of pairs '(name,value)'.
1317   -
1318   - Notice that the name of a form may be used by an actioner which is off the form, so as
1319   - to get the operands provided by this form. Also notice that several actioners may refer
1320   - to the same form, being either in the form, or referring to the form from the
1321   - outside. These actioners simply get the same set of operands, even if they correspond
1322   - to distinct actions.
1323   -
1324   - Input elements may be put only within a form.
1325   -
1326   -
1327   -
1328   - *** (3.5) Local popup.
1329   -
1330   - This element looks like a link or a rollover button. When this button is clicked upon,
1331   - a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
1332   - page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
1333   - particular, clicking on the button does not open any connection. This is why it is
1334   - called 'local'. The arguments have the following roles:
1335   -
1336   - Actioner_Aspect aspect of the button (same semantics as for actioners)
1337   - content content of the popup window
1338   - x, y, position of the popup window on the HTML page (not relative
1339   - to the button but to the page itself)
1340   - title title of the popup window
1341   - color color of the title bar and close button in the popup window.
1342   - A lightened version of this color is used for the background
1343   - of the popup window.
1344   - width width of the title bar
1345   -
1346   -
1347   -
1348   -
1349   -
1350   - --- That's all for the public part ! --------------------------------------------------
1351   -
1352   -
1353   -
1354   -
1355   -
1356   - ----------------------------------- Table of Contents ---------------------------------
1357   -
1358   - *** [1] States.
1359   - *** [1.1] Saving and retrieving states.
1360   - *** [1.2] Deleting out of date states.
1361   -
1362   - *** [2] Tools.
1363   - *** [2.1] Directories.
1364   - *** [2.2] Secondary documents.
1365   -
1366   - *** [3] Managing web arguments.
1367   - *** [3.1] Prefixing web arguments names.
1368   - *** [3.2] Separating web arguments.
1369   - *** [3.3] Applying an action.
1370   -
1371   - *** [4] Web site descriptions and the 'awp handlers'.
1372   - *** [4.1] The type 'Web_Site'.
1373   - *** [4.2] Making a web site description.
1374   - *** [4.3] Starting the servers.
1375   -
1376   - *** [5] HTML Formating.
1377   - *** [5.1] The type 'HTML_Any($T)'.
1378   - *** [5.2] Formating a color.
1379   - *** [5.3] Creating buttons.
1380   - *** [5.4] Formating an actioner.
1381   - *** [5.5] Formating a private download link.
1382   - *** [5.6] Formating rows and cells in a table.
1383   - *** [5.7] Formating elements which may be put anywhere.
1384   - *** [5.8] Formating 'in form' elements.
1385   - *** [5.9] Formating 'off form' elements.
1386   - *** [5.10] Formating meta-tags.
1387   -
1388   - ---------------------------------------------------------------------------------------
1389   -
1390   -
1391   -
1392   -
1393   -public define String
1394   - doctype_w3c_header
1395   - =
1396   - "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "+
1397   - "\"http://www.w3c.org/TR/html4/loose.dtd\">\n".
1398   -
1399   -
1400   -
1401   -
1402   - *** [1] States.
1403   -
1404   - We have to define functions for saving a state, retrieving a state, deleting out of
1405   - date states. We need one such function per web site. The types of the first two
1406   - functions depend on the parameter $State. This is not the case of the third one. The
1407   - fact that the instance of $State is variable from one web sites to the other implies
1408   - rather subtle manipulations using full functionality.
1409   -
1410   -
1411   - *** [1.1] Saving and retrieving states.
1412   -
1413   - Each state is saved into a file on the server's disk (in the directory represented by
1414   - the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
1415   - state is saved together with a time stamp whose value is obtained by adding the current
1416   - time to the given timeout for states. The state receives a name obtained by hashing
1417   - (using sha1) the content of the file itself, and then encoding the hash with
1418   - 'web_arg_encode'. The name of the file into which the state is saved is the
1419   - concatenation of "s" and the name of the state.
1420   -
1421   -read CXM_web_arg_encode.anubis
1422   -
1423   - The tool below constructs the function which is able to save a state on the server's
1424   - disk.
1425   -
1426   -define (Maybe($State) s) -> String // the function constructed returns the name of the state
1427   - make_save_state_function
1428   - (
1429   - Int32 timeout,
1430   - String state_directory
1431   - ) =
1432   - (Maybe($State) mbs) |->
1433   - if mbs is
1434   - {
1435   - failure then "",
1436   - success(s) then
1437   - with time_stamp = now+timeout,
1438   - to_be_saved = (time_stamp,s),
1439   - state_name = web_arg_encode(sha1(s)),
1440   - if save(to_be_saved,state_directory+"/s"+state_name) is ok
1441   - then state_name
1442   - else (print("Cannot create state file in '"+state_directory+"'.\n"); "")
1443   - }.
1444   -
1445   -
1446   - When a request arrives, we need to retrieve the previous state from the server's
1447   - disk. We receive the name of that state. If the state is out of date, the state file is
1448   - kept 3 days, and then deleted.
1449   -
1450   -type PreviousState($State):
1451   - not_found, // cannot retrieve the previous state
1452   - out_of_date($State), // the previous state is out of date
1453   - still_valid($State). // the previous state is still valid
1454   -
1455   -define (String state_name) -> PreviousState($State)
1456   - make_retrieve_state_function
1457   - (
1458   - String state_directory
1459   - ) =
1460   - (String state_name) |->
1461   - with file_path = state_directory+"/s"+state_name,
1462   - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
1463   - then (
1464   - if d is (time_stamp,s) then
1465   - if time_stamp < now
1466   - then (
1467   - forget(remove(file_path));
1468   - out_of_date(s)
1469   - )
1470   - else still_valid(s) // state has been successfully retrieved
1471   - )
1472   - else not_found.
1473   -
1474   -
1475   -
1476   - *** [1.2] Deleting out of date states.
1477   -
1478   - We also need to delete states which are out of date and which will never be deleted by
1479   - the above method. This may be performed by a machine doing this periodically (say once
1480   - per states life time period).
1481   -
1482   -define (List(String) file_names) -> One
1483   - make_delete_out_of_date_states_function
1484   - (
1485   - Maybe($State) dummy,
1486   - String state_directory
1487   - ) =
1488   - (List(String) file_names) |-df->
1489   - if file_names is
1490   - {
1491   - [ ] then unique,
1492   - [h . t] then
1493   - with file_path = state_directory+"/"+h,
1494   - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
1495   - then (
1496   - if d is (time_stamp,data) then
1497   - if time_stamp < now
1498   - then (forget(remove(file_path)); df(t))
1499   - else df(t)
1500   - )
1501   - else (forget(remove(file_path)); df(t))
1502   - }.
1503   -
1504   -
1505   - The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
1506   -
1507   - Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
1508   - function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
1509   - does not accept a parameter in the body of a function (here the parameter is required
1510   - by the use of 'retrieve') if this parameter does not appear in the type of the
1511   - function. This is because this would create ambiguities that no explicit typing may
1512   - ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
1513   - and if you compile this file, you will get a message like this one:
1514   -
1515   - Error in 'making_a_web_site.anubis', line 1300, column 7:
1516   - A definition may not contain parameters which are not present
1517   - in the declaration part (hidden parameters):
1518   - $State
1519   -
1520   - The type of the function constructed by 'make_delete_out_of_date_states_function' is
1521   - independant of the parameter $State. This is important because this allows to create
1522   - the list of such functions for all web sites. From this list, it is possible to call
1523   - the functions one after the other, so deleting out of date states for all web
1524   - sites. Actually, the next function receives a list of pairs (state_directory,function),
1525   - one for each web site.
1526   -
1527   -define One
1528   - delete_out_of_date_states // for all web sites
1529   - (
1530   - List((String, List(String) -> One)) directories_and_functions
1531   - ) =
1532   - if directories_and_functions is
1533   - {
1534   - [ ] then unique,
1535   - [h . t] then if h is (state_directory,function) then
1536   - function(directory_list(state_directory,"s*"));
1537   - delete_out_of_date_states(t)
1538   - }.
1539   -
1540   -
1541   - The above function must be called periodically in a separate virtual machine. The
1542   - period we have choosen is (rather logically) the life time of states itself. This may
1543   - be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
1544   - be really infinite, because the servers may be shutdown. Hence, our loop must test
1545   - (rather frequently; say every second) if the servers are down. If they are, the loop
1546   - must be exited.
1547   -
1548   -define One
1549   - delete_states_loop
1550   - (
1551   - List((String,List(String) -> One)) directories_and_functions,
1552   - Int32 timeout,
1553   - Int32 next_time,
1554   - Server http_server,
1555   - Server https_server,
1556   - Var(Bool) shutdown_required
1557   - ) =
1558   - if *shutdown_required
1559   - then (shutdown(http_server); shutdown(https_server))
1560   - else unique;
1561   - if (is_down(http_server) & is_down(https_server))
1562   - then unique
1563   - else if now > next_time
1564   - then
1565   - (
1566   - delete_out_of_date_states(directories_and_functions);
1567   - delete_states_loop(directories_and_functions,
1568   - timeout,
1569   - now+timeout,
1570   - http_server,
1571   - https_server,
1572   - shutdown_required)
1573   - )
1574   - else
1575   - (
1576   - sleep(1000); // sleep just one second and try again
1577   - delete_states_loop(directories_and_functions,
1578   - timeout,
1579   - next_time,
1580   - http_server,
1581   - https_server,
1582   - shutdown_required)
1583   - ).
1584   -
1585   - The above loop must be run in a separate virtual machine. This will be done just after
1586   - the two servers are started.
1587   -
1588   -
1589   -
1590   -
1591   -
1592   - *** [2] Tools.
1593   -
1594   - *** [2.1] Directories.
1595   -
1596   - We need a tool for creating directories (if needed).
1597   -
1598   - (This tool has been moved to 'tools/basis.anubis').
1599   -
1600   -
1601   -
1602   -
1603   - *** [2.2] Secondary documents.
1604   -
1605   - Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
1606   - from the current document, but only through an URL. For this reason, we implement a
1607   - mecanism for creating secondary documents on the fly. To that end we use the 'private
1608   - download' mecanism.
1609   -
1610   - A secondary document is formated by the same functions as the main document itself. The
1611   - next function takes an 'off form' element, creates the file containing the secondary
1612   - document in HTML format, and returns the URL at which the document will be available.
1613   -
1614   -define String
1615   - create_secondary_document
1616   - (
1617   - String sd, // site directory
1618   - String as, // authorization_secret
1619   - String sn, // state name
1620   - $T -> Printable_tree format_element,
1621   - $T content,
1622   - HTML_Size width
1623   - ) =
1624   - with private_download_directory = sd+"/private_download",
1625   - hash = web_arg_encode(sha1(content)),
1626   - file_content = (Printable_tree)
1627   - [doctype_w3c_header,
1628   - "<html><body><table ",
1629   - if width is
1630   - {
1631   - absolute(w) then ["width=\"",w-25],
1632   - percentage(w) then ["width=\"95%\""]
1633   - },"\"><tr><td align=right>",
1634   - format_element(content),
1635   - "</td></tr></table></body></html>"
1636   - ],
1637   - file_name = "sd"+hash+".html",
1638   - file_path = private_download_directory+"/"+file_name,
1639   - if write_to_file(file_path,file_content) is
1640   - {
1641   - cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
1642   - write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
1643   - ok then file_name+"?zauth="+
1644   - make_authorization(sd,as,private_download_directory+"/"+file_name)
1645   - }.
1646   -
1647   -
1648   -
1649   -
1650   - *** [2.3] Generating unique ids.
1651   -
1652   - In order to uniquely name object for JavaScript we generate unique ids from a counter.
1653   -
1654   -define Int32
1655   - new_idnum
1656   - (
1657   - Var(Int32) ic_v // 'idnum' counter variable
1658   - ) =
1659   - protect
1660   - with result = *ic_v+1,
1661   - ic_v <- result;
1662   - result.
1663   -
1664   -
1665   -
1666   -
1667   -
1668   -
1669   - *** [3] Managing web arguments.
1670   -
1671   - Web arguments are those pairs 'name=value' which are transmitted through the HTTP
1672   - protocol. We need precise naming conventions for these web arguments.
1673   -
1674   -
1675   -
1676   - *** [3.1] Prefixing web arguments names.
1677   -
1678   - We want to assign different roles to web arguments, and we also want to be able to
1679   - recognize its role directly from the name of a web argument. The name "s" is reserved
1680   - for the web argument whose value is the name of the current state. The name "a" is
1681   - reserved for the web argument whose value is the name of the action to be
1682   - performed. Other web arguments receive arbitrary names, and in order to avoid clashes,
1683   - these names are prefixed by:
1684   -
1685   - "p" for names of password inputs,
1686   - "o" for other web arguments
1687   -
1688   - The reason why password input names have a distinct prefix is that this allows the HTTP
1689   - server to hide the passwords on the console of the server and in the journal.
1690   -
1691   -
1692   -
1693   -
1694   - *** [3.2] Separating web arguments.
1695   -
1696   - When a new request arrives, we need to separate the web arguments, that is to say:
1697   -
1698   - - find the value of "s", and recover the corresponding state,
1699   - - find the value of "a", which is the name of the action to be performed,
1700   - - get the list of all the remaining web arguments (operands of the action).
1701   -
1702   - We must also determine if the previous state may be recovered. If it is not the case
1703   - (either because the previous state name is invalid, or the previous state is out of
1704   - date), we must check if there is an action name. Indeed, the presence of an action name
1705   - indicates that the user has clicked on one of our buttons or links. If on the contrary
1706   - there is no action name the user has just entered our address in his browser. In this
1707   - last case, we must send the first page of our site (maybe a 'login' page), but if there
1708   - is an action, we must send a page just saying that the session ticket has expired. If
1709   - the previous state is recovered and there is no action, the new state is the same as
1710   - the previous state.
1711   -
1712   - The result of the separation of the web arguments is of type:
1713   -
1714   -type Separated_Web_Args($State):
1715   - swa(Maybe(PreviousState($State)) previous_state,
1716   - Maybe(String) action_name,
1717   - List(Web_arg) operands).
1718   -
1719   -
1720   -
1721   - The next function constructs the function which separates the web arguments.
1722   -
1723   -define (List(Web_arg) lwa) -> Separated_Web_Args($State)
1724   - make_separate_web_args_function
1725   - (
1726   - String state_directory,
1727   - String -> PreviousState($State) retrieve_state
1728   - ) =
1729   - (List(Web_arg) lwa) |-swaf->
1730   - if lwa is
1731   - {
1732   - [ ] then
1733   - //
1734   - // no web arg found => no previous state and no action
1735   - //
1736   - swa(failure,failure,[]),
1737   -
1738   - [wa_1 . wa_others] then
1739   - //
1740   - // at least one web arg =>
1741   - // separate other web args, and insert the first one as needed
1742   - //
1743   - if (Separated_Web_Args($State))swaf(wa_others) is
1744   - {
1745   - swa(ps1, // possible previous state
1746   - an1, // maybe an action name
1747   - op1) // operands so far
1748   - then
1749   - if wa_1 is
1750   - {
1751   - web_arg(n,v) then
1752   - with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),
1753   - name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),
1754   - if prefix = "s" then
1755   - swa(success(retrieve_state(v)),an1,op1) else
1756   - if prefix = "a" then
1757   - swa(ps1,success(v),op1) else
1758   - if prefix = "t" then
1759   - swa(ps1,an1,[web_arg("target",v) . op1]) else
1760   - if prefix = "p" then
1761   - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
1762   - if prefix = "o" then
1763   - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
1764   - swa(ps1,an1,op1),
1765   -
1766   - upload(n,v,t) then
1767   - swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])
1768   - }}
1769   - }.
1770   -
1771   -
1772   -
1773   -
1774   -
1775   -
1776   - *** [3.3] Applying an action.
1777   -
1778   - When the web arguments are separated (and their names cleaned up from prefixes), we may
1779   - apply the action to the operands and the current state. We search for the action to be
1780   - applied in the list of actions. If no action is found, the new state is the same as
1781   - the previous state. Also, we deny the application of an HTTP action if the request
1782   - arrives through the HTTPS channel and conversely.
1783   -
1784   -
1785   -define (Maybe($State) previous,
1786   - String action_name,
1787   - HTTP_Info http_info,
1788   - List(Web_arg) lwa,
1789   - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))
1790   - make_apply_action_function
1791   - (
1792   - List(Web_Action($SessionTicket, $State)) actions
1793   - ) =
1794   - with f =
1795   - (Maybe($State) previous,
1796   - String action_name,
1797   - HTTP_Info http_info,
1798   - List(Web_arg) lwa,
1799   - Bool is_https,
1800   - List(Web_Action($SessionTicket, $State)) actions) |-f->
1801   - if actions is
1802   - {
1803   - [ ] then (print("action '"+action_name+
1804   - "' not found.\n"); (failure, previous, [])),
1805   - [ac1 . others] then if ac1 is
1806   - {
1807   - http_action(an,allow,do_it) then
1808   - if an = action_name
1809   - then if is_https
1810   - then (print("HTTP action '"+an+
1811   - "' called through HTTPS (denied).\n");
1812   - (failure, previous, []))
1813   - else if allow(previous)
1814   - then do_it(http_info,lwa,previous)
1815   - else (failure, previous, [])
1816   - else f(previous,action_name,http_info,lwa,is_https,others),
1817   -
1818   - https_action(an,allow,do_it) then
1819   - if an = action_name
1820   - then if is_https
1821   - then if allow(previous)
1822   - then do_it(http_info,lwa,previous)
1823   - else (failure, previous, [])
1824   - else (print("HTTPS action '"+an+
1825   - "' called through HTTP (denied).\n");
1826   - (failure, previous, []))
1827   - else f(previous,action_name,http_info,lwa,is_https,others),
1828   -
1829   - http_https_action(an,allow,do_it) then
1830   - if an = action_name
1831   - then if allow(previous)
1832   - then do_it(http_info,lwa,previous)
1833   - else (failure, previous, [])
1834   - else f(previous,action_name,http_info,lwa,is_https,others),
1835   -
1836   - }
1837   - },
1838   - (Maybe($State) previous,
1839   - String action_name,
1840   - HTTP_Info http_info,
1841   - List(Web_arg) lwa,
1842   - Bool is_https) |->
1843   - f(previous,action_name,http_info,lwa,is_https,actions).
1844   -
1845   -
1846   -
1847   -
1848   -
1849   -
1850   -
1851   -
1852   - *** [4] Web site descriptions and the 'awp handlers'.
1853   -
1854   - *** [4.1] The type 'Web_Site'.
1855   -
1856   - The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
1857   - need another one, because, we have some extra informations to record for each site.
1858   -
1859   -public type Web_Site:
1860   - web_site((Int32,Int32) -> Web_Site_Description description,
1861   - List(String) -> One delete_out_of_date).
1862   -
1863   -
1864   -
1865   -
1866   - *** [4.2] Making a web site description.
1867   -
1868   - Below is the function which creates a web site description. It first creates (if
1869   - needed) the directories for the site, then constructs the tool functions for the site,
1870   - and the site handler. Finally, it constructs the web site description.
1871   -
1872   - We gather common (constant) informations in the following type:
1873   -
1874   -type CommonInfo:
1875   - info(String common_name,
1876   - Int32 http_port,
1877   - Int32 https_port,
1878   - String site_directory,
1879   - String authorization_secret
1880   - ).
1881   -
1882   - We need a forward declaration.
1883   -
1884   -public define Printable_tree
1885   - format
1886   - (
1887   - CommonInfo cinfo,
1888   - String state_name,
1889   - HTML_Page page,
1890   - Bool is_https,
1891   - String charset
1892   - ).
1893   -
1894   -
1895   -define Printable_tree
1896   - format
1897   - (
1898   - HTML_Size s
1899   - ) =
1900   - if s is
1901   - {
1902   - absolute(x) then ["\"",x,"\""],
1903   - percentage(x) then ["\"",x,"%\""]
1904   - }.
1905   -
1906   -define Printable_tree
1907   - top_redirection_page
1908   - (
1909   - String common_name
1910   - ) =
1911   - [ doctype_w3c_header,
1912   - "<html><head>",
1913   - "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\">",
1914   - "</head><body></body></html>"
1915   - ].
1916   -
1917   -public define Web_Site
1918   - make_web_site_description
1919   - (
1920   - List(String) common_names, // for example: ["www.our-business.com"]
1921   - String site_directory,
1922   - One -> One init,
1923   - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
1924   - ($State expired,
1925   - HTTP_Info,
1926   - List(Web_arg),
1927   - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
1928   - (HTTP_Info,
1929   - List(Web_arg),
1930   - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
1931   - List(Web_Action($SessionTicket, $State)) actions,
1932   - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
1933   - Int32 timeout,
1934   - List(Redirection) redirections,
1935   - String charset,
1936   - List(String) journal_extensions,
1937   - List(String) journal_headers,
1938   - String secret,
1939   - List(MIME) known_mime_types,
1940   - (String action_name,
1941   - List(Web_arg) args) -> One before_send_file
1942   - ) =
1943   - init(unique);
1944   -
1945   -
1946   - //
1947   - // make required directories (if needed)
1948   - //
1949   - with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),
1950   - base_directory = make_directory(site_directory),
1951   - state_directory = make_directory(site_directory+"/states"),
1952   - forget(make_directory(site_directory+"/public"));
1953   - //
1954   - // construct tool functions
1955   - //
1956   - with save_state = make_save_state_function(timeout,state_directory),
1957   - retrieve_state = make_retrieve_state_function(state_directory),
1958   - separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),
1959   - apply_action = make_apply_action_function(actions),
1960   - //
1961   - // construct the site handler
1962   - //
1963   - site_handler = (Int32 http_port, Int32 https_port) |->
1964   - ((String host_name,
1965   - HTTP_Info http_info,
1966   - List(Web_arg) lwa,
1967   - Bool is_https) |->
1968   - ((List(HTTP_header),Printable_tree))
1969   - if separate_web_args(lwa) is
1970   - {
1971   - swa(mb_previous_state,mb_action_name,operands) then
1972   - with state_and_headers = if mb_previous_state is
1973   - {
1974   - failure then
1975   - if mb_action_name is
1976   - {
1977   - failure then initial_state(http_info),
1978   - success(action_name) then
1979   - apply_action(failure,action_name,http_info,operands,is_https)
1980   - },
1981   - success(previous_state) then if previous_state is
1982   - {
1983   - not_found then
1984   - if mb_action_name is
1985   - {
1986   - failure then initial_state(http_info),
1987   - success(_) then
1988   - ticket_lost_state(http_info,lwa,is_https)
1989   - },
1990   -
1991   - out_of_date(state) then
1992   - ticket_expired_state(state,http_info,lwa,is_https),
1993   -
1994   - still_valid(state) then
1995   - if mb_action_name is
1996   - {
1997   - failure then (failure, success(state), []),
1998   - success(action_name) then
1999   - apply_action(success(state),action_name,http_info,operands,is_https)
2000   - }
2001   - }
2002   - },
2003   - if state_and_headers is (session_ticket, mb_new_state, headers) then
2004   - with state_name = save_state(mb_new_state),
2005   - (headers,
2006   - format(info(host_name, http_port, https_port, site_directory, secret),
2007   - state_name,
2008   - compute_page(session_ticket, mb_new_state),
2009   - is_https,
2010   - charset))
2011   - }),
2012   - //
2013   - // make the delete_out_of_date function
2014   - //
2015   - delete_out_of_date =
2016   - make_delete_out_of_date_states_function((Maybe($State))failure,
2017   - site_directory+"/states"),
2018   - //
2019   - // construct the web site description
2020   - //
2021   - web_site((Int32 http_port, Int32 https_port) |->
2022   - web_site_description(common_names,
2023   - site_directory,
2024   - redirections,
2025   - charset,
2026   - journal_extensions,
2027   - journal_headers,
2028   - secret,
2029   - known_mime_types,
2030   - site_handler(http_port,https_port),
2031   - (List(Web_arg) lwa) |-> if separate_web_args(lwa) is
2032   - swa(mb_previous_state,mb_action_name,operands) then
2033   - if mb_action_name is
2034   - {
2035   - failure then unique
2036   - success(an) then before_send_file(an,operands)
2037   - }),
2038   - delete_out_of_date).
2039   -
2040   -
2041   -
2042   -
2043   - *** [4.3] Starting the servers.
2044   -
2045   -public define Start_Web_Sites_Result
2046   - start_web_sites
2047   - (
2048   - Int32 ip_address, // the IP address shared by the web sites
2049   - Int32 http_port, // usually: 80
2050   - Int32 https_port, // usually: 443
2051   - String ssl_certificate_common_name,
2052   - List(Web_Site) web_sites, // web sites to be started
2053   - Var(Bool) shutdown_required
2054   - ) =
2055   - with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
2056   - with http_server_r =
2057   - start_http_server(ip_address,http_port,
2058   - map(get_description,web_sites),
2059   - load_denial_of_service_info),
2060   - with https_server_r =
2061   - start_https_server(ip_address,https_port,
2062   - ssl_certificate_common_name,
2063   - map(get_description,web_sites),
2064   - load_denial_of_service_info),
2065   - if http_server_r is ok(http_server)
2066   - then
2067   - (
2068   - if https_server_r is ok(https_server)
2069   - then
2070   - (
2071   - start_http_servers_tasks(map(get_description,web_sites),
2072   - [http_server,https_server],
2073   - 600); // period of 10 minutes
2074   - delegate
2075   - delete_states_loop(
2076   - map((Web_Site ws) |->
2077   - (site_directory(description(ws)(http_port,https_port))+
2078   - "/states",delete_out_of_date(ws)),
2079   - web_sites),
2080   - 3600*24*3, // keep out of date states 3 days
2081   - now,
2082   - http_server,
2083   - https_server,
2084   - shutdown_required),
2085   - ok(http_server,https_server)
2086   - )
2087   - else cannot_bind_to_port(https_port)
2088   - )
2089   - else
2090   - (
2091   - if https_server_r is ok(https_server)
2092   - then cannot_bind_to_port(http_port)
2093   - else cannot_bind_to_port(http_port,https_port)
2094   - ).
2095   -
2096   -
2097   -public define One
2098   - start_web_sites
2099   - (
2100   - Int32 ip_address, // the IP address shared by the web sites
2101   - Int32 http_port, // usually: 80
2102   - Int32 https_port, // usually: 443
2103   - String ssl_certificate_common_name,
2104   - List(Web_Site) web_sites, // web sites to be started
2105   - Var(Bool) shutdown_required
2106   - ) =
2107   - if (Start_Web_Sites_Result)start_web_sites(ip_address,
2108   - http_port,
2109   - https_port,
2110   - ssl_certificate_common_name,
2111   - web_sites,
2112   - shutdown_required) is
2113   - {
2114   - cannot_bind_to_port(n) then print("Cannot bind to port: "+n+"\n"),
2115   - cannot_bind_to_port(n,m) then print("Cannot bind to ports: "+n+", "+m+"\n"),
2116   - ok(s1,s2) then print("Servers started.\n")
2117   - }.
2118   -
2119   -
2120   -
2121   -
2122   -
2123   - *** [5] HTML Formating.
2124   -
2125   - We need to translate HTML elements as defined above into actual HTML text.
2126   -
2127   - Actioners require special informations, which must be transmitted when needed by the
2128   - 'format' functions:
2129   -
2130   - - the 'common name', which is used for URLs,
2131   - - the HTTP/HTTPS port number,
2132   - - the 'state name', which must be transmitted when the actioner is clicked upon,
2133   - - the 'form name' (if any) to which the actioner refers.
2134   -
2135   - If the actioner is off form, and if it refers to a form, the name of that form is
2136   - already known by the actioner. On the contrary, if the actioner is 'in form', it refers
2137   - implicitly to the form containing it. The name of that form is transmitted to the
2138   - 'format' functions called from within the formating of that form.
2139   -
2140   -
2141   -
2142   -
2143   - *** [5.1] The type 'HTML_Any($T)'.
2144   -
2145   - The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
2146   - parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
2147   -
2148   -type HTML_Any($T):
2149   - any_text (List(Text_Option), String the_text),
2150   - any_preformated (List(Text_Option), String),
2151   - any_paragraph (List(Text_Option), String the_text),
2152   - any_image (String url),
2153   - any_image (String url, Int32 width, Int32 height),
2154   - any_table (List(Table_Option), List(HTML_Row($T))),
2155   - any_center ($T),
2156   - any_mail_to (String email, $T element),
2157   - any_scroller (Int32 width, Int32 height,
2158   - Int32 content_width, Int32 content_height,
2159   - $T content),
2160   - any_fixed_size (HTML_Size width, HTML_Size height, $T content),
2161   - any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
2162   - any_actioner (Actioner_Connection,
2163   - Actioner_Target,
2164   - Actioner_Aspect,
2165   - String action_name,
2166   - List((String,String)) extra_ops,
2167   - List(Actioner_Local_Action),
2168   - Maybe(String) form_name),
2169   - any_foreign_link (List(Text_Option), String url, String name),
2170   - any_private_download (String abs_path, String name, String extra_ext,
2171   - Maybe((String,List((String,String))))),
2172   - any_div (List(DIV_Option), $T element),
2173   - any_div_empty (List(DIV_Option)),
2174   - any_coreattrs (List(CoreAttrs)).
2175   -
2176   -
2177   -
2178   - *** [5.2] Formating a color.
2179   -
2180   - RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
2181   - hexadecimal values.
2182   -
2183   -define String
2184   - html_format
2185   - (
2186   - RGB color
2187   - ) =
2188   - if color is rgb(r,g,b) then
2189   - "#" + hexadecimal(word8_to_int32(r),2)
2190   - + hexadecimal(word8_to_int32(g),2)
2191   - + hexadecimal(word8_to_int32(b),2).
2192   -
2193   -
2194   - The following is a very arbitrary definition of the opposite color. The thing which is
2195   - important is that it is far from the original, so that characters in 'opposite' color
2196   - are clearly visible over the original.
2197   -
2198   -define RGB
2199   - opposite
2200   - (
2201   - RGB color
2202   - ) =
2203   - if color is rgb(r,g,b) then
2204   - with r1 = word8_to_int32(r),
2205   - with g1 = word8_to_int32(g),
2206   - with b1 = word8_to_int32(b),
2207   - rgb(truncate_to_word8(255-r1),
2208   - truncate_to_word8(255-g1),
2209   - truncate_to_word8(255-b1)).
2210   -
2211   -
2212   -
2213   -
2214   - *** [5.3] Creating buttons.
2215   -
2216   - We want to be able to create buttons in the form of a pair of images (rollovers)
2217   - automatically. We use the JPEG interface, because for the time being Anubis cannot
2218   - handle other kinds of images.
2219   -
2220   -
2221   - Computing printed text length.
2222   -
2223   - define Int32
2224   - printed_text_width
2225   - (
2226   - Word8 -> Int32 char_size,
2227   - List(Word8) l
2228   - ) =
2229   - if l is
2230   - {
2231   - [] then (Int32) 0,
2232   - [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
2233   - }.
2234   -
2235   - define Int32
2236   - printed_text_width
2237   - (
2238   - SystemFont font,
2239   - String s
2240   - ) =
2241   - printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
2242   - explode(s)).
2243   -
2244   -
2245   -
2246   - Converting RGB to RGBA.
2247   -
2248   -define RGBA
2249   - to_rgba
2250   - (
2251   - RGB color
2252   - ) =
2253   - if color is rgb(r,g,b) then rgba(r,g,b,255).
2254   -
2255   -
2256   - Drawing a 'relief'.
2257   -
2258   - define One
2259   - draw_relief
2260   - (
2261   - RGBAImage dest,
2262   - RGBA color,
2263   - Int32 contrast,
2264   - Int32 x,
2265   - Int32 y,
2266   - Int32 width,
2267   - Int32 height
2268   - ) =
2269   - with l = lighten(color,contrast),
2270   - d = darken(color,contrast),
2271   - draw_rectangle(dest,rect(x,y,x+width,y+1),l);
2272   - draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
2273   - draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
2274   - draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
2275   -
2276   -
2277   - Creating a button background.
2278   -
2279   - define RGBAImage
2280   - create_button_background
2281   - (
2282   - RGBA color,
2283   - Int32 width,
2284   - Int32 height
2285   - ) =
2286   - with result = create_rgba_image(width,height,color),
2287   - draw_relief(result,color,100,0,0,width,height);
2288   - draw_relief(result,color,70,1,1,width-2,height-2);
2289   - draw_relief(result,color,55,2,2,width-4,height-4);
2290   - draw_relief(result,color,35,3,3,width-6,height-6);
2291   - draw_relief(result,color,20,4,4,width-8,height-8);
2292   - draw_relief(result,color,10,5,5,width-10,height-10);
2293   - draw_relief(result,color,5,6,6,width-12,height-12);
2294   - result.
2295   -
2296   -
2297   - Drawing the text over the background.
2298   -
2299   - define One
2300   - draw_button_text
2301   - (
2302   - RGBAImage image,
2303   - String text,
2304   - Int32 text_index,
2305   - Int32 pixel_x,
2306   - Int32 y,
2307   - Rectangle clip,
2308   - RGBA color,
2309   - SystemFont font,
2310   - ) =
2311   - if nth(text_index,text) is
2312   - {
2313   - failure then unique,
2314   - success(c) then
2315   - with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
2316   - draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
2317   - }.
2318   -
2319   - define One
2320   - draw_button_text
2321   - (
2322   - RGBAImage image,
2323   - String text,
2324   - Int32 text_width,
2325   - RGBA light_color,
2326   - RGBA dark_color,
2327   - SystemFont font
2328   - ) =
2329   - with image_width = width(image),
2330   - image_height = height(image),
2331   - x_pos = (image_width-text_width)>>1,
2332   - clip = rect(0,0,image_width,image_height),
2333   - new_light_color = lighten(light_color,150),
2334   - new_dark_color = darken(dark_color,40),
2335   - draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
2336   - draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
2337   -
2338   -
2339   - The next function creates the two images for a button. The information given is the
2340   - main color of the button, the text of the button and the minimal width (in pixels) of
2341   - the button. The function does not create the button if the images already exist. The
2342   - two images are stored in the directory 'site_directory/buttons'. The names of the files
2343   - are of the form:
2344   -
2345   - bxxxx_off.jpg
2346   - bxxxx_on.jpg
2347   -
2348   - where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
2349   - 'rm'), and where 'xxxx' is created from the given informations by the formula:
2350   -
2351   - xxxx = web_arg_encode(sha1((color,text,width)))
2352   -
2353   - Hence, distinct informations give distinct file names.
2354   -
2355   -
2356   - define String // returns xxxx
2357   - create_button_images
2358   - (
2359   - String site_directory,
2360   - RGBA color,
2361   - String text,
2362   - Int32 width,
2363   - SystemFont font
2364   - ) =
2365   - with xxxx = web_arg_encode(sha1((color,text,width))),
2366   - buttons_dir = site_directory+"/public/buttons",
2367   - off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
2368   - on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
2369   - if file_exists(on_filepath)
2370   - then xxxx
2371   - else with
2372   - text_width = printed_text_width(font,text),
2373   - button_width = max(width,text_width+12),
2374   - button_height = (Int32)20,
2375   - light_color = lighten(color,60),
2376   - very_light_color = lighten(light_color,30),
2377   - dark_color = darken(color,40),
2378   - background_off =
2379   - create_button_background(color,button_width,button_height),
2380   - background_on =
2381   - create_button_background(light_color,button_width,button_height),
2382   -
2383   - draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
2384   - draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
2385   - forget(write_image_to_JPEG_file(to_JPEG(background_off),
2386   - off_filepath,
2387   - 100));
2388   - forget(write_image_to_JPEG_file(to_JPEG(background_on),
2389   - on_filepath,
2390   - 100));
2391   - xxxx.
2392   -
2393   -
2394   -
2395   -
2396   -
2397   - *** [5.4] Formating an actioner.
2398   -
2399   - An actioner works as follows. Assume first that it refers to a form. When it is clicked
2400   - upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
2401   - form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
2402   - actioner does not refer to a form, it fires the URL directly via 'href', because in
2403   - that case, the actioner is always an <a> tag.
2404   -
2405   - The URL itself is composed using the connection sort (same, http or https), the common
2406   - name and port number (if needed), the state name, the action name, and the extra
2407   - operands, which are put into a query string. It may look like this:
2408   -
2409   - http://common_name:port/?s=state_name&a=action_name&oname=value...
2410   -
2411   - Each extra operand is a pair of strings: (name,value). It is formated as:
2412   -
2413   - &oname=value
2414   -
2415   -
2416   -define String
2417   - format_extra_operands
2418   - (
2419   - List((String,String)) l
2420   - ) =
2421   - if l is
2422   - {
2423   - [ ] then "",
2424   - [h . t] then if h is (n,v) then
2425   - "&amp;o"+n+"="+v+format_extra_operands(t)
2426   - }.
2427   -
2428   -
2429   - It seams that the standard requires "&amp;" instead of "&" !
2430   -
2431   - In case the target is another window, we need to format the options for this window.
2432   -
2433   -define String
2434   - format
2435   - (
2436   - List(Other_Window_Option) l
2437   - ) =
2438   - if l is
2439   - {
2440   - [ ] then "",
2441   - [h . t] then if h is
2442   - {
2443   - resizable then "resizable",
2444   - scrollbars then "scrollbars",
2445   - width(w) then "width="+w,
2446   - height(h) then "height="+h
2447   - } + if t is [ ] then "" else (","+format(t))
2448   - }.
2449   -
2450   -
2451   -
2452   - Formating choices for a <select> tag.
2453   -
2454   -public define Printable_tree
2455   - format_choices
2456   - (
2457   - List(String) l
2458   - ) =
2459   - if l is
2460   - {
2461   - [ ] then [ ],
2462   - [h . t] then ["<option>",h . format_choices(t)]
2463   - }.
2464   -
2465   -
2466   -public define Printable_tree
2467   - format_choices
2468   - (
2469   - List(String) l,
2470   - String selected
2471   - ) =
2472   - if l is
2473   - {
2474   - [ ] then [ ],
2475   - [h . t] then if h = selected
2476   - then ["<option selected>",h . format_choices(t)]
2477   - else ["<option>",h . format_choices(t,selected)]
2478   - }.
2479   -
2480   -
2481   -public define Printable_tree
2482   - format_choices
2483   - (
2484   - List((String,String)) l
2485   - ) =
2486   - if l is
2487   - {
2488   - [ ] then [ ],
2489   - [h . t] then
2490   - if h is (val,item)
2491   - then ["<option value=\""+val+"\">",item . format_choices(t)]
2492   - }.
2493   -
2494   -public define Printable_tree
2495   - format_choices
2496   - (
2497   - List((String,String)) l,
2498   - String selected
2499   - ) =
2500   - if l is
2501   - {
2502   - [ ] then [ ],
2503   - [h . t] then
2504   - if h is (val,item) then
2505   - if item = selected
2506   - then ["<option value=\""+val+"\" selected>",item . format_choices(t)]
2507   - else ["<option value=\""+val+"\">",item . format_choices(t,selected)]
2508   - }.
2509   -
2510   -
2511   -
2512   -variable Int32 count = 0.
2513   -
2514   - Note: this counter is private to the virtual machine, hence there is one counter by
2515   - client.
2516   -
2517   -define Int32
2518   - new_count
2519   - =
2520   - count <- *count+1;
2521   - *count.
2522   -
2523   -
2524   - The next function composes the URL. It is a JavaScript URL when the target is another
2525   - window.
2526   -
2527   -define String
2528   - make_actioner_url
2529   - (
2530   - CommonInfo cinfo,
2531   - Actioner_Connection connection,
2532   - Actioner_Target target,
2533   - String state_name,
2534   - String action_name,
2535   - List((String,String)) extra_ops,
2536   - Bool is_https
2537   - ) =
2538   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
2539   - with strict_url =
2540   - if connection is
2541   - {
2542   - same then "/",
2543   - /*
2544   - same then if is_https
2545   - then "https://"+common_name+":"+https_port+"/"
2546   - else "http://"+common_name+":"+https_port+"/",
2547   - */
2548   -
2549   - http then "http://"+common_name+":"+http_port+"/",
2550   - https then "https://"+common_name+":"+https_port+"/",
2551   - } +
2552   - "?s=" + state_name + "&amp;a=" + action_name +
2553   - format_extra_operands(extra_ops),
2554   - if target is
2555   - {
2556   - same then strict_url,
2557   - same(label) then strict_url+"#"+label,
2558   - other(wn,ops) then
2559   - "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
2560   - "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
2561   - }.
2562   -
2563   -
2564   - Depending on the fact that the actioner refers to a form or not, the URL is used in two
2565   - different ways. If the actioner does not refer to a form, it is realized by a '<a>'
2566   - tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
2567   - tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
2568   - event handler. The handler calls a JavaScript function which puts the URL as the value
2569   - of the 'action' attribute of the form, and submits the form.
2570   -
2571   -type URL_or_JavaScript:
2572   - url (String),
2573   - javascript (Printable_tree script, Printable_tree handler).
2574   -
2575   - define URL_or_JavaScript
2576   - format_action
2577   - (
2578   - String the_url,
2579   - Maybe(String) mb_form_name
2580   - ) =
2581   - url(the_url).
2582   -
2583   -
2584   - It was as shown below, in order to allow submission of a form from outside the form,
2585   - but this makes problems:
2586   -
2587   -define URL_or_JavaScript
2588   - format_action
2589   - (
2590   - String the_url,
2591   - Maybe(String) mb_form_name
2592   - ) =
2593   - if mb_form_name is
2594   - {
2595   - failure then
2596   - url(the_url),
2597   - success(form_name) then
2598   - with n = new_count,
2599   - javascript(
2600   - [
2601   - "<script type=\"text/javascript\" language =\"JavaScript\">\n",
2602   - "function pfu",form_name,n,"() {\n",
2603   - " var fa = document.forms.f",form_name,";\n",
2604   - " var u = \"",the_url,"\";\n",
2605   - " fa.action = u;\n",
2606   - " fa.submit(); }",
2607   - "</script>"
2608   - ],
2609   - ["pfu",form_name,n,"();"]
2610   - )
2611   - }.
2612   -
2613   -
2614   -
2615   - Now, we format the actioner according to its aspect.
2616   -
2617   -define List(Text_Option)
2618   - get_text_options
2619   - (
2620   - List(Text_Option) l
2621   - )
2622   - =
2623   - if l is
2624   - {
2625   - [] then [],
2626   - [h . t ] then
2627   - if h is class(class) then
2628   - get_text_options(t)
2629   - else
2630   - [ h . get_text_options(t) ]
2631   -
2632   - }.
2633   -
2634   - /**
2635   - * Extract the CSS class list from the list of Text_Option
2636   - */
2637   -define List(Text_Option)
2638   - get_css_class
2639   - (
2640   - List(Text_Option) l
2641   - )
2642   - =
2643   - if l is
2644   - {
2645   - [] then [],
2646   - [h . t ] then
2647   -
2648   -
2649   - if h is class(class) then
2650   - [ h . get_css_class(t) ]
2651   - else
2652   - get_css_class(t)
2653   - }
2654   - .
2655   -
2656   -define String
2657   - format_text_options
2658   - (
2659   - List(Text_Option) l
2660   - )
2661   - =
2662   - with text_options = get_text_options(l),
2663   - css_classes = get_css_class(l),
2664   - if text_options is
2665   - {
2666   - [] then "",
2667   - [_._] then " style=\"" + format(text_options) + "\" "
2668   - }
2669   - +
2670   - if css_classes is
2671   - {
2672   - [] then "",
2673   - [_._] then format(css_classes)
2674   - }.
2675   -
2676   -define Printable_tree
2677   - format
2678   - (
2679   - List(Actioner_Local_Action) l
2680   - ) =
2681   - if l is
2682   - {
2683   - [ ] then [ ],
2684   - [h . t] then [if h is
2685   - {
2686   - close_window then [" window.close(); "]
2687   - }
2688   - . format(t)]
2689   - }.
2690   -
2691   -define String
2692   - format_coreattrs
2693   - (
2694   - List(CoreAttrs) attributs
2695   - )=
2696   - if attributs is
2697   - {
2698   - [] then "",
2699   - [h .t] then
2700   - with current = if h is
2701   - {
2702   - id(id_name) then
2703   - " id=\"" + id_name + "\"",
2704   - class(class_name) then
2705   - " class=\"" + class_name + "\"",
2706   - style(style_string) then
2707   - " style=\"" + style_string + "\"",
2708   -
2709   - title(title_string) then
2710   - " title=\"" + title_string + "\"",
2711   - },
2712   - current + format_coreattrs(t)
2713   - }.
2714   -
2715   -define String
2716   - _format
2717   - (
2718   - List(DIV_Option) opt
2719   - )=
2720   - if opt is
2721   - {
2722   - [] then "",
2723   - [h .t] then
2724   - with current = if h is
2725   - {
2726   - id(id_name) then
2727   - " id=\"" + id_name + "\"",
2728   - class(class_name) then
2729   - " class=\"" + class_name + "\"",
2730   - style(style_string) then
2731   - " style=\"" + style_string + "\"",
2732   -
2733   - title(title_string) then
2734   - " title=\"" + title_string + "\"",
2735   - lang(lang) then
2736   - " xml:lang=" + lang,
2737   - dir(reading_Way) then
2738   - if reading_Way is
2739   - {
2740   - ltr then " dir=ltr",
2741   - rtl then " dir=rtl"
2742   - }
2743   - },
2744   - current + _format(t)
2745   - }
2746   - .
2747   -
2748   -define Printable_tree
2749   - format_div_option
2750   - (
2751   - List(DIV_Option) options
2752   - )=
2753   - ["<DIV" + _format(options) + ">"] .
2754   -
2755   -
2756   -define Printable_tree
2757   - format_actioner
2758   - (
2759   - CommonInfo cinfo,
2760   - String state_name,
2761   - Actioner_Connection connection,
2762   - Actioner_Target target,
2763   - Actioner_Aspect aspect,
2764   - String action_name,
2765   - List((String,String)) extra_ops,
2766   - List(Actioner_Local_Action) local_actions,
2767   - Maybe(String) mb_form_name,
2768   - Bool is_https,
2769   - ) =
2770   - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
2771   - with url = make_actioner_url(cinfo,connection,target,
2772   - state_name,action_name,extra_ops,is_https),
2773   - with action = format_action(url,mb_form_name),
2774   - if aspect is
2775   - {
2776   -
2777   - link(opt, text) then [
2778   - if action is
2779   - {
2780   - url(u) then
2781   - ["<a href=\"",u,"\"", format_text_options(opt), ">"],
2782   -// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],
2783   - javascript(s,h) then
2784   - [s,"<a href=\"javascript: ",h,"\">"]
2785   - },
2786   - text,
2787   - "</a>"
2788   - ],
2789   -
2790   - push_button(options, text) then
2791   - [
2792   - if action is
2793   - {
2794   - url(u) then ["<a href=\"",u,"\"", format_coreattrs(options), ">",
2795   - text, "</a>"
2796   - ],
2797   - javascript(s,h) then
2798   - [s,"<INPUT TYPE =\"button\" Value=\"",text,"\"", format_coreattrs(options), " onClick=\"",h,"\">"]
2799   - }
2800   - ],
2801   -
2802   - button(url_off,url_on) then
2803   - [ if action is
2804   - {
2805   - url(u) then ["<a href=\"",u],
2806   - javascript(s,h) then [s,"<a onMouseDown=\"",h]
2807   - },
2808   - "\" style=\"text-decoration:none\">",
2809   - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
2810   - " onMouseOver=\"this.src='",url_on,"'\" ",
2811   - " onMouseOut=\"this.src='",url_off,"'\">",
2812   - "</a>"
2813   - ],
2814   -
2815   - button(url_off,url_on,w,h) then
2816   - [ if action is
2817   - {
2818   - url(u) then ["<a href=\"",u],
2819   - javascript(s,h) then [s,"<a onMouseDown=\"",h]
2820   - },
2821   - "\" style=\"text-decoration:none\">",
2822   - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
2823   - " onMouseOver=\"this.src='",url_on,"'\" ",
2824   - " onMouseOut=\"this.src='",url_off,"'\">",
2825   - "</a>"
2826   - ],
2827   -
2828   - immediate_selector(name,size,choices) then
2829   - [ if action is
2830   - {
2831   - url(u) then ["<select href=\"",u]
2832   - javascript(s,h) then [s,"<select onchange=\"",h]
2833   - },
2834   - "\" name=o",name," size=",size,">",
2835   - format_choices(choices),"</select>"
2836   - ]
2837   - }.
2838   -
2839   -
2840   -define Printable_tree
2841   - format_local_popup_button
2842   - (
2843   - CommonInfo cinfo,
2844   - Actioner_Aspect aspect,
2845   - Int32 n,
2846   - ) =
2847   - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
2848   - [ "<script type=\"text/javascript\" language=\"JavaScript\">",
2849   - " var lpust_",n," = new Array(); ",
2850   - " lpust_",n,"[0] = 0; ",
2851   - "</script>",
2852   - "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",
2853   - if aspect is
2854   - {
2855   - link(opt,text) then [text],
2856   - push_button(opt, text) then [text],
2857   - button(url_off,url_on) then
2858   - [
2859   - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
2860   - " onMouseOver=\"this.src='",url_on,"'\" ",
2861   - " onMouseOut=\"this.src='",url_off,"'\">",
2862   - ],
2863   -
2864   - button(url_off,url_on,w,h) then
2865   - [
2866   - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
2867   - " onMouseOver=\"this.src='",url_on,"'\" ",
2868   - " onMouseOut=\"this.src='",url_off,"'\">",
2869   - ],
2870   -
2871   - immediate_selector(name,size,choices) then alert,
2872   -
2873   - },
2874   - "</a>"].
2875   -
2876   -
2877   -
2878   -
2879   -
2880   - *** [5.5] Formating a private download link.
2881   -
2882   - We get the absolute path of the file to be downloaded, and the name under which it
2883   - should appear to the client. The function 'format_private_download' creates an
2884   - hypertext link for downloading the file. The secured mecanism of private download is
2885   - used. This function is called by the function which formats HTML_Any($T) elements.
2886   -
2887   -
2888   -define Printable_tree
2889   - format_private_download
2890   - (
2891   - CommonInfo cinfo,
2892   - String sn, // state name
2893   - String abs_path, // absolute file path on server
2894   - String name, // name of file as it appears in the browser
2895   - String extra, // extra extension
2896   - Maybe((String,List((String,String)))) action
2897   -
2898   - ) =
2899   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
2900   - with private_download_directory = site_directory+"/private_download",
2901   - with auth = make_authorization(site_directory,secret,abs_path),
2902   - [
2903   - "<a href=\"",name,extra,"?zauth=",auth,
2904   - if action is
2905   - {
2906   - failure then [ ]
2907   - success(a) then if a is (an,args) then
2908   - ["&amp;a=",an,format_extra_operands(args)]
2909   - },
2910   - "\">",
2911   - name,
2912   - "</a>"
2913   - ].
2914   -
2915   -
2916   -
2917   -
2918   - *** [5.6] Formating rows and cells in a table.
2919   -
2920   -define Int32
2921   - percent
2922   - (
2923   - Int32 p
2924   - ) =
2925   - if p < 0 then 0 else if p > 100 then 100 else p.
2926   -
2927   -
2928   -
2929   -
2930   - Formating cell options.
2931   -
2932   -
2933   -
2934   -define String
2935   - format
2936   - (
2937   - BackgroundOption o
2938   - ) =
2939   - if o is
2940   - {
2941   - repeat then "",
2942   - repeat_horizontal then "; background-repeat: repeat-x",
2943   - repeat_vertical then "; background-repeat: repeat-y",
2944   - no_repeat then "; background-repeat: no-repeat",
2945   - center then "; background-position: center top"
2946   - }.
2947   -
2948   -define String
2949   - format
2950   - (
2951   - List(BackgroundOption) l
2952   - ) =
2953   - if l is
2954   - {
2955   - [ ] then "",
2956   - [h . t] then format(h)+format(t)
2957   - }.
2958   -
2959   -
2960   -define String
2961   - format
2962   - (
2963   - List(Cell_Option) options
2964   - ) =
2965   - if options is
2966   - {
2967   - [ ] then "",
2968   - [h . t] then
2969   - if h is
2970   - {
2971   - left then " align=left",
2972   - h_center then " align=center",
2973   - right then " align=right",
2974   - top then " valign=top",
2975   - v_center then " valign=middle",
2976   - bottom then " valign=bottom",
2977   - base_line then " valign=baseline",
2978   - background_color(c) then " bgcolor=\""+html_format(c)+"\"",
2979   - background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
2980   - width(w) then " width=\""+w+"\"",
2981   - percentage_width(n) then " width=\""+percent(n)+"%\"",
2982   - height(h) then " height="+h,
2983   - columns(n) then " colspan="+n,
2984   - rows(n) then " rowspan="+n,
2985   - nowrap then " nowrap"
2986   - }
2987   - + format(t)
2988   - }.
2989   -
2990   -
2991   - Normalizing a list of cell options (horizontal position must be specified; the default
2992   - is 'left').
2993   -
2994   -define List(Cell_Option)
2995   - normalize
2996   - (
2997   - List(Cell_Option) l
2998   - ) =
2999   - if member(l,left) then l else
3000   - if member(l,h_center) then l else
3001   - if member(l,right) then l else
3002   - [left . l].
3003   -
3004   -
3005   - Formating cells in a row.
3006   -
3007   -define Printable_tree
3008   - format
3009   - (
3010   - List(HTML_Cell($T)) cells,
3011   - $T -> Printable_tree format_element
3012   - ) =
3013   - if cells is
3014   - {
3015   - [ ] then [ ],
3016   - [h . t] then if h is cell(options,element) then
3017   - ["<td ",format(reverse(normalize(options))),">",
3018   - format_element(element),
3019   - "</td>"
3020   - . format(t,format_element)]
3021   - }.
3022   -
3023   -
3024   - Formating the rows in a table.
3025   -
3026   -define Printable_tree
3027   - format
3028   - (
3029   - List(HTML_Row($T)) rows,
3030   - $T -> Printable_tree format_element,
3031   - ) =
3032   - if rows is
3033   - {
3034   - [ ] then [ ],
3035   - [h . t] then if h is row(options,cells) then
3036   - ["<tr ",format(reverse(options)),">",
3037   - format(cells,format_element),
3038   - "</tr>"
3039   - . format(t,format_element)]
3040   - }.
3041   -
3042   -
3043   -
3044   -define Printable_tree
3045   - format1
3046   - (
3047   - List(TextAreaOption) l
3048   - ) =
3049   - if l is
3050   - {
3051   - [] then [],
3052   - [h . t] then if h is
3053   - {
3054   - disabled then [" disabled " . format1(t)]
3055   - read_only then [" readonly " . format1(t)]
3056   - wrap_lines then [" wrap " . format1(t)]
3057   - }
3058   - }.
3059   -
3060   -define Printable_tree
3061   - format
3062   - (
3063   - List(TextAreaOption) l
3064   - ) =
3065   - if member(l,wrap_lines)
3066   - then format1(l)
3067   - else [" wrap=off " . format1(l)].
3068   -
3069   -
3070   - *** [5.7] Formating elements which may be put anywhere.
3071   -
3072   - The function below involves the parameter $T which is later instantiated as
3073   - 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
3074   - for these two types, and because formating of tables requires recursive calls of such
3075   - functions, it is necessary to provide the 'format' function to be called recursively as
3076   - an argument. Putting naively a call to 'format' will not work, because the compiler
3077   - will look for a function able to format data of type $T (which is at that time distinct
3078   - from any other type, including our two types). Such a function does not exist. Hence
3079   - the function to be called for formating elements must be passed as a functional
3080   - argument (called 'format_element' below). Actually, what we pass is a function taking
3081   - a unique argument of type $T. Other informations (like the name of the state) are
3082   - already in the function by way of full functionality.
3083   -
3084   -
3085   - Formating text options. They are formated in CSS syntax, to be used within a
3086   - 'style=...'.
3087   -
3088   -define String
3089   - format
3090   - (
3091   - List(Text_Option) l
3092   - ) =
3093   - if l is
3094   - {
3095   - [ ] then "",
3096   - [h . t] then if h is
3097   - {
3098   - size(n) then "font-size:"+n+"pt",
3099   - font(fn) then "font-family:"+fn,
3100   - color(c) then if c is rgb(r,g,b) then
3101   - "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",
3102   - italic then "font-style:italic",
3103   - oblique then "font-style:oblique",
3104   - small_capitals then "font-variant:small-caps",
3105   - bold then "font-weight:bold",
3106   - underlined then "text-decoration:underline",
3107   - left_justified then "text-align:left",
3108   - right_justified then "text-align:right",
3109   - justified then "text-align:justify",
3110   - line_through then "text-decoration:line-through",
3111   - nowrap then "white-space:nowrap",
3112   - class(class_name)then " class=\"" +class_name +"\""
3113   - } + if t is [ ] then "" else ("; "+format(t))
3114   - }.
3115   -
3116   -
3117   -
3118   - Formating table options.
3119   -
3120   -define String
3121   - format
3122   - (
3123   - List(Table_Option) l,
3124   - Bool border_seen
3125   - ) =
3126   - if l is
3127   - {
3128   - [ ] then if border_seen then "" else " border=0 cellspacing=0 cellpadding=0",
3129   - [h . t] then if h is
3130   - {
3131   - background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
3132   - background_image(url) then " background="+url+format(t,border_seen),
3133   - border(o,top,i,c) then " border="+o+" cellspacing="+top+" cellpadding="+i+
3134   - //" bordercolor="+format(c)+
3135   - format(t,true),
3136   - width(w) then " width=\""+w+"\""+format(t,border_seen),
3137   - percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
3138   - }
3139   - }.
3140   -
3141   -
3142   -
3143   -
3144   -
3145   -define Printable_tree
3146   - format_scroller
3147   - (
3148   - String sn,
3149   - Int32 width,
3150   - Int32 height,
3151   - Int32 content_width,
3152   - Int32 content_height,
3153   - Int32 idnum, // identifying the scroller
3154   - $T content,
3155   - $T -> Printable_tree format_element
3156   - ) =
3157   - [
3158   - "<script type = \"text/javascript\" language=\"JavaScript\">",
3159   - "function doscroll_",idnum,"(dx,dy) {\n",
3160   - " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
3161   - " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
3162   - idnum,"').style\"); } else\n",
3163   - " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
3164   - " var x_",idnum," = parseInt(c_",idnum,".left);\n",
3165   - " var y_",idnum," = parseInt(c_",idnum,".top);\n",
3166   - " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
3167   - " { x_",idnum," += dx; }\n",
3168   - " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
3169   - " { y_",idnum," += dy; }\n",
3170   - " c_",idnum,".left = x_",idnum,";\n",
3171   - " c_",idnum,".top = y_",idnum,";\n",
3172   - " }\n",
3173   - "</script>",
3174   - "<table>",
3175   - "<tr>",
3176   - "<td align=left valign=top",
3177   - " width=",width,
3178   - " height=",height,
3179   - ">",
3180   - "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
3181   - " clip:rect(0px ",width,"px ",height,"px 0px)\">",
3182   - "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
3183   - format_element(content),
3184   - "</div>",
3185   - "</div>",
3186   - "</td>",
3187   - "<td valign=bottom>",
3188   - "<table>",
3189   - "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onMouseDown=\"doscroll_",
3190   - idnum,"(0,20);\"></td></tr>",
3191   - "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onMouseDown=\"doscroll_",
3192   - idnum,"(0,-20);\"></td></tr>",
3193   - "</table>",
3194   - "</td>",
3195   - "</tr>",
3196   - (if content_width > width then
3197   - [
3198   - "<tr>",
3199   - "<td align=right>",
3200   - "<table>",
3201   - "<tr>",
3202   - "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onMouseDown=\"doscroll_",
3203   - idnum,"(20,0);\"></td>",
3204   - "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onMouseDown=\"doscroll_",
3205   - idnum,"(-20,0);\"></td>",
3206   - "</tr>",
3207   - "</table>",
3208   - "</td>",
3209   - "</tr>",
3210   - ] else [ ]),
3211   - "</table>"
3212   - ].
3213   -
3214   -
3215   -
3216   - define Printable_tree
3217   - popup_topbar
3218   - (
3219   - CommonInfo cinfo,
3220   - String title,
3221   - RGB color,
3222   - Int32 width,
3223   - ) =
3224   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3225   - with xxxx = web_arg_encode(sha1((title,color,width))),
3226   - path = site_directory+"/public/buttons/t"+xxxx+".jpg",
3227   - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],
3228   - if file_exists(path) then result else
3229   - with col = to_rgba(color),
3230   - bg = create_button_background(col,width,20),
3231   - very_light_color = lighten(col,70),
3232   - dark_color = darken(col,40),
3233   - title_width = printed_text_width(font,title),
3234   - draw_button_text(bg,title,title_width,very_light_color,dark_color,font);
3235   - forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));
3236   - result.
3237   -
3238   -
3239   - define Printable_tree
3240   - popup_close_button
3241   - (
3242   - CommonInfo cinfo,
3243   - RGB color,
3244   - String div_name,
3245   - String state_var_name,
3246   - ) =
3247   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3248   - with xxxx = web_arg_encode(sha1(color)),
3249   - path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",
3250   - path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",
3251   - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",
3252   - " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",
3253   - " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",
3254   - " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],
3255   - if file_exists(path_on) then result else
3256   - with col = to_rgba(color),
3257   - title = "x",
3258   - title_width = printed_text_width(font,title),
3259   - bg_on = create_button_background(lighten(col,30),20,20),
3260   - bg_off = create_button_background(col,20,20),
3261   - very_light_color = lighten(col,70),
3262   - dark_color = darken(col,40),
3263   - draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);
3264   - draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);
3265   - forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));
3266   - forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));
3267   - result.
3268   -
3269   -
3270   -
3271   -
3272   -
3273   - // The function below formats a datum of type 'HTML_Any($T)'.
3274   -
3275   -define Printable_tree
3276   - format
3277   - (
3278   - CommonInfo cinfo,
3279   - String sn, // state_name
3280   - Var(Int32) ic_v,
3281   - HTML_Any($T) element,
3282   - $T -> Printable_tree format_element, // able to format a datum of type $T
3283   - Bool is_https,
3284   - ) =
3285   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3286   - if element is
3287   - {
3288   - any_text(opts,t) then
3289   - ["<span ", format_text_options(opts), ">",t,"</span>"],
3290   - any_preformated(opts,s) then
3291   - ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],
3292   - //["<pre>",s,"</pre>"],
3293   - any_paragraph(opts,t) then
3294   - ["<p ", format_text_options(opts), ">",t,"</p>"],
3295   - any_image(url) then
3296   - ["<img alt=\"",url,"\" src=\"",url,"\">"],
3297   - any_image(url,w,h) then
3298   - ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h,">"],
3299   - any_table(opts,rows) then
3300   - ["<table ",format(reverse(opts),false),">",format(rows,format_element),"</table>"],
3301   - any_center(e) then
3302   - ["<center>",format_element(e),"</center>"],
3303   - any_mail_to(email,elem) then
3304   - ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
3305   - any_scroller(w,h,cw,ch,c) then
3306   - format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
3307   - any_fixed_size(w,h,c) then
3308   - with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
3309   - ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
3310   - "secondary document",
3311   - "</object>"],
3312   - any_fixed_size_2(w,h,fn) then
3313   - with url = fn+"?zauth="+make_authorization(site_directory,secret,
3314   - fn),
3315   - ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
3316   - "secondary document",
3317   - "</object>"],
3318   - any_actioner(c,t,a,an,eo,ja,fn) then
3319   - format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),
3320   - any_foreign_link(options,url,name) then
3321   - ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
3322   - any_private_download(url,name,extra_ext,action) then
3323   - format_private_download(cinfo,sn,url,name,extra_ext,action),
3324   - any_div(options, e) then
3325   - [format_div_option(options), format_element(e),"</DIV>"],
3326   - any_div_empty(options) then
3327   - [format_div_option(options), "</DIV>"],
3328   - any_coreattrs(attributs) then
3329   - [format_coreattrs(attributs)]
3330   - }.
3331   -
3332   -
3333   -
3334   -
3335   - // *** [5.8] Formating 'in form' elements.
3336   -
3337   -
3338   -
3339   -
3340   -
3341   -define Printable_tree
3342   - format
3343   - (
3344   - CommonInfo cinfo,
3345   - String fn, // form_name
3346   - String sn, // state_name
3347   - Var(Int32) ic_v, // idnum counter variable
3348   - HTML_In_Form element,
3349   - Bool is_https,
3350   - ) =
3351   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3352   - with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),
3353   - if element is
3354   - {
3355   - literal_pt(t) then t,
3356   - literal(t) then [t],
3357   - sequence(l) then flat(map(format_element,l))
3358   - text(opts,t) then
3359   - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
3360   - preformated(o,s) then
3361   - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
3362   - paragraph(opts,t) then
3363   - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
3364   - image(url) then
3365   - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
3366   - image(url,w,h) then
3367   - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
3368   - table(opts,rows) then
3369   - format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
3370   - center(e) then
3371   - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
3372   - mail_to(a,e) then
3373   - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
3374   - scroller(w,h,cw,ch,c) then
3375   - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
3376   - actioner(c,t,a,an,eo,ja) then
3377   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
3378   - foreign_link(options,url,name) then
3379   - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
3380   - private_download(url,name,extra,action) then
3381   - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
3382   - text_input(label_text, label, name,i,w) then
3383   - [ "<label for=\"",label,"\">",label_text,"</label>",
3384   - "<input type=text name=o",name," id=",label," size=",w," value=\"",i,"\">"],
3385   - //["&nbsp; <input type=text name=o",n," size=",w," value=\"",i,"\">"],
3386   - password_input(label_text, label, name,w) then
3387   - [ "<label for=\"",label,"\">",label_text,"</label>",
3388   - "<input type=password name=p",name," id=",label," size=",w,">"],
3389   - //["&nbsp; <input type=password name=p",n," size=",w,">"],
3390   - text_area(opts,n,i,w,h) then
3391   - ["<textarea ",format(opts)," name=o",n," cols=",w," rows=",h,">",i,"</textarea>"],
3392   - file_upload(n,w) then
3393   - ["<input type=file size=",w," name=o",n,">"],
3394   - selector(n,s,cs) then
3395   - ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
3396   - selector(n,s,cs,sd) then
3397   - ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
3398   - selector_c(n,s,cs) then
3399   - ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
3400   - selector_c(n,s,cs,sd) then
3401   - ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
3402   -
3403   - radio_button(label_text,label,n,v,c) then
3404   - [ "<label for=\"",label,"\">",label_text,"</label>",
3405   - "<input type=radio name=o",n," id=",label," value=\"",v,"\"",(if c then " checked" else ""),">"],
3406   - check_box(label_text, label,n,c) then
3407   - [ "<label for=\"",label,"\">",label_text,"</label>",
3408   - "<input type=checkbox name=o",n," id=\"",label,"\""+(if c then " checked " else ""),">"]
3409   - div(options, e) then
3410   - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
3411   - div_empty(options) then
3412   - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
3413   - hidden(name, value) then
3414   - ["<input type=hidden name=o",name," value=\"",value,"\">"],
3415   -
3416   - }.
3417   -
3418   -
3419   -
3420   -
3421   -
3422   -
3423   - *** [5.9] Formating 'off form' elements.
3424   -
3425   - The encryption type 'multipart/form-data' is required for a form containing an upload.
3426   -
3427   -
3428   -define Bool
3429   - contains_an_upload
3430   - (
3431   - HTML_In_Form form_content
3432   - ).
3433   -
3434   -define Bool
3435   - contains_an_upload
3436   - (
3437   - HTML_Row(HTML_In_Form) row
3438   - ) =
3439   - mapor(contains_an_upload,
3440   - map(content,cells(row))).
3441   -
3442   -
3443   -define Bool
3444   - contains_an_upload
3445   - (
3446   - HTML_In_Form form_content
3447   - ) =
3448   - if form_content is
3449   - {
3450   - literal_pt(t) then false,
3451   - literal(t) then false,
3452   - sequence(l) then mapor(contains_an_upload,l)
3453   - text(o,t) then false,
3454   - preformated(o,s) then false,
3455   - paragraph(o,t) then false,
3456   - image(u) then false,
3457   - image(u,w,h) then false,
3458   - table(o,rows) then mapor(contains_an_upload,rows),
3459   - center(e) then contains_an_upload(e),
3460   - mail_to(m,e) then false, // 'e' may but should not contain an upload
3461   - scroller(w,h,cw,ch,e) then contains_an_upload(e),
3462   - actioner(c,t,a,an,eo,ja) then false,
3463   - foreign_link(o,u,n) then false,
3464   - private_download(p,n,e,a) then false,
3465   - text_input(lt,l,n,i,w) then false,
3466   - password_input(lt,l,n,w) then false,
3467   - text_area(o,n,i,w,h) then false,
3468   - file_upload(n,w) then true,
3469   - selector(n,s,c) then false,
3470   - selector(n,s,c,p) then false,
3471   - selector_c(n,s,c) then false,
3472   - selector_c(n,s,c,p) then false,
3473   - radio_button(_,_,n,v,c) then false,
3474   - check_box(_,_,n,c) then false,
3475   - div(o,c) then false,
3476   - div_empty(o) then false,
3477   - hidden(_,_) then false
3478   - }.
3479   -
3480   -define String
3481   - enctype
3482   - (
3483   - HTML_In_Form form_content
3484   - ) =
3485   - if contains_an_upload(form_content)
3486   - then " enctype=multipart/form-data"
3487   - else "".
3488   -
3489   -
3490   -
3491   -define Printable_tree
3492   - format
3493   - (
3494   - CommonInfo cinfo,
3495   - String sn, // state_name
3496   - Var(Int32) ic_v, // 'idnum' counter variable
3497   - HTML_Off_Form element,
3498   - Bool is_https,
3499   - ) =
3500   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3501   - with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),
3502   - if element is
3503   - {
3504   - literal_pt(t) then t,
3505   - literal(t) then [t],
3506   - sequence(l) then flat(map(format_element,l)),
3507   - text(opts,t) then
3508   - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
3509   - preformated(o,s) then
3510   - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
3511   - paragraph(opts,t) then
3512   - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
3513   - image(url) then
3514   - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
3515   - image(url,w,h) then
3516   - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
3517   - table(opts,rows) then
3518   - format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
3519   - center(e) then
3520   - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
3521   - mail_to(a,e) then
3522   - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
3523   - scroller(w,h,cw,ch,c) then
3524   - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
3525   - fixed_size(w,h,c) then
3526   - format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),
3527   - fixed_size_2(w,h,fn) then
3528   - format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),
3529   - actioner(c,t,a,an,eo,ja) then
3530   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),
3531   - actioner(c,t,a,an,eo,ja,fn) then
3532   - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
3533   - foreign_link(options,url,name) then
3534   - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
3535   - private_download(url,name,extra,action) then
3536   - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
3537   - label(n) then ["<a name=\"",n,"\">"],
3538   - form(fn,attributs, c) then
3539   - [
3540   - "<form name=\"f",fn,"\"",
3541   - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),
3542   - " method=POST",
3543   - enctype(c),
3544   - " action=\"http",
3545   - if is_https then "s" else "",
3546   - "://",common_name,":",http_port,"/\">",
3547   - // action is set dynamically by
3548   - // the actioner using JavaScript
3549   - format(cinfo,fn,sn,ic_v,c,is_https),
3550   - "</form>"
3551   - ]
3552   - div(options, e) then
3553   - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
3554   - div_empty(options) then
3555   - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
3556   -
3557   - }.
3558   -
3559   -
3560   -
3561   -
3562   - *** [5.10] Formating meta-tags.
3563   -
3564   -define Printable_tree
3565   - format_keywords
3566   - (
3567   - List(String) l
3568   - ) =
3569   - if l is
3570   - {
3571   - [] then [ ],
3572   - [h . t] then if t is []
3573   - then [h]
3574   - else [h , ", " . format_keywords(t)]
3575   - }.
3576   -
3577   -
3578   -define Printable_tree
3579   - format
3580   - (
3581   - CommonInfo cinfo,
3582   - String state_name,
3583   - HTML_Meta m,
3584   - Bool is_https
3585   - ) =
3586   - if m is
3587   - {
3588   - keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\">"],
3589   - refresh(co,ta,an,delay) then
3590   - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
3591   - make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\">"],
3592   - meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\">"],
3593   - http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\">"],
3594   - generic_meta(l) then ["<meta ",
3595   - flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
3596   - l)),
3597   - ">"],
3598   - literal(s) then [s]
3599   - }.
3600   -
3601   -
3602   -define Printable_tree
3603   - format
3604   - (
3605   - CommonInfo cinfo,
3606   - String state_name,
3607   - List(HTML_Meta) metas,
3608   - Bool is_https,
3609   - String charset
3610   - ) =
3611   - if metas is
3612   - {
3613   - [] then [format(cinfo,state_name,http_equiv("content-type",
3614   - "text/html; charset="+charset),is_https)],
3615   - [h . t] then [format(cinfo,state_name,h,is_https)
3616   - . format(cinfo,state_name,t,is_https,charset)]
3617   - }.
3618   -
3619   -
3620   -define Printable_tree
3621   - format
3622   - (
3623   - Body_Option o
3624   - ) =
3625   - if o is
3626   - {
3627   - background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
3628   - background_image(n) then [" background=", n],
3629   - background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]
3630   -
3631   - }.
3632   -
3633   -
3634   -
3635   -define Printable_tree
3636   - format
3637   - (
3638   - List(Body_Option) l
3639   - ) =
3640   - if l is
3641   - {
3642   - [ ] then [ ],
3643   - [h . t] then [format(h) . format(t)]
3644   - }.
3645   -
3646   -define Printable_tree
3647   - add_css_files
3648   - (
3649   - List(CSS_File) l
3650   - ) =
3651   - if l is
3652   - {
3653   - [ ] then [ ],
3654   - [h . t] then
3655   - [ ["<LINK rel=\"stylesheet\" type=\"text/css\" href=" + file_name(h) + ">\n" ]
3656   - . add_css_files(t)]
3657   - }.
3658   -
3659   -define Printable_tree
3660   - add_css_styles
3661   - (
3662   - List(CSS_Style) css_styles
3663   - ) =
3664   -
3665   - if css_styles is
3666   - {
3667   - [] then [],
3668   - [_._] then [ "<style type=\"text/css\"><!--\n",
3669   - format_css_styles(css_styles),
3670   - " --></style>"
3671   - ]
3672   - }.
3673   -
3674   -define Printable_tree
3675   - format
3676   - (
3677   - CommonInfo cinfo,
3678   - String state_name,
3679   - HTML_Page page,
3680   - Bool is_https,
3681   - String charset
3682   - ) =
3683   - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
3684   - with ic_v = var((Int32)0),
3685   - if page is
3686   - {
3687   - html_page(title,metas,css_styles, css_files, body) then
3688   - if body is body(options,element) then
3689   - [ doctype_w3c_header,
3690   - "<html>",
3691   - "<head>",
3692   - add_css_styles(css_styles),
3693   - add_css_files(css_files),
3694   - "<link rel=\"shortcut icon\" href=\"favicon.ico\">",
3695   - "<script type = \"text/javascript\" language=\"JavaScript\">",
3696   - " function show_local_popup(divname,stvname) {",
3697   - " if (document.layers) { var d = eval(document.divname); } else\n",
3698   - " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
3699   - " if (document.all) { var d = eval(document.all.divname.style)};\n",
3700   - // " alert(typeof(eval(stvname))); ",
3701   - " var s = eval(stvname); ",
3702   - " if (s[0]==0) ",
3703   - " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
3704   - " { s[0]=0; d.style.visibility = 'hidden'; }; }",
3705   - "</script>",
3706   - "<title>",title,"</title>", // put title
3707   - format(cinfo,state_name,metas,is_https,charset), // format the metas
3708   - "</head>",
3709   - "<body ", format(options), ">", // format body options
3710   - //"<center>",
3711   - format(cinfo,state_name,ic_v,element,is_https),
3712   - //"</center>",
3713   - "</body>",
3714   - "</html>"
3715   - ]
3716   - }.
3717   -
3718   -
3719   -
3720   -
3721   -
3722   -
  1 +
  2 +
  3 + *Project* Anubis
  4 +
  5 + *Title* Making interactive Web sites.
  6 +
  7 + *Copyright* Copyright (c) Alain Prouté 2004-2005.
  8 +
  9 +
  10 + *Author* Alain Prouté
  11 +
  12 + *Revised* January 2005.
  13 +
  14 +
  15 + *Overview*
  16 +
  17 + In this file we propose simple tools for making well structured interactive and secured
  18 + web sites.
  19 +
  20 +
  21 + ----------------------------------- Table of Contents ---------------------------------
  22 +
  23 + * (1) Structure of a web site.
  24 + ** (1.1) Three sorts of data.
  25 + ** (1.2) How requests are handled.
  26 + ** (1.3) What web pages are made of.
  27 + ** (1.4) Actions.
  28 + ** (1.5) States.
  29 +
  30 + * (2) Carrying on.
  31 + ** (2.1) Describing your web sites.
  32 + ** (2.2) Directories on the server's disk.
  33 + ** (2.3) Starting your web sites.
  34 +
  35 + * (3) The HTML interface.
  36 + ** (3.1) Types used by the HTML interface.
  37 + ** (3.2) ``in form'' versus ``off form''.
  38 + ** (3.3) Defining your own style.
  39 + ** (3.4) Actioners and forms.
  40 + ** (3.5) Local popup.
  41 +
  42 + ---------------------------------------------------------------------------------------
  43 +
  44 +
  45 +read tools/basis.anubis
  46 +read CXM_common.anubis
  47 +read CXM_multihost_http_server.anubis
  48 +read CXM_mime.anubis
  49 +
  50 +
  51 +
  52 + * (1) Structure of a web site.
  53 +
  54 + First of all we need to explain what a web site should be made of. Ideally, the
  55 + visitor (also called the 'client') should see the web site working as any other
  56 + interactive computer software. So, it should be clear that a 'session' (i.e. a visit
  57 + to the web site, including the consultation of several pages) is some kind of
  58 + conversation between the visitor and the web site, and that the web site should
  59 + maintain a 'current state' of this conversation. At each new request (click) from the
  60 + visitor, this state must be updated. This whole conversation is called a 'session' and
  61 + should not be confused with a single request.
  62 +
  63 +
  64 +
  65 + ** (1.1) Three sorts of data.
  66 +
  67 + All the data needed for putting a web site at work may be dispatched into three
  68 + categories:
  69 +
  70 + 1. Constant data (data that never change). These data may be hard coded into the
  71 + Anubis source files of the web site.
  72 +
  73 + 2. Permanent data (data which always exist independantly of the users connected to
  74 + the web site). These data are normally recorded into data bases.
  75 +
  76 + 3. Session data (data which depend on a particular visitor and which exist only
  77 + during the time he visits the web site). These data are stored into so-called
  78 + 'states'.
  79 +
  80 +
  81 + It is important to determine which data belongs to which category. This is part of your
  82 + design decisions.
  83 +
  84 +
  85 +
  86 + ** (1.2) How requests are handled.
  87 +
  88 + We want to separate the following two functionalities (which are used at each request
  89 + (click) during a single session):
  90 +
  91 + - computing the new state from the previous state and from the client request, and
  92 + updating the data base,
  93 +
  94 + - computing the page to be sent to the client from the new current state and from
  95 + the informations in the data base.
  96 +
  97 +
  98 + The next picture shows the structure we have in mind:
  99 +
  100 +
  101 + request +---------+ HTML page (with a hidden state name)
  102 + .-------------------| client |<--------------.
  103 + | .-----------------| | |
  104 + | | previous state +---------+ |
  105 + | | name (if any) |
  106 + | | | client side
  107 + ............................................................................
  108 + | | | server side
  109 + | | |
  110 + | | .-------------------. |
  111 + | | | previous state | |
  112 + V V V | |
  113 + +---------------+ +---------------+ +--------------+
  114 + | compute state | | server's disk | | compute page |
  115 + +---------------+ +---------------+ +--------------+
  116 + ^ | | ^ ^ ^ ^ ^
  117 + | | | | | | | |
  118 + | | `--------------------+--------------------' | |
  119 + | | new state | | |
  120 + read | `------------------------+--------------------' |
  121 + write | new state name |
  122 + update V |
  123 + +-----------+ |
  124 + | data base |--------------------------------------------'
  125 + +-----------+ read only
  126 +
  127 +
  128 + When the client begins a session, there is no previous state. In this case, a default
  129 + 'initial state' is used instead.
  130 +
  131 + The data base may be updated by 'compute state' box, but should not be update by the
  132 + 'compute page' box. The 'compute page' box should be allowed only to read the data
  133 + base.
  134 +
  135 + In this file, all the above stuff is defined, except the 'compute state' and 'compute
  136 + page' boxes. You just have to provide the function for computing a new state (compute
  137 + state) and the function for computing the page (compute page) from the new state. You
  138 + don't have to worry about state names, saving and retrieving states and the like.
  139 +
  140 +
  141 +
  142 +
  143 +
  144 + ** (1.3) What web pages are made of.
  145 +
  146 + What the client can see in his browser's window may be called a 'page'. Within a page,
  147 + we have several sorts of components:
  148 +
  149 + - 'local' components, i.e. all components which do not open a connection, like
  150 + texts, images, etc... possibly using JavaScript programmation,
  151 +
  152 + - 'actioners', which, when clicked upon, open a connection with our web site; they
  153 + may appear as links or buttons, etc...
  154 +
  155 + - 'foreign links', which when clicked upon, open a connection with another web site
  156 + (or ours eventually).
  157 +
  158 + Of course, what an actioner does is just ask our web site to perform an action. To that
  159 + end, the actioner essentially sends the name of the action to be performed. However, it
  160 + may be necessary to provide additional informations which may be seen as 'operands' of
  161 + the action. In order to attach operands to an action, HTML provides the notion of
  162 + 'form'. Indeed, a form contains essentially a set of input fields into which the client
  163 + may put values for the required operands of the action, and a submit button, which is
  164 + the actioner itself. Notice that a single form may contain several submit buttons,
  165 + which simply means that there are several distincts actions taking the same set of
  166 + operands.
  167 +
  168 + Restrictions must be put on the use of all theses gadgets. Indeed, for example,
  169 + putting a form within another form is officially meaningless in HTML, and the client's
  170 + browser may be seriously disturbed by this. In this file, we propose an interface to
  171 + the HTML language, which forbids such meaningless things, simply by imposing a strict
  172 + typing of HTML concepts.
  173 +
  174 + Each web site may be accessible through two communication channels:
  175 +
  176 + - a non secured channel (HTTP),
  177 + - a secured channel (HTTPS).
  178 +
  179 + Nevertheless, the whole thing should be considered as a single web site. For example,
  180 + you may have a secured page, obtained through HTTPS, containing public images obtained
  181 + through HTTP. An actioner in a non secured page may open a secured connection, and
  182 + conversely.
  183 +
  184 + Summarizing, a web page is made of local elements, foreign links and actioners.
  185 + Actioners receive operands from forms, and they also choose to communicate through the
  186 + non secured or through the secured channel.
  187 +
  188 +
  189 +
  190 + +-------------------+
  191 + | page |
  192 + | | +---------------+
  193 + | +--------------+ | | next page |
  194 + | | form | | | (non secured) |
  195 + | | +----------+ | | HTTP | |
  196 + | | | actioner |---------------------------->| |
  197 + | | +----------+ | | +---------------+
  198 + | | | |
  199 + | | +----------+ | | +---------------+
  200 + | | | actioner |---------------------------->| next page |
  201 + | | +----------+ | | HTTPS | (secured) |
  202 + | | | | | |
  203 + | +--------------+ | | |
  204 + | | +---------------+
  205 + | |
  206 + +-------------------+
  207 +
  208 +
  209 + Notice that actioners need no be necessarily put into forms. In that case, they work as
  210 + ordinary links, but they still may receive operands as we shall see.
  211 +
  212 +
  213 +
  214 +
  215 + ** (1.4) Actions.
  216 +
  217 + The client opens a new connection with our web site whenever he clicks on an
  218 + actioner. The result is that a request is sent, essentially made of a list of 'web
  219 + arguments'. Each web argument is a pair (name,value). One of these web arguments, the
  220 + 'action' web argument (whose name is "a"), determines the action to be performed. The
  221 + other web arguments (not including "s", used to identify the state) are the operands
  222 + for this action.
  223 +
  224 + Hence, the 'compute state' box in the picture above, splits naturally into as many
  225 + sub-boxes as there are actions. For this reason, we define the following type for
  226 + representing actions (where '$State' is the type representing session informations):
  227 +
  228 +public type Web_Action($SessionTicket, $State):
  229 + http_action (String name, // name of action
  230 + (Maybe($State)) -> Bool allow, // true if action allowed
  231 + (HTTP_Info http_info,
  232 + List(Web_arg) web_args, // actually only 'operands' web arguments
  233 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  234 + https_action (String name, // name of action
  235 + (Maybe($State)) -> Bool allow, // true if action allowed
  236 + (HTTP_Info http_info,
  237 + List(Web_arg) web_args, // actually only 'operands' web arguments
  238 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  239 + http_https_action (String name,
  240 + (Maybe($State)) -> Bool allow, // true if action allowed
  241 + (HTTP_Info http_info,
  242 + List(Web_arg) web_args,
  243 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).
  244 +
  245 + 'http_action's are executed only under HTTP, and 'https_action's are executed only
  246 + under HTTPS. 'http_https_action's may be executed under both types of connections.
  247 +
  248 + Each action has a name, which is used to identify the action. Each action also has a
  249 + function 'allow' whose job is to verify that the action is allowed in the current
  250 + state, and a function 'do_it' for performing the action. The function 'do_it' receives
  251 + a lot of informations:
  252 +
  253 + - 'HTTP informations':
  254 + - the IP address of the client,
  255 + - the URI requested by the client (after redirection),
  256 + - the list of HTTP headers generated by the client's browser,
  257 + - the list of web arguments sent by the client (except "s" and "a"),
  258 + - the previous state (or the 'initial' or 'ticket expired' state if no previous
  259 + state can be found).
  260 +
  261 + In most cases, HTTP informations are not used. This is the reason why they are gathered
  262 + for simplicity into a unique datum of type 'HTTP_Info'.
  263 +
  264 +// For your convenience, we introduce the following simpler variants:
  265 +//
  266 +//public define Web_Action($State)
  267 +// http_action
  268 +// (
  269 +// String name,
  270 +// $State -> Bool allow,
  271 +// (List(Web_arg),$State) -> $State do_it
  272 +// ) =
  273 +// http_action(name,
  274 +// (Maybe($State) ms) |-> if ms is
  275 +// {
  276 +// failure then true,
  277 +// success(s) then allow(s)
  278 +// },
  279 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  280 +// {
  281 +// failure then (failure, []),
  282 +// success(s2) then (success(do_it(l,s2)), [])
  283 +// }).
  284 +//
  285 +//public define Web_Action($State)
  286 +// https_action
  287 +// (
  288 +// String name,
  289 +// $State -> Bool allow,
  290 +// (List(Web_arg),$State) -> $State do_it
  291 +// ) =
  292 +// https_action(name,
  293 +// (Maybe($State) ms) |-> if ms is
  294 +// {
  295 +// failure then true,
  296 +// success(s) then allow(s)
  297 +// },
  298 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  299 +// {
  300 +// failure then (failure, []),
  301 +// success(s2) then (success(do_it(l,s2)), [])
  302 +// }).
  303 +//
  304 +//public define Web_Action($State)
  305 +// http_https_action
  306 +// (
  307 +// String name,
  308 +// $State -> Bool allow,
  309 +// (List(Web_arg),$State) -> $State do_it
  310 +// ) =
  311 +// http_https_action(name,
  312 +// (Maybe($State) ms) |-> if ms is
  313 +// {
  314 +// failure then true,
  315 +// success(s) then allow(s)
  316 +// },
  317 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  318 +// {
  319 +// failure then (failure, []),
  320 +// success(s2) then (success(do_it(l,s2)), [])
  321 +// }).
  322 +
  323 +
  324 +
  325 + When you define your web site, you must provide the list of all the actions of the
  326 + site. When a new state has been computed, a graphical representation of this state
  327 + must be sent to the client. To that end, you must provide a function (named below
  328 + 'compute_page') of type:
  329 +
  330 + $State -> HTML_Page
  331 +
  332 + where the type 'HTML_Page' (defined below in this file) abstractly represents HTML
  333 + pages.
  334 +
  335 +public type HTML_Page:...
  336 +
  337 + It should be clear that states and pages are deeply linked together. Indeed, we really
  338 + understand the page shown to the client as a representation of the current state of the
  339 + conversation between the client and the web site, but also containing informations
  340 + taken from the data bases.
  341 +
  342 +
  343 +
  344 +
  345 +
  346 + ** (1.5) States.
  347 +
  348 + Now, we explain how you can define the type (say 'State') to be used as an instance of
  349 + the type parameter '$State'. The following is just a suggestion.
  350 +
  351 + Each state determines a page (since 'compute_page' computes a page from a
  352 + state). However, some components of the state may be independant of the page. It may be
  353 + the case for example for the indication of the natural language used by the
  354 + client. Hence, a state should be made of (at least) two parts:
  355 +
  356 + - informations which are the same for all pages,
  357 + - informations which are particular to each page.
  358 +
  359 + For example, you could define:
  360 +
  361 + type Page: // one alternative per page, with particular informations
  362 + login(...), // in the components
  363 + main_page(...),
  364 + ...etc...
  365 +
  366 + Now, the type 'State' could be defined as follows:
  367 +
  368 + type State:
  369 + state(Language, // informations valid for all pages
  370 + ...,
  371 + Page). // informations particular to a page
  372 +
  373 + However, if you are making a secured web site within which clients should be identified
  374 + (by id and password), it may be a good idea to have two sorts of states, one for non
  375 + identified clients and one for identified clients. In this case, define the type
  376 + 'State' as follows (this is just a suggestion):
  377 +
  378 + type State:
  379 + non_identified(Language),
  380 + identified(String id,
  381 + Language,
  382 + Page).
  383 +
  384 + When a request arrives, check if the previous state is 'identified(...)' or
  385 + 'non_identified(...)', and don't provide access to certain pages to non identified
  386 + clients. This is required for security.
  387 +
  388 + Some more words on security. If your site needs to identify clients, define the
  389 + initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
  390 + password of the client. If the id and password are correct, then change the state of
  391 + the client to 'identified(...)'. No other action should be able to do that. Now, be
  392 + confident that clients cannot forge states. The only information they have is the name
  393 + of a state, not the state itself which is never sent over the network, but only stored
  394 + on the server's disk. The name of the state is constructed using strong cryptographical
  395 + methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
  396 + state names cannot be seen by a third party. So, if the system retrieves a previous
  397 + state of the form 'identified(...)', you can be confident that your client is well
  398 + identified, and you can send him confidential informations.
  399 +
  400 + States have a limited life time. It may happen that a client clicks on a button at a
  401 + time its state is out of date. In this case, this system considers that the new state
  402 + is a special state named 'ticket expired'. You must provide a function producing this
  403 + state when you describe your web site. The page corresponding to this state must just
  404 + inform the client that he/she waited a too long time before clicking on a button, and
  405 + has to restart (a new conversation) from the begining.
  406 +
  407 +
  408 +
  409 + * (2) Carrying on.
  410 +
  411 + ** (2.1) Describing your web sites.
  412 +
  413 + Before you may start your web site, you must describe it, i.e. produce a datum of the
  414 + opaque type 'Web_Site'.
  415 +
  416 +public type Web_Site:...
  417 +
  418 + Producing such a datum may be performed by:
  419 +
  420 +public define Web_Site
  421 + make_web_site_description
  422 + (
  423 + List(String) common_names, // for example: ["www.our-business.com",
  424 + // "192.168.0.1"]
  425 + // the second one is just for testing
  426 + String site_directory, // where 'public' and other directories are
  427 + // located (should NOT end with '/')
  428 + One -> One init,
  429 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  430 + ($State expired,
  431 + HTTP_Info,
  432 + List(Web_arg),
  433 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  434 + (HTTP_Info,
  435 + List(Web_arg),
  436 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  437 + List(Web_Action($SessionTicket, $State)) actions,
  438 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  439 + Int32 timeout, // seconds (todo: minutes)
  440 + List(Redirection) redirections,
  441 + String charset,
  442 + List(String) journal_extensions,
  443 + List(String) journal_headers,
  444 + String authorization_secret,
  445 + List(MIME) known_mime_types,
  446 + (String action_name,
  447 + List(Web_arg) args)-> One before_send_file
  448 + ).
  449 +
  450 +
  451 + Explanations:
  452 +
  453 + 'common_names' is the list of names of the site (the name the browser must send as the
  454 + value of the 'Host' HTTP header in order to access the site must be in that list). Such
  455 + a name generally looks like this:
  456 +
  457 + www.somewhere.com
  458 +
  459 + If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
  460 + the site must be exactly the same as the name on the certificate (which is precisely
  461 + called the 'common name' in the X.509 jargon). If the two names do not match, the site
  462 + will still work, but the transaction will not be transparent to the client. His browser
  463 + will complain that the name of the certificate does not match the name of the site, and
  464 + he will have to accept the certificate manually.
  465 +
  466 + 'site_directory' is the absolute path to the directory where the files needed by the
  467 + site are located. Usually this directory looks like:
  468 +
  469 + my_anubis/web_sites/www.somewhere.com
  470 +
  471 + However, this information is not computed from 'common_name', so that you can change
  472 + the common name (for example temporarily, for networking reasons) without loosing
  473 + access to the files.
  474 +
  475 + 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
  476 + whose graphical representation is a page explaining to the user that its 'ticket' (or
  477 + 'session information') has expired, and that he/she must close all popup windows and
  478 + start a new session. The arguments of the function contain the previous (expired)
  479 + state and all current informations concerning the user. This arguments may be useful
  480 + for example for producing the expiration message in the language chosen by the user.
  481 + You can also (and this may be much smarter) send a 'ticket prolongation page'
  482 + (including a new login for example), and resume the same conversation, since you have
  483 + all the pertinent informations at hand. In the case the ticket is definitely lost, the
  484 + second fonction 'ticket_lost_state' is used.
  485 +
  486 + Notice that despite the fact that the parameter $State is involved in the arguments of
  487 + the above function, the type 'Web_Site' does not depend on this parameter. This allows
  488 + to produce lists of web site descriptions, where each description may be constructed
  489 + with a different instance of $State. This is required because distinct sites must have
  490 + distinct types of session informations. This is made possible by the fact that the
  491 + type is obscure, and the constructor replaced by a function which assembles
  492 + 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
  493 + entity not depending on $State. You should have a look to the private part of this file
  494 + if you want more precisions about this programming technique.
  495 +
  496 + 'charset' is a string which will determine the character encoding to be used by the
  497 + browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
  498 + etc...
  499 +
  500 + 'before_send_file' is a function which is executed just before the HTTP server sends a
  501 + file. It gets an action name and the web arguments received with the request for that
  502 + file. Notice that this action name and these web arguments may be put into a
  503 + 'private_download' element, and will come back to the server at the time of the
  504 + download.
  505 +
  506 +
  507 +
  508 + ** (2.2) Directories on the server's disk.
  509 +
  510 + The description of you site contains the name of the directory within which the
  511 + required files are located. This may be for example:
  512 +
  513 + my_anubis/web_sites/www.our-business.com/
  514 +
  515 + This is called the 'site directory' (for the given site). Within the site directory,
  516 + the following directories are created by this program:
  517 +
  518 + states
  519 + public
  520 + journal
  521 + private_download
  522 + upload_temporary
  523 +
  524 + The directory 'states' is used for storing states (session informations). Out of date
  525 + states are automatically removed after some time.
  526 +
  527 + The tree rooted at 'public' contains files that the server is allowed to send to the
  528 + clients. For security reasons, the server never sends a file which is not within the
  529 + tree whose root is this 'public' directory (except for the 'private download' mecanism;
  530 + see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
  531 + must have been recognized before the file may be sent.
  532 +
  533 + The directory 'journal' contains the jounal files. The roles of the remaining
  534 + directories 'private_download' and 'upload_temporary' is explained in
  535 + 'multihost_http_server.anubis', where you will also find further informations on
  536 + 'public' and 'journal'.
  537 +
  538 +
  539 +
  540 +
  541 + ** (2.3) Web servers parameters.
  542 +
  543 + The web servers have several parameters useful for administration. They are described
  544 + as follows:
  545 +
  546 + public type WebServersParameters:
  547 + wsparms(Var(Bool) shutdown_required,
  548 +
  549 +
  550 +
  551 +
  552 + ** (2.4) Starting your web sites.
  553 +
  554 + When you have described all your web sites (you may want to have several web sites, and
  555 + they are distinguished by their 'common name'), you may start them all together using
  556 + 'start_web_sites' below. This function returns a result of the following type:
  557 +
  558 +public type Start_Web_Sites_Result:
  559 + cannot_bind_to_port(Int32),
  560 + cannot_bind_to_port(Int32,Int32),
  561 + ok(Server http_server,
  562 + Server https_server).
  563 +
  564 + Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
  565 + ports (or to both). The main reason is that another server is already listening on that
  566 + port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
  567 + the 's' bit has not been set for 'user' and 'group' (there is not such problem under
  568 + Windows). Also notice that the Linux kernel may need a rather long time (up to several
  569 + minutes) before liberating a listening port. Now, if the system can bind to the two
  570 + ports, the pair of the two servers is returned. Two tools are useful for manipulating
  571 + servers:
  572 +
  573 + shutdown of type Server -> One
  574 + is_down of type Server -> Bool
  575 +
  576 + They are defined in 'predefined.anubis' (together with the type 'Server').
  577 +
  578 +
  579 +public define Start_Web_Sites_Result
  580 + start_web_sites
  581 + (
  582 + Int32 ip_address, // the IP address shared by the web sites
  583 + Int32 http_port, // usually: 80
  584 + Int32 https_port, // usually: 443
  585 + String ssl_certificate_common_name,
  586 + List(Web_Site) web_sites, // web sites to be started
  587 + Var(Bool) shutdown_required
  588 + ).
  589 +
  590 + 'ip_address' is the IP address on which the two servers listen. If you put 0, the
  591 + servers listen on all the IP addresses of the machine. This may be useful if the
  592 + machine has several network interfaces.
  593 +
  594 + 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
  595 + loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
  596 + certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
  597 + and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
  598 +
  599 + Notice that the number of servers is always 2, regardless of the number of web sites
  600 + you are starting.
  601 +
  602 + The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
  603 + servers from within the web site (typically the administration part). The servers will
  604 + shutdown as soon as this variable contains 'true'. So you must provide a variable
  605 + containing 'false' otherwise your servers will not run. You may also use the primitive
  606 + 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
  607 +
  608 +
  609 +
  610 +
  611 +
  612 +
  613 + * (3) The HTML interface.
  614 +
  615 + We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
  616 + of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
  617 + reactive and attractive on the client side.
  618 +
  619 +
  620 + ** (3.1) Types used by the HTML interface.
  621 +
  622 + For easy reference, we gather below the definitions of all the types used by the HTML
  623 + interface, and we comment them immediately.
  624 +
  625 +
  626 +public type HTML_Size:
  627 + absolute(Int32), // in pixels
  628 + percentage(Int32).
  629 +
  630 +
  631 +public type Text_Option:
  632 + size(Int32), // size of character font to use
  633 + font(String), // name of character font to use (such as "helvetica",...)
  634 + color(RGB), // color to be used for characters
  635 + italic,
  636 + oblique,
  637 + small_capitals,
  638 + bold,
  639 + underlined,
  640 + left_justified,
  641 + right_justified,
  642 + justified, // justified on both sides
  643 + line_through,
  644 + nowrap,
  645 + class(String). //CSS class
  646 +
  647 + A list of 'Text_Option' must be given with each text you want to put in your page.
  648 +
  649 + This indicate the way of reading text.
  650 +public type Reading_Way:
  651 + ltr, //the text is readable from "Left To Right" like english
  652 + rtl. //the text is readable from "Right To Left" like arabic
  653 +
  654 +
  655 +
  656 +public type CoreAttrs:
  657 + id (String),
  658 + class (String),
  659 + style (String),
  660 + title (String).
  661 +
  662 +public type I18n:
  663 + lang (String),
  664 + dir (Reading_Way).
  665 +
  666 +public type DIV_Option:
  667 + id (String),
  668 + class (String),
  669 + style (String),
  670 + title (String),
  671 + lang (String),
  672 + dir (Reading_Way).
  673 +
  674 +
  675 + A list of 'DIV_Option' must be given with each DIV you want to put in your page.
  676 +
  677 +
  678 +public type Table_Option:
  679 + background_color(RGB), // applied to all cells in the table
  680 + background_image(String url),
  681 + border(Int32 width_of_outer_edge, // if not present, all values are 0
  682 + Int32 width_of_top_of_relief,
  683 + Int32 width_of_inner_edge,
  684 + RGB border_color),
  685 + width(Int32), // sets a minimal width for the table
  686 + percentage_width(Int32).
  687 +
  688 +
  689 +public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
  690 +
  691 +
  692 + A list of 'Table_Option' must be given with each table.
  693 +
  694 +
  695 +public type BackgroundOption:
  696 + repeat, // repeat the background in both directions
  697 + repeat_horizontal, // repeat the background only horizontally
  698 + repeat_vertical, // repeat the background only verticall
  699 + no_repeat, // don't repeat the background
  700 + center.
  701 +
  702 +
  703 +public type Cell_Option:
  704 + left, // put the content of the cell on the left
  705 + h_center, // center the content of the cell horizontally
  706 + right, // put the content of the cell on the right
  707 + top, // put the content of the cell upwards
  708 + v_center, // center the content of tye cell vertically,
  709 + bottom, // put the content of the cell downwards
  710 + base_line, // align the content vertically according to base lines
  711 + background_color(RGB),
  712 + background_image(String url, BackgroundOption),
  713 + width(Int32), // sets a minimal width for the cell
  714 + percentage_width(Int32),
  715 + height(Int32), // sets a minimal height for the cell
  716 + columns(Int32), // lets the cell span over several columns
  717 + rows(Int32), // lets the cell span over several rows
  718 + nowrap. // do not allow text wrapping within the cell
  719 +
  720 + A list of 'Cell_Option' must be given with each cell and each row in a table. Options
  721 + given with a row apply to all the cells in the row, but are superseded by options given
  722 + with cells, which apply only to the cell they are given with.
  723 +
  724 +
  725 +public type HTML_Cell($T):
  726 + cell(List(Cell_Option) options, $T content).
  727 +
  728 + The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
  729 + depending on where you put your table (within a form or not within a form). For your
  730 + convenience, we define the following particular case:
  731 +
  732 +public define HTML_Cell($T)
  733 + cell
  734 + (
  735 + $T content
  736 + ) =
  737 + cell([],content).
  738 +
  739 +
  740 +
  741 +public type HTML_Row($T):
  742 + row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
  743 +
  744 + Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
  745 +
  746 +public define HTML_Row($T)
  747 + row
  748 + (
  749 + List(HTML_Cell($T)) cells
  750 + ) =
  751 + row([],cells).
  752 +
  753 +public define HTML_Row($T)
  754 + row
  755 + (
  756 + HTML_Cell($T) cell
  757 + ) =
  758 + row([],[cell]).
  759 +
  760 +public type Actioner_Connection:
  761 + same, // use same type of connection as current page
  762 + http, // use non secured connection
  763 + https. // use secured connection
  764 +
  765 +public type Other_Window_Option:
  766 + resizable, // the new window may be resized by the client
  767 + scrollbars, // the new window has scrollbars
  768 + width(Int32), // the new window has the specified width
  769 + height(Int32). // the new window has the specified height
  770 +
  771 +public type Actioner_Target:
  772 + same,
  773 + same (String label),
  774 + other(String window_name, List(Other_Window_Option)).
  775 +
  776 +public type Actioner_Aspect:
  777 + link (List(Text_Option),String text), // hypertext link
  778 + push_button (List(CoreAttrs),String text),
  779 + button (String url_off, String url_on), // rollover button
  780 + button (String url_off, String url_on, Int32 w, Int32 h), // idem with size
  781 + immediate_selector (String name, Int32 size, List(String) choices).
  782 +
  783 +
  784 +public type Actioner_Local_Action:
  785 + close_window.
  786 +
  787 +
  788 +public define Actioner_Aspect
  789 + link
  790 + (
  791 + String text
  792 + ) =
  793 + link([],text).
  794 +
  795 +
  796 +public define Actioner_Aspect
  797 + link
  798 + (
  799 + List(Text_Option) options,
  800 + Int32 i
  801 + ) =
  802 + link(options,integer_to_string(i)).
  803 +
  804 +public define Actioner_Aspect
  805 + link
  806 + (
  807 + Int32 i
  808 + ) =
  809 + link([],i).
  810 +
  811 +public define Actioner_Aspect
  812 + button
  813 + (
  814 + String url_img
  815 + ) =
  816 + button(url_img,url_img).
  817 +
  818 +
  819 +
  820 + Actioners are explained in details below.
  821 +
  822 +
  823 +public type TextAreaOption:
  824 + disabled,
  825 + read_only,
  826 + wrap_lines.
  827 +
  828 +public type HTML_In_Form:
  829 + literal_pt (Printable_tree),
  830 + literal (String),
  831 + sequence (List(HTML_In_Form) items),
  832 + text (List(Text_Option), String the_text),
  833 + preformated (List(Text_Option), String),
  834 + paragraph (List(Text_Option), String the_text),
  835 + image (String url),
  836 + image (String url, Int32 width, Int32 height),
  837 + table (List(Table_Option), List(HTML_Row(HTML_In_Form))),
  838 + center (HTML_In_Form),
  839 + mail_to (String email, HTML_In_Form element),
  840 + scroller (Int32 width, Int32 height,
  841 + Int32 content_width, Int32 content_height,
  842 + HTML_In_Form content),
  843 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  844 + String action_name, List((String,String)) extra_ops,
  845 + List(Actioner_Local_Action)),
  846 + foreign_link (List(Text_Option), String url, String name),
  847 + private_download (String abs_path, String name, String extra_ext,
  848 + Maybe((String,List((String,String)))) action),
  849 + text_input (String label_text, String label, String name, String init, Int32 width),
  850 + password_input (String label_text, String label, String name, Int32 width),
  851 + text_area (List(TextAreaOption), String name, String init, Int32 width, Int32 height),
  852 + file_upload (String name, Int32 width),
  853 + selector (String name, Int32 size, List(String) choices),
  854 + selector (String name, Int32 size, List(String) choices, String selected),
  855 + // List((String,String)) = List((code,name)) where :
  856 + // name appears in selector
  857 + // code is the web-arg value
  858 + selector_c (String name, Int32 size, List((String,String)) choices),
  859 + selector_c (String name, Int32 size, List((String,String)) choices, String selected),
  860 + radio_button (String label_text, String label, String name, String value, Bool checked),
  861 + check_box (String label_text, String label, String name, Bool checked),
  862 + div (List(DIV_Option), HTML_In_Form content),
  863 + div_empty (List(DIV_Option)),
  864 + hidden (String name, String value).
  865 +
  866 +
  867 + 'HTML_In_Form' defines all the elements you may put within a form. We define a
  868 + convenience function:
  869 +
  870 +public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
  871 +
  872 +public define HTML_In_Form
  873 + foreign_link
  874 + (
  875 + Int32 tsize,
  876 + String url,
  877 + String name
  878 + ) =
  879 + foreign_link([size(tsize)],url,name).
  880 +
  881 +public define HTML_In_Form
  882 + actioner
  883 + (
  884 + Actioner_Connection conn,
  885 + Actioner_Target targ,
  886 + Actioner_Aspect asp,
  887 + String action_name,
  888 + List((String,String)) extra_ops
  889 + ) =
  890 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  891 +
  892 +
  893 +
  894 +public define HTML_In_Form
  895 + text_area
  896 + (
  897 + String name,
  898 + String init,
  899 + Int32 width,
  900 + Int32 height
  901 + ) =
  902 + text_area([],name,init,width,height).
  903 +
  904 +public define HTML_In_Form
  905 + table
  906 + (
  907 + List(HTML_Row(HTML_In_Form)) rows
  908 + ) =
  909 + table([],rows).
  910 +
  911 +
  912 +public define HTML_In_Form
  913 + private_download
  914 + (
  915 + String abs_path,
  916 + String name,
  917 + String extra_ext
  918 + ) =
  919 + private_download(abs_path,name,extra_ext,failure).
  920 +
  921 +public define HTML_In_Form
  922 + private_download
  923 + (
  924 + String abs_path,
  925 + String name,
  926 + String extra_ext,
  927 + String action_name,
  928 + List((String,String)) args
  929 + ) =
  930 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  931 +
  932 +public define HTML_In_Form
  933 + text
  934 + (
  935 + String s
  936 + ) =
  937 + text([],s).
  938 +
  939 +
  940 +
  941 +
  942 +public type HTML_Off_Form:
  943 + literal_pt (Printable_tree),
  944 + literal (String),
  945 + sequence (List(HTML_Off_Form) items),
  946 + text (List(Text_Option), String the_text),
  947 + preformated (List(Text_Option), String),
  948 + paragraph (List(Text_Option), String the_text),
  949 + image (String url),
  950 + image (String url, Int32 width, Int32 height),
  951 + table (List(Table_Option), List(HTML_Row(HTML_Off_Form))),
  952 + center (HTML_Off_Form),
  953 + mail_to (String email, HTML_Off_Form element),
  954 + scroller (Int32 width, Int32 height,
  955 + Int32 content_width, Int32 content_height,
  956 + HTML_Off_Form content),
  957 + fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
  958 + fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  959 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  960 + String action_name, List((String,String)) extra_ops,
  961 + List(Actioner_Local_Action)),
  962 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  963 + String action_name, List((String,String)) extra_ops,
  964 + List(Actioner_Local_Action), String form_name),
  965 + foreign_link (List(Text_Option), String url, String name),
  966 + private_download (String abs_path, String name, String extra_ext,
  967 + Maybe((String,List((String,String)))) action),
  968 + label (String name),
  969 + form (String form_name, List(CoreAttrs), HTML_In_Form content),
  970 + div (List(DIV_Option), HTML_Off_Form content),
  971 + div_empty (List(DIV_Option)).
  972 +
  973 + 'HTML_Off_Form' defines all the elements you may put outside any form.
  974 +
  975 +
  976 +public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
  977 +public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
  978 + = fixed_size_2(width,height,name_of_HTML_file).
  979 +
  980 +
  981 +public define HTML_Off_Form
  982 + foreign_link
  983 + (
  984 + Int32 tsize,
  985 + String url,
  986 + String name
  987 + ) =
  988 + foreign_link([size(tsize)],url,name).
  989 +
  990 +
  991 +public define HTML_Off_Form
  992 + actioner
  993 + (
  994 + Actioner_Connection conn,
  995 + Actioner_Target targ,
  996 + Actioner_Aspect asp,
  997 + String action_name,
  998 + List((String,String)) extra_ops
  999 + ) =
  1000 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1001 +
  1002 +public define HTML_Off_Form
  1003 + table
  1004 + (
  1005 + List(HTML_Row(HTML_Off_Form)) rows
  1006 + ) =
  1007 + table([],rows).
  1008 +
  1009 +
  1010 +
  1011 + We add two convenience functions for 'row'. The reason why we add two functions, one
  1012 + for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
  1013 + arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
  1014 + the arguments of the function do not refer to any of the types defined here.
  1015 +
  1016 +public define HTML_Row(HTML_In_Form)
  1017 + row
  1018 + (
  1019 + HTML_In_Form content
  1020 + ) =
  1021 + row([],[cell([],content)]).
  1022 +
  1023 +public define HTML_Row(HTML_Off_Form)
  1024 + row
  1025 + (
  1026 + HTML_Off_Form content
  1027 + ) =
  1028 + row([],[cell([],content)]).
  1029 +
  1030 +
  1031 +public define HTML_Off_Form
  1032 + private_download
  1033 + (
  1034 + String abs_path,
  1035 + String name,
  1036 + String extra_ext
  1037 + ) =
  1038 + private_download(abs_path,name,extra_ext,failure).
  1039 +
  1040 +
  1041 +public define HTML_Off_Form
  1042 + private_download
  1043 + (
  1044 + String abs_path,
  1045 + String name,
  1046 + String extra_ext,
  1047 + String action_name,
  1048 + List((String,String)) args
  1049 + ) =
  1050 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1051 +
  1052 +public define HTML_Off_Form
  1053 + text
  1054 + (
  1055 + List(Text_Option) lto,
  1056 + Int32 i
  1057 + ) =
  1058 + text(lto,integer_to_string(i)).
  1059 +
  1060 +
  1061 +public define HTML_Off_Form
  1062 + text
  1063 + (
  1064 + Int32 i
  1065 + ) =
  1066 + text([],i).
  1067 +
  1068 +public define HTML_Off_Form
  1069 + text
  1070 + (
  1071 + String s
  1072 + ) =
  1073 + text([],s).
  1074 +
  1075 + - Cell a gap between two other cells :
  1076 +
  1077 +public define HTML_Cell(HTML_Off_Form)
  1078 + width_gap
  1079 + (
  1080 + Int32 w
  1081 + ) =
  1082 + cell([width(w)],text([],"")).
  1083 +
  1084 +public define HTML_Cell(HTML_In_Form)
  1085 + width_gap
  1086 + (
  1087 + Int32 w
  1088 + ) =
  1089 + cell([width(w)],text([],"")).
  1090 +
  1091 +
  1092 + - Row a gap between two other rows :
  1093 +
  1094 +public define HTML_Row(HTML_Off_Form)
  1095 + height_gap
  1096 + (
  1097 + Int32 h
  1098 + ) =
  1099 + row([],[cell([height(h)],text([],""))]).
  1100 +
  1101 +public define HTML_Row(HTML_In_Form)
  1102 + height_gap
  1103 + (
  1104 + Int32 h
  1105 + ) =
  1106 + row([],[cell([height(h)],text([],""))]).
  1107 +
  1108 +
  1109 + Notice that the two types have alternatives in common (same name, same arguments types,
  1110 + up to the value of the parameter $T), which correspond to elements which may be put
  1111 + anywhere in the page.
  1112 +
  1113 +
  1114 +
  1115 +public type CSS_Style:
  1116 + text_options(List(Text_Option)).
  1117 +
  1118 +public type CSS_File:
  1119 + css_file(String file_name).
  1120 +
  1121 +public type JS_File:
  1122 + js_file(String file_name).
  1123 +
  1124 +define String
  1125 + format
  1126 + (
  1127 + List(Text_Option) l
  1128 + ).
  1129 +
  1130 +
  1131 +
  1132 +
  1133 +define Printable_tree
  1134 + format_css_styles
  1135 + (
  1136 + List(CSS_Style) l
  1137 + ) =
  1138 + if l is
  1139 + {
  1140 + [ ] then [ ],
  1141 + [h . t] then
  1142 + [ if h is
  1143 + {
  1144 + text_options(tos) then
  1145 + [" body, span, p { ", format(tos), " }\n" ]
  1146 + }
  1147 + . format_css_styles(t)]
  1148 + }.
  1149 +
  1150 +
  1151 +
  1152 +public type HTML_Meta:
  1153 + keywords (List(String)),
  1154 + refresh (Actioner_Connection connection,
  1155 + Actioner_Target target,
  1156 + String action_name,
  1157 + Int32 delay), // in seconds
  1158 + meta (String name, String content),
  1159 + http_equiv (String name, String content),
  1160 + generic_meta (List((String,String))),
  1161 + literal (String).
  1162 +
  1163 + Meta tags are put in the 'head' of the HTML page.
  1164 +
  1165 +
  1166 +public type Body_Option:
  1167 + background_color (RGB),
  1168 + background_image (String url),
  1169 + background_image (String url, List(BackgroundOption)).
  1170 +
  1171 +public type HTML_Body:
  1172 + body(List(Body_Option) options, HTML_Off_Form content).
  1173 +
  1174 +
  1175 +public type HTML_Page:
  1176 + html_page(String title,
  1177 + List(HTML_Meta) meta_tags,
  1178 + List(CSS_Style) styles,
  1179 + List(CSS_File) css_files,
  1180 + List(JS_File) js_files,
  1181 + HTML_Body body).
  1182 +
  1183 +public define HTML_Page
  1184 + html_page
  1185 + (
  1186 + String title,
  1187 + List(HTML_Meta) metas,
  1188 + HTML_Body body
  1189 + ) =
  1190 + html_page(title,metas,[],[],[],body).
  1191 +
  1192 +public define HTML_Page
  1193 + html_page
  1194 + (
  1195 + String title,
  1196 + List(HTML_Meta) metas,
  1197 + List(CSS_Style) styles,
  1198 + HTML_Body body
  1199 + ) =
  1200 + html_page(title, metas, styles, [], [], body).
  1201 +
  1202 + 'HTML_Page' represents the final product of the construction of a web page.
  1203 +
  1204 +
  1205 +
  1206 +
  1207 + *** (3.2) ``in form'' versus ``off form''.
  1208 +
  1209 + There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
  1210 + of them may have a content, which is yet another HTML element (or several). Hence, it
  1211 + is meaningful to say that an element is 'within' another one. Now, putting any element
  1212 + within any other one may be meaningless. For example, an input element must be put
  1213 + within a form (otherwise, it is useless), and a from within another form has no precise
  1214 + meaning (and is forbidden by the HTML specification).
  1215 +
  1216 + Actually, the main criterium is ``within a form or not within a form''. So, HTML
  1217 + elements in a given page are separated into two categories: those who are within a
  1218 + form, and the others. Nevertheless, there are elements which may belong to both
  1219 + categories, like images and texts. We want to make use of the strong typing mecanism of
  1220 + Anubis in order to forbid non meaningful placement of elements.
  1221 +
  1222 + The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
  1223 + 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
  1224 + and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
  1225 + but the two types are not cross recursive. This is the reason why it is impossible to
  1226 + put a form within a form. In order to construct a web page, you essentially have to
  1227 + produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
  1228 +
  1229 + In practice, you don't have to worry so much about these two types, because elements
  1230 + which may be put anywhere are constructed for both types by functions with the same
  1231 + name and the same arguments. Hence, for both types, you just write the same thing. You
  1232 + are warned by the compiler only when you try to put an element at a place it is not
  1233 + allowed.
  1234 +
  1235 +
  1236 +
  1237 + *** (3.3) Defining your own style.
  1238 +
  1239 + We provide generic tools for constructing HTML elements. However, your web site needs
  1240 + to have a ``style''.
  1241 +
  1242 + To that end, you need to write down a set of ``styling functions'', using the tools
  1243 + defined here. These styling functions allow the introduction of your colors and other
  1244 + visual characteristics into the constructed elements once and for all. For example, you
  1245 + may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
  1246 + some 'text_color'. You may write something like this:
  1247 +
  1248 + define RGB text_color = rgb(10,40,40).
  1249 +
  1250 + define HTML_Off_Form
  1251 + text
  1252 + (
  1253 + String the_text
  1254 + ) =
  1255 + text([font("helvetica"),size(14),color(text_color)],
  1256 + the_text).
  1257 +
  1258 + (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
  1259 + page, you just write:
  1260 +
  1261 + text("... some text ...")
  1262 +
  1263 + and you don't have to provide the font, size and color for each text. If you want to
  1264 + have several styles of text presentation, you just write several sets of such
  1265 + convenience functions. This also suggests a trick. You may want for example different
  1266 + colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
  1267 + defining two functions as above, with the same name and same argument type, but
  1268 + returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
  1269 +
  1270 + If this preliminary work is well done, you will not waste your time later when you
  1271 + concentrate on the actual informational content of your pages.
  1272 +
  1273 + This general principle should be applied to all sorts of elements. This is the best
  1274 + thing to do in order to separate the functions defining the visual style from the
  1275 + functions defining the informational content itself, so that changing the style without
  1276 + changing the content becomes easy. This is also the best way for having a clean and
  1277 + easily readable source for your web site.
  1278 +
  1279 +
  1280 +
  1281 +
  1282 +
  1283 + *** (3.4) Actioners and forms.
  1284 +
  1285 + We have gathered several notions from HTML into that of an 'actioner'. An actioner is
  1286 + an HTML element which opens a connection to our server when clicked upon. Actioners may
  1287 + have different visual aspects. They may look like hypertext links or like buttons
  1288 + (rollovers), or even like selectors (with immediate action). In any case, their
  1289 + behavior is the same: they open a connection to our server, and send a set of 'web
  1290 + arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
  1291 + the action to be performed, and the others should be considered as operands for this
  1292 + action. Actually, the precise behavior of the actioner has several variants.
  1293 +
  1294 + The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
  1295 + type 'Actioner_Connection' above.
  1296 +
  1297 + You must also choose where the answer must be rendered. This may be in the same window
  1298 + or in another window (or frame). If it is in another window, the name of that window
  1299 + must be given. If the window does not exist, the browser will create it. Optionally,
  1300 + you may give the dimensions of the new window and other characteristics. See the type
  1301 + 'Actioner_Target' above.
  1302 +
  1303 + The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
  1304 + case of a rollover button, you provide the URLs of two images (of the same size)
  1305 + representing the button:
  1306 +
  1307 + url_off: to be used when the mouse is not over the button,
  1308 + url_on: to be used when the mouse is over the button.
  1309 +
  1310 + You can also create rollover buttons without creating images. Just use the second
  1311 + alternative named 'button'. The server creates the images automatically.
  1312 +
  1313 + The purpose of forms is just to give operands to actioners. If the actioner is placed
  1314 + within a form, all the input elements which are within this form provide operands to
  1315 + the actioner (except sometimes when they are not set by the client). If it is not put
  1316 + within a form, the actioner gets no operand, except if the name of a form is explicitly
  1317 + given, in which case the actioner gets all the inputs from that form as
  1318 + operands. Furthermore, you may want to give extra operands to the actioner. This may be
  1319 + useful for separating families of actioners with the same action name. Extra operands
  1320 + 'name=value' must be given in the form of pairs '(name,value)'.
  1321 +
  1322 + Notice that the name of a form may be used by an actioner which is off the form, so as
  1323 + to get the operands provided by this form. Also notice that several actioners may refer
  1324 + to the same form, being either in the form, or referring to the form from the
  1325 + outside. These actioners simply get the same set of operands, even if they correspond
  1326 + to distinct actions.
  1327 +
  1328 + Input elements may be put only within a form.
  1329 +
  1330 +
  1331 +
  1332 + *** (3.5) Local popup.
  1333 +
  1334 + This element looks like a link or a rollover button. When this button is clicked upon,
  1335 + a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
  1336 + page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
  1337 + particular, clicking on the button does not open any connection. This is why it is
  1338 + called 'local'. The arguments have the following roles:
  1339 +
  1340 + Actioner_Aspect aspect of the button (same semantics as for actioners)
  1341 + content content of the popup window
  1342 + x, y, position of the popup window on the HTML page (not relative
  1343 + to the button but to the page itself)
  1344 + title title of the popup window
  1345 + color color of the title bar and close button in the popup window.
  1346 + A lightened version of this color is used for the background
  1347 + of the popup window.
  1348 + width width of the title bar
  1349 +
  1350 +
  1351 +
  1352 +
  1353 +
  1354 + --- That's all for the public part ! --------------------------------------------------
  1355 +
  1356 +
  1357 +
  1358 +
  1359 +
  1360 + ----------------------------------- Table of Contents ---------------------------------
  1361 +
  1362 + *** [1] States.
  1363 + *** [1.1] Saving and retrieving states.
  1364 + *** [1.2] Deleting out of date states.
  1365 +
  1366 + *** [2] Tools.
  1367 + *** [2.1] Directories.
  1368 + *** [2.2] Secondary documents.
  1369 +
  1370 + *** [3] Managing web arguments.
  1371 + *** [3.1] Prefixing web arguments names.
  1372 + *** [3.2] Separating web arguments.
  1373 + *** [3.3] Applying an action.
  1374 +
  1375 + *** [4] Web site descriptions and the 'awp handlers'.
  1376 + *** [4.1] The type 'Web_Site'.
  1377 + *** [4.2] Making a web site description.
  1378 + *** [4.3] Starting the servers.
  1379 +
  1380 + *** [5] HTML Formating.
  1381 + *** [5.1] The type 'HTML_Any($T)'.
  1382 + *** [5.2] Formating a color.
  1383 + *** [5.3] Creating buttons.
  1384 + *** [5.4] Formating an actioner.
  1385 + *** [5.5] Formating a private download link.
  1386 + *** [5.6] Formating rows and cells in a table.
  1387 + *** [5.7] Formating elements which may be put anywhere.
  1388 + *** [5.8] Formating 'in form' elements.
  1389 + *** [5.9] Formating 'off form' elements.
  1390 + *** [5.10] Formating meta-tags.
  1391 +
  1392 + ---------------------------------------------------------------------------------------
  1393 +
  1394 +
  1395 +
  1396 +
  1397 +public define String
  1398 + doctype_w3c_header
  1399 + =
  1400 + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "+
  1401 + "\"http://www.w3c.org/TR/html4/loose.dtd\">\n".
  1402 +
  1403 +
  1404 +
  1405 +
  1406 + *** [1] States.
  1407 +
  1408 + We have to define functions for saving a state, retrieving a state, deleting out of
  1409 + date states. We need one such function per web site. The types of the first two
  1410 + functions depend on the parameter $State. This is not the case of the third one. The
  1411 + fact that the instance of $State is variable from one web sites to the other implies
  1412 + rather subtle manipulations using full functionality.
  1413 +
  1414 +
  1415 + *** [1.1] Saving and retrieving states.
  1416 +
  1417 + Each state is saved into a file on the server's disk (in the directory represented by
  1418 + the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
  1419 + state is saved together with a time stamp whose value is obtained by adding the current
  1420 + time to the given timeout for states. The state receives a name obtained by hashing
  1421 + (using sha1) the content of the file itself, and then encoding the hash with
  1422 + 'web_arg_encode'. The name of the file into which the state is saved is the
  1423 + concatenation of "s" and the name of the state.
  1424 +
  1425 +read CXM_web_arg_encode.anubis
  1426 +
  1427 + The tool below constructs the function which is able to save a state on the server's
  1428 + disk.
  1429 +
  1430 +define (Maybe($State) s) -> String // the function constructed returns the name of the state
  1431 + make_save_state_function
  1432 + (
  1433 + Int32 timeout,
  1434 + String state_directory
  1435 + ) =
  1436 + (Maybe($State) mbs) |->
  1437 + if mbs is
  1438 + {
  1439 + failure then "",
  1440 + success(s) then
  1441 + with time_stamp = now+timeout,
  1442 + to_be_saved = (time_stamp,s),
  1443 + state_name = web_arg_encode(sha1(s)),
  1444 + if save(to_be_saved,state_directory+"/s"+state_name) is ok
  1445 + then state_name
  1446 + else (print("Cannot create state file in '"+state_directory+"'.\n"); "")
  1447 + }.
  1448 +
  1449 +
  1450 + When a request arrives, we need to retrieve the previous state from the server's
  1451 + disk. We receive the name of that state. If the state is out of date, the state file is
  1452 + kept 3 days, and then deleted.
  1453 +
  1454 +type PreviousState($State):
  1455 + not_found, // cannot retrieve the previous state
  1456 + out_of_date($State), // the previous state is out of date
  1457 + still_valid($State). // the previous state is still valid
  1458 +
  1459 +define (String state_name) -> PreviousState($State)
  1460 + make_retrieve_state_function
  1461 + (
  1462 + String state_directory
  1463 + ) =
  1464 + (String state_name) |->
  1465 + with file_path = state_directory+"/s"+state_name,
  1466 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1467 + then (
  1468 + if d is (time_stamp,s) then
  1469 + if time_stamp < now
  1470 + then (
  1471 + forget(remove(file_path));
  1472 + out_of_date(s)
  1473 + )
  1474 + else still_valid(s) // state has been successfully retrieved
  1475 + )
  1476 + else not_found.
  1477 +
  1478 +
  1479 +
  1480 + *** [1.2] Deleting out of date states.
  1481 +
  1482 + We also need to delete states which are out of date and which will never be deleted by
  1483 + the above method. This may be performed by a machine doing this periodically (say once
  1484 + per states life time period).
  1485 +
  1486 +define (List(String) file_names) -> One
  1487 + make_delete_out_of_date_states_function
  1488 + (
  1489 + Maybe($State) dummy,
  1490 + String state_directory
  1491 + ) =
  1492 + (List(String) file_names) |-df->
  1493 + if file_names is
  1494 + {
  1495 + [ ] then unique,
  1496 + [h . t] then
  1497 + with file_path = state_directory+"/"+h,
  1498 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1499 + then (
  1500 + if d is (time_stamp,data) then
  1501 + if time_stamp < now
  1502 + then (forget(remove(file_path)); df(t))
  1503 + else df(t)
  1504 + )
  1505 + else (forget(remove(file_path)); df(t))
  1506 + }.
  1507 +
  1508 +
  1509 + The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
  1510 +
  1511 + Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
  1512 + function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
  1513 + does not accept a parameter in the body of a function (here the parameter is required
  1514 + by the use of 'retrieve') if this parameter does not appear in the type of the
  1515 + function. This is because this would create ambiguities that no explicit typing may
  1516 + ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
  1517 + and if you compile this file, you will get a message like this one:
  1518 +
  1519 + Error in 'making_a_web_site.anubis', line 1300, column 7:
  1520 + A definition may not contain parameters which are not present
  1521 + in the declaration part (hidden parameters):
  1522 + $State
  1523 +
  1524 + The type of the function constructed by 'make_delete_out_of_date_states_function' is
  1525 + independant of the parameter $State. This is important because this allows to create
  1526 + the list of such functions for all web sites. From this list, it is possible to call
  1527 + the functions one after the other, so deleting out of date states for all web
  1528 + sites. Actually, the next function receives a list of pairs (state_directory,function),
  1529 + one for each web site.
  1530 +
  1531 +define One
  1532 + delete_out_of_date_states // for all web sites
  1533 + (
  1534 + List((String, List(String) -> One)) directories_and_functions
  1535 + ) =
  1536 + if directories_and_functions is
  1537 + {
  1538 + [ ] then unique,
  1539 + [h . t] then if h is (state_directory,function) then
  1540 + function(directory_list(state_directory,"s*"));
  1541 + delete_out_of_date_states(t)
  1542 + }.
  1543 +
  1544 +
  1545 + The above function must be called periodically in a separate virtual machine. The
  1546 + period we have choosen is (rather logically) the life time of states itself. This may
  1547 + be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
  1548 + be really infinite, because the servers may be shutdown. Hence, our loop must test
  1549 + (rather frequently; say every second) if the servers are down. If they are, the loop
  1550 + must be exited.
  1551 +
  1552 +define One
  1553 + delete_states_loop
  1554 + (
  1555 + List((String,List(String) -> One)) directories_and_functions,
  1556 + Int32 timeout,
  1557 + Int32 next_time,
  1558 + Server http_server,
  1559 + Server https_server,
  1560 + Var(Bool) shutdown_required
  1561 + ) =
  1562 + if *shutdown_required
  1563 + then (shutdown(http_server); shutdown(https_server))
  1564 + else unique;
  1565 + if (is_down(http_server) & is_down(https_server))
  1566 + then unique
  1567 + else if now > next_time
  1568 + then
  1569 + (
  1570 + delete_out_of_date_states(directories_and_functions);
  1571 + delete_states_loop(directories_and_functions,
  1572 + timeout,
  1573 + now+timeout,
  1574 + http_server,
  1575 + https_server,
  1576 + shutdown_required)
  1577 + )
  1578 + else
  1579 + (
  1580 + sleep(1000); // sleep just one second and try again
  1581 + delete_states_loop(directories_and_functions,
  1582 + timeout,
  1583 + next_time,
  1584 + http_server,
  1585 + https_server,
  1586 + shutdown_required)
  1587 + ).
  1588 +
  1589 + The above loop must be run in a separate virtual machine. This will be done just after
  1590 + the two servers are started.
  1591 +
  1592 +
  1593 +
  1594 +
  1595 +
  1596 + *** [2] Tools.
  1597 +
  1598 + *** [2.1] Directories.
  1599 +
  1600 + We need a tool for creating directories (if needed).
  1601 +
  1602 + (This tool has been moved to 'tools/basis.anubis').
  1603 +
  1604 +
  1605 +
  1606 +
  1607 + *** [2.2] Secondary documents.
  1608 +
  1609 + Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
  1610 + from the current document, but only through an URL. For this reason, we implement a
  1611 + mecanism for creating secondary documents on the fly. To that end we use the 'private
  1612 + download' mecanism.
  1613 +
  1614 + A secondary document is formated by the same functions as the main document itself. The
  1615 + next function takes an 'off form' element, creates the file containing the secondary
  1616 + document in HTML format, and returns the URL at which the document will be available.
  1617 +
  1618 +define String
  1619 + create_secondary_document
  1620 + (
  1621 + String sd, // site directory
  1622 + String as, // authorization_secret
  1623 + String sn, // state name
  1624 + $T -> Printable_tree format_element,
  1625 + $T content,
  1626 + HTML_Size width
  1627 + ) =
  1628 + with private_download_directory = sd+"/private_download",
  1629 + hash = web_arg_encode(sha1(content)),
  1630 + file_content = (Printable_tree)
  1631 + [doctype_w3c_header,
  1632 + "<html><body><table ",
  1633 + if width is
  1634 + {
  1635 + absolute(w) then ["width=\"",w-25],
  1636 + percentage(w) then ["width=\"95%\""]
  1637 + },"\"><tr><td align=right>",
  1638 + format_element(content),
  1639 + "</td></tr></table></body></html>"
  1640 + ],
  1641 + file_name = "sd"+hash+".html",
  1642 + file_path = private_download_directory+"/"+file_name,
  1643 + if write_to_file(file_path,file_content) is
  1644 + {
  1645 + cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
  1646 + write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
  1647 + ok then file_name+"?zauth="+
  1648 + make_authorization(sd,as,private_download_directory+"/"+file_name)
  1649 + }.
  1650 +
  1651 +
  1652 +
  1653 +
  1654 + *** [2.3] Generating unique ids.
  1655 +
  1656 + In order to uniquely name object for JavaScript we generate unique ids from a counter.
  1657 +
  1658 +define Int32
  1659 + new_idnum
  1660 + (
  1661 + Var(Int32) ic_v // 'idnum' counter variable
  1662 + ) =
  1663 + protect
  1664 + with result = *ic_v+1,
  1665 + ic_v <- result;
  1666 + result.
  1667 +
  1668 +
  1669 +
  1670 +
  1671 +
  1672 +
  1673 + *** [3] Managing web arguments.
  1674 +
  1675 + Web arguments are those pairs 'name=value' which are transmitted through the HTTP
  1676 + protocol. We need precise naming conventions for these web arguments.
  1677 +
  1678 +
  1679 +
  1680 + *** [3.1] Prefixing web arguments names.
  1681 +
  1682 + We want to assign different roles to web arguments, and we also want to be able to
  1683 + recognize its role directly from the name of a web argument. The name "s" is reserved
  1684 + for the web argument whose value is the name of the current state. The name "a" is
  1685 + reserved for the web argument whose value is the name of the action to be
  1686 + performed. Other web arguments receive arbitrary names, and in order to avoid clashes,
  1687 + these names are prefixed by:
  1688 +
  1689 + "p" for names of password inputs,
  1690 + "o" for other web arguments
  1691 +
  1692 + The reason why password input names have a distinct prefix is that this allows the HTTP
  1693 + server to hide the passwords on the console of the server and in the journal.
  1694 +
  1695 +
  1696 +
  1697 +
  1698 + *** [3.2] Separating web arguments.
  1699 +
  1700 + When a new request arrives, we need to separate the web arguments, that is to say:
  1701 +
  1702 + - find the value of "s", and recover the corresponding state,
  1703 + - find the value of "a", which is the name of the action to be performed,
  1704 + - get the list of all the remaining web arguments (operands of the action).
  1705 +
  1706 + We must also determine if the previous state may be recovered. If it is not the case
  1707 + (either because the previous state name is invalid, or the previous state is out of
  1708 + date), we must check if there is an action name. Indeed, the presence of an action name
  1709 + indicates that the user has clicked on one of our buttons or links. If on the contrary
  1710 + there is no action name the user has just entered our address in his browser. In this
  1711 + last case, we must send the first page of our site (maybe a 'login' page), but if there
  1712 + is an action, we must send a page just saying that the session ticket has expired. If
  1713 + the previous state is recovered and there is no action, the new state is the same as
  1714 + the previous state.
  1715 +
  1716 + The result of the separation of the web arguments is of type:
  1717 +
  1718 +type Separated_Web_Args($State):
  1719 + swa(Maybe(PreviousState($State)) previous_state,
  1720 + Maybe(String) action_name,
  1721 + List(Web_arg) operands).
  1722 +
  1723 +
  1724 +
  1725 + The next function constructs the function which separates the web arguments.
  1726 +
  1727 +define (List(Web_arg) lwa) -> Separated_Web_Args($State)
  1728 + make_separate_web_args_function
  1729 + (
  1730 + String state_directory,
  1731 + String -> PreviousState($State) retrieve_state
  1732 + ) =
  1733 + (List(Web_arg) lwa) |-swaf->
  1734 + if lwa is
  1735 + {
  1736 + [ ] then
  1737 + //
  1738 + // no web arg found => no previous state and no action
  1739 + //
  1740 + swa(failure,failure,[]),
  1741 +
  1742 + [wa_1 . wa_others] then
  1743 + //
  1744 + // at least one web arg =>
  1745 + // separate other web args, and insert the first one as needed
  1746 + //
  1747 + if (Separated_Web_Args($State))swaf(wa_others) is
  1748 + {
  1749 + swa(ps1, // possible previous state
  1750 + an1, // maybe an action name
  1751 + op1) // operands so far
  1752 + then
  1753 + if wa_1 is
  1754 + {
  1755 + web_arg(n,v) then
  1756 + with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),
  1757 + name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),
  1758 + if prefix = "s" then
  1759 + swa(success(retrieve_state(v)),an1,op1) else
  1760 + if prefix = "a" then
  1761 + swa(ps1,success(v),op1) else
  1762 + if prefix = "t" then
  1763 + swa(ps1,an1,[web_arg("target",v) . op1]) else
  1764 + if prefix = "p" then
  1765 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1766 + if prefix = "o" then
  1767 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1768 + swa(ps1,an1,op1),
  1769 +
  1770 + upload(n,v,t) then
  1771 + swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])
  1772 + }}
  1773 + }.
  1774 +
  1775 +
  1776 +
  1777 +
  1778 +
  1779 +
  1780 + *** [3.3] Applying an action.
  1781 +
  1782 + When the web arguments are separated (and their names cleaned up from prefixes), we may
  1783 + apply the action to the operands and the current state. We search for the action to be
  1784 + applied in the list of actions. If no action is found, the new state is the same as
  1785 + the previous state. Also, we deny the application of an HTTP action if the request
  1786 + arrives through the HTTPS channel and conversely.
  1787 +
  1788 +
  1789 +define (Maybe($State) previous,
  1790 + String action_name,
  1791 + HTTP_Info http_info,
  1792 + List(Web_arg) lwa,
  1793 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))
  1794 + make_apply_action_function
  1795 + (
  1796 + List(Web_Action($SessionTicket, $State)) actions
  1797 + ) =
  1798 + with f =
  1799 + (Maybe($State) previous,
  1800 + String action_name,
  1801 + HTTP_Info http_info,
  1802 + List(Web_arg) lwa,
  1803 + Bool is_https,
  1804 + List(Web_Action($SessionTicket, $State)) actions) |-f->
  1805 + if actions is
  1806 + {
  1807 + [ ] then (print("action '"+action_name+
  1808 + "' not found.\n"); (failure, previous, [])),
  1809 + [ac1 . others] then if ac1 is
  1810 + {
  1811 + http_action(an,allow,do_it) then
  1812 + if an = action_name
  1813 + then if is_https
  1814 + then (print("HTTP action '"+an+
  1815 + "' called through HTTPS (denied).\n");
  1816 + (failure, previous, []))
  1817 + else if allow(previous)
  1818 + then do_it(http_info,lwa,previous)
  1819 + else (failure, previous, [])
  1820 + else f(previous,action_name,http_info,lwa,is_https,others),
  1821 +
  1822 + https_action(an,allow,do_it) then
  1823 + if an = action_name
  1824 + then if is_https
  1825 + then if allow(previous)
  1826 + then do_it(http_info,lwa,previous)
  1827 + else (failure, previous, [])
  1828 + else (print("HTTPS action '"+an+
  1829 + "' called through HTTP (denied).\n");
  1830 + (failure, previous, []))
  1831 + else f(previous,action_name,http_info,lwa,is_https,others),
  1832 +
  1833 + http_https_action(an,allow,do_it) then
  1834 + if an = action_name
  1835 + then if allow(previous)
  1836 + then do_it(http_info,lwa,previous)
  1837 + else (failure, previous, [])
  1838 + else f(previous,action_name,http_info,lwa,is_https,others),
  1839 +
  1840 + }
  1841 + },
  1842 + (Maybe($State) previous,
  1843 + String action_name,
  1844 + HTTP_Info http_info,
  1845 + List(Web_arg) lwa,
  1846 + Bool is_https) |->
  1847 + f(previous,action_name,http_info,lwa,is_https,actions).
  1848 +
  1849 +
  1850 +
  1851 +
  1852 +
  1853 +
  1854 +
  1855 +
  1856 + *** [4] Web site descriptions and the 'awp handlers'.
  1857 +
  1858 + *** [4.1] The type 'Web_Site'.
  1859 +
  1860 + The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
  1861 + need another one, because, we have some extra informations to record for each site.
  1862 +
  1863 +public type Web_Site:
  1864 + web_site((Int32,Int32) -> Web_Site_Description description,
  1865 + List(String) -> One delete_out_of_date).
  1866 +
  1867 +
  1868 +
  1869 +
  1870 + *** [4.2] Making a web site description.
  1871 +
  1872 + Below is the function which creates a web site description. It first creates (if
  1873 + needed) the directories for the site, then constructs the tool functions for the site,
  1874 + and the site handler. Finally, it constructs the web site description.
  1875 +
  1876 + We gather common (constant) informations in the following type:
  1877 +
  1878 +type CommonInfo:
  1879 + info(String common_name,
  1880 + Int32 http_port,
  1881 + Int32 https_port,
  1882 + String site_directory,
  1883 + String authorization_secret
  1884 + ).
  1885 +
  1886 + We need a forward declaration.
  1887 +
  1888 +public define Printable_tree
  1889 + format
  1890 + (
  1891 + CommonInfo cinfo,
  1892 + String state_name,
  1893 + HTML_Page page,
  1894 + Bool is_https,
  1895 + String charset
  1896 + ).
  1897 +
  1898 +
  1899 +define Printable_tree
  1900 + format
  1901 + (
  1902 + HTML_Size s
  1903 + ) =
  1904 + if s is
  1905 + {
  1906 + absolute(x) then ["\"",x,"\""],
  1907 + percentage(x) then ["\"",x,"%\""]
  1908 + }.
  1909 +
  1910 +define Printable_tree
  1911 + top_redirection_page
  1912 + (
  1913 + String common_name
  1914 + ) =
  1915 + [ doctype_w3c_header,
  1916 + "<html><head>",
  1917 + "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\">",
  1918 + "</head><body></body></html>"
  1919 + ].
  1920 +
  1921 +public define Web_Site
  1922 + make_web_site_description
  1923 + (
  1924 + List(String) common_names, // for example: ["www.our-business.com"]
  1925 + String site_directory,
  1926 + One -> One init,
  1927 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  1928 + ($State expired,
  1929 + HTTP_Info,
  1930 + List(Web_arg),
  1931 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  1932 + (HTTP_Info,
  1933 + List(Web_arg),
  1934 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  1935 + List(Web_Action($SessionTicket, $State)) actions,
  1936 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  1937 + Int32 timeout,
  1938 + List(Redirection) redirections,
  1939 + String charset,
  1940 + List(String) journal_extensions,
  1941 + List(String) journal_headers,
  1942 + String secret,
  1943 + List(MIME) known_mime_types,
  1944 + (String action_name,
  1945 + List(Web_arg) args) -> One before_send_file
  1946 + ) =
  1947 + init(unique);
  1948 +
  1949 +
  1950 + //
  1951 + // make required directories (if needed)
  1952 + //
  1953 + with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),
  1954 + base_directory = make_directory(site_directory),
  1955 + state_directory = make_directory(site_directory+"/states"),
  1956 + forget(make_directory(site_directory+"/public"));
  1957 + //
  1958 + // construct tool functions
  1959 + //
  1960 + with save_state = make_save_state_function(timeout,state_directory),
  1961 + retrieve_state = make_retrieve_state_function(state_directory),
  1962 + separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),
  1963 + apply_action = make_apply_action_function(actions),
  1964 + //
  1965 + // construct the site handler
  1966 + //
  1967 + site_handler = (Int32 http_port, Int32 https_port) |->
  1968 + ((String host_name,
  1969 + HTTP_Info http_info,
  1970 + List(Web_arg) lwa,
  1971 + Bool is_https) |->
  1972 + ((List(HTTP_header),Printable_tree))
  1973 + if separate_web_args(lwa) is
  1974 + {
  1975 + swa(mb_previous_state,mb_action_name,operands) then
  1976 + with state_and_headers = if mb_previous_state is
  1977 + {
  1978 + failure then
  1979 + if mb_action_name is
  1980 + {
  1981 + failure then initial_state(http_info),
  1982 + success(action_name) then
  1983 + apply_action(failure,action_name,http_info,operands,is_https)
  1984 + },
  1985 + success(previous_state) then if previous_state is
  1986 + {
  1987 + not_found then
  1988 + if mb_action_name is
  1989 + {
  1990 + failure then initial_state(http_info),
  1991 + success(_) then
  1992 + ticket_lost_state(http_info,lwa,is_https)
  1993 + },
  1994 +
  1995 + out_of_date(state) then
  1996 + ticket_expired_state(state,http_info,lwa,is_https),
  1997 +
  1998 + still_valid(state) then
  1999 + if mb_action_name is
  2000 + {
  2001 + failure then (failure, success(state), []),
  2002 + success(action_name) then
  2003 + apply_action(success(state),action_name,http_info,operands,is_https)
  2004 + }
  2005 + }
  2006 + },
  2007 + if state_and_headers is (session_ticket, mb_new_state, headers) then
  2008 + with state_name = save_state(mb_new_state),
  2009 + (headers,
  2010 + format(info(host_name, http_port, https_port, site_directory, secret),
  2011 + state_name,
  2012 + compute_page(session_ticket, mb_new_state),
  2013 + is_https,
  2014 + charset))
  2015 + }),
  2016 + //
  2017 + // make the delete_out_of_date function
  2018 + //
  2019 + delete_out_of_date =
  2020 + make_delete_out_of_date_states_function((Maybe($State))failure,
  2021 + site_directory+"/states"),
  2022 + //
  2023 + // construct the web site description
  2024 + //
  2025 + web_site((Int32 http_port, Int32 https_port) |->
  2026 + web_site_description(common_names,
  2027 + site_directory,
  2028 + redirections,
  2029 + charset,
  2030 + journal_extensions,
  2031 + journal_headers,
  2032 + secret,
  2033 + known_mime_types,
  2034 + site_handler(http_port,https_port),
  2035 + (List(Web_arg) lwa) |-> if separate_web_args(lwa) is
  2036 + swa(mb_previous_state,mb_action_name,operands) then
  2037 + if mb_action_name is
  2038 + {
  2039 + failure then unique
  2040 + success(an) then before_send_file(an,operands)
  2041 + }),
  2042 + delete_out_of_date).
  2043 +
  2044 +
  2045 +
  2046 +
  2047 + *** [4.3] Starting the servers.
  2048 +
  2049 +public define Start_Web_Sites_Result
  2050 + start_web_sites
  2051 + (
  2052 + Int32 ip_address, // the IP address shared by the web sites
  2053 + Int32 http_port, // usually: 80
  2054 + Int32 https_port, // usually: 443
  2055 + String ssl_certificate_common_name,
  2056 + List(Web_Site) web_sites, // web sites to be started
  2057 + Var(Bool) shutdown_required
  2058 + ) =
  2059 + with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
  2060 + with http_server_r =
  2061 + start_http_server(ip_address,http_port,
  2062 + map(get_description,web_sites),
  2063 + load_denial_of_service_info),
  2064 + with https_server_r =
  2065 + start_https_server(ip_address,https_port,
  2066 + ssl_certificate_common_name,
  2067 + map(get_description,web_sites),
  2068 + load_denial_of_service_info),
  2069 + if http_server_r is ok(http_server)
  2070 + then
  2071 + (
  2072 + if https_server_r is ok(https_server)
  2073 + then
  2074 + (
  2075 + start_http_servers_tasks(map(get_description,web_sites),
  2076 + [http_server,https_server],
  2077 + 600); // period of 10 minutes
  2078 + delegate
  2079 + delete_states_loop(
  2080 + map((Web_Site ws) |->
  2081 + (site_directory(description(ws)(http_port,https_port))+
  2082 + "/states",delete_out_of_date(ws)),
  2083 + web_sites),
  2084 + 3600*24*3, // keep out of date states 3 days
  2085 + now,
  2086 + http_server,
  2087 + https_server,
  2088 + shutdown_required),
  2089 + ok(http_server,https_server)
  2090 + )
  2091 + else cannot_bind_to_port(https_port)
  2092 + )
  2093 + else
  2094 + (
  2095 + if https_server_r is ok(https_server)
  2096 + then cannot_bind_to_port(http_port)
  2097 + else cannot_bind_to_port(http_port,https_port)
  2098 + ).
  2099 +
  2100 +
  2101 +public define One
  2102 + start_web_sites
  2103 + (
  2104 + Int32 ip_address, // the IP address shared by the web sites
  2105 + Int32 http_port, // usually: 80
  2106 + Int32 https_port, // usually: 443
  2107 + String ssl_certificate_common_name,
  2108 + List(Web_Site) web_sites, // web sites to be started
  2109 + Var(Bool) shutdown_required
  2110 + ) =
  2111 + if (Start_Web_Sites_Result)start_web_sites(ip_address,
  2112 + http_port,
  2113 + https_port,
  2114 + ssl_certificate_common_name,
  2115 + web_sites,
  2116 + shutdown_required) is
  2117 + {
  2118 + cannot_bind_to_port(n) then print("Cannot bind to port: "+n+"\n"),
  2119 + cannot_bind_to_port(n,m) then print("Cannot bind to ports: "+n+", "+m+"\n"),
  2120 + ok(s1,s2) then print("Servers started.\n")
  2121 + }.
  2122 +
  2123 +
  2124 +
  2125 +
  2126 +
  2127 + *** [5] HTML Formating.
  2128 +
  2129 + We need to translate HTML elements as defined above into actual HTML text.
  2130 +
  2131 + Actioners require special informations, which must be transmitted when needed by the
  2132 + 'format' functions:
  2133 +
  2134 + - the 'common name', which is used for URLs,
  2135 + - the HTTP/HTTPS port number,
  2136 + - the 'state name', which must be transmitted when the actioner is clicked upon,
  2137 + - the 'form name' (if any) to which the actioner refers.
  2138 +
  2139 + If the actioner is off form, and if it refers to a form, the name of that form is
  2140 + already known by the actioner. On the contrary, if the actioner is 'in form', it refers
  2141 + implicitly to the form containing it. The name of that form is transmitted to the
  2142 + 'format' functions called from within the formating of that form.
  2143 +
  2144 +
  2145 +
  2146 +
  2147 + *** [5.1] The type 'HTML_Any($T)'.
  2148 +
  2149 + The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
  2150 + parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
  2151 +
  2152 +type HTML_Any($T):
  2153 + any_text (List(Text_Option), String the_text),
  2154 + any_preformated (List(Text_Option), String),
  2155 + any_paragraph (List(Text_Option), String the_text),
  2156 + any_image (String url),
  2157 + any_image (String url, Int32 width, Int32 height),
  2158 + any_table (List(Table_Option), List(HTML_Row($T))),
  2159 + any_center ($T),
  2160 + any_mail_to (String email, $T element),
  2161 + any_scroller (Int32 width, Int32 height,
  2162 + Int32 content_width, Int32 content_height,
  2163 + $T content),
  2164 + any_fixed_size (HTML_Size width, HTML_Size height, $T content),
  2165 + any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  2166 + any_actioner (Actioner_Connection,
  2167 + Actioner_Target,
  2168 + Actioner_Aspect,
  2169 + String action_name,
  2170 + List((String,String)) extra_ops,
  2171 + List(Actioner_Local_Action),
  2172 + Maybe(String) form_name),
  2173 + any_foreign_link (List(Text_Option), String url, String name),
  2174 + any_private_download (String abs_path, String name, String extra_ext,
  2175 + Maybe((String,List((String,String))))),
  2176 + any_div (List(DIV_Option), $T element),
  2177 + any_div_empty (List(DIV_Option)),
  2178 + any_coreattrs (List(CoreAttrs)).
  2179 +
  2180 +
  2181 +
  2182 + *** [5.2] Formating a color.
  2183 +
  2184 + RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
  2185 + hexadecimal values.
  2186 +
  2187 +define String
  2188 + html_format
  2189 + (
  2190 + RGB color
  2191 + ) =
  2192 + if color is rgb(r,g,b) then
  2193 + "#" + hexadecimal(word8_to_int32(r),2)
  2194 + + hexadecimal(word8_to_int32(g),2)
  2195 + + hexadecimal(word8_to_int32(b),2).
  2196 +
  2197 +
  2198 + The following is a very arbitrary definition of the opposite color. The thing which is
  2199 + important is that it is far from the original, so that characters in 'opposite' color
  2200 + are clearly visible over the original.
  2201 +
  2202 +define RGB
  2203 + opposite
  2204 + (
  2205 + RGB color
  2206 + ) =
  2207 + if color is rgb(r,g,b) then
  2208 + with r1 = word8_to_int32(r),
  2209 + with g1 = word8_to_int32(g),
  2210 + with b1 = word8_to_int32(b),
  2211 + rgb(truncate_to_word8(255-r1),
  2212 + truncate_to_word8(255-g1),
  2213 + truncate_to_word8(255-b1)).
  2214 +
  2215 +
  2216 +
  2217 +
  2218 + *** [5.3] Creating buttons.
  2219 +
  2220 + We want to be able to create buttons in the form of a pair of images (rollovers)
  2221 + automatically. We use the JPEG interface, because for the time being Anubis cannot
  2222 + handle other kinds of images.
  2223 +
  2224 +
  2225 + Computing printed text length.
  2226 +
  2227 + define Int32
  2228 + printed_text_width
  2229 + (
  2230 + Word8 -> Int32 char_size,
  2231 + List(Word8) l
  2232 + ) =
  2233 + if l is
  2234 + {
  2235 + [] then (Int32) 0,
  2236 + [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
  2237 + }.
  2238 +
  2239 + define Int32
  2240 + printed_text_width
  2241 + (
  2242 + SystemFont font,
  2243 + String s
  2244 + ) =
  2245 + printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
  2246 + explode(s)).
  2247 +
  2248 +
  2249 +
  2250 + Converting RGB to RGBA.
  2251 +
  2252 +define RGBA
  2253 + to_rgba
  2254 + (
  2255 + RGB color
  2256 + ) =
  2257 + if color is rgb(r,g,b) then rgba(r,g,b,255).
  2258 +
  2259 +
  2260 + Drawing a 'relief'.
  2261 +
  2262 + define One
  2263 + draw_relief
  2264 + (
  2265 + RGBAImage dest,
  2266 + RGBA color,
  2267 + Int32 contrast,
  2268 + Int32 x,
  2269 + Int32 y,
  2270 + Int32 width,
  2271 + Int32 height
  2272 + ) =
  2273 + with l = lighten(color,contrast),
  2274 + d = darken(color,contrast),
  2275 + draw_rectangle(dest,rect(x,y,x+width,y+1),l);
  2276 + draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
  2277 + draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
  2278 + draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
  2279 +
  2280 +
  2281 + Creating a button background.
  2282 +
  2283 + define RGBAImage
  2284 + create_button_background
  2285 + (
  2286 + RGBA color,
  2287 + Int32 width,
  2288 + Int32 height
  2289 + ) =
  2290 + with result = create_rgba_image(width,height,color),
  2291 + draw_relief(result,color,100,0,0,width,height);
  2292 + draw_relief(result,color,70,1,1,width-2,height-2);
  2293 + draw_relief(result,color,55,2,2,width-4,height-4);
  2294 + draw_relief(result,color,35,3,3,width-6,height-6);
  2295 + draw_relief(result,color,20,4,4,width-8,height-8);
  2296 + draw_relief(result,color,10,5,5,width-10,height-10);
  2297 + draw_relief(result,color,5,6,6,width-12,height-12);
  2298 + result.
  2299 +
  2300 +
  2301 + Drawing the text over the background.
  2302 +
  2303 + define One
  2304 + draw_button_text
  2305 + (
  2306 + RGBAImage image,
  2307 + String text,
  2308 + Int32 text_index,
  2309 + Int32 pixel_x,
  2310 + Int32 y,
  2311 + Rectangle clip,
  2312 + RGBA color,
  2313 + SystemFont font,
  2314 + ) =
  2315 + if nth(text_index,text) is
  2316 + {
  2317 + failure then unique,
  2318 + success(c) then
  2319 + with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
  2320 + draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
  2321 + }.
  2322 +
  2323 + define One
  2324 + draw_button_text
  2325 + (
  2326 + RGBAImage image,
  2327 + String text,
  2328 + Int32 text_width,
  2329 + RGBA light_color,
  2330 + RGBA dark_color,
  2331 + SystemFont font
  2332 + ) =
  2333 + with image_width = width(image),
  2334 + image_height = height(image),
  2335 + x_pos = (image_width-text_width)>>1,
  2336 + clip = rect(0,0,image_width,image_height),
  2337 + new_light_color = lighten(light_color,150),
  2338 + new_dark_color = darken(dark_color,40),
  2339 + draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
  2340 + draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
  2341 +
  2342 +
  2343 + The next function creates the two images for a button. The information given is the
  2344 + main color of the button, the text of the button and the minimal width (in pixels) of
  2345 + the button. The function does not create the button if the images already exist. The
  2346 + two images are stored in the directory 'site_directory/buttons'. The names of the files
  2347 + are of the form:
  2348 +
  2349 + bxxxx_off.jpg
  2350 + bxxxx_on.jpg
  2351 +
  2352 + where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
  2353 + 'rm'), and where 'xxxx' is created from the given informations by the formula:
  2354 +
  2355 + xxxx = web_arg_encode(sha1((color,text,width)))
  2356 +
  2357 + Hence, distinct informations give distinct file names.
  2358 +
  2359 +
  2360 + define String // returns xxxx
  2361 + create_button_images
  2362 + (
  2363 + String site_directory,
  2364 + RGBA color,
  2365 + String text,
  2366 + Int32 width,
  2367 + SystemFont font
  2368 + ) =
  2369 + with xxxx = web_arg_encode(sha1((color,text,width))),
  2370 + buttons_dir = site_directory+"/public/buttons",
  2371 + off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
  2372 + on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
  2373 + if file_exists(on_filepath)
  2374 + then xxxx
  2375 + else with
  2376 + text_width = printed_text_width(font,text),
  2377 + button_width = max(width,text_width+12),
  2378 + button_height = (Int32)20,
  2379 + light_color = lighten(color,60),
  2380 + very_light_color = lighten(light_color,30),
  2381 + dark_color = darken(color,40),
  2382 + background_off =
  2383 + create_button_background(color,button_width,button_height),
  2384 + background_on =
  2385 + create_button_background(light_color,button_width,button_height),
  2386 +
  2387 + draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
  2388 + draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
  2389 + forget(write_image_to_JPEG_file(to_JPEG(background_off),
  2390 + off_filepath,
  2391 + 100));
  2392 + forget(write_image_to_JPEG_file(to_JPEG(background_on),
  2393 + on_filepath,
  2394 + 100));
  2395 + xxxx.
  2396 +
  2397 +
  2398 +
  2399 +
  2400 +
  2401 + *** [5.4] Formating an actioner.
  2402 +
  2403 + An actioner works as follows. Assume first that it refers to a form. When it is clicked
  2404 + upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
  2405 + form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
  2406 + actioner does not refer to a form, it fires the URL directly via 'href', because in
  2407 + that case, the actioner is always an <a> tag.
  2408 +
  2409 + The URL itself is composed using the connection sort (same, http or https), the common
  2410 + name and port number (if needed), the state name, the action name, and the extra
  2411 + operands, which are put into a query string. It may look like this:
  2412 +
  2413 + http://common_name:port/?s=state_name&a=action_name&oname=value...
  2414 +
  2415 + Each extra operand is a pair of strings: (name,value). It is formated as:
  2416 +
  2417 + &oname=value
  2418 +
  2419 +
  2420 +define String
  2421 + format_extra_operands
  2422 + (
  2423 + List((String,String)) l
  2424 + ) =
  2425 + if l is
  2426 + {
  2427 + [ ] then "",
  2428 + [h . t] then if h is (n,v) then
  2429 + "&amp;o"+n+"="+v+format_extra_operands(t)
  2430 + }.
  2431 +
  2432 +
  2433 + It seams that the standard requires "&amp;" instead of "&" !
  2434 +
  2435 + In case the target is another window, we need to format the options for this window.
  2436 +
  2437 +define String
  2438 + format
  2439 + (
  2440 + List(Other_Window_Option) l
  2441 + ) =
  2442 + if l is
  2443 + {
  2444 + [ ] then "",
  2445 + [h . t] then if h is
  2446 + {
  2447 + resizable then "resizable",
  2448 + scrollbars then "scrollbars",
  2449 + width(w) then "width="+w,
  2450 + height(h) then "height="+h
  2451 + } + if t is [ ] then "" else (","+format(t))
  2452 + }.
  2453 +
  2454 +
  2455 +
  2456 + Formating choices for a <select> tag.
  2457 +
  2458 +public define Printable_tree
  2459 + format_choices
  2460 + (
  2461 + List(String) l
  2462 + ) =
  2463 + if l is
  2464 + {
  2465 + [ ] then [ ],
  2466 + [h . t] then ["<option>",h . format_choices(t)]
  2467 + }.
  2468 +
  2469 +
  2470 +public define Printable_tree
  2471 + format_choices
  2472 + (
  2473 + List(String) l,
  2474 + String selected
  2475 + ) =
  2476 + if l is
  2477 + {
  2478 + [ ] then [ ],
  2479 + [h . t] then if h = selected
  2480 + then ["<option selected>",h . format_choices(t)]
  2481 + else ["<option>",h . format_choices(t,selected)]
  2482 + }.
  2483 +
  2484 +
  2485 +public define Printable_tree
  2486 + format_choices
  2487 + (
  2488 + List((String,String)) l
  2489 + ) =
  2490 + if l is
  2491 + {
  2492 + [ ] then [ ],
  2493 + [h . t] then
  2494 + if h is (val,item)
  2495 + then ["<option value=\""+val+"\">",item . format_choices(t)]
  2496 + }.
  2497 +
  2498 +public define Printable_tree
  2499 + format_choices
  2500 + (
  2501 + List((String,String)) l,
  2502 + String selected
  2503 + ) =
  2504 + if l is
  2505 + {
  2506 + [ ] then [ ],
  2507 + [h . t] then
  2508 + if h is (val,item) then
  2509 + if item = selected
  2510 + then ["<option value=\""+val+"\" selected>",item . format_choices(t)]
  2511 + else ["<option value=\""+val+"\">",item . format_choices(t,selected)]
  2512 + }.
  2513 +
  2514 +
  2515 +
  2516 +variable Int32 count = 0.
  2517 +
  2518 + Note: this counter is private to the virtual machine, hence there is one counter by
  2519 + client.
  2520 +
  2521 +define Int32
  2522 + new_count
  2523 + =
  2524 + count <- *count+1;
  2525 + *count.
  2526 +
  2527 +
  2528 + The next function composes the URL. It is a JavaScript URL when the target is another
  2529 + window.
  2530 +
  2531 +define String
  2532 + make_actioner_url
  2533 + (
  2534 + CommonInfo cinfo,
  2535 + Actioner_Connection connection,
  2536 + Actioner_Target target,
  2537 + String state_name,
  2538 + String action_name,
  2539 + List((String,String)) extra_ops,
  2540 + Bool is_https
  2541 + ) =
  2542 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  2543 + with strict_url =
  2544 + if connection is
  2545 + {
  2546 + same then "/",
  2547 + /*
  2548 + same then if is_https
  2549 + then "https://"+common_name+":"+https_port+"/"
  2550 + else "http://"+common_name+":"+https_port+"/",
  2551 + */
  2552 +
  2553 + http then "http://"+common_name+":"+http_port+"/",
  2554 + https then "https://"+common_name+":"+https_port+"/",
  2555 + } +
  2556 + "?s=" + state_name + "&amp;a=" + action_name +
  2557 + format_extra_operands(extra_ops),
  2558 + if target is
  2559 + {
  2560 + same then strict_url,
  2561 + same(label) then strict_url+"#"+label,
  2562 + other(wn,ops) then
  2563 + "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
  2564 + "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
  2565 + }.
  2566 +
  2567 +
  2568 + Depending on the fact that the actioner refers to a form or not, the URL is used in two
  2569 + different ways. If the actioner does not refer to a form, it is realized by a '<a>'
  2570 + tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
  2571 + tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
  2572 + event handler. The handler calls a JavaScript function which puts the URL as the value
  2573 + of the 'action' attribute of the form, and submits the form.
  2574 +
  2575 +type URL_or_JavaScript:
  2576 + url (String),
  2577 + javascript (Printable_tree script, Printable_tree handler).
  2578 +
  2579 + define URL_or_JavaScript
  2580 + format_action
  2581 + (
  2582 + String the_url,
  2583 + Maybe(String) mb_form_name
  2584 + ) =
  2585 + url(the_url).
  2586 +
  2587 +
  2588 + It was as shown below, in order to allow submission of a form from outside the form,
  2589 + but this makes problems:
  2590 +
  2591 +define URL_or_JavaScript
  2592 + format_action
  2593 + (
  2594 + String the_url,
  2595 + Maybe(String) mb_form_name
  2596 + ) =
  2597 + if mb_form_name is
  2598 + {
  2599 + failure then
  2600 + url(the_url),
  2601 + success(form_name) then
  2602 + with n = new_count,
  2603 + javascript(
  2604 + [
  2605 + "<script type=\"text/javascript\" language =\"JavaScript\">\n",
  2606 + "function pfu",form_name,n,"() {\n",
  2607 + " var fa = document.forms.f",form_name,";\n",
  2608 + " var u = \"",the_url,"\";\n",
  2609 + " fa.action = u;\n",
  2610 + " fa.submit(); }",
  2611 + "</script>"
  2612 + ],
  2613 + ["pfu",form_name,n,"();"]
  2614 + )
  2615 + }.
  2616 +
  2617 +
  2618 +
  2619 + Now, we format the actioner according to its aspect.
  2620 +
  2621 +define List(Text_Option)
  2622 + get_text_options
  2623 + (
  2624 + List(Text_Option) l
  2625 + )
  2626 + =
  2627 + if l is
  2628 + {
  2629 + [] then [],
  2630 + [h . t ] then
  2631 + if h is class(class) then
  2632 + get_text_options(t)
  2633 + else
  2634 + [ h . get_text_options(t) ]
  2635 +
  2636 + }.
  2637 +
  2638 + /**
  2639 + * Extract the CSS class list from the list of Text_Option
  2640 + */
  2641 +define List(Text_Option)
  2642 + get_css_class
  2643 + (
  2644 + List(Text_Option) l
  2645 + )
  2646 + =
  2647 + if l is
  2648 + {
  2649 + [] then [],
  2650 + [h . t ] then
  2651 +
  2652 +
  2653 + if h is class(class) then
  2654 + [ h . get_css_class(t) ]
  2655 + else
  2656 + get_css_class(t)
  2657 + }
  2658 + .
  2659 +
  2660 +define String
  2661 + format_text_options
  2662 + (
  2663 + List(Text_Option) l
  2664 + )
  2665 + =
  2666 + with text_options = get_text_options(l),
  2667 + css_classes = get_css_class(l),
  2668 + if text_options is
  2669 + {
  2670 + [] then "",
  2671 + [_._] then " style=\"" + format(text_options) + "\" "
  2672 + }
  2673 + +
  2674 + if css_classes is
  2675 + {
  2676 + [] then "",
  2677 + [_._] then format(css_classes)
  2678 + }.
  2679 +
  2680 +define Printable_tree
  2681 + format
  2682 + (
  2683 + List(Actioner_Local_Action) l
  2684 + ) =
  2685 + if l is
  2686 + {
  2687 + [ ] then [ ],
  2688 + [h . t] then [if h is
  2689 + {
  2690 + close_window then [" window.close(); "]
  2691 + }
  2692 + . format(t)]
  2693 + }.
  2694 +
  2695 +define String
  2696 + format_coreattrs
  2697 + (
  2698 + List(CoreAttrs) attributs
  2699 + )=
  2700 + if attributs is
  2701 + {
  2702 + [] then "",
  2703 + [h .t] then
  2704 + with current = if h is
  2705 + {
  2706 + id(id_name) then
  2707 + " id=\"" + id_name + "\"",
  2708 + class(class_name) then
  2709 + " class=\"" + class_name + "\"",
  2710 + style(style_string) then
  2711 + " style=\"" + style_string + "\"",
  2712 +
  2713 + title(title_string) then
  2714 + " title=\"" + title_string + "\"",
  2715 + },
  2716 + current + format_coreattrs(t)
  2717 + }.
  2718 +
  2719 +define String
  2720 + _format
  2721 + (
  2722 + List(DIV_Option) opt
  2723 + )=
  2724 + if opt is
  2725 + {
  2726 + [] then "",
  2727 + [h .t] then
  2728 + with current = if h is
  2729 + {
  2730 + id(id_name) then
  2731 + " id=\"" + id_name + "\"",
  2732 + class(class_name) then
  2733 + " class=\"" + class_name + "\"",
  2734 + style(style_string) then
  2735 + " style=\"" + style_string + "\"",
  2736 +
  2737 + title(title_string) then
  2738 + " title=\"" + title_string + "\"",
  2739 + lang(lang) then
  2740 + " xml:lang=" + lang,
  2741 + dir(reading_Way) then
  2742 + if reading_Way is
  2743 + {
  2744 + ltr then " dir=ltr",
  2745 + rtl then " dir=rtl"
  2746 + }
  2747 + },
  2748 + current + _format(t)
  2749 + }
  2750 + .
  2751 +
  2752 +define Printable_tree
  2753 + format_div_option
  2754 + (
  2755 + List(DIV_Option) options
  2756 + )=
  2757 + ["<DIV" + _format(options) + ">"] .
  2758 +
  2759 +
  2760 +define Printable_tree
  2761 + format_actioner
  2762 + (
  2763 + CommonInfo cinfo,
  2764 + String state_name,
  2765 + Actioner_Connection connection,
  2766 + Actioner_Target target,
  2767 + Actioner_Aspect aspect,
  2768 + String action_name,
  2769 + List((String,String)) extra_ops,
  2770 + List(Actioner_Local_Action) local_actions,
  2771 + Maybe(String) mb_form_name,
  2772 + Bool is_https,
  2773 + ) =
  2774 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  2775 + with url = make_actioner_url(cinfo,connection,target,
  2776 + state_name,action_name,extra_ops,is_https),
  2777 + with action = format_action(url,mb_form_name),
  2778 + if aspect is
  2779 + {
  2780 +
  2781 + link(opt, text) then [
  2782 + if action is
  2783 + {
  2784 + url(u) then
  2785 + ["<a href=\"",u,"\"", format_text_options(opt), ">"],
  2786 +// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],
  2787 + javascript(s,h) then
  2788 + [s,"<a href=\"javascript: ",h,"\">"]
  2789 + },
  2790 + text,
  2791 + "</a>"
  2792 + ],
  2793 +
  2794 + push_button(options, text) then
  2795 + [
  2796 + if action is
  2797 + {
  2798 + url(u) then ["<a href=\"",u,"\"", format_coreattrs(options), ">",
  2799 + text, "</a>"
  2800 + ],
  2801 + javascript(s,h) then
  2802 + [s,"<INPUT TYPE =\"button\" Value=\"",text,"\"", format_coreattrs(options), " onClick=\"",h,"\">"]
  2803 + }
  2804 + ],
  2805 +
  2806 + button(url_off,url_on) then
  2807 + [ if action is
  2808 + {
  2809 + url(u) then ["<a href=\"",u],
  2810 + javascript(s,h) then [s,"<a onMouseDown=\"",h]
  2811 + },
  2812 + "\" style=\"text-decoration:none\">",
  2813 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2814 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2815 + " onMouseOut=\"this.src='",url_off,"'\">",
  2816 + "</a>"
  2817 + ],
  2818 +
  2819 + button(url_off,url_on,w,h) then
  2820 + [ if action is
  2821 + {
  2822 + url(u) then ["<a href=\"",u],
  2823 + javascript(s,h) then [s,"<a onMouseDown=\"",h]
  2824 + },
  2825 + "\" style=\"text-decoration:none\">",
  2826 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2827 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2828 + " onMouseOut=\"this.src='",url_off,"'\">",
  2829 + "</a>"
  2830 + ],
  2831 +
  2832 + immediate_selector(name,size,choices) then
  2833 + [ if action is
  2834 + {
  2835 + url(u) then ["<select href=\"",u]
  2836 + javascript(s,h) then [s,"<select onchange=\"",h]
  2837 + },
  2838 + "\" name=o",name," size=",size,">",
  2839 + format_choices(choices),"</select>"
  2840 + ]
  2841 + }.
  2842 +
  2843 +
  2844 +define Printable_tree
  2845 + format_local_popup_button
  2846 + (
  2847 + CommonInfo cinfo,
  2848 + Actioner_Aspect aspect,
  2849 + Int32 n,
  2850 + ) =
  2851 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  2852 + [ "<script type=\"text/javascript\" language=\"JavaScript\">",
  2853 + " var lpust_",n," = new Array(); ",
  2854 + " lpust_",n,"[0] = 0; ",
  2855 + "</script>",
  2856 + "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",
  2857 + if aspect is
  2858 + {
  2859 + link(opt,text) then [text],
  2860 + push_button(opt, text) then [text],
  2861 + button(url_off,url_on) then
  2862 + [
  2863 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2864 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2865 + " onMouseOut=\"this.src='",url_off,"'\">",
  2866 + ],
  2867 +
  2868 + button(url_off,url_on,w,h) then
  2869 + [
  2870 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2871 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2872 + " onMouseOut=\"this.src='",url_off,"'\">",
  2873 + ],
  2874 +
  2875 + immediate_selector(name,size,choices) then alert,
  2876 +
  2877 + },
  2878 + "</a>"].
  2879 +
  2880 +
  2881 +
  2882 +
  2883 +
  2884 + *** [5.5] Formating a private download link.
  2885 +
  2886 + We get the absolute path of the file to be downloaded, and the name under which it
  2887 + should appear to the client. The function 'format_private_download' creates an
  2888 + hypertext link for downloading the file. The secured mecanism of private download is
  2889 + used. This function is called by the function which formats HTML_Any($T) elements.
  2890 +
  2891 +
  2892 +define Printable_tree
  2893 + format_private_download
  2894 + (
  2895 + CommonInfo cinfo,
  2896 + String sn, // state name
  2897 + String abs_path, // absolute file path on server
  2898 + String name, // name of file as it appears in the browser
  2899 + String extra, // extra extension
  2900 + Maybe((String,List((String,String)))) action
  2901 +
  2902 + ) =
  2903 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  2904 + with private_download_directory = site_directory+"/private_download",
  2905 + with auth = make_authorization(site_directory,secret,abs_path),
  2906 + [
  2907 + "<a href=\"",name,extra,"?zauth=",auth,
  2908 + if action is
  2909 + {
  2910 + failure then [ ]
  2911 + success(a) then if a is (an,args) then
  2912 + ["&amp;a=",an,format_extra_operands(args)]
  2913 + },
  2914 + "\">",
  2915 + name,
  2916 + "</a>"
  2917 + ].
  2918 +
  2919 +
  2920 +
  2921 +
  2922 + *** [5.6] Formating rows and cells in a table.
  2923 +
  2924 +define Int32
  2925 + percent
  2926 + (
  2927 + Int32 p
  2928 + ) =
  2929 + if p < 0 then 0 else if p > 100 then 100 else p.
  2930 +
  2931 +
  2932 +
  2933 +
  2934 + Formating cell options.
  2935 +
  2936 +
  2937 +
  2938 +define String
  2939 + format
  2940 + (
  2941 + BackgroundOption o
  2942 + ) =
  2943 + if o is
  2944 + {
  2945 + repeat then "",
  2946 + repeat_horizontal then "; background-repeat: repeat-x",
  2947 + repeat_vertical then "; background-repeat: repeat-y",
  2948 + no_repeat then "; background-repeat: no-repeat",
  2949 + center then "; background-position: center top"
  2950 + }.
  2951 +
  2952 +define String
  2953 + format
  2954 + (
  2955 + List(BackgroundOption) l
  2956 + ) =
  2957 + if l is
  2958 + {
  2959 + [ ] then "",
  2960 + [h . t] then format(h)+format(t)
  2961 + }.
  2962 +
  2963 +
  2964 +define String
  2965 + format
  2966 + (
  2967 + List(Cell_Option) options
  2968 + ) =
  2969 + if options is
  2970 + {
  2971 + [ ] then "",
  2972 + [h . t] then
  2973 + if h is
  2974 + {
  2975 + left then " align=left",
  2976 + h_center then " align=center",
  2977 + right then " align=right",
  2978 + top then " valign=top",
  2979 + v_center then " valign=middle",
  2980 + bottom then " valign=bottom",
  2981 + base_line then " valign=baseline",
  2982 + background_color(c) then " bgcolor=\""+html_format(c)+"\"",
  2983 + background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
  2984 + width(w) then " width=\""+w+"\"",
  2985 + percentage_width(n) then " width=\""+percent(n)+"%\"",
  2986 + height(h) then " height="+h,
  2987 + columns(n) then " colspan="+n,
  2988 + rows(n) then " rowspan="+n,
  2989 + nowrap then " nowrap"
  2990 + }
  2991 + + format(t)
  2992 + }.
  2993 +
  2994 +
  2995 + Normalizing a list of cell options (horizontal position must be specified; the default
  2996 + is 'left').
  2997 +
  2998 +define List(Cell_Option)
  2999 + normalize
  3000 + (
  3001 + List(Cell_Option) l
  3002 + ) =
  3003 + if member(l,left) then l else
  3004 + if member(l,h_center) then l else
  3005 + if member(l,right) then l else
  3006 + [left . l].
  3007 +
  3008 +
  3009 + Formating cells in a row.
  3010 +
  3011 +define Printable_tree
  3012 + format
  3013 + (
  3014 + List(HTML_Cell($T)) cells,
  3015 + $T -> Printable_tree format_element
  3016 + ) =
  3017 + if cells is
  3018 + {
  3019 + [ ] then [ ],
  3020 + [h . t] then if h is cell(options,element) then
  3021 + ["<td ",format(reverse(normalize(options))),">",
  3022 + format_element(element),
  3023 + "</td>"
  3024 + . format(t,format_element)]
  3025 + }.
  3026 +
  3027 +
  3028 + Formating the rows in a table.
  3029 +
  3030 +define Printable_tree
  3031 + format
  3032 + (
  3033 + List(HTML_Row($T)) rows,
  3034 + $T -> Printable_tree format_element,
  3035 + ) =
  3036 + if rows is
  3037 + {
  3038 + [ ] then [ ],
  3039 + [h . t] then if h is row(options,cells) then
  3040 + ["<tr ",format(reverse(options)),">",
  3041 + format(cells,format_element),
  3042 + "</tr>"
  3043 + . format(t,format_element)]
  3044 + }.
  3045 +
  3046 +
  3047 +
  3048 +define Printable_tree
  3049 + format1
  3050 + (
  3051 + List(TextAreaOption) l
  3052 + ) =
  3053 + if l is
  3054 + {
  3055 + [] then [],
  3056 + [h . t] then if h is
  3057 + {
  3058 + disabled then [" disabled " . format1(t)]
  3059 + read_only then [" readonly " . format1(t)]
  3060 + wrap_lines then [" wrap " . format1(t)]
  3061 + }
  3062 + }.
  3063 +
  3064 +define Printable_tree
  3065 + format
  3066 + (
  3067 + List(TextAreaOption) l
  3068 + ) =
  3069 + if member(l,wrap_lines)
  3070 + then format1(l)
  3071 + else [" wrap=off " . format1(l)].
  3072 +
  3073 +
  3074 + *** [5.7] Formating elements which may be put anywhere.
  3075 +
  3076 + The function below involves the parameter $T which is later instantiated as
  3077 + 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
  3078 + for these two types, and because formating of tables requires recursive calls of such
  3079 + functions, it is necessary to provide the 'format' function to be called recursively as
  3080 + an argument. Putting naively a call to 'format' will not work, because the compiler
  3081 + will look for a function able to format data of type $T (which is at that time distinct
  3082 + from any other type, including our two types). Such a function does not exist. Hence
  3083 + the function to be called for formating elements must be passed as a functional
  3084 + argument (called 'format_element' below). Actually, what we pass is a function taking
  3085 + a unique argument of type $T. Other informations (like the name of the state) are
  3086 + already in the function by way of full functionality.
  3087 +
  3088 +
  3089 + Formating text options. They are formated in CSS syntax, to be used within a
  3090 + 'style=...'.
  3091 +
  3092 +define String
  3093 + format
  3094 + (
  3095 + List(Text_Option) l
  3096 + ) =
  3097 + if l is
  3098 + {
  3099 + [ ] then "",
  3100 + [h . t] then if h is
  3101 + {
  3102 + size(n) then "font-size:"+n+"pt",
  3103 + font(fn) then "font-family:"+fn,
  3104 + color(c) then if c is rgb(r,g,b) then
  3105 + "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",
  3106 + italic then "font-style:italic",
  3107 + oblique then "font-style:oblique",
  3108 + small_capitals then "font-variant:small-caps",
  3109 + bold then "font-weight:bold",
  3110 + underlined then "text-decoration:underline",
  3111 + left_justified then "text-align:left",
  3112 + right_justified then "text-align:right",
  3113 + justified then "text-align:justify",
  3114 + line_through then "text-decoration:line-through",
  3115 + nowrap then "white-space:nowrap",
  3116 + class(class_name)then " class=\"" +class_name +"\""
  3117 + } + if t is [ ] then "" else ("; "+format(t))
  3118 + }.
  3119 +
  3120 +
  3121 +
  3122 + Formating table options.
  3123 +
  3124 +define String
  3125 + format
  3126 + (
  3127 + List(Table_Option) l,
  3128 + Bool border_seen
  3129 + ) =
  3130 + if l is
  3131 + {
  3132 + [ ] then if border_seen then "" else " border=0 cellspacing=0 cellpadding=0",
  3133 + [h . t] then if h is
  3134 + {
  3135 + background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
  3136 + background_image(url) then " background="+url+format(t,border_seen),
  3137 + border(o,top,i,c) then " border="+o+" cellspacing="+top+" cellpadding="+i+
  3138 + //" bordercolor="+format(c)+
  3139 + format(t,true),
  3140 + width(w) then " width=\""+w+"\""+format(t,border_seen),
  3141 + percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
  3142 + }
  3143 + }.
  3144 +
  3145 +
  3146 +
  3147 +
  3148 +
  3149 +define Printable_tree
  3150 + format_scroller
  3151 + (
  3152 + String sn,
  3153 + Int32 width,
  3154 + Int32 height,
  3155 + Int32 content_width,
  3156 + Int32 content_height,
  3157 + Int32 idnum, // identifying the scroller
  3158 + $T content,
  3159 + $T -> Printable_tree format_element
  3160 + ) =
  3161 + [
  3162 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3163 + "function doscroll_",idnum,"(dx,dy) {\n",
  3164 + " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
  3165 + " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
  3166 + idnum,"').style\"); } else\n",
  3167 + " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
  3168 + " var x_",idnum," = parseInt(c_",idnum,".left);\n",
  3169 + " var y_",idnum," = parseInt(c_",idnum,".top);\n",
  3170 + " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
  3171 + " { x_",idnum," += dx; }\n",
  3172 + " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
  3173 + " { y_",idnum," += dy; }\n",
  3174 + " c_",idnum,".left = x_",idnum,";\n",
  3175 + " c_",idnum,".top = y_",idnum,";\n",
  3176 + " }\n",
  3177 + "</script>",
  3178 + "<table>",
  3179 + "<tr>",
  3180 + "<td align=left valign=top",
  3181 + " width=",width,
  3182 + " height=",height,
  3183 + ">",
  3184 + "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
  3185 + " clip:rect(0px ",width,"px ",height,"px 0px)\">",
  3186 + "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
  3187 + format_element(content),
  3188 + "</div>",
  3189 + "</div>",
  3190 + "</td>",
  3191 + "<td valign=bottom>",
  3192 + "<table>",
  3193 + "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onMouseDown=\"doscroll_",
  3194 + idnum,"(0,20);\"></td></tr>",
  3195 + "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onMouseDown=\"doscroll_",
  3196 + idnum,"(0,-20);\"></td></tr>",
  3197 + "</table>",
  3198 + "</td>",
  3199 + "</tr>",
  3200 + (if content_width > width then
  3201 + [
  3202 + "<tr>",
  3203 + "<td align=right>",
  3204 + "<table>",
  3205 + "<tr>",
  3206 + "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onMouseDown=\"doscroll_",
  3207 + idnum,"(20,0);\"></td>",
  3208 + "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onMouseDown=\"doscroll_",
  3209 + idnum,"(-20,0);\"></td>",
  3210 + "</tr>",
  3211 + "</table>",
  3212 + "</td>",
  3213 + "</tr>",
  3214 + ] else [ ]),
  3215 + "</table>"
  3216 + ].
  3217 +
  3218 +
  3219 +
  3220 + define Printable_tree
  3221 + popup_topbar
  3222 + (
  3223 + CommonInfo cinfo,
  3224 + String title,
  3225 + RGB color,
  3226 + Int32 width,
  3227 + ) =
  3228 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3229 + with xxxx = web_arg_encode(sha1((title,color,width))),
  3230 + path = site_directory+"/public/buttons/t"+xxxx+".jpg",
  3231 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],
  3232 + if file_exists(path) then result else
  3233 + with col = to_rgba(color),
  3234 + bg = create_button_background(col,width,20),
  3235 + very_light_color = lighten(col,70),
  3236 + dark_color = darken(col,40),
  3237 + title_width = printed_text_width(font,title),
  3238 + draw_button_text(bg,title,title_width,very_light_color,dark_color,font);
  3239 + forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));
  3240 + result.
  3241 +
  3242 +
  3243 + define Printable_tree
  3244 + popup_close_button
  3245 + (
  3246 + CommonInfo cinfo,
  3247 + RGB color,
  3248 + String div_name,
  3249 + String state_var_name,
  3250 + ) =
  3251 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3252 + with xxxx = web_arg_encode(sha1(color)),
  3253 + path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",
  3254 + path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",
  3255 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",
  3256 + " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",
  3257 + " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",
  3258 + " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],
  3259 + if file_exists(path_on) then result else
  3260 + with col = to_rgba(color),
  3261 + title = "x",
  3262 + title_width = printed_text_width(font,title),
  3263 + bg_on = create_button_background(lighten(col,30),20,20),
  3264 + bg_off = create_button_background(col,20,20),
  3265 + very_light_color = lighten(col,70),
  3266 + dark_color = darken(col,40),
  3267 + draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);
  3268 + draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);
  3269 + forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));
  3270 + forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));
  3271 + result.
  3272 +
  3273 +
  3274 +
  3275 +
  3276 +
  3277 + // The function below formats a datum of type 'HTML_Any($T)'.
  3278 +
  3279 +define Printable_tree
  3280 + format
  3281 + (
  3282 + CommonInfo cinfo,
  3283 + String sn, // state_name
  3284 + Var(Int32) ic_v,
  3285 + HTML_Any($T) element,
  3286 + $T -> Printable_tree format_element, // able to format a datum of type $T
  3287 + Bool is_https,
  3288 + ) =
  3289 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3290 + if element is
  3291 + {
  3292 + any_text(opts,t) then
  3293 + ["<span ", format_text_options(opts), ">",t,"</span>"],
  3294 + any_preformated(opts,s) then
  3295 + ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],
  3296 + //["<pre>",s,"</pre>"],
  3297 + any_paragraph(opts,t) then
  3298 + ["<p ", format_text_options(opts), ">",t,"</p>"],
  3299 + any_image(url) then
  3300 + ["<img alt=\"",url,"\" src=\"",url,"\">"],
  3301 + any_image(url,w,h) then
  3302 + ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h,">"],
  3303 + any_table(opts,rows) then
  3304 + ["<table ",format(reverse(opts),false),">",format(rows,format_element),"</table>"],
  3305 + any_center(e) then
  3306 + ["<center>",format_element(e),"</center>"],
  3307 + any_mail_to(email,elem) then
  3308 + ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
  3309 + any_scroller(w,h,cw,ch,c) then
  3310 + format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
  3311 + any_fixed_size(w,h,c) then
  3312 + with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
  3313 + ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
  3314 + "secondary document",
  3315 + "</object>"],
  3316 + any_fixed_size_2(w,h,fn) then
  3317 + with url = fn+"?zauth="+make_authorization(site_directory,secret,
  3318 + fn),
  3319 + ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
  3320 + "secondary document",
  3321 + "</object>"],
  3322 + any_actioner(c,t,a,an,eo,ja,fn) then
  3323 + format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),
  3324 + any_foreign_link(options,url,name) then
  3325 + ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
  3326 + any_private_download(url,name,extra_ext,action) then
  3327 + format_private_download(cinfo,sn,url,name,extra_ext,action),
  3328 + any_div(options, e) then
  3329 + [format_div_option(options), format_element(e),"</DIV>"],
  3330 + any_div_empty(options) then
  3331 + [format_div_option(options), "</DIV>"],
  3332 + any_coreattrs(attributs) then
  3333 + [format_coreattrs(attributs)]
  3334 + }.
  3335 +
  3336 +
  3337 +
  3338 +
  3339 + // *** [5.8] Formating 'in form' elements.
  3340 +
  3341 +
  3342 +
  3343 +
  3344 +
  3345 +define Printable_tree
  3346 + format
  3347 + (
  3348 + CommonInfo cinfo,
  3349 + String fn, // form_name
  3350 + String sn, // state_name
  3351 + Var(Int32) ic_v, // idnum counter variable
  3352 + HTML_In_Form element,
  3353 + Bool is_https,
  3354 + ) =
  3355 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3356 + with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),
  3357 + if element is
  3358 + {
  3359 + literal_pt(t) then t,
  3360 + literal(t) then [t],
  3361 + sequence(l) then flat(map(format_element,l))
  3362 + text(opts,t) then
  3363 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3364 + preformated(o,s) then
  3365 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3366 + paragraph(opts,t) then
  3367 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3368 + image(url) then
  3369 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3370 + image(url,w,h) then
  3371 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3372 + table(opts,rows) then
  3373 + format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
  3374 + center(e) then
  3375 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3376 + mail_to(a,e) then
  3377 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3378 + scroller(w,h,cw,ch,c) then
  3379 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3380 + actioner(c,t,a,an,eo,ja) then
  3381 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3382 + foreign_link(options,url,name) then
  3383 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3384 + private_download(url,name,extra,action) then
  3385 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3386 + text_input(label_text, label, name,i,w) then
  3387 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3388 + "<input type=text name=o",name," id=",label," size=",w," value=\"",i,"\">"],
  3389 + //["&nbsp; <input type=text name=o",n," size=",w," value=\"",i,"\">"],
  3390 + password_input(label_text, label, name,w) then
  3391 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3392 + "<input type=password name=p",name," id=",label," size=",w,">"],
  3393 + //["&nbsp; <input type=password name=p",n," size=",w,">"],
  3394 + text_area(opts,n,i,w,h) then
  3395 + ["<textarea ",format(opts)," name=o",n," cols=",w," rows=",h,">",i,"</textarea>"],
  3396 + file_upload(n,w) then
  3397 + ["<input type=file size=",w," name=o",n,">"],
  3398 + selector(n,s,cs) then
  3399 + ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
  3400 + selector(n,s,cs,sd) then
  3401 + ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
  3402 + selector_c(n,s,cs) then
  3403 + ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
  3404 + selector_c(n,s,cs,sd) then
  3405 + ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
  3406 +
  3407 + radio_button(label_text,label,n,v,c) then
  3408 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3409 + "<input type=radio name=o",n," id=",label," value=\"",v,"\"",(if c then " checked" else ""),">"],
  3410 + check_box(label_text, label,n,c) then
  3411 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3412 + "<input type=checkbox name=o",n," id=\"",label,"\""+(if c then " checked " else ""),">"]
  3413 + div(options, e) then
  3414 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3415 + div_empty(options) then
  3416 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3417 + hidden(name, value) then
  3418 + ["<input type=hidden name=o",name," value=\"",value,"\">"],
  3419 +
  3420 + }.
  3421 +
  3422 +
  3423 +
  3424 +
  3425 +
  3426 +
  3427 + *** [5.9] Formating 'off form' elements.
  3428 +
  3429 + The encryption type 'multipart/form-data' is required for a form containing an upload.
  3430 +
  3431 +
  3432 +define Bool
  3433 + contains_an_upload
  3434 + (
  3435 + HTML_In_Form form_content
  3436 + ).
  3437 +
  3438 +define Bool
  3439 + contains_an_upload
  3440 + (
  3441 + HTML_Row(HTML_In_Form) row
  3442 + ) =
  3443 + mapor(contains_an_upload,
  3444 + map(content,cells(row))).
  3445 +
  3446 +
  3447 +define Bool
  3448 + contains_an_upload
  3449 + (
  3450 + HTML_In_Form form_content
  3451 + ) =
  3452 + if form_content is
  3453 + {
  3454 + literal_pt(t) then false,
  3455 + literal(t) then false,
  3456 + sequence(l) then mapor(contains_an_upload,l)
  3457 + text(o,t) then false,
  3458 + preformated(o,s) then false,
  3459 + paragraph(o,t) then false,
  3460 + image(u) then false,
  3461 + image(u,w,h) then false,
  3462 + table(o,rows) then mapor(contains_an_upload,rows),
  3463 + center(e) then contains_an_upload(e),
  3464 + mail_to(m,e) then false, // 'e' may but should not contain an upload
  3465 + scroller(w,h,cw,ch,e) then contains_an_upload(e),
  3466 + actioner(c,t,a,an,eo,ja) then false,
  3467 + foreign_link(o,u,n) then false,
  3468 + private_download(p,n,e,a) then false,
  3469 + text_input(lt,l,n,i,w) then false,
  3470 + password_input(lt,l,n,w) then false,
  3471 + text_area(o,n,i,w,h) then false,
  3472 + file_upload(n,w) then true,
  3473 + selector(n,s,c) then false,
  3474 + selector(n,s,c,p) then false,
  3475 + selector_c(n,s,c) then false,
  3476 + selector_c(n,s,c,p) then false,
  3477 + radio_button(_,_,n,v,c) then false,
  3478 + check_box(_,_,n,c) then false,
  3479 + div(o,c) then false,
  3480 + div_empty(o) then false,
  3481 + hidden(_,_) then false
  3482 + }.
  3483 +
  3484 +define String
  3485 + enctype
  3486 + (
  3487 + HTML_In_Form form_content
  3488 + ) =
  3489 + if contains_an_upload(form_content)
  3490 + then " enctype=multipart/form-data"
  3491 + else "".
  3492 +
  3493 +
  3494 +
  3495 +define Printable_tree
  3496 + format
  3497 + (
  3498 + CommonInfo cinfo,
  3499 + String sn, // state_name
  3500 + Var(Int32) ic_v, // 'idnum' counter variable
  3501 + HTML_Off_Form element,
  3502 + Bool is_https,
  3503 + ) =
  3504 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3505 + with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),
  3506 + if element is
  3507 + {
  3508 + literal_pt(t) then t,
  3509 + literal(t) then [t],
  3510 + sequence(l) then flat(map(format_element,l)),
  3511 + text(opts,t) then
  3512 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3513 + preformated(o,s) then
  3514 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3515 + paragraph(opts,t) then
  3516 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3517 + image(url) then
  3518 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3519 + image(url,w,h) then
  3520 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3521 + table(opts,rows) then
  3522 + format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
  3523 + center(e) then
  3524 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3525 + mail_to(a,e) then
  3526 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3527 + scroller(w,h,cw,ch,c) then
  3528 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3529 + fixed_size(w,h,c) then
  3530 + format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),
  3531 + fixed_size_2(w,h,fn) then
  3532 + format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),
  3533 + actioner(c,t,a,an,eo,ja) then
  3534 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),
  3535 + actioner(c,t,a,an,eo,ja,fn) then
  3536 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3537 + foreign_link(options,url,name) then
  3538 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3539 + private_download(url,name,extra,action) then
  3540 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3541 + label(n) then ["<a name=\"",n,"\">"],
  3542 + form(fn,attributs, c) then
  3543 + [
  3544 + "<form name=\"f",fn,"\"",
  3545 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),
  3546 + " method=POST",
  3547 + enctype(c),
  3548 + " action=\"http",
  3549 + if is_https then "s" else "",
  3550 + "://",common_name,":",http_port,"/\">",
  3551 + // action is set dynamically by
  3552 + // the actioner using JavaScript
  3553 + format(cinfo,fn,sn,ic_v,c,is_https),
  3554 + "</form>"
  3555 + ]
  3556 + div(options, e) then
  3557 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3558 + div_empty(options) then
  3559 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3560 +
  3561 + }.
  3562 +
  3563 +
  3564 +
  3565 +
  3566 + *** [5.10] Formating meta-tags.
  3567 +
  3568 +define Printable_tree
  3569 + format_keywords
  3570 + (
  3571 + List(String) l
  3572 + ) =
  3573 + if l is
  3574 + {
  3575 + [] then [ ],
  3576 + [h . t] then if t is []
  3577 + then [h]
  3578 + else [h , ", " . format_keywords(t)]
  3579 + }.
  3580 +
  3581 +
  3582 +define Printable_tree
  3583 + format
  3584 + (
  3585 + CommonInfo cinfo,
  3586 + String state_name,
  3587 + HTML_Meta m,
  3588 + Bool is_https
  3589 + ) =
  3590 + if m is
  3591 + {
  3592 + keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\">"],
  3593 + refresh(co,ta,an,delay) then
  3594 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
  3595 + make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\">"],
  3596 + meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\">"],
  3597 + http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\">"],
  3598 + generic_meta(l) then ["<meta ",
  3599 + flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
  3600 + l)),
  3601 + ">"],
  3602 + literal(s) then [s]
  3603 + }.
  3604 +
  3605 +
  3606 +define Printable_tree
  3607 + format
  3608 + (
  3609 + CommonInfo cinfo,
  3610 + String state_name,
  3611 + List(HTML_Meta) metas,
  3612 + Bool is_https,
  3613 + String charset
  3614 + ) =
  3615 + if metas is
  3616 + {
  3617 + [] then [format(cinfo,state_name,http_equiv("content-type",
  3618 + "text/html; charset="+charset),is_https)],
  3619 + [h . t] then [format(cinfo,state_name,h,is_https)
  3620 + . format(cinfo,state_name,t,is_https,charset)]
  3621 + }.
  3622 +
  3623 +
  3624 +define Printable_tree
  3625 + format
  3626 + (
  3627 + Body_Option o
  3628 + ) =
  3629 + if o is
  3630 + {
  3631 + background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
  3632 + background_image(n) then [" background=", n],
  3633 + background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]
  3634 +
  3635 + }.
  3636 +
  3637 +
  3638 +
  3639 +define Printable_tree
  3640 + format
  3641 + (
  3642 + List(Body_Option) l
  3643 + ) =
  3644 + if l is
  3645 + {
  3646 + [ ] then [ ],
  3647 + [h . t] then [format(h) . format(t)]
  3648 + }.
  3649 +
  3650 +define Printable_tree
  3651 + add_css_files
  3652 + (
  3653 + List(CSS_File) l
  3654 + ) =
  3655 + if l is
  3656 + {
  3657 + [ ] then [ ],
  3658 + [h . t] then
  3659 + [ ["<LINK rel=\"stylesheet\" type=\"text/css\" href=" + file_name(h) + ">\n" ]
  3660 + . add_css_files(t)]
  3661 + }.
  3662 +
  3663 +define Printable_tree
  3664 + add_js_files
  3665 + (
  3666 + List(JS_File) l
  3667 + ) =
  3668 + if l is
  3669 + {
  3670 + [ ] then [ ],
  3671 + [h . t] then
  3672 + [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\"></script>\n" ]
  3673 + . add_js_files(t)]
  3674 + }.
  3675 +
  3676 +define Printable_tree
  3677 + add_css_styles
  3678 + (
  3679 + List(CSS_Style) css_styles
  3680 + ) =
  3681 +
  3682 + if css_styles is
  3683 + {
  3684 + [] then [],
  3685 + [_._] then [ "<style type=\"text/css\"><!--\n",
  3686 + format_css_styles(css_styles),
  3687 + " --></style>"
  3688 + ]
  3689 + }.
  3690 +
  3691 +define Printable_tree
  3692 + format
  3693 + (
  3694 + CommonInfo cinfo,
  3695 + String state_name,
  3696 + HTML_Page page,
  3697 + Bool is_https,
  3698 + String charset
  3699 + ) =
  3700 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3701 + with ic_v = var((Int32)0),
  3702 + if page is
  3703 + {
  3704 + html_page(title,metas,css_styles, css_files, js_files, body) then
  3705 + if body is body(options,element) then
  3706 + [ doctype_w3c_header,
  3707 + "<html>",
  3708 + "<head>",
  3709 + add_css_styles(css_styles),
  3710 + add_css_files(css_files),
  3711 + add_js_files(js_files),
  3712 + "<link rel=\"shortcut icon\" href=\"favicon.ico\">",
  3713 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3714 + " function show_local_popup(divname,stvname) {",
  3715 + " if (document.layers) { var d = eval(document.divname); } else\n",
  3716 + " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
  3717 + " if (document.all) { var d = eval(document.all.divname.style)};\n",
  3718 + // " alert(typeof(eval(stvname))); ",
  3719 + " var s = eval(stvname); ",
  3720 + " if (s[0]==0) ",
  3721 + " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
  3722 + " { s[0]=0; d.style.visibility = 'hidden'; }; }",
  3723 + "</script>",
  3724 + "<title>",title,"</title>", // put title
  3725 + format(cinfo,state_name,metas,is_https,charset), // format the metas
  3726 + "</head>",
  3727 + "<body ", format(options), ">", // format body options
  3728 + //"<center>",
  3729 + format(cinfo,state_name,ic_v,element,is_https),
  3730 + //"</center>",
  3731 + "</body>",
  3732 + "</html>"
  3733 + ]
  3734 + }.
  3735 +
  3736 +
  3737 +
  3738 +
  3739 +
  3740 +
... ...