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