*Project* The Anubis Project *Title* Producing HTML/Javascript code. *Copyright* Copyright (c) Alain Prouté 2001. read tools/basis.anubis read system/string.anubis read tools/printable_tree.anubis *** Managing Web Arguments. When a client submits a form, he sends informations to the server. This information is transformed by the server into a list of data of type 'Web_arg'. This is the reason why a 'web page' operation always has a unique argument of type 'List(Web_arg)'. The type 'Web_arg' is defined in 'web/common.anubis' as follows: public type Web_arg: web_arg(String name, String value), upload (String name, String value, String temp_file_path). read CXM_common.anubis In other words, a 'web argument' is just a pair made of the name of the argument, and the value of the argument, and both are character strings. 'upload' will be explained later. The next variable is a multipurpose counter (used to generate unique names). variable Int web_count = 0. define Int new_web_count = web_count <- *web_count+1; *web_count. Names for Web colors. public type Web_color_name: aliceblue, antiquewhite1, antiquewhite2, antiquewhite3, antiquewhite4, aquamarine1, aquamarine2, aquamarine3, aquamarine4, azure1, azure2, azure3, azure4, yellow. and so on ... (see below why I did not do more). Web colors. public type Web_color: rgb(Word8,Word8,Word8), /* give the color by its components */ _(Web_color_name). /* or by its name */ The following produces '' tags, which are put in the head of the document. public type WebMeta: keywords(List(String)), refresh(String url, Int delay), // in seconds meta(String name, String content), http_equiv(String name, String content). ******************************************************* * Web items * * (the many kinds of things one may put in a page) * ******************************************************* public type Web_item: [ ], /* empty (invisible) item */ ... this is a cross recursive type. Options for web page body. public type LayerDisposition: horizontal, vertical. public type FollowPathCommand: // this type is used by the Web_body_option 'follow_path'. pos(Int x, // x coordinate of position Int y, // y coordinate of position Int image_number, // the image to display at that position Int delay). // wait that milliseconds before leaving this position public type Web_body_option: background_color(Web_color), /* color for the background */ // // 'psychedelic_background' produces a background color which is continuously changing. // 'average' is the average luminosity of the color. 'amplitude' is the maximal variation // the luminosity around the average. 'delay' is the number of milliseconds between two // color changes. For example, you may try 'psychedelic_background(200,50,1000)', which // produces a background whose color changes very slowly (this is not tiring) among rather // light pastel colors. // psychedelic_background(Int average, /* average light (0 to 255) */ Int amplitude, /* amplitude of variation of light */ Int delay), /* in milliseconds */ background_image(String file_name), /* name of image file for the background */ // // 'scrolling_layer' produces a layer above the page which is scrolling continuously either // vertically or horizontally. The 'content' is indefinitly repeated. // scrolling_layer(LayerDisposition, Int steps, /* number of pixels of each move */ Int margin, /* measured from left or top in pixels */ Int delay, /* milliseconds for one move */ Web_item content, /* content of layer (will be repeated) */ Int period), /* number of pixels between two instances of 'content' */ // // 'bounce' shows its content above the page and let it move and bounce on the edges of a rectangle. // The rectangle is determined by the last 4 arguments. // bounce(Web_item content, Int left, Int right, Int top, Int bottom), // // put something over the page in any position you want: // over(Web_item content, Int left, Int top), // // follow_path: let a changing image follow a path on the screen. This gadget shows // an image following a polygonal path on the screen. The image may change at regular // intervals, thus providing extra animation. The images are displayed in the order // they are given in the first argument. When the last image has been displayed, the // first image is displayed again, and so on. The path is a sequence of absolute positions // on screen (actually in the browser's window or frame), which is followed in the // order given in the 'path' argument. If 'loop' is true, the path is followed again and again. // Otherwise, it is followed only once. If you want to make a closed loop, the last // position must be the same as the first one. 'steps' is the number of pixels of distance // between two successive positions of the image, and 'delay' the number of milliseconds // between two successive positions. 'change_every' is the number of steps (a 'step' is // passing from one position to the next one) after which the displayed image is replaced // by the next image. // // Each position 'pos(x,y,i,d)' has 4 parameters. 'x' and 'y' are the coordinates of the // position in the browser's window or frame. 'i' is the number of the image to display // at this position (i.e. the rank of the image in the list 'filename'. The first one has // rank 0). 'd' is the delay in milliseconds to wait before leaving that position. // follow_path(List(String) filenames, /* the changing images which follows the path */ Int change_every, /* number of steps betwen two changes */ List(FollowPathCommand) path, /* the polygonal path and commands */ Bool loop, /* if true do it repeatedly, otherwise only once */ Int steps, /* approximative distance (in pixels) between two successive positions */ Int delay), /* milliseconds between two successive positions */ load_image(String name), /* load an image (for next page), which is not displayed */ left_margin(Int), /* left margin for document */ top_margin(Int), /* top margin for document */ margin_width(Int), margin_height(Int), reload_frame(String name, /* name of target frame */ String url), /* url to load in this frame */ onload(String function_name). /* nom de la fonction javascript (sans les '()') */ public type BodyOnload: reload_frame(String name, String url). variable List(BodyOnload) body_onloads = [ ]. define One add_body_onload ( BodyOnload item ) = body_onloads <- [item . *body_onloads]. define Printable_tree format ( BodyOnload item ) = if item is { reload_frame(name,url) then (Printable_tree) [ " window.open('",url,"','",name,"');" ] }. define Printable_tree format ( List(BodyOnload) l ) = if l is { [ ] then (Printable_tree)[ ], [h . t] then (Printable_tree) [format(h) . format(t)] }. ---- Body of a web page. -------------------------------------------- public type Page_body: body(List(Web_body_option), /* list of body options */ Web_item content). /* the content of the page */ public type VFrame: frame(Int height, Printable_tree url, String name). ---- Web pages. ----------------------------------------------------- public type Web_page: web_page(String title, /* title appearing on top of browser */ List(WebMeta) meta_tags, Printable_tree head_scripts, /* scripts à placer dans la balise head */ Page_body body), /* body of page */ standard_frameset(String title, List(WebMeta) meta_tags, Int height, /* height of 'top menu' (pixels) */ Int width, /* width of 'left menu' (pixels) */ Printable_tree main). /* url for main */ +---------+--------------------------+ | | ^ | |<-width->| top height | | | v | | left +--------------------------+ | | | | | main | | | | | | | | | | +---------+--------------------------+ Note: top and left frames must be loaded through the Web_body_option 'reload_frame'. public define Web_page web_page ( String title, Page_body body ) = web_page(title,[],[], body). public define Web_page web_page ( String title, List(WebMeta) meta_tags, Page_body body ) = web_page(title, meta_tags, [], body). public define Web_page web_page ( String title, Printable_tree head_scripts, Page_body body ) = web_page(title, [], head_scripts, body). public define Web_page standard_frameset ( String title, Int height, Int width, Printable_tree main ) = standard_frameset(title, [], height, width, main). variable Printable_tree scripts = []. define One add_script ( Printable_tree script ) = scripts <- [*scripts . script]. ---- Non empty web items. ------------------------------------------- We have already seen the empty web item. Together with the following one, it enables to make (pseudo-)lists of web items, which will be presented one after the other (from left to right) in the browser's window. public type Web_item: [Web_item . Web_item],... A web item may be a simple string or a simple integer: public type Web_item: text(String), text_pt(Printable_tree), text_nowrap(String), text_nowrap_pt(Printable_tree), par(String), preformated_text(String text), integer(Int), float(Float,Int),... You may want to center a web item in a page. Just enclose it into 'center(...)': public type Web_item: center(Web_item),... You may want to write characters of a given item with a big font: public type Web_item: bigger(Int,Web_item), smaller(Int,Web_item), bold(Web_item), italic(Web_item), big(Web_item), very_big(Web_item),... Most of the previous are subsumed by 'style': public type WebStyle: background_image(String file_name), background_color(Web_color color), background_transparent, background_repeat_horizontal, // repeat the background image only horizontally background_repeat_vertical, background_no_repeat, color(Web_color color), float_to_left, // the web item will float to the left and text will wrap around float_to_right, font_family(String font_name), // "verdana" "helvetica" "times" etc... font_size(Int size), italic, oblique, small_capitals, bold, bolder, lighter, line_height(Int height), text_center, text_left, text_right, text_justify, text_underline, text_blink, text_line_through, width(Int n). public type Web_item: style(List(WebStyle) styles, Web_item content),... public type Web_item: spacer(Int width, Int height), image(String file_name), /* image */ image_d(String file_name, String description), image_pt(Printable_tree file_name), on_image(String file_name, Web_item content), turning_images(NonEmptyList(String) filenames, Int millisecs),... A 'rollover' has the same role as a submit button or link, but it is prettier. It is made of two images. The first one 'image_on' determines the aspect of the button when the mouse cursor is on it. The other one 'image_off' determines the aspect of the button when the mouse cursor is anywhere else. The two images should be of the same size, otherwise bad effects may occur. The last operand 'description' is a small text which describes the role of the button. It appears in a bubble in the browser's window. public type Web_item: rollover(List(String) preload_images, // images to preload before the rollover is effective String url, // URL with possible web arguments String target, String image_on, // file name of 'highlighted' image String image_off, // file name of 'non highlighted' image String description), // short behavior description rollover(List(String) preload_images, String url, String target, String image_on, String image_off, Int width, Int height, String description), ... Mouse sensitive images are images with predefined zones which are clickable. When clicking in a zone, the specified corresponding URL is loaded by the browser. If two zones overlap, the first one (in the order they are defined) is selected. Zones are of 3 sorts: rectangles, circles and polygons. Point's coordinates are specified as pairs of integers (of anonymous agglomeration type (Int,Int)). The first coordinate counts pixels from the left of the image. The second coordinate counts pixels from the top of the image. With polygons, you can construct zones which are almost as complicated as you want. You may also construct a zone as the overlapping of several zones with the same URL. public type Mouse_Sensitive_Zone: rectangle ( (Int,Int) left_top, (Int,Int) right_bottom, String url ), circle ( (Int,Int) center, Int radius, String url ), polygon ( List((Int,Int)) vertices, String url ). public type Web_item: mouse_sensitive_image(String image_file_name, // the image itself List(Mouse_Sensitive_Zone) zones),... In project: mouse sensitive images, whose zones behave like submission buttons (to be used within a form). public type Web_item: background_sound(String sound_file_name, Bool loop),... ************************* * FORMS * ************************* Use 'forms' in order to get informations back from the client. The constructor 'form' take 2 arguments: - the name of the form, which must be the name of an Anubis web page. Indeed, when the user will submit the form, this page will be sent to him. - the content of the form, which may be any web item, but which normally (amongh other things) contains input fields and a submit button. public type Web_item: form(Printable_tree name, Web_item content), form_target(Printable_tree name, Web_item content, String target), form(Printable_tree name, String label_name, Web_item content), form_name(String form_name, // option name de form Web_item content),... public define Web_item form ( Printable_tree name, Web_item content ) = form("", name, content). Within a form, you may put 'text input fields', that the client may edit. The constructor 'text_input' has the following arguments: - name of input field. This will be the name of the correponding web argument in the Anubis web page referred to by the form. - size of field (as it appears on client screen), - initial value of field (the text that appears in the field, when the client downloads the page). public type Text_Input_Option public type Web_item: text_input(String name, /* text field to be documented by user */ Int size, String initial_value),... public define Web_item text_input ( String name, Int size, String initial_value ) = text_input( (List(Text_Input_Option)) [], name, size, initial_value). public type Web_item: password_input(String name, Int size), text_area(String name, Int columns, Int rows, String initial_text), upload(String name, Int size),... public type Web_item: submit(String button_text), /* submit button with text on it */ submit_pt(Printable_tree button_text), submit(String name, String text), submit_close(String name, String text), submit_pt2(String name, Printable_tree text), image_submit(String name, String image_file_name), image_submit(String name, String value, String image_file, Web_item content), hl_image_submit(String action_name, String value, String image_name, String image_file, String hl_image_file), text_submit(String name, String value, String text), web_submit(String web_args, Web_item content), button(String name, String text, String on_click_fonction, Int width, Int height),... public type Web_item: /* mark the form with an information */ mark(String name, String value), mark_pt(String name, Printable_tree value),... public type Web_item: close_button, /* button that closes the window */ close_button(String image_file_name), ... ********************************* * LABELS * ********************************* A 'label' is just a name that you may give to a position in a document. Use the following invisible Web_item 'label' to this end. Now, you can also create links in the same document, which, when clicked by the user, scroll the document, so that the position whose name is the given label is shown just at the top of the browser's window. public type Web_item: label(String label_name), /* give a name to a position in the page */ go_to_label(String label_name, /* a link for jumping to a label */ Web_item content),... ******************************** * TABLES * ******************************** A web item may be a table. A table is produced by the constructor 'table' from the type 'Web_item'. This constructor takes 2 arguments: - a list of 'table options', - a list of 'table rows'. Of course, you use as many options as you want, including none (if you do not want any option, put the empty list '[ ]' as this argument). Some options have precedence over others. For example a background image will hide the background color. Table options are defined below: public type Table_option: /* use a color as a background for the table, if you want it to be different from the background of the page */ background_color(Web_color), /* or use an image as the background of the table */ background_image(String file_name), /* draw a border line around the table (and around each cell in the table). You may also specify a geometry (in pixels) for the border. This makes the 'in relief' part of the border appear more or less wide. You may also specify a color for the border. */ border, nude, /* equivalent to 'border(0,0,0)' (below) */ border(Int, /* width of exterior (pixels) */ Int, /* width of top */ Int), /* width of interior */ border_color(Web_color), absolute_width(Int). A 'table row' is made of a list of 'row options', and a list of 'cells'. A 'cell' itself has a list of 'cell options', and a web item, which is its content. We begin by the description of options. public type Row_option: /* following concerns the horizontal positions of items within the cells of the row */ left, h_center, right, /* the following concerns the vertical positions of items, within the cells of the row */ top, v_center, bottom, absolute_height(Int), base_line, /* set the background color of all cells in the row */ background_color(Web_color). public type Cell_option: /* all row options are available for individual cells, and apply here only to one cell. */ left, h_center, right, top, v_center, bottom, base_line, background_color(Web_color), /* you can set the width of the cell either absolutely (in pixels) or as a percentage of the width of the table. */ background_image(String file_name), absolute_width(Int), relative_width(Int), absolute_height(Int), relative_height(Int), /* a cell may span over several columns or rows in the table */ columns(Int), rows(Int), nowrap. public type Cell: cell(List(Cell_option), Web_item). public type Table_row: row(List(Row_option), List(Cell)). public define Table_row row(Web_item i) = row([],[cell([],i)]). public define Table_row row(Cell c) = row([],[c]). public define Table_row row(List(Cell) l) = row([],l). public type Web_item: table(List(Table_option), List(Table_row)),... public type Web_item: list(List(Web_item)),... public type Web_item: link(String name, Web_item), link(String name, String target, Web_item),... public type Web_item: link_for_download(String filename, Web_item),... // the filename is relative to the public directory public type Web_item: mail_to(String addr, Web_item),... public type Web_item: select(String name, Int size, List(String) choices), select(String name, Int size, List(String) choices, String selected), immediate_select(String name, // selection will immediately submit the form Int size, List(String) choices),... public type Web_item: radio_button (Printable_tree name, String value), checked_radio_button (Printable_tree name, String value), check_box (Printable_tree name, String value), checked_box (Printable_tree name, String value),... public type Web_item: link_to_window(Printable_tree name, Web_item), link_to_window(Printable_tree name, String window_name, Web_item), link_to_window_with_ticket(String name, String web_args, String window_name, Web_item content, Int width, Int height), link_to_window_with_ticket_and_scroll (String name, String web_args, String window_name, Web_item content, Int width, Int height), link_to_window_with_ticket_and_scroll (String name, String label_name, String web_args, String window_name, Web_item content, Int width, Int height), link_to_frame (Printable_tree name, String frame_name, Web_item). ---- Formating operations (Anubis --> HTML/Javascript) --------------------------- Stupid operation formating a web color name. public define String format ( Web_color_name n ) = if n is { aliceblue then "aliceblue", antiquewhite1 then "antiquewhite1", antiquewhite2 then "antiquewhite2", antiquewhite3 then "antiquewhite3", antiquewhite4 then "antiquewhite4", aquamarine1 then "aquamarine1", aquamarine2 then "aquamarine2", aquamarine3 then "aquamarine3", aquamarine4 then "aquamarine4", azure1 then "azure1", azure2 then "azure2", azure3 then "azure3", azure4 then "azure4", yellow then "yellow", }. Anubis really needs some system of 'macros' to avoid this... Formating a web color. public define String format ( Web_color wc ) = if wc is { rgb(r,g,b) then "\"#" + to_hexa(r) + to_hexa(g) + to_hexa(b) + "\"", _(c) then format(c) }. public define String format_without_quotes ( Web_color wc ) = if wc is { rgb(r,g,b) then "#" + to_hexa(r) + to_hexa(g) + to_hexa(b) + "", _(c) then format(c) }. define Printable_tree format ( Web_color c ) = [(String)format(c)]. public define String format_without_sharp ( Web_color wc ) = if wc is { rgb(r,g,b) then "" + to_hexa(r) + to_hexa(g) + to_hexa(b) + "", _(c) then format(c) }. define Printable_tree format_without_sharp ( Web_color c ) = [(String)format_without_sharp(c)]. define Printable_tree [Word32 x . Printable_tree t] = [to_Int(x) . t]. define Printable_tree psychedelic_bg ( Int average, Int amplitude, Int delay ) = with ampl = if amplitude >= 120 then 120 else if amplitude =< 1 then 1 else amplitude, with aver = if average+ampl >= 254 then 254-ampl else if average-ampl =< 1 then 1+ampl else average, [ ""]. define List(Web_body_option) replace_background_init ( List(Web_body_option) l, Int average, Int amplitude, Int delay ) = if l is { [ ] then [ ], [h . t] then if h is background_color(_) then [background_color(rgb(truncate_to_Word8(average+amplitude), truncate_to_Word8(average), truncate_to_Word8(average-amplitude))) . replace_background_init(t,average,amplitude,delay)] else [h . replace_background_init(t,average,amplitude,delay)] }. define Maybe((Int,Int,Int)) get_psy ( List(Web_body_option) l ) = if l is { [ ] then failure, [h . t] then if h is psychedelic_background(a,f,d) then success((a,f,d)) else get_psy(t) }. define List(Web_body_option) prepare ( List(Web_body_option) l ) = if get_psy(l) is { failure then l, success(op) then if op is (a,f,d) then replace_background_init(l,a,f,d) }. public define Printable_tree format ( String c_ticket, String s_ticket, Web_item i ). define Printable_tree move_layer_command ( String property, Int n, Int num, Int i, Int period ) = if i >= num then [ ] else [" if (document.layers)", " { document.nslay",n,"_",i,".",property,"=layp",n,"+(",((i-1)),"); } ", " else ", " { ielay",n,"_",i,".style.",property,"=layp",n,"+(",(i-1)*period,"); } " . move_layer_command(property,n,num,i+1,period)]. define Printable_tree format_layers ( LayerDisposition disp, Int margin, Web_item content, Int n, Int num, Int i, Int period ) = if i >= num then [ ] else ["", "
", format("","",content), "
" . format_layers(disp,margin,content,n,num,i+1,period)]. define Printable_tree s_layer ( LayerDisposition disp, Int steps, Int margin, Int delay, Web_item content, Int period, Int num ) = with n = new_web_count, [ "", format_layers(disp,margin,content,n,num,0,period), ]. define Printable_tree over ( Web_item i, Int left, Int top ) = with n = new_web_count, [ "", "
", format("","",i), "
" ]. define Printable_tree bnce ( Web_item i, Int left, Int right, Int top, Int bottom ) = with n = new_web_count, [ "", "", "
", format("","",i), "
" ]. define Printable_tree folp_switch ( Int n, Int i, List(FollowPathCommand) path, Bool loop, Int steps ) = if path is { [ ] then [ ], [p0 . t0] then if p0 is pos(x0,y0,i0,d0) then if t0 is { [ ] then if loop then [" default: ", " folpx",n,"=",x0,"; folpy",n,"=",y0,"; ", " folpseg",n,"=0; ", " folpwait",n,"=",d0,";", " if(document.layers)", " document.nslay",n,".document.folpim",n,".src=folpimages",n,"[",i0,"].src;", " else document.folpim",n,".src=folpimages",n,"[",i0,"].src;", " folpstpmax",n," = 0;", " folpstp",n,"=0;", " folpdx",n,"=0; ", " folpdy",n,"=0; ", " break;"] else [" default: folpend",n,"=1; break; "], [p1 . t1] then if p1 is pos(x1,y1,i1,d1) then [ " case ",i,": ", " folpx",n,"=",x0,"; folpy",n,"=",y0,"; ", " folpseg",n,"=",i+1,"; ", " folpwait",n,"=",d0,";", " if(document.layers)", " document.nslay",n,".document.folpim",n,".src=folpimages",n,"[",i0,"].src;", " else document.folpim",n,".src=folpimages",n,"[",i0,"].src;", " folpstpmax",n," = ", "Math.round(Math.sqrt(Math.pow(",x1,"-",x0,",2)+Math.pow(",y1,"-",y0,",2))/",steps,");", " folpstp",n,"=0;", " folpdx",n,"=((",x1,"-",x0,")/folpstpmax",n,"); ", " folpdy",n,"=((",y1,"-",y0,")/folpstpmax",n,"); ", " break; " . folp_switch(n,i+1,t0,loop,steps) ] }}. define Printable_tree set_folpimages ( Int n, List(String) filenames, Int i, ) = if filenames is { [ ] then [ ], [h . t] then [ " folpimages",n,"[",i,"].src=\"",h,"\";" . set_folpimages(n,t,i+1)] }. define Printable_tree follow_path ( List(String) filenames, Int change_every, List(FollowPathCommand) path, Bool loop, Int steps, Int delay ) = if filenames is { [ ] then (print("Error in usage of 'follow_path': 'filenames' must be non empty."); []), [im1 . other_ims] then if steps < 1 then (print("Error in usage of 'follow_path': 'step' must be >= 1."); [ ]) else if path is { [ ] then [ ], [p0 . t0] then if p0 is pos(x0,y0,i0,d0) then if t0 is { [ ] then (print("Error in usage of 'follow_path': 'path' must have at least 2 positions."); []), [p1 . t1] then if p1 is pos(x1,y1,i1,d1) then with n = new_web_count, [ "", "
", "", "
", "", ] }}}. public type ImageToLoad: simple(String image_name), with_rollover(String image_name, String rollover_name). variable List(ImageToLoad) images_to_load = []. public define Printable_tree format ( Web_body_option o ) = if o is { background_color(c) then [" bgcolor=" , (String)format(c)], psychedelic_background(a,f,d) then add_script(psychedelic_bg(a,f,d)); [ ], background_image(n) then [" background=", n], scrolling_layer(disp,st,m,t,c,p) then add_script(s_layer(disp,st,m,t,c,p,2000\p)); [ ], bounce(i,l,r,t,b) then add_script(bnce(i,l,r,t,b)); [ ], over(i,l,t) then add_script(over(i,l,t)); [ ], follow_path(li,ns,p,l,s,d) then add_script(follow_path(li,ns,p,l,s,d)); [ ], load_image(n) then images_to_load <- [simple(n) . *images_to_load]; [ ], left_margin(n) then [" leftmargin=", n], top_margin(n) then [" topmargin=", n], margin_width(n) then [" marginwidth=", n], margin_height(n) then [" marginheight=", n], reload_frame(n,url) then add_body_onload(reload_frame(n,url)); [ ], onload(n) then [" onLoad=\"", n, "()\""] }. define Printable_tree preload_list ( List(ImageToLoad) images, Int n, ) = if images is { [ ] then [ ], [h . t] then [" preloaded_images[",n,"].src = '",image_name(h),"';", if h is { simple(_) then [], with_rollover(n1,r) then [" preloaded_images[",n1,"].onload = 'allow_rollover(\"",r,"\")';"] } . preload_list(t,n-1)] }. define Printable_tree load_image_script ( List(ImageToLoad) images ) = if images is { [ ] then [ ], [_ . _] then [ "" ] }. public define Printable_tree format(String c_ticket, String s_ticket, Web_item i). public define Printable_tree format(List(Table_option) l) = if l is { [ ] then [ ], [h . t] then [if h is { background_color(c) then [" bgcolor=", (String)format(c)], background_image(f) then [" background=",f], border then [" border"], nude then [" border=\"0\" cellspacing=\"0\" cellpadding=\"0\""], border(e,top,i) then [" border=",e," cellspacing=",top," cellpadding=",i], border_color(c) then [" bordercolor=", (String)format(c)], absolute_width(n) then [" width=",n] }, format(t)] }. public define Printable_tree format(List(Row_option) l) = if l is { [ ] then [ ], [first . others] then [if first is { left then [" align=left"], h_center then [" align=center"], right then [" align=right"], top then [" valign=top"], v_center then [" valign=center"], bottom then [" valign=bottom"], absolute_height(n) then [" height=\"",n,"\""], base_line then [" valign=baseline"], background_color(c) then [" bgcolor=",(String)format(c)] }, format(others)] }. public define Int percentage(Int n) = if n < 0 then 0 else if n > 100 then 100 else n. public define Int percentage(Int n) = n. public define Printable_tree format ( List(Web_body_option) l ) = if l is { [ ] then [ ], [h . t] then [format(h) . format(t)] }. public define Printable_tree format(List(Cell_option) l) = if l is { [ ] then [ ], [first . others] then [if first is { left then (Printable_tree)[" align=left"], h_center then (Printable_tree)[" align=center"], right then (Printable_tree)[" align=right"], top then (Printable_tree)[" valign=top"], v_center then (Printable_tree)[" valign=center"], bottom then (Printable_tree)[" valign=bottom"], base_line then (Printable_tree)[" valign=baseline"], background_color(c) then (Printable_tree)[" bgcolor=",(String)format(c)], background_image(n) then (Printable_tree)[" style=\"background: url(",n,")\""], absolute_width(w) then (Printable_tree)[" width=",w], relative_width(r) then (Printable_tree)[" width=",percentage(r),""], absolute_height(h) then (Printable_tree)[" height=",h], relative_height(r) then (Printable_tree)[" height=",percentage(r),""], columns(n) then (Printable_tree)[" colspan=",n], rows(n) then (Printable_tree)[" rowspan=",n], nowrap then (Printable_tree)[" nowrap"] } . format(others)] }. public define Printable_tree format ( String c_ticket, String s_ticket, List(Cell) l ) = if l is { [ ] then [ ], [first . others] then [if first is cell(options,item) then ["", format(c_ticket,s_ticket,item),""], format(c_ticket,s_ticket,others)] }. public define Printable_tree format ( String c_ticket, String s_ticket, List(Table_row) l ) = if l is { [ ] then [ ], [first_row . other_rows] then [if first_row is { row(options,cells) then [ "", format(c_ticket,s_ticket,cells),""] }, format(c_ticket,s_ticket,other_rows)] }. public define Printable_tree format_choices ( List(String) l ) = if l is { [ ] then [ ], [h . t] then ["