*Project* Anubis *Title* Making interactive Web sites. *Copyright* Copyright (c) Alain Prouté 2004-2005. Copyright (c) Calexium 2007-2017. Copyright (c) David René 2007-2020. *Authors* Alain Prouté David René Jérémy Larrieu Julien Verneuil *Revised* May 2015 *Overview* In this file we propose simple tools for making well structured interactive and secured web sites. ----------------------------------- Table of Contents --------------------------------- * (1) Structure of a web site. ** (1.1) Three sorts of data. ** (1.2) How requests are handled. ** (1.3) What web pages are made of. ** (1.4) Actions. ** (1.5) States. * (2) Carrying on. ** (2.1) Describing your web sites. ** (2.2) Directories on the server's disk. ** (2.3) Starting your web sites. * (3) The HTML interface. ** (3.1) Types used by the HTML interface. ** (3.2) ``in form'' versus ``off form''. ** (3.3) Defining your own style. ** (3.4) Actioners and forms. ** (3.5) Local popup. --------------------------------------------------------------------------------------- transmit tools/basis.anubis read tools/printable_tree.anubis read tools/base64.anubis read tools/random.anubis read tools/dictionaries.anubis read system/lists.anubis read system/string.anubis read system/logger.anubis transmit CXM_common.anubis transmit CXM_multihost_http_server.anubis read web/mime.anubis read CXM_cookies.anubis read CXM_json.anubis transmit CXM_web_dump.anubis transmit CXM_web_arg_utils.anubis transmit CXM_web_session.anubis transmit CXM_web_action.anubis transmit xlib/web/types/web_action_name.anubis transmit xlib/web/types/making_a_web_site.anubis //TODO move anywhere define List(Word8) /** Test for HTML entity which must be encoded * and return the encoded entity if needed */ get_entity ( Word8 _char, List(Word8) tail )= //test for the ampersand if _char = '&' then //test if it's already an encoded string like & < etc. In that case we do nothing if insensitive_equal(['&','a','m','p',';'], tail, 0) then [_char] else if insensitive_equal(['&','l','t',';'], tail, 0) then [_char] else if insensitive_equal(['&','g','t',';'], tail, 0) then [_char] else if insensitive_equal(['&','q','u','o','t',';'], tail, 0) then [_char] else [';','p','m','a','&'] //test for '<' etc. else if _char = '<' then [';','t','l','&'] else if _char = '>' then [';','t','g','&'] else if _char = '\"' then [';','t','o','u','q','&'] else [_char] . define String _HTML_encode_entities ( List(Word8) source, List(Word8) current )= if source is { [] then implode(reverse(current)), [c . t] then _HTML_encode_entities(t, get_entity(c, t) + current) } . public define String encode_HTML_entities ( String source )= _HTML_encode_entities(explode(source), []) . //public define String // encode_HTML_entities // ( // String source // )= //. //read CXM_html_tooltip.anubis * (1) Structure of a web site. First of all we need to explain what a web site should be made of. Ideally, the visitor (also called the 'client') should see the web site working as any other interactive computer software. So, it should be clear that a 'session' (i.e. a visit to the web site, including the consultation of several pages) is some kind of conversation between the visitor and the web site, and that the web site should maintain a 'current state' of this conversation. At each new request (click) from the visitor, this state must be updated. This whole conversation is called a 'session' and should not be confused with a single request. ** (1.1) Three sorts of data. All the data needed for putting a web site at work may be dispatched into three categories: 1. Constant data (data that never change). These data may be hard coded into the Anubis source files of the web site. 2. Permanent data (data which always exist independantly of the users connected to the web site). These data are normally recorded into data bases. 3. Session data (data which depend on a particular visitor and which exist only during the time he visits the web site). These data are stored into so-called 'states'. It is important to determine which data belongs to which category. This is part of your design decisions. ** (1.2) How requests are handled. We want to separate the following two functionalities (which are used at each request (click) during a single session): - computing the new state from the previous state and from the client request, and updating the data base, - computing the page to be sent to the client from the new current state and from the informations in the data base. The next picture shows the structure we have in mind: request +---------+ HTML page (with state name in cookie) .-------------------| client |<--------------. | .-----------------| | | | | session +---------+ | | | in cookie | | | | client side ............................................................................ | | | server side | | | | | .-------------------. | | | | previous session | | V V V | | +---------------+ +---------------+ +--------------+ | compute state | | server's disk | | compute page | +---------------+ +---------------+ +--------------+ ^ | | ^ ^ ^ ^ ^ | | | | | | | | | | `--------------------+--------------------' | | | | new state | | | read | `------------------------+--------------------' | write | new state name | update V | +-----------+ | | data base |--------------------------------------------' +-----------+ read only When the client begins a session, there is no previous state. In this case, a default 'initial state' is used instead. The data base may be updated by 'compute state' box, but should not be update by the 'compute page' box. The 'compute page' box should be allowed only to read the data base. In this file, all the above stuff is defined, except the 'compute state' and 'compute page' boxes. You just have to provide the function for computing a new state (compute state) and the function for computing the page (compute page) from the new state. You don't have to worry about state names, saving and retrieving states and the like. ** (1.3) What web pages are made of. What the client can see in his browser's window may be called a 'page'. Within a page, we have several sorts of components: - 'local' components, i.e. all components which do not open a connection, like texts, images, etc... possibly using JavaScript programmation, - 'actioners', which, when clicked upon, open a connection with our web site; they may appear as links or buttons, etc... - 'foreign links', which when clicked upon, open a connection with another web site (or ours eventually). Of course, what an actioner does is just ask our web site to perform an action. To that end, the actioner essentially sends the name of the action to be performed. However, it may be necessary to provide additional informations which may be seen as 'operands' of the action. In order to attach operands to an action, HTML provides the notion of 'form'. Indeed, a form contains essentially a set of input fields into which the client may put values for the required operands of the action, and a submit button, which is the actioner itself. Notice that a single form may contain several submit buttons, which simply means that there are several distincts actions taking the same set of operands. Restrictions must be put on the use of all theses gadgets. Indeed, for example, putting a form within another form is officially meaningless in HTML, and the client's browser may be seriously disturbed by this. In this file, we propose an interface to the HTML language, which forbids such meaningless things, simply by imposing a strict typing of HTML concepts. Each web site may be accessible through two communication channels: - a non secured channel (HTTP), - a secured channel (HTTPS). Nevertheless, the whole thing should be considered as a single web site. For example, you may have a secured page, obtained through HTTPS, containing public images obtained through HTTP. An actioner in a non secured page may open a secured connection, and conversely. Summarizing, a web page is made of local elements, foreign links and actioners. Actioners receive operands from forms, and they also choose to communicate through the non secured or through the secured channel. +-------------------+ | page | | | +---------------+ | +--------------+ | | next page | | | form | | | (non secured) | | | +----------+ | | HTTP | | | | | actioner |---------------------------->| | | | +----------+ | | +---------------+ | | | | | | +----------+ | | +---------------+ | | | actioner |---------------------------->| next page | | | +----------+ | | HTTPS | (secured) | | | | | | | | +--------------+ | | | | | +---------------+ | | +-------------------+ Notice that actioners need no be necessarily put into forms. In that case, they work as ordinary links, but they still may receive operands as we shall see. ** (1.4) Actions. The client opens a new connection with our web site whenever he clicks on an actioner. The result is that a request is sent, essentially made of a list of 'web arguments'. Each web argument is a pair (name,value). One of these web arguments, the 'action' web argument (whose name is "a"), determines the action to be performed. The other web arguments (not including "s", used to identify the state) are the operands for this action. Hence, the 'compute state' box in the picture above, splits naturally into as many sub-boxes as there are actions. For this reason, we define the following type for representing actions (where '$State' is the type representing session informations): 'http_action's are executed only under HTTP, and 'https_action's are executed only under HTTPS. 'http_https_action's may be executed under both types of connections. Each action has a name, which is used to identify the action. Each action also has a function 'allow' whose job is to verify that the action is allowed in the current state, and a function 'do_it' for performing the action. The function 'do_it' receives a lot of informations: - 'HTTP informations': - the IP address of the client, - the URI requested by the client (after redirection), - the list of HTTP headers generated by the client's browser, - the list of web arguments sent by the client (except "s" and "a"), - the previous state (or the 'initial' or 'ticket expired' state if no previous state can be found). In most cases, HTTP informations are not used. This is the reason why they are gathered for simplicity into a unique datum of type 'HTTP_Info'. When you define your web site, you must provide the list of all the actions of the site. When a new state has been computed, a graphical representation of this state must be sent to the client. To that end, you must provide a function (named below 'compute_page') of type: $State -> HTTP_Answer where the type 'HTTP_Answer' (defined below in this file) abstractly represents HTML pages. //public type HTTP_Answer:... It should be clear that states and pages are deeply linked together. Indeed, we really understand the page shown to the client as a representation of the current state of the conversation between the client and the web site, but also containing informations taken from the data bases. //public type HTML_Partial_Content:... public define List(HTTP_header) make_session_cookie_headers ( String website_name, String session_name ) = //println("Set-Cookie state_"+website_name+"="+state_name); [ http_header("Set-Cookie", "session_"+website_name+"="+session_name) ] . When a request arrives, we need to retrieve the previous state from the server's disk. We receive the name of that state. If the state is out of date, the state file is kept 3 days, and then deleted. define One delete_out_of_date_sessions // for all web sites ( List((String, List(String) -> One)) directories_and_functions )= if directories_and_functions is { [ ] then unique, [h . t] then since h is (state_directory, function), function(directory_list(state_directory,"*")); delete_out_of_date_sessions(t) } . ** (1.5) States. Now, we explain how you can define the type (say 'State') to be used as an instance of the type parameter '$State'. The following is just a suggestion. Each state determines a page (since 'compute_page' computes a page from a state). However, some components of the state may be independant of the page. It may be the case for example for the indication of the natural language used by the client. Hence, a state should be made of (at least) two parts: - informations which are the same for all pages, - informations which are particular to each page. For example, you could define: type Page: // one alternative per page, with particular informations login(...), // in the components main_page(...), ...etc... Now, the type 'State' could be defined as follows: type State: state(Language, // informations valid for all pages ..., Page). // informations particular to a page However, if you are making a secured web site within which clients should be identified (by id and password), it may be a good idea to have two sorts of states, one for non identified clients and one for identified clients. In this case, define the type 'State' as follows (this is just a suggestion): type State: non_identified(Language), identified(String id, Language, Page). When a request arrives, check if the previous state is 'identified(...)' or 'non_identified(...)', and don't provide access to certain pages to non identified clients. This is required for security. Some more words on security. If your site needs to identify clients, define the initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and password of the client. If the id and password are correct, then change the state of the client to 'identified(...)'. No other action should be able to do that. Now, be confident that clients cannot forge states. The only information they have is the name of a state, not the state itself which is never sent over the network, but only stored on the server's disk. The name of the state is constructed using strong cryptographical methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even state names cannot be seen by a third party. So, if the system retrieves a previous state of the form 'identified(...)', you can be confident that your client is well identified, and you can send him confidential informations. States have a limited life time. It may happen that a client clicks on a button at a time its state is out of date. In this case, this system considers that the new state is a special state named 'ticket expired'. You must provide a function producing this state when you describe your web site. The page corresponding to this state must just inform the client that he/she waited a too long time before clicking on a button, and has to restart (a new conversation) from the begining. * (2) Carrying on. ** (2.1) Describing your web sites. Before you may start your web site, you must describe it, i.e. produce a datum of the opaque type 'Web_Site'. public type Web_Site:... Producing such a datum may be performed by: public define Web_Site make_web_site_description ( List(String) common_names, // for example: ["www.our-business.com", // "192.168.0.1"] // the second one is just for testing String site_directory, // where 'public' and other directories are // located (should NOT end with '/') String state_directory, One -> One init, (HTTP_Info, List(Web_arg), Bool is_https) -> $State initial_state, ($State expired, Maybe(String), HTTP_Info, List(Web_arg), Bool is_https) -> $State ticket_expired_state, (Maybe(String), HTTP_Info, List(Web_arg), Bool is_https) -> $State ticket_lost_state, List(Web_Action($State)) actions, $State -> HTTP_Answer compute_page, $State -> List(HTTP_header) additional_headers, List(HTTP_header) constant_additional_headers, Int timeout, // seconds (todo: minutes) Redirections redirections, String charset, List(String) journal_extensions, List(String) journal_headers, String authorization_secret, List(MIME) known_mime_types, (String action_name, List(Web_arg) args)-> One before_send_file ). Explanations: 'common_names' is the list of names of the site (the name the browser must send as the value of the 'Host' HTTP header in order to access the site must be in that list). Such a name generally looks like this: www.somewhere.com If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of the site must be exactly the same as the name on the certificate (which is precisely called the 'common name' in the X.509 jargon). If the two names do not match, the site will still work, but the transaction will not be transparent to the client. His browser will complain that the name of the certificate does not match the name of the site, and he will have to accept the certificate manually. 'site_directory' is the absolute path to the directory where the files needed by the site are located. Usually this directory looks like: my_anubis/web_sites/www.somewhere.com However, this information is not computed from 'common_name', so that you can change the common name (for example temporarily, for networking reasons) without loosing access to the files. 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state whose graphical representation is a page explaining to the user that its 'ticket' (or 'session information') has expired, and that he/she must close all popup windows and start a new session. The arguments of the function contain the previous (expired) state and all current informations concerning the user. This arguments may be useful for example for producing the expiration message in the language chosen by the user. You can also (and this may be much smarter) send a 'ticket prolongation page' (including a new login for example), and resume the same conversation, since you have all the pertinent informations at hand. In the case the ticket is definitely lost, the second fonction 'ticket_lost_state' is used. Notice that despite the fact that the parameter $State is involved in the arguments of the above function, the type 'Web_Site' does not depend on this parameter. This allows to produce lists of web site descriptions, where each description may be constructed with a different instance of $State. This is required because distinct sites must have distinct types of session informations. This is made possible by the fact that the type is obscure, and the constructor replaced by a function which assembles 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single entity not depending on $State. You should have a look to the private part of this file if you want more precisions about this programming technique. 'charset' is a string which will determine the character encoding to be used by the browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252", etc... 'before_send_file' is a function which is executed just before the HTTP server sends a file. It gets an action name and the web arguments received with the request for that file. Notice that this action name and these web arguments may be put into a 'private_download' element, and will come back to the server at the time of the download. ** (2.2) Directories on the server's disk. The description of your site contains the name of the directory within which the required files are located. This may be for example: my_anubis/web_sites/www.our-business.com/ This is called the 'site directory' (for the given site). Within the site directory, the following directories are created by this program: states public journal private_download upload_temporary The directory 'states' is used for storing states (session informations). Out of date states are automatically removed after some time. The tree rooted at 'public' contains files that the server is allowed to send to the clients. For security reasons, the server never sends a file which is not within the tree whose root is this 'public' directory (except for the 'private download' mecanism; see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis') must have been recognized before the file may be sent. The directory 'journal' contains the jounal files. The roles of the remaining directories 'private_download' and 'upload_temporary' is explained in 'multihost_http_server.anubis', where you will also find further informations on 'public' and 'journal'. ** (2.3) Web servers parameters. The web servers have several parameters useful for administration. They are described as follows: public type WebServersParameters: wsparms(Var(Bool) shutdown_required, ** (2.4) Starting your web sites. When you have described all your web sites (you may want to have several web sites, and they are distinguished by their 'common name'), you may start them all together using 'start_web_sites' below. This function returns a result of the following type: public type Start_Web_Sites_Result: cannot_bind_to_port(Word32), cannot_bind_to_port(Word32,Word32), ok(Server http_server, Server https_server). Indeed, it may happen that the system cannot bind (begin to listen) to one of the two ports (or to both). The main reason is that another server is already listening on that port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that the 's' bit has not been set for 'user' and 'group' (there is not such problem under Windows). Also notice that the Linux kernel may need a rather long time (up to several minutes) before liberating a listening port. Now, if the system can bind to the two ports, the pair of the two servers is returned. Two tools are useful for manipulating servers: shutdown of type Server -> One is_down of type Server -> Bool They are defined in 'predefined.anubis' (together with the type 'Server'). public define Start_Web_Sites_Result start_web_sites ( Word32 ip_address, // the IP address shared by the web sites Word32 http_port, // usually: 80 Word32 https_port, // usually: 443 String ssl_certificate_common_name, List(Web_Site) web_sites, // web sites to be started (One) -> Bool shutdown_required ). 'ip_address' is the IP address on which the two servers listen. If you put 0, the servers listen on all the IP addresses of the machine. This may be useful if the machine has several network interfaces. 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec' loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server certificate. This is due to a problem of conception of SSL itself. See the book 'SSL and TLS' by Eric Rescorla (at Addison Wesley) for more explanations. Notice that the number of servers is always 2, regardless of the number of web sites you are starting. The dynamic variable 'shutdown_required' may be used to control the shutdown of the two servers from within the web site (typically the administration part). The servers will shutdown as soon as this variable contains 'true'. So you must provide a variable containing 'false' otherwise your servers will not run. You may also use the primitive 'must_restart' (see 'predefined.anubis') to control the restarting of your servers. * (3) The HTML interface. We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more reactive and attractive on the client side. ** (3.1) Types used by the HTML interface. For easy reference, we gather below the definitions of all the types used by the HTML interface, and we comment them immediately. //public type HTML_Off_Form:... public define Printable_tree [HTML_Id x . Printable_tree y] = str_pt(x.id, y). public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y). public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y). public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y). public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y). public define CoreAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false"). public define InputAttrs attr(String name, Bool value) = attr(name, if value then "true" else "false"). public define Text_Option tooltip(String s) = title(s). A list of 'Text_Option' must be given with each text you want to put in your page. public define CoreAttrs tooltip(String s) = title(s). public define InputAttrs tooltip(String s) = title(s). public type I18n: lang (String), dir (Reading_Way). A list of 'DIV_Option' must be given with each DIV you want to put in your page. public define String event_name ( HtmlEvents e ) = if e is { onafterprint then "onafterprint", //HTML5 Script to be run after the document is printed onbeforeprint then "onbeforeprint", //HTML5 Script to be run before the document is printed onbeforeunload then "onbeforeunload", //HTML5 Script to be run when the document is about to be unloaded onerror then "onerror", //HTML5 Script to be run when an error occurs onhashchange then "onhashchange", //HTML5 Script to be run when there has been changes to the anchor part of the a URL onload then "onload", // Fires after the page is finished loading onmessage then "onmessage", //HTML5 Script to be run when the message is triggered onoffline then "onoffline", //HTML5 Script to be run when the browser starts to work offline ononline then "ononline", //HTML5 Script to be run when the browser starts to work online onpagehide then "onpagehide", //HTML5 Script to be run when a user navigates away from a page onpageshow then "onpageshow", //HTML5 Script to be run when a user navigates to a page onpopstate then "onpopstate", //HTML5 Script to be run when the window's history changes onresize then "onresize", //HTML5 Fires when the browser window is resized onstorage then "onstorage", //HTML5 Script to be run when a Web Storage area is updated onunload then "onunload", // Fires once a page has unloaded (or the browser window has been closed) // Form element events onblur then "onblur", // Fires the moment that the element loses focus onchange then "onchange", // Fires the moment when the value of the element is changed oncontextmenu then "oncontextmenu", //HTML5 Script to be run when a context menu is triggered onfocus then "onfocus", // Fires the moment when the element gets focus oninput then "oninput", //HTML5 Script to be run when an element gets user input oninvalid then "oninvalid", //HTML5 Script to be run when an element is invalid onreset then "onreset", // Fires when the Reset button in a form is clicked onsearch then "onsearch", // Fires when the user writes something in a search field (for ) onselect then "onselect", // Fires after some text has been selected in an element onsubmit then "onsubmit", // Fires when a form is submitted // Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.) onkeydown then "onkeydown", // Fires when a user is pressing a key onkeypress then "onkeypress", // Fires when a user presses a key onkeyup then "onkeyup", // Fires when a user releases a key // Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.) onclick then "onclick", // Fires on a mouse click on the element ondblclick then "ondblclick", // Fires on a mouse double-click on the element onmousedown then "onmousedown", // Fires when a mouse button is pressed down on an element onmousemove then "onmousemove", // Fires when the mouse pointer is moving while it is over an element onmouseout then "onmouseout", // Fires when the mouse pointer moves out of an element onmouseover then "onmouseover", // Fires when the mouse pointer moves over an element onmouseup then "onmouseup", // Fires when a mouse button is released over an element onwheel then "onwheel", //HTML5 Fires when the mouse wheel rolls up or down over an element //Drag Events ondrag then "ondrag", //HMTL5 Script to be run when an element is dragged ondragend then "ondragend", //HTML5 Script to be run at the end of a drag operation ondragenter then "ondragenter", //HTML5 Script to be run when an element has been dragged to a valid drop target ondragleave then "ondragleave", //HTML5 Script to be run when an element leaves a valid drop target ondragover then "ondragover", //HTML5 Script to be run when an element is being dragged over a valid drop target ondragstart then "ondragstart", //HTML5 Script to be run at the start of a drag operation ondrop then "ondrop", //HTML5 Script to be run when dragged element is being dropped onscroll then "onscroll", //HTML5 Script to be run when an element's scrollbar is being scrolled //Clipboard Events oncopy then "oncopy", //HTML5 Fires when the user copies the content of an element oncut then "oncut", //HTML5 Fires when the user cuts the content of an element onpaste then "onpaste", //HTML5 Fires when the user pastes some content in an element //Media Events onabort then "onabort", //HTML5 Script to be run on abort oncanplay then "oncanplay", //HTML5 Script to be run when a file is ready to start playing (when it has buffered enough to begin) oncanplaythrough then "oncanplaythrough", //HTML5 Script to be run when a file can be played all the way to the end without pausing for buffering oncuechange then "oncuechange", //HTML5 Script to be run when the cue changes in a element ondurationchange then "ondurationchange", //HTML5 Script to be run when the length of the media changes onemptied then "onemptied", //HTML5 Script to be run when something bad happens and the file is suddenly unavailable (like unexpectedly disconnects) onended then "onended", //HTML5 Script to be run when the media has reach the end (a useful event for messages like "thanks for listening") onerror then "onerror", //HTML5 Script to be run when an error occurs when the file is being loaded onloadeddata then "onloadeddata", //HTML5 Script to be run when media data is loaded onloadedmetadata then "onloadedmetadata", //HTML5 Script to be run when meta data (like dimensions and duration) are loaded onloadstart then "onloadstart", //HTML5 Script to be run just as the file begins to load before anything is actually loaded onpause then "onpause", //HTML5 Script to be run when the media is paused either by the user or programmatically onplay then "onplay", //HTML5 Script to be run when the media is ready to start playing onplaying then "onplaying", //HTML5 Script to be run when the media actually has started playing onprogress then "onprogress", //HTML5 Script to be run when the browser is in the process of getting the media data onratechange then "onratechange", //HTML5 Script to be run each time the playback rate changes (like when a user switches to a slow motion or fast forward mode) onseeked then "onseeked", //HTML5 Script to be run when the seeking attribute is set to false indicating that seeking has ended onseeking then "onseeking", //HTML5 Script to be run when the seeking attribute is set to true indicating that seeking is active onstalled then "onstalled", //HTML5 Script to be run when the browser is unable to fetch the media data for whatever reason onsuspend then "onsuspend", //HTML5 Script to be run when fetching the media data is stopped before it is completely loaded for whatever reason ontimeupdate then "ontimeupdate", //HTML5 Script to be run when the playing position has changed (like when the user fast forwards to a different point in the media) onvolumechange then "onvolumechange", //HTML5 Script to be run each time the volume is changed which (includes setting the volume to "mute") onwaiting then "onwaiting", //HTML5 Script to be run when the media has paused but is expected to resume (like when the media pauses to buffer more data) //Misc Events onshow then "onshow", //HTML5 Fires when a element is shown as a context menu ontoggle then "ontoggle" }. public define Table_Option nude = border(0,0,0,rgb(0,0,0)). A list of 'Table_Option' must be given with each table. A list of 'Cell_Option' must be given with each cell and each row in a table. Options given with a row apply to all the cells in the row, but are superseded by options given with cells, which apply only to the cell they are given with. The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form', depending on where you put your table (within a form or not within a form). For your convenience, we define the following particular case: public define HTML_Cell($T) cell ( $T content )= cell([],content) . public define HTML_Cell($T) cell ( Cell_Option option, $T content ) = cell([option],content) . public define HTML_Header_Cell($T) header_cell ( $T content ) = header_cell([],content). Same remark as for 'HTML_Cell($T)'. We define several convenience functions: public define HTML_Row($T) row ( List(HTML_Cell($T)) cells )= row([],cells). public define HTML_Row($T) row ( HTML_Cell($T) cell )= row([],[cell]). public define HTML_Row($T) row ( List(Cell_Option) options, HTML_Cell($T) cell )= row(options,[cell]). public define HTML_Row($T) row ( Cell_Option option, HTML_Cell($T) cell )= row([option],[cell]). public define HTML_Header_Row($T) header_row ( List(HTML_Header_Cell($T)) cells ) = header_row([],cells). public define HTML_Header_Row($T) header_row ( HTML_Header_Cell($T) cell ) = header_row([],[cell]). public define Actioner_Aspect immediate_selector ( List(CoreAttrs) core_attrs, WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices )= immediate_selector(core_attrs, name, size, choices, failure, "") . public define Actioner_Aspect immediate_selector ( List(CoreAttrs) core_attrs, WebArgName name, Int size, List((List(CoreAttrs),WebArgValue,String)) choices, Maybe(InitialValue) selected )= immediate_selector(core_attrs, name, size, choices, selected, "") . public define Actioner_Aspect link ( List(Text_Option) options, String text )= link(options, text, failure). public define Actioner_Aspect link ( String text ) = link([], text, failure). public define Actioner_Aspect link ( List(Text_Option) options, Int i ) = link(options, to_decimal(i), failure). public define Actioner_Aspect link ( Int i ) = link([], i). public define Actioner_Aspect button ( String url_img ) = button(url_img,url_img). Actioners are explained in details below. public define String to_String ( CSS_File_Media css_file_media )= if css_file_media is { screen then "screen", tty then "tty", tv then "tv", projection then "projection", handheld then "handheld", print then "print", braille then "braille", aural then "aural", all then "all" }. //public type HTML_Meta:... public define HTML_Head_Tag js_script ( String script_content )= js_inline(script("text/javascript", script_content)) . /* Label with help */ public define HTML_tooltip html_tooltip ( String keyword, Int width )= html_tooltip("", keyword, width). public define HTML_tooltip html_tooltip ( String title, String keyword )= html_tooltip(title, keyword, 0). public define HTML_Help tooltip ( HTML_tooltip h_tooltip )= tooltip(h_tooltip, left) . /* It contains * - a List of HTML_Head_Tag representing necessaries head tags for the content. ex: jquery js files, js script, css specific styles, ... * - a HTML_Off_Form representing the HTML code for the part of the page we want to display */ //public type HTML_In_Form:... public define List(HTML_Head_Tag) to_HTML_Head_Tag ( List(CSS_File) css_file_list )= map((CSS_File css_f) |-> css(css_f), css_file_list). public define List(HTML_Head_Tag) to_HTML_Head_Tag ( List(JS_File) js_file_list )= map((JS_File js_f) |-> js(js_f), js_file_list). 'HTML_In_Form' defines all the elements you may put within a form. We define a convenience function: public define HTML_In_Form text_input(HTML_Label label, HTML_Id id, WebArgName name, InitialValue init, Int width) = text_input([], label, id, name, init, width). public define HTML_In_Form literal(Printable_tree t) = literal_pt(t). public define HTML_In_Form image(String url) = image([], url, url). public define HTML_In_Form image(String url, Int width, Int height) = image([], url, url, width, height). public define HTML_In_Form foreign_link ( Int tsize, String url, String name ) = foreign_link([size(tsize)],url,name). public define HTML_In_Form /* Older call with String as action instead of WEB_Action_Name. * This function exists only for compatibility with older project. * Please condiser to use above function instead */ actioner ( Actioner_Connection conn, Actioner_Target targ, Actioner_Aspect asp, String action, List((String,String)) extra_ops, List(Actioner_Local_Action) local_actions )= actioner(conn,targ,asp,action_name(action),extra_ops,local_actions) . public define HTML_In_Form actioner ( Actioner_Connection conn, Actioner_Target targ, Actioner_Aspect asp, WEB_Action_Name action, List((String,String)) extra_ops )= actioner(conn,targ,asp,action,extra_ops,[]) . public define HTML_In_Form /* Older call with String as action instead of WEB_Action_Name. * This function exists only for compatibility with older project. * Please condiser to use above function instead */ actioner ( Actioner_Connection conn, Actioner_Target targ, Actioner_Aspect asp, String action, List((String,String)) extra_ops )= actioner(conn,targ,asp,action_name(action),extra_ops,[]) . public define HTML_In_Form actioner ( Actioner_Connection conn, Actioner_Target targ, Actioner_Aspect asp, )= actioner(conn, targ, asp, action_name(""), [], []). public define HTML_In_Form text_area ( WebArgName name, InitialValue init, Int width, Int height ) = text_area([], no_label, html_Id(""), name,init,width,height). public define HTML_In_Form text_area ( List(TextAreaOption) options, WebArgName name, InitialValue init, Int width, Int height ) = text_area(options, no_label, html_Id(""), name,init,width,height). public define HTML_In_Form table ( List(HTML_Row(HTML_In_Form)) rows ) = table([],empty,rows,empty). public define HTML_In_Form table ( List(Table_Option) options, List(HTML_Row(HTML_In_Form)) rows ) = table(options,empty,rows,empty). public define HTML_In_Form table ( HTML_Header_Row(HTML_In_Form) h_row, List(HTML_Row(HTML_In_Form)) rows ) = table([],h_row,rows,empty). public define HTML_In_Form table ( List(Table_Option) options, HTML_Header_Row(HTML_In_Form) h_row, List(HTML_Row(HTML_In_Form)) rows ) = table(options,h_row,rows,empty). public define HTML_In_Form private_download ( String abs_path, String name, String extra_ext ) = private_download(abs_path,name,extra_ext,failure). public define HTML_In_Form private_download ( String abs_path, String name, String extra_ext, String action_name, List((String,String)) args ) = private_download(abs_path,name,extra_ext,success((action_name,args))). public define HTML_In_Form text ( List(CoreAttrs) l, Int i ) = text(l, to_decimal(i)). public define HTML_In_Form text ( String s )= text([],s) . public define HTML_In_Form text ( Int i ) = text([], i) . public define HTML_In_Form div ( HTML_In_Form element )= div([], [element]) . public define HTML_In_Form div ( List(CoreAttrs) attrs, HTML_In_Form element )= div(attrs, [element]) . public define HTML_In_Form span ( String s )= span([],s) . public define HTML_In_Form span ( Int i ) = span([], to_decimal(i)) . public define HTML_In_Form radio_button (List(CoreAttrs) options, HTML_Label label, WebArgName n, WebArgValue value, Bool checked) = radio_button (options, label, html_Id(n.name), n, value, checked). public define HTML_In_Form radio_button_r (List(CoreAttrs) options, HTML_Label label, WebArgName n, WebArgValue value, Bool checked) = radio_button_r (options, label, html_Id(n.name), n, value, checked). public define HTML_In_Form check_box (List(CoreAttrs) options, HTML_Label label, WebArgName n, Bool checked) = check_box (options, label, html_Id(n.name), n, checked). public define HTML_In_Form check_box_r (List(CoreAttrs) options, HTML_Label label, WebArgName n, Bool checked) = check_box_r (options, label, html_Id(n.name), n, checked). public define HTML_In_Form file_upload (WebArgName n, Int width) = file_upload([], no_label, html_Id(n.name), n, width). public define HTML_In_Form hidden (WebArgName name, WebArgValue value) = hidden(html_Id(""), name, value). public type Input_Type: button, checkbox, color, date, datetime_local, email, file, hidden, image, month, number, password, radio, range, reset, search, submit, tel, text, time, url, week . //public type HTML_Body:... 'HTML_Off_Form' defines all the elements you may put outside any form. public define HTML_Off_Form text(List(CoreAttrs) attrs, Int i) = text(attrs, to_decimal(i)). public define HTML_Off_Form text(CoreAttrs attr, Int i) = text([attr], i). public define HTML_Off_Form text(Int i) = text([], i). public define HTML_Off_Form text(String s) = text([], s). public define HTML_Off_Form text(CoreAttrs attr, String s) = text([attr], s). public define HTML_Off_Form a(A_href _href) = a([], _href, _self, []). public define HTML_Off_Form a(List(CoreAttrs) attrs, A_href _href) = a(attrs, _href, _self, []). public define HTML_Off_Form a(List(CoreAttrs) attrs, A_href _href, HTML_Off_Form content) = a(attrs, _href, _self, [content]). public define HTML_Off_Form a(List(CoreAttrs) attrs, A_href _href, List(HTML_Off_Form) content) = a(attrs, _href, _self, content). public define HTML_Off_Form a(A_href _href, HTML_Off_Form content) = a([], _href, _self, [content]). public define HTML_Off_Form a(A_href _href, List(HTML_Off_Form) content) = a([], _href, _self, content). public define HTML_Off_Form a(List(CoreAttrs) attrs, A_href _href, A_target _target, HTML_Off_Form content) = a(attrs, _href, _target, [content]). public define HTML_Off_Form a(CoreAttrs attr, A_href _href, A_target _target, HTML_Off_Form content) = a([attr], _href, _target, [content]). public define HTML_Off_Form a(A_href _href, A_target _target, HTML_Off_Form content) = a([], _href, _target, [content]). public define HTML_Off_Form a(A_href _href, A_target _target, List(HTML_Off_Form) content) = a([], _href, _target, content). public define HTML_Off_Form a(CoreAttrs attr, A_href _href, A_target _target, List(HTML_Off_Form) content) = a([attr], _href, _target, content). //
public define HTML_Off_Form article(List(CoreAttrs) attrs, List(HTML_Off_Form) content) = html_tag("article", attrs, content). public define HTML_Off_Form article(List(CoreAttrs) attrs, HTML_Off_Form content) = html_tag("article", attrs, [content]). public define HTML_Off_Form article(List(HTML_Off_Form) content) = html_tag("article", [], content). public define HTML_Off_Form article(HTML_Off_Form content) = html_tag("article", [], [content]). //