Commit e281d82ed75b007ea0148654519fbfdf43743df0

Authored by Cédric RICARD
1 parent aadc3df9

Adding 'attr' alternative on DIV_Option type

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