paulb@615 | 1 | <?xml version="1.0" encoding="iso-8859-1"?> |
paulb@176 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
paulb@270 | 3 | <html xmlns="http://www.w3.org/1999/xhtml"><head> |
paulb@176 | 4 | <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" /> |
paulb@615 | 5 | <title>Creating Applications: In-Page Updates</title> |
paulb@270 | 6 | <link href="styles.css" rel="stylesheet" type="text/css" /></head> |
paulb@176 | 7 | <body> |
paulb@176 | 8 | <h1>Creating Applications: In-Page Updates</h1> |
paulb@176 | 9 | <p>One fashionable avenue in Web application design has been that of |
paulb@176 | 10 | updating Web pages in applications without having to refresh the entire |
paulb@176 | 11 | page every time an action is performed. Together with some JavaScript |
paulb@176 | 12 | support in the browser, XSLForms also provides some functionality for |
paulb@176 | 13 | such "in-page" or "live" updates.</p> |
paulb@176 | 14 | <p>Consider the addition of a comment field to our application. Here is |
paulb@176 | 15 | how the HTML code might look:</p> |
paulb@270 | 16 | <pre><div template:element="item"><br /> <p><br /> Some item: <input template:attribute-field="value" name="..." type="text" value="..." /><br /> <input name="..." template:selector-field="remove" type="submit" value="Remove" /><br /> </p><br /> <p><br /> Item type:<br /> <select template:multiple-choice-list-field="type,type-enum,value" name="..." multiple="multiple"><br /> <option template:multiple-choice-list-value="type-enum,value,selected" value="..." /><br /> </select><br /> </p><br /> <span style="font-weight: bold;"><p template:element="options"></span><br style="font-weight: bold;" /><span style="font-weight: bold;"> <span </span><span style="font-weight: bold;">template:element="comment"></span><span style="font-weight: bold;">Comment:</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> <textarea template:attribute-area="value,insert" name="..." cols="40" rows="3"></span><br style="font-weight: bold;" /><span style="font-weight: bold;"> Some comment</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> </textarea><br /> </span><br style="font-weight: bold;" /></span><span style="font-weight: bold;"> </p></span><br /> <p><br /> Itself containing more items:<br /> </p><br /> <p template:element="subitem"><br /> Sub-item: <input template:attribute-field="subvalue" name="..." type="text" value="..." /><br /> <input name="..." template:selector-field="remove2" type="submit" value="Remove" /><br /> </p><br /> <p><br /> <input name="..." template:selector-field="add2,subitem" type="submit" value="Add subitem" /><br /> </p><br /></div></pre> |
paulb@615 | 17 | <p>Here, a <code>textarea</code> element has been added with a special <code>template:attribute-area</code> annotation being used to state that the contents of the element are to be mapped to the <code>value</code> attribute, and that the attribute contents are to be inserted inside the <code>textarea</code> element (replacing the <code>Some Comment</code> text).</p><p>The newly-added <code>textarea</code> field might actually be |
paulb@615 | 18 | presented in the application in its current state, despite the lack of any <code>options</code> or <code>comment</code> |
paulb@270 | 19 | elements |
paulb@176 | 20 | manipulated by the |
paulb@270 | 21 | application, due to the document initialisation mechanism employed by |
paulb@270 | 22 | the application. However, what would be more interesting is the |
paulb@270 | 23 | possibility of only showing the comment field if something else in the |
paulb@270 | 24 | document had a certain value or state.</p> |
paulb@270 | 25 | <p>Let us imagine that if the type of an item was set to "Personal", |
paulb@270 | 26 | the comment field would appear and permit the recording of some text |
paulb@615 | 27 | for that item. One approach that would make this possible is to |
paulb@270 | 28 | add a transformation which checks the type values set for each of the |
paulb@615 | 29 | items and removes the <code>options</code> and <code>comment</code> elements for items which do not qualify. In the Web resource, we make the following |
paulb@270 | 30 | changes:</p> |
paulb@270 | 31 | <pre> transform_resources = {<br /> "comments" : ["structure_comments.xsl"]<br /> }</pre> |
paulb@270 | 32 | <p>What this does is to state that when we carry out the <code>comments</code> |
paulb@270 | 33 | transformation, the specified stylesheet is employed, filtering out the |
paulb@270 | 34 | comments for non-qualifying items and preserving them for qualifying |
paulb@270 | 35 | items.</p><p>Further down in the code, we add a transformation:</p><pre> # After the document initialisation...<br /><br /> # Add the comments.<br /><br /> comments_xsl_list = self.prepare_transform("comments")<br /> structure = self.get_result(comments_xsl_list, structure)</pre> |
paulb@176 | 36 | <p>This new stylesheet works according to the following principles:</p> |
paulb@176 | 37 | <ol> |
paulb@176 | 38 | <li>Descend into the form data structure, copying all elements, |
paulb@176 | 39 | attributes and text that the stylesheet is not programmed to recognise.</li> |
paulb@615 | 40 | <li>When encountering an <code>item</code> element (which the |
paulb@176 | 41 | stylesheet is programmed to recognise), do the following:<br /> |
paulb@176 | 42 | <ol> |
paulb@615 | 43 | <li>Copy the element "skeleton" and its attributes so that |
paulb@615 | 44 | the <code>value</code> attribute is retained.</li> |
paulb@176 | 45 | <li>Produce a new <code>options</code> element and process it.</li> |
paulb@176 | 46 | </ol> |
paulb@176 | 47 | </li> |
paulb@176 | 48 | <li>When processing a new <code>options</code> element, do the |
paulb@176 | 49 | following:<br /> |
paulb@176 | 50 | <ol> |
paulb@176 | 51 | <li>Inside this new <code>options</code> element, investigate |
paulb@615 | 52 | the values associated with the <code>type</code> element.</li> |
paulb@444 | 53 | <li>If any of the selected type values represents the "Personal" category, make a new <code>comment</code> |
paulb@615 | 54 | element, then add any attributes that may be found on |
paulb@179 | 55 | existing <code>comment</code> elements within the current <code>type</code> |
paulb@176 | 56 | element.</li> |
paulb@176 | 57 | </ol> |
paulb@176 | 58 | </li> |
paulb@176 | 59 | </ol> |
paulb@270 | 60 | <p>Since this stylesheet is used after the document initialisation, |
paulb@270 | 61 | we may (and even must) take advantage of the results of that activity, including noting that selected values on <code>type-enum</code> |
paulb@176 | 62 | elements are marked with the <code>value-is-set</code> attribute.</p> |
paulb@615 | 63 | <p>The stylesheet source code can be found in <code>examples/Common/VerySimple/Resources/structure_comments.xsl</code>.</p> |
paulb@176 | 64 | <h2>Limitations and Enhancements</h2> |
paulb@176 | 65 | <p>Whilst the above modifications adds a comment field for each item |
paulb@176 | 66 | with a type of "Personal", and whilst the comment field will appear and |
paulb@176 | 67 | disappear for items as their type changes, such updates only take place |
paulb@176 | 68 | when items and subitems are added and removed. We could add an update |
paulb@176 | 69 | button to the page which performs an explicit refresh of the page |
paulb@176 | 70 | without adding or removing anything, and for the sake of usability, we |
paulb@615 | 71 | probably should add such a button (just below the <code>Add item</code> |
paulb@176 | 72 | button):</p> |
paulb@176 | 73 | <pre><p><br /> <input name="update" type="submit" value="Update" /><br /></p></pre> |
paulb@176 | 74 | <p>However, we could also add an in-page update to make each comments |
paulb@176 | 75 | field appear and disappear as soon as we have changed the type of an |
paulb@176 | 76 | item.</p> |
paulb@176 | 77 | <h3>Template Changes</h3> |
paulb@176 | 78 | <p>We must first define a region of the template where a comment fields |
paulb@176 | 79 | can be added and removed, regardless of whether such a field existed |
paulb@176 | 80 | there before. The above template code needs modifying slightly to |
paulb@176 | 81 | permit this:</p> |
paulb@624 | 82 | <pre> <span style="font-weight: bold;"><p template:element="options" template:section="comment-node" id="{template:this-element()}"></span><br style="font-weight: bold;" /><span style="font-weight: bold;"> <span template:element="comment"></span>Comment:<br /> <textarea template:attribute-area="value,insert" name="..." cols="40" rows="3"><br /> Some comment<br /> </textarea><br /> <span style="font-weight: bold;"></span></span><br /> </p></pre> |
paulb@176 | 83 | <p>Here, we have added this region definition to the paragraph |
paulb@176 | 84 | surrounding the comment field, annotating the paragraph with the |
paulb@176 | 85 | following attributes:</p> |
paulb@176 | 86 | <ul> |
paulb@624 | 87 | <li>The <code>template:section</code> attribute is used to define a |
paulb@176 | 88 | template fragment used only to prepare the updated part of the Web |
paulb@176 | 89 | page. Here we define the fragment or region as being just this |
paulb@176 | 90 | paragraph.</li> |
paulb@615 | 91 | <li>The standard HTML <code>id</code> attribute is used to |
paulb@176 | 92 | define which part of the active Web page will be replaced when |
paulb@176 | 93 | performing an in-page update. This attribute needs to have a unique |
paulb@176 | 94 | value, but the easiest basis for such a value is a selector-style |
paulb@615 | 95 | reference to the <code>options</code> element within which the <code>comment</code> |
paulb@176 | 96 | element resides.</li> |
paulb@176 | 97 | </ul> |
paulb@615 | 98 | <p>Another change has been to put the <code>template:element</code> |
paulb@176 | 99 | annotation inside the above fragment or region annotations. Had we not |
paulb@615 | 100 | done this, the lack of a <code>comment</code> element in the form |
paulb@615 | 101 | data could have prevented the <code>id</code> attribute from |
paulb@176 | 102 | appearing in the Web page, this preventing any hope of an in-page |
paulb@176 | 103 | update since there would be no way of knowing where such an update |
paulb@176 | 104 | should be applied.</p> |
paulb@176 | 105 | <h3>Adding JavaScript</h3> |
paulb@176 | 106 | <p>Since we rely on JavaScript support in the browser, the following |
paulb@176 | 107 | references to scripts must also be added to the template, as shown in |
paulb@176 | 108 | the following excerpt:</p> |
paulb@270 | 109 | <pre><head><br /> <title>Example</title><br /> <span style="font-weight: bold;"><script type="text/javascript" src="scripts/sarissa.js"> </script></span><br style="font-weight: bold;" /><span style="font-weight: bold;"> <script type="text/javascript" src="scripts/XSLForms.js"> </script></span><br /></head></pre> |
paulb@615 | 110 | <p>These special script files can be found in <code>examples/Common/VerySimple/Resources/scripts</code>.</p> |
paulb@176 | 111 | <p>Now we can concentrate on adding the event which triggers an in-page |
paulb@176 | 112 | update. Since it is the type values that cause each comment field to be |
paulb@176 | 113 | added or removed, we add an event attribute on the form field |
paulb@176 | 114 | responsible for displaying the type values:</p> |
paulb@482 | 115 | <pre> <p><br /> Item type:<br /> <select template:multiple-choice-list-field="type,type-enum,value" name="..." multiple="multiple"<br /> <span style="font-weight: bold;">onchange="requestUpdate(</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> 'comments',</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:list-attribute('type-enum', 'value')}',</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:other-elements(../options)}',</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:child-attribute('value', template:child-element('comment', 1, template:other-elements(../options)))}',</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> '/structure/item')"</span>><br /> <option template:multiple-choice-list-value="type-enum,value,selected" value="..." /><br /> </select><br /> </p></pre> |
paulb@176 | 116 | <p>This complicated string calls a special update request JavaScript |
paulb@176 | 117 | function which triggers the in-page update, and it specifies the |
paulb@176 | 118 | following things:</p> |
paulb@180 | 119 | <dl> |
paulb@183 | 120 | <dt><span style="font-weight: bold;">'comments'</span></dt> |
paulb@180 | 121 | <dd>The URL which will serve the in-page update requested by this |
paulb@183 | 122 | field. Since the value stated is a relative reference to a resource, it |
paulb@183 | 123 | will produce something like the following:<br /> |
paulb@180 | 124 | <pre>http://localhost:8080/comments</pre> |
paulb@180 | 125 | So the request for an in-page update will be sent to this |
paulb@180 | 126 | generated URL.</dd> |
paulb@180 | 127 | <dt><span style="font-weight: bold;">'{template:list-attribute('type-enum', |
paulb@180 | 128 | 'value')}'</span></dt> |
paulb@180 | 129 | <dd>The fields which are going to be used in the processing of the |
paulb@176 | 130 | update. Since the presence of the comment field depends on a |
paulb@615 | 131 | specific <code>type</code> element and its <code>type-enum</code> |
paulb@615 | 132 | elements' <code>value</code> attributes, we specify the names of |
paulb@180 | 133 | the fields which yield these values.</dd> |
paulb@180 | 134 | <dt><span style="font-weight: bold;">'{template:other-elements(../options)}'</span></dt> |
paulb@180 | 135 | <dd>The region which is to be updated. Here, we recall that we |
paulb@176 | 136 | defined the region using a special reference to the <code>options</code> |
paulb@615 | 137 | element holding <code>comment</code> element. Thus, we use a |
paulb@176 | 138 | special value which also refers to that element from the context of |
paulb@615 | 139 | the <code>type</code> element.</dd> |
paulb@180 | 140 | <dt><span style="font-weight: bold;">'{template:child-attribute('value', |
paulb@180 | 141 | template:child-element('comment', 1, |
paulb@180 | 142 | template:other-elements(../options)))}'</span></dt> |
paulb@180 | 143 | <dd>Even when the types are changed, it may be the case that an |
paulb@176 | 144 | exposed comment field does not disappear (for example, if we already |
paulb@176 | 145 | have "Personal" selected but select "Important" in addition), and so we |
paulb@176 | 146 | need to provide the details of the field which holds the value of the |
paulb@179 | 147 | comment text. We find such details by referencing the <code>options</code> |
paulb@179 | 148 | element from the <code>type</code> element and stating that we want |
paulb@179 | 149 | the <code>value</code> attribute on any <code>comment</code> |
paulb@179 | 150 | element that may exist. Note that we cannot reference the <code>comment</code> |
paulb@179 | 151 | element directly since it may not exist at first, but then come into |
paulb@179 | 152 | being after an update, but not be referenced here in this parameter; |
paulb@179 | 153 | therefore, we need to make up the final part of the reference using the |
paulb@179 | 154 | special <code>template:child-attribute</code> |
paulb@180 | 155 | and <code>template:child-element</code> functions.</dd> |
paulb@482 | 156 | <dt><span style="font-weight: bold;">'/structure/item'</span></dt> |
paulb@180 | 157 | <dd>Finally, we need to provide some context to the application to |
paulb@176 | 158 | tell it something about where in the complete form data structure the |
paulb@180 | 159 | updated information resides.</dd> |
paulb@180 | 160 | </dl> |
paulb@180 | 161 | <p>Of course, all this is pretty complicated and at some point in the |
paulb@180 | 162 | future, a simplified way of triggering in-page updates will be |
paulb@180 | 163 | introduced.</p> |
paulb@180 | 164 | <h3>Updating the Web Application</h3> |
paulb@180 | 165 | <p>To support both normal requests for Web pages and the special |
paulb@180 | 166 | in-page requests, we must make some modifications to the Web |
paulb@180 | 167 | application. First, we must introduce some infrastructure to handle the |
paulb@180 | 168 | requests for the JavaScript files separately from the requests for |
paulb@180 | 169 | pages from our application. Some standard WebStack resources can be |
paulb@180 | 170 | used to help with this, and we add some imports at the top of our |
paulb@180 | 171 | source file:</p> |
paulb@353 | 172 | <pre>#!/usr/bin/env python<br /><br />"A very simple example application."<br /><br />import WebStack.Generic<br />import XSLForms.Resources.WebResources<br />import XSLForms.Utils<br />import os<br /><br /><span style="font-weight: bold;"># Site map imports.</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;">from WebStack.Resources.ResourceMap import MapResource</span><br style="font-weight: bold;" /><span style="font-weight: bold;">from WebStack.Resources.Static import DirectoryResource</span></pre> |
paulb@180 | 173 | <p>Then, we define the resource class as <a href="Web-resource.html">before</a>, |
paulb@180 | 174 | but with an additional attribute:</p> |
paulb@510 | 175 | <pre># Resource classes.<br /><br />class VerySimpleResource(XSLForms.Resources.WebResources.XSLFormsResource):<br /><br /> "A very simple resource providing a hierarchy of editable fields."<br /><br /> resource_dir = os.path.join(os.path.split(__file__)[0], "Resources")<br /> encoding = "utf-8"<br /> template_resources = {<br /> "structure" : ("structure_multivalue_template.xhtml", "structure_output.xsl")<br /> }<br /> init_resources = {<br /> "structure" : ("structure_multivalue_template.xhtml", "structure_input.xsl")<br /> }<br /> transform_resources = {<br /> "comments" : ["structure_comments.xsl"]<br /> }<br /> document_resources = {<br /> "types" : "structure_types.xml"<br /> }<br /><span style="font-weight: bold;"> in_page_resources = {</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> "comments" : (</span><span style="font-weight: bold;">"structure", </span><span style="font-weight: bold;">"structure_output_comments.xsl", "comment-node")</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> }</span></pre> |
paulb@180 | 176 | <p>This new attribute provides information about the in-page request to |
paulb@180 | 177 | retrieve comment regions of the Web form, and it consists of the |
paulb@180 | 178 | stylesheet filename that will be generated to produce the page |
paulb@180 | 179 | fragments for such comment regions, along with the region marker that |
paulb@180 | 180 | we defined above.</p> |
paulb@615 | 181 | <p>The <code>respond_to_form</code> method now also includes some |
paulb@180 | 182 | additional code:</p> |
paulb@270 | 183 | <pre> def respond_to_form(self, trans, form):<br /><br /> """<br /> Respond to a request having the given transaction 'trans' and the given<br /> 'form' information.<br /> """<br /><br /> <span style="font-weight: bold;">in_page_resource = self.get_in_page_resource(trans)</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> parameters = form.get_parameters()</span><br /> documents = form.get_documents()<br /></pre> |
paulb@180 | 184 | <p>Here, we find out whether an in-page update is requested, along with |
paulb@180 | 185 | the raw parameters of the request, some of which will be used later on |
paulb@180 | 186 | in the method.</p> |
paulb@180 | 187 | <p>The discovery of the form data structure and the addition and |
paulb@180 | 188 | removal of elements happens as before, as does the merging of type |
paulb@180 | 189 | values and the comment field, if applicable:</p> |
paulb@270 | 190 | <pre> # Ensure the presence of a document.<br /><br /> if documents.has_key("structure"):<br /> structure = documents["structure"]<br /> else:<br /> structure = form.new_instance("structure")<br /><br /> # Add and remove elements according to the selectors found.<br /><br /> selectors = form.get_selectors()<br /> XSLForms.Utils.remove_elements(selectors.get("remove2"))<br /> XSLForms.Utils.add_elements(selectors.get("add2"), "subitem")<br /> XSLForms.Utils.remove_elements(selectors.get("remove"))<br /> XSLForms.Utils.add_elements(selectors.get("add"), "item")<br /><br /> # Initialise the document, adding enumerations/ranges.<br /><br /> structure_xsl = self.prepare_initialiser("structure")<br /> types_xml = self.prepare_document("types")<br /> structure = self.get_result([structure_xsl], structure, references={"type" : types_xml})<br /><br /> # Add the comments.<br /><br /> comments_xsl_list = self.prepare_transform("comments")<br /> structure = self.get_result(comments_xsl_list, structure)<br /></pre> |
paulb@180 | 191 | <p>The significant changes begin when presenting the result of the |
paulb@180 | 192 | request processing:</p> |
paulb@510 | 193 | <pre> # Start the response.<br /><br /> trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))<br /><br /><span style="font-weight: bold;"></span><span style="font-weight: bold;"> # Ensure that an output stylesheet exists.</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> if in_page_resource in self.in_page_resources.keys():</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> trans_xsl = self.prepare_fragment(in_page_resource)</span><span style="font-weight: bold;"></span><br style="font-weight: bold;" /><span style="font-weight: bold;"> stylesheet_parameters = self.prepare_parameters(parameters)</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> else:</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> trans_xsl = self.prepare_output("structure")<br /></span> <span style="font-weight: bold;"></span><span style="font-weight: bold;"> stylesheet_parameters = {}</span></pre> |
paulb@615 | 194 | <p>Instead of just obtaining a stylesheet for the <code>structure</code> |
paulb@180 | 195 | document, we instead check to see if an in-page update is being |
paulb@180 | 196 | requested and, if so, prepare the stylesheet representing the fragment |
paulb@444 | 197 | of the Web form to be presented. Additionally, we obtain special |
paulb@444 | 198 | stylesheet parameters using the raw request parameters; this introduces |
paulb@444 | 199 | information that will be used to control the |
paulb@180 | 200 | stylesheet when making the final Web page output.</p> |
paulb@183 | 201 | <p>Finally, we send the output to the user but employing the additional |
paulb@183 | 202 | stylesheet parameters to configure the result:</p> |
paulb@270 | 203 | <pre><span style="font-weight: bold;"> # Complete the response.<br /><br /> self.send_output(trans, [trans_xsl], structure<span style="font-weight: bold;">, stylesheet_parameters</span>)</span></pre> |
paulb@180 | 204 | <p>In order to introduce the infrastructure mentioned above which |
paulb@180 | 205 | separates requests for Web pages from requests for JavaScript files, we |
paulb@615 | 206 | need to provide a more sophisticated implementation of the <code>get_site</code> |
paulb@180 | 207 | function:</p> |
paulb@270 | 208 | <pre><span style="font-weight: bold;"># Site map initialisation.</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;">def get_site():</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> "Return a simple Web site resource."</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> # Get the main resource and the directory used by the application.</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> very_simple_resource = VerySimpleResource()</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> directory = very_simple_resource.resource_dir</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> # Make a simple Web site.</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> resource = MapResource({</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> # Static resources:</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> "scripts" : DirectoryResource(os.path.join(directory, "scripts"), {"js" : "text/javascript"}),</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> # Main page and in-page resources:</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> None : very_simple_resource</span><br style="font-weight: bold;" /><span style="font-weight: bold;"> })</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;"> return resource</span></pre> |
paulb@180 | 209 | <p>What this does is to create a resource for the application, as |
paulb@180 | 210 | before, but then to place the resource into a special WebStack resource |
paulb@180 | 211 | which examines the path or URL on the incoming requests and directs |
paulb@180 | 212 | such requests according to the following scheme:</p> |
paulb@180 | 213 | <ul> |
paulb@615 | 214 | <li>If the request mentions something under <code>scripts</code> |
paulb@180 | 215 | in its URL, we employ the WebStack <code>DirectoryResource</code> to |
paulb@615 | 216 | send the file from the <code>scripts</code> subdirectory of the |
paulb@180 | 217 | application's <code>Resources</code> directory.</li> |
paulb@180 | 218 | <li>Otherwise, we pass the request on to our application resource in |
paulb@180 | 219 | order to produce a Web page for the user.</li> |
paulb@180 | 220 | </ul> |
paulb@180 | 221 | <p>Thus, when the user's browser asks for a script file, it gets a |
paulb@180 | 222 | script file; otherwise it gets a Web page showing either all of the |
paulb@180 | 223 | form (if a normal request is received), or a part of the form (if an |
paulb@180 | 224 | in-page request is received).</p> |
paulb@615 | 225 | </body></html> |