The series of d1 to dN directories provide a stepwise buildup of a tableless XML drawer for kupu. Notes ----- 1. For the best XSLT tutorial, check out the free chapter in the XML Bible at http://www.cafeconleche.org/books/bible2/chapters/ch17.html. 2. Note that all shared images and css are in the "files" directory. 3. Make sure you are viewing the XML file in Oxygen (not the XSLT) when you try to run or edit a transformation scenario. 4. Also, make sure you update your scenario for each exercise (d2 -> d3). 5. To learn a bit about using oxygen, view the QuickTime movie here: http://zea.zope-europe.org/~paul/oxygen/oxygen.html D1, Limi's Mockup ----------------- d1 is completely static, containing the original files from Alex. D2, Mostly-Static ----------------- d2 converts the kupudrawer.html into a kupudrawer.xsl. There is also an imagelibrary.xml added to the d2 directory to provide the XML content, although it doesn't actually iterate over any XML content. The imagellibrary.xml file adds an 'xml-stylesheet' processing instruction at the top. This allows you to simply open the XML file directly from disk, in Mozilla, and see the XML content being rendered by the XSL view. You can also setup a transformation scenario in oxygen: a. Open d2/imagedrawer.xml and d2/kupudrawer.xsl in Oxygen. b. Click on the toolbar button w/ a wrench to create a transformation scenario. c. Name the scenario D2. d. On the XSLT tab, open d2/kupudrawer.xsl e. On the output, click the folder icon beside "Save as" and give an output.html in the d2 folder. f. Check "open in browser". g. Close the scenario. h. Click the Play button. You will then see the output in Safari. D3, Using XML Data ------------------ d3 starts using the XML contents. To make this first data-driven step easy, only one thing is done (lines 14-20 in d3/kupudrawer.xsl): This changes the left column (library sources) to have one for each "library" in the XML file. This is done with xsl:for-each, which matches on each element in the XML file. We show a title for each source by using the xsl:value-of tag, which matches on each library's title subelement. To view this, either reload the file:/// URL for imagelibrary.xml in Firefox/Mozilla or re-run the transform (the Play button) in Oxygen. *Remember to make a new transform scenario or edit the old one to point at the d3 directory!* *Note*: XSL templates are usually organized in terms of matching patterns. However, as shown here, you can also use it in a procedural, top-to-bottom fashion. I'm doing this here so the drawers will fit the brain of ZPT folks. D4, href and src ---------------- This is just a slight extension of the previous example. We now use the XML file to indicate the icon to use and the target of the . For the icon, we make some small changes to the XML file, putting folder.gif as the value for each . Notice that the href and src attributes (for the and ) have values with a curly bracket in them. Whattup wid dat? Actually, it makes sense. The xsl:for-each's select doesn't need curly brackets, because xsl:for-each is in the XSL namespace. The processor knows what to do with the select attribute. It doesn't know what to do with values of HTML attributes, as it expects literal values there. The curly brackets signal the XSL processor that the attribute value contains an XPath expression. *Note*: In the final drawer, we don't want normal hyperlink behavior. The onclick should be changed to have a JavaScript handler. D5, Middle Column ----------------- We now want to dynamically render the middle column based on data in the XML. In the actual Kupu drawers, the XML for the library sources gets turned into a JS variable for an XML DOM object. When you click on a library source, it retrieves a folder-full of XML data and prunes the new tree into the XML DOM. As you get more data, the cycle repeats: grab a folder-full, prune XML into the big pile of stuff. If you click on a folder where you already have the data, no trip to the server. We will mimic this behavior in our static, non-JS, non-Plone development environment. The d5/imagelibrary.xml now has a library source (the first) that contains a folder-full of data under the element. We change the kupu-drawerlisting
at line 21 and make it dynamically rendered, using a pattern very similar to the left column:
The xsl:for-each iterates over all the nodes in . For each resource, we show its src, icon, and title subelements. Reloading Firefox or configuring a new transform scenario for d5 will show the results. D6, Showing the Right Folder ---------------------------- We now edit d6/imagelibrary.xml to have for each library. However, how do we decide which library to highlight and show in the middle column? In Kupu, user interactions modify the long-lived XML DOM. When you click on another library source, the XML DOM gets changed to mark that library as "selected". It does this by setting an attribute "selected" on that element. We then change the XSL to only draw the middel column for the selected library. The d6/imagelibrary has two changes: (a) each library has (with some different title elements to make the differences visible) and (b) the second library has a "selected" attribute. The XSLT has a couple of small changes. First, we change the rule for the middle coluumn: This only matches on nodes with an attribute of selected. To prove this, try editing d6/imagelibary.xml and moving the @selected=1 to another library node (or removing it altogether, which makes the middle column empty). Second, we want the currently-selected library to be highlighted. We change the library listing slightly: background-color: #cccccc; There is an xsl:if inside the . If the current has an attribute of selected, then we add a style attribute to the in the output. This highlights it. (Note: Of course we would refactor this to move the style information out of the HTML. There are several ways to approach this, perhaps using CSS attribute selectors to reduce complexity.) Don't forget to update your transformation scenario to point at d6. D7, Selected Resource --------------------- Next let's do the same thing for the middle column. When the user clicks on Image 12, we want to do two things: (a) highlight that entry in the middle column and (b) update the contents of the right column. The latter means more information is needed in the XML data file. We need a description, the URL for previewing the image, etc. Kupu itself has very rich semantics for the right column, but we'll stick to the basics. First we change d7/imagelibrary.xml. We change Image 12 in the second library to have a selected attribute. Thus, library[@selected]/resource[@selected] points at Image 12. We then put the same highlighting trick used in the left column, into the middle column: Thus, if a has a selected attribute, it gets a style setting. Next the harder part. We need to add some more data to all the nodes in d7/imagelibrary.xml. That's a lot of work, so we only do it for Image 12 in the second library: image_icon.gif Image 12 This is Alex, and he's taking over all work Kupu drawers. {wink} limi.jpg 27kb In Kupu, we have several other elements to help make decisions on client-side preview and scaling, as well as allow the server to be responsible for thumbnails. For now we'll just put the image in a scaled box. To draw the right box, we need to grab the resource that was clicked on in the middle column. Furthermore, if no resource has been selected, we should show nothing. We can do both with another xsl:for-each, just inside the
for the right column. We'll match on the selected resource. If no resource is selected, then we'll only show the "Details" caption:
Details
D8, Parameters and Variables ---------------------------- When we switch to Kupu, we'd like to use the same XSLT view in different "modes". For example, since the image drawer and the link drawer are very similar, it doesn't make sense to have two versions to retrieve into the browser and to maintain as a developer. Instead, we can have one template that knows when to show the image stuff versus the link stuff. This applies to the different "modes" for the search box (standard versus livesearch). We'll use the XSL idea of "parameters" for this. With this, you can pass values into the transform which get bound as global variables. We can do this from JavaScript in Kupu, but we can also do this in Oxygen. There aren't any changes to d8/imagelibrary.xml. We first edit d8/kupudrawer.xsl to have a parameter at the top: Insert link Note the default value. If we don't pass in a parameter, this string is used. Next, we edit the h1 to use the variable created by this parameter:

The XSL select has the XSL variable syntax, with a dollar sign preceding the variable name. Reloading this in Firefox or running a transformation scenario for d8 should show no change. Edit the scenario for d8 and, on the XSLT tab, click on the button in the middle named "Parameters". Add a new parameter with a Name of drawertitle and a Value of Insert image. When you click the Play button now you'll see a different label in the drawer. D9, In-browser With Sarissa --------------------------- Now we jump off the deep end. So far we've shown how to increase productivity by working outside of Plone and even JavaScript. In this step, we will start to mimic more closely how Kupu works. Specifically, we will setup d9 so that a plain HTML page loads the XML and XSLT from JavaScript, runs the transform, and puts the HTML fragment into the main page. *Note: I just discovered that the new Sarissa has a helper function that greatly simplifies usage, but only *if* we return XSLT output that contains only the right fragment. I won't show that here, since this isn't the mode that Kupu uses, but it's an exciting opportunity for later.* First we re-arrange our files a bit. We put Sarissa (well-documented and downloadable from sarissa.sf.net) in the files directory. We put our XML and XSLT in d9/kupudrawers, to better mimic Kupu's relative structure. Finally, we put create a d9/kupu.html as the main document. This main document has a div whose contents gets updated from the remote data. For now, we simply have a button that, when pressed, loads the data, performs the transform, and copies in resulting HTML. We need the resulting HTML to have a specially-named
, giving us the content to grab. We choose to name both the
in the XSLT and the
in the main kupu.html document with the same ID. Ordinarily, we'd use the onload handler to load and cache the XSLT. For demonstration purposes, we leave it in the kupu.js file's runTransform function. This allows you to change either the XML or XSLT and press the button. You'll see the new results. Notes 1. This is only a mild form of how it works in Kupu. For example, we ignore IE in d9, and use a technique for merging DOMs that only works in Firefox/Moz/Safari/Opera/everythingelse. :^) 2. Moving elements between documents (the response of the transform vs. the main document) can be painful. First, IE doesn't support it, so you have to do innerHTML. Second, namespaces frequently cause baffling surprises. Note that the XSLT tranform, near the top, specificies an output-method of html, so that the namespace of the transform is the same as the main document. 3. I removed the xmlns declaration on the element in the XSLT. This is only for convenience: Mozilla comes up with some annoying, though valid, namespace prefixes when left to its own devices. 4. I cheated and used synchronous loading on the model and view. Doing async requires a few lines extra for callback functions. D10, Respond To Events ---------------------- We can now show how events work. When the user clicks in the browser on a different library source, we move the "selection" from one XML node to another. First we need to get the mockup data ready. We start by making the first library source selected, since that is what the user would expect. We also choose one of the nodes for the other two libraries elements to be the selected resource. This also means we need to copy some of the extra data to this node. The XSLT gets changed to have an onclick handler for each in the left column. This onclick handler calls a JavaScript function named "changeLibrary", passing in an identifier in the XML DOM for the newly-clicked library. We also pass in the event object. In the JS, we first make the model and view variables into globals, so we can use them across functions. The comments in this JS function explain the ideas.