Commit 235d10eb82e0a042d672c5dbb70c688aca5e879a

Authored by Cédric RICARD
1 parent a91b94d1

Changing 'hidden()' interface

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