Slide Show Beta

Published 14 years, 3 weeks ago

Not many people know about it, but several major version numbers ago, Opera introduced a feature called Opera Show.  This feature allowed you to invoke a projection-medium display mode by hitting a single key.  (They’ve since introduced a similar single-key invocation of a handheld device.)  In this mode, any style sheets that applied to the projection medium were used to present the document.  It was a lightweight form of Powerpoint—not as powerful, perhaps, since it was best suited to showing a slide show of static pages, but definitely useful.  Many of my talks over the last two years have used Opera Show.

The great thing is that with one (X)HTML document, you can have a slide show, a printer-friendly version, and a screen presentation.  I put markup and CSS examples right there in the document, ready to be printed, and simply hide them in the slide show.  In some cases, I printed out the file for handouts, and then used the exact same file for the slide show.  It was very, very handy.  It was also browser-specific… and when Opera 7.5 for OS X came out, it introduced a problem: the banner ads showed up even in slide shows.  I didn’t really feel like buying a Web browser just to make my slide shows neater—if I were going to spend money on a presentation solution, I’d be much more likely to buy Keynote.

About the same time, though, Tantek Çelik was using a slide show system he’d cooked up, one that was nominally cross-browser.  It used CSS, JavaScript, and a single HTML document to create slide shows.  Here’s one example of a slide show using his approach.  I liked what he’d done, but when I dug into the guts I found that it had certain limitations I didn’t like.

(Aside: Apparently Steve Champeon has been using a similar slide show system for a while—here’s an example—but it has many of the same things I didn’t like about Tantek’s approach, such as explicitly ID’ing each slide in the markup.  My script assigns IDs dynamically, thus freeing you from having to number the slides in the markup and thus making it much easier to rearrange or insert slides.)

So I took Tantek’s idea and expanded on it, making it more flexible on the markup end and adding some features.  I’ve run into some stumbling blocks, though, and so in the best tradition of the LazyWeb, I’m turning to you folks for assistance.  Here’s the latest test file, and here’s an archive containing the test file and its associated files.  At the moment, the best (as in “most like what I expect”) rendering of the slide show is in Firefox, although it may seem a bit sluggish.  Other browsers have one or more problems; these are documented in the test file.  My goal is to bring Firefox, Explorer, and Safari together in terms of how they act.  Opera is secondary because I currently plan to hide all of the stuff I’m doing from Opera, and let it handle the slide show via the built-in Opera Show.  It won’t have quite the same functionality, I admit, but it will be good enough for me to call it done.

Note that the test file itself contains a bullet-point explanation of what’s going on, and lists the bugs I’ve yet to squash.  If you’d like to help squash them in return for credit in the source code, go crazy.  If you’re more in the “I want to use this” crowd, then you might want to wait until the system exits beta.  How will you know when that happens?  First, I’ll announce it on meyerweb.  Second, it will be given a jazzy name of some kind (thus causing the name of its directory to change).  And third, the word “[BETA]” won’t be plastered all over the test document.

Most of my current bugs are DOM and JavaScript related, although there’s a presentation problem in IE/Win that I frankly just haven’t had the energy to tackle.  Note that I’m willing to use detection methods in the JS to make the features work, but I am not willing to serve up browser-specific style sheets.  Call me a purist, but I just can’t bring myself to go there.

I’ll leave comments open for people to share information on bug fixes, or suggestions for ways to go about fixing them.  Also, if you run into a problem not listed in the slide show, you can leave a comment.  NOTE: I don’t care if the slide show feature doesn’t work in NN4.x, because I’m planning to hide all CSS and JavaScript from that browser before this exits beta.  That means NN4.x users will see a perfectly straightforward HTML document, not a horribly mangled attempt at the slide show.

My appreciation for whatever assistance people can provide.


  1. […] — written at 14:28 h Eric Meyer introduces the first steps of a presentation technique that relies entirely on XHTML-Documents, Cascading Style […]

  2. […] created a new slideshow beta using standards compliant xhtml and CSS. You can check it out Here.

    Permalink

    –>

    Comments […]

  3. God bless you. Now I don’t have to bug Bowman for the secrets to his similar PHP solution :-) This is very useful.

  4. equally yay! on my part, too. (I’m just getting ready to do my first-ever big presentation…I have everything in one XHTML page, but hadn’t gotten to this particular layer.)

    I’m no JS whiz, but I’ll be downloading it & trying it out with my own stuff to see if I can ferret out problems.

    (oh, and while I’m here, Dan, you are my Short Duration Personal Savior this week.)

  5. Can I make a motion that you never mention the phrase “Netscape 4” on your blog again?

  6. Wow… I was just yesterday, reading Dan’s book again, and was thinking about the Projection medium myself. “Gee… that would be very cool to implement that. I have just the client that could use their website as a presentation”.

    And low and behold you make a post about it, and Dan is the first to comment. Crazy!

  7. Your Print stylesheet is being affected by the Javascript, at least in Firefox. If you print (or Print Preview) right away, you will get all the slides in order. But if you print after moving forward in the document, it will print “venue line” at the top, followed by white space where preceding entries would be, until finally printing the slide that you are on and the ones that follow.

    Is there a way to tell the Javascript to go away when printing?

  8. I happen to use Mike Hearn’s xslt transform to get a nice flowing slideshow. Here is this Mike’s WineConf presentation. It just does not work with IE6 and Win2000 for some reason while WinXP and the rest are OK.

  9. it seems that Safari does not consider a single line break after the opening tag as a text node. If you put two line breaks, the menu gets populated OK. Maybe you should look for the first non text node:
    instead of

    var menu = obj.childNodes[1 - isIE];

    you can put:

    var menu = obj.firstChild;
    if (!menu)
    continue; // to cope with empty slides
    while (menu && menu.nodeType == 3) {
    menu = menu.nextSibling;
    }
    if (!menu)
    continue; // to cope with slides with only text nodes

    in function slideLabel.

    And you get rid of one browser sniffing !

    works in Safari and Camino

  10. You know, there’s really no need to assign IDs at all. So long as you have the class names, all you need to do is build an array (basically just your getByClass function) and store the result and move the index.

    I’d also suggest inserting the next/prev/toggle bits with JS, since they only make sense in that context. Might as well add some support for arrow keys as well.

  11. As for the toggle problem in IE5/Mac, setting the disabled property of the link object to true or false seems to work.

  12. One feature request – go to next slide upon pressing space or enter. This needn’t be difficult. The neatest way of doing this that I have seen is simply to ensure the focus is in the correct place so you just have to hit enter to go to the next slide. See for example the slides on Web Localization in Perl.

    In this case, it’s multi-page, rather than all-in-one page, but I’m sure that the technique could be reused.

  13. Interesting. I tried doing a
    similar thing
    in XUL(very alpha).

  14. Assuming you can show/hide it, you can fix the labels in the menu in IE5/Mac by using the label attribute of the option tag :


    var newopt = document.createElement('option');
    newopt.setAttribute('value',n);
    newopt.label = n+' : '+otext;
    list.add(newopt);

  15. Eric, I wouldnt write the dhtml part in the html, since that is behaviour layer, not structure. Web standards are mostly known by people like you, zeldman, etc that always talk about separating contents from presentation. Let’s do the same for behaviour ;)

    David, I wouldnt even use class. You (know you) can populate an array of h2’s and use their parentNode’s to collect the actual “slide”, for instance. From the top of my head, smth like:

    var slides = []

    var titles = document.getElementsByTagName(“h2”);
    for(var a = 0; a < titles.length; a++){
    slides.push(titles[a].parentNode);
    }

    You’ll need to fix IE5 lack of push method with smth like:

    // Adds one or more elements to an array.
    if(!Array.prototype.push){
    Array.prototype.push = function(){
    for(var i = 0; i < arguments.length; i++){
    this[this.length] = arguments[ i ];
    }
    return this.length;
    }
    }

    If I have mor etime I could add more code, but that might be a tarter to separate content from behaviour.

  16. sorry, i should’ve added code in a code block ;)


    var slides = []
    ...
    var titles = document.getElementsByTagName("h2");
    for(var a = 0; a < titles.length; a++){
    slides.push(titles[a].parentNode);
    }
    ...
    // Adds one or more elements to an array.
    if(!Array.prototype.push){
    Array.prototype.push = function(){
    for(var i = 0; i < arguments.length; i++){
    this[this.length] = arguments[ i ];
    }
    return this.length;
    }
    }

  17. I’ve been using a similar kind of thing except using an XSLT processor to break up the single XML file into individual XHTML slides that then form the presentation. It is based on a hacked up version that a company called Mulberry put online (http://www.mulberrytech.com/slideshow).

    Along the lines of what Dominic mentioned above, it would be great if MozPoint could be integrated into the final product. Heck — if I get any time to myself this weekend, I’ll give it a shot…

  18. Opera showing ads in full screen mode is considered a bug by Opera Software (according to posts in their forums), one that I believe is fixed already, but as I don’t have a Mac I can’t say for sure. Also, the Opera Feature Tour has some accompanying JavaScript that can jump to specific slides. Don’t forget Opera’s 2-for-1 offer – Win+Mac, Win+Linux, etc.

  19. I had an issue about printing in Firefox: Only the current slide was shown.

    I fixed it with an additional visibility:visible !important; in the *-selector (print stylesheet, of course).

    But it’s a great idea, none the less. Thumbs Up!

  20. I’ve also created something along these lines — my Bash tips and Vim tips talks give differently formatted examples.

    My version defaults to the slide-list view rather than the slideshow view, on the basis that the latter is only really of use to me when actually giving the presentation, and the former is much more useful for other people when browsing the content. Therefore I’ve only bothered to make the slideshow view work in gecko-based browsers at 1024×768; in practice you have to pick a single resolution so that any diagrams and photos appear the correct size on the screen.

    Your code generally seems more polished. Mine has a few more features that you might be interested in copying. The JavaScript that switches between the views also switches in different images; this allows for the images in the slide show to be bigger, or even in different colours to fit in better with on-screen or paper (see slide 4 of the Bash tips, for an example).

    I think one of the most important features of a slide presentation system is for the presenter to be able to navigate the slides as easily as possible without having to break off her/his conversation with the audience. As such my slides go forward when pressing Space or clicking anywhere on a slide — no messing about having to move the mouse or hold down a key chord. (Similarly, Bkspc for backwards; I tried to do right-clicking too, but couldn’t get that to work.)

    I also have shortcuts for jumping to a particular numbered slide — useful if you have questions at the end of a talk (either type something like 12s for slide 12, or just press s then enter the slide number in the pop-up box) and reverting to where you were before jumping (r).

    Having paragraphs of notes between the slides is really useful; that way you can provide more detailed information to your audience without having to clutter up the slides with too many words.

    Finally, while I’m impressed that your system doesn’t require having IDs on each slide, I actually think that per-slide IDs are a very good thing: they enable people to link directly to specific slides (like my link to slide 4 above), which is handy on mailing lists and the like.

    Anyway, congratulations on your good work and I hope some of my comments have been helpful.

    Smylers

  21. Scattered responses:

    • Rémi: thank you! The slide title collection is working beautifully in Firefox, Safari, and IE/Win. I’d seen references to nodeTypes while trying to figure out something else but it never occurred to me to use them here instead of trying to count nodes. I was less successful getting the toggle function to work in IE/Mac; I seemed unable to affect disabled on the link element. Feel free to e-mail me code, or post it here.
    • Dominic: I implemented the auto-focus on the “next” key, but ended up taking it out. In its place, I implemented generic key handling, the code for which I adapted from MozPoint. I actually was finishing up the implementation in the hours before Smylers posted. Right now the recognized key events are:
      • Next slide: return, enter, space, right arrow, down arrow
      • Previous slide: left arrow, up arrow
      • Toggle styles: t

      These aren’t the last word by any means, but they’re a good start. Anyone know the keycodes for escape, delete, page up, and page down? And how best to capture and recognize mouse clicks? (Smylers, I’m looking in your direction…)

    • Peter: go for it, dude!
    • Mike M. and Eric E.: good catch on the print problems, which I hope are now fixed (I added the rule Eric suggested). I’d encountered that same problem with the ‘projection’ styles when toggling, and fixed it via JS, but failed to extrapolate from there to deduce likely print trouble.

    Also, I prefer having the IDs there for various reasons, not least of which is, why not? More importantly, it appears that I should be able use them to skip directly to a slide in Opera when it’s using Opera Show, which is a huge advantage. The navigation menu might actually be made to work in Opera without having to completely hack around Opera Show.

  22. Keycode for “escape” key is IE-only. (27, if interested).

    47, is for “delete” key. I dunno about PgUp & PgDn…

    here’s a decent link regarding capturing mouse-clicks: http://www.webreference.com/dhtml/column3/capEvents.html

    And not one to steal code, feel free to look here:
    http://www.codelifter.com/main/javascript/capturemouseposition1.html (for code to get current X,Y coordinates of mouse…if that’s what you want…but I doubt it….)

  23. I have some problems using Firefox. I cant see all the content in some slides.

  24. Just developed a “direct link extension” for the slideshow.

    For example This link leads to “The Advantages”.

    I inserted this little function into the slides.js:

    function slicelink() {
    var dest = window.location.hash.slice(1,15);
    var cid = "s" + snum;
    var ne = document.getElementById(dest);
    if (!ne) {
    ne = document.getElementById("s0");
    snum = 0;
    }
    document.getElementById(cid).style.visibility = "hidden";
    ne.style.visibility = "visible";
    }

    and extend the body tag to: <body onload="slideLabel(); slicelink();">

    You can download a new archive.

  25. This may be redundant. (I don’t have time to read everything
    here.) But see my slide system (which works with Mozilla or
    Firefox fullscreen mode) at, for example, the lectures listed
    under “Schedule” in

    http://www.sas.upenn.edu/~baron/p153.htm
    (which also gives instructions how to use it). There is an
    alternative style sheet for printing the slides, which removes
    the white space. This is for students who print them before
    class. Many of the ideas for this system came from David
    Baron.

  26. Excellent, very handy indeed. I have a problem in Firefox v1.0 Win, I can’t seem to bring up the menu in the bottom right hand corner. It appears as a select menu, but I am unable to make a selection.

  27. Anyone else seeing the same problem as Luke? I don’t see it in Firefox 0.9.1 OS X, but something could have changed as they move toward 1.0.

    Currently, the presentation should be better in IE/Win, plus there’s now a slide-number indication, based on code I got from Steve Champeon’s slide system. I also added Eric’s sliceLink (renamed); thanks, Eric! The archive file as been updated to include the latest changes.

  28. I’ve added support for navigating with the mouse:

    function clicked(e){
    //make links work!?
    if(e.target.href != null)
    return true;

    if(e.which==1)
    go(1);

    //middle mouse-button for backwards
    if(e.which==2)
    go(-1);
    }

    Activate this function via document.onclick = clicked;.

    It works in Opera. IE complains about the target-object, though…

  29. The only trade-off I found is that I can’t seem to get it to work with Salling Clicker and my Sony Ericcson phone just like I’m used to do with Keynote.

  30. David Schontzler, storing references to the DOM nodes in JavaScript will lead to memory leaks in IE, storing the ID’s is the safer solution.

  31. Congratulations on the ongoing work! I’ve been doing something similar for a web site project a year and a half ago. However not having many slides per page I had assigned IDs manually (!) in the (X)HTML code. Comment #22 adding direct segment identifier access is indeed a nice addition I had implemented in my code (and is backward-compatible with CSS-disabled browsers). Also for the sake of I18N, I think access keys should be ‘J’ for previous and ‘K’ for next — I’m an old Apple ][ gamer, you know. Keyboard layout is likely to change across countries (e.g. Q, W, A, Z and M keys). Likewise I think the current JavaScript code is lacking some OOD but this may change over the upcoming versions of this interesting project.

  32. I’ve got to plug my own CSS Slideshow tool. It uses purely structural selectors, no classes or ids, and it scales completely with the text zoom buttons. The print mode prints one slide per page.

  33. Steffen: the problem I encountered is that clicking on the navigation menu to summon it up also advanced to the next slide. Sadly, I don’t know JS well enough to prevent it from happening. Any suggestions?

    /T: If the Salling Clicker can be configured to pass plain keystrokes to the browser, then I don’t see any reason why it wouldn’t work. It might be worth reporting to the Salling folks to see what they say– it may be a bug of theirs, but then it could be a bug of mine.

    Michael: The JS is about as good as you can expect from me; I don’t know JS very well and haven’t been a programmer for, oh, about a decade now. I’ll gladly accept assistance tightening the code once the features are set.

  34. I should have tested it more thoroughly. Anyways…

    Replacing if(e.target.href != null) with if(e.target.href != null || e.target.id=="jumplist" || e.target.parentNode.id=="jumplist") whould do the trick.

  35. The IE/Win scroll bar issue is due to its wierd box model. Here’s one way to fix it:

    On #footer, take out padding: 1em;
    Replace with padding-top: 1em; and padding-bottome: 1em;

    on #footer h1 and #footer h2 add padding-left: 1em; and padding-right: 1em;

    Works for me on IE6/Win

  36. Re: Mike’s comment, the way around this is to not apply styles with javascript, but to append classes which are defined for the specific media in question. If you don’t want something to appear in print, give it a className such as “noPrint” which would be defined in the print stylesheet as display: none;. I did something similar (albeit much simpler) in this article. It’s toward the end of the article and can be viewed in the final version of the file.

  37. In an attempt to keep the control links from going wacky, I ended up with the following:


    function clicker(e) {
    if (e.target.href != null ||
    e.target.id=="jumplist" ||
    e.target.parentNode.id=="jumplist" ||
    e.target.id == "toggle" ||
    e.target.parentNode.id=="toggle" ||
    e.target.id == "next" ||
    e.target.parentNode.id=="next" ||
    e.target.id=="prev" ||
    e.target.parentNode.id=="prev") return true;
    if (e.which == 1) go(1);
    }

    Otherwise, clicking the “next” link advanced two slides, and “previous” landed you right back on the current slide, while toggling the styles off or on advanced one slide.

    The only problem is that the “previous” button has to be exactly clicked on, or else you advance instead of moving back, which is counterintuitive. (At least without clicker missing it would result in no change.) What I’d really like to do, but apparently can’t, is something like:


    function clicker(e) {
    if (e.target.href != null || e.target.ancestorNode.id=="controls") return true;
    if (e.which == 1) go(1);
    }

    Anyone have suggestions on how to simulate the ‘ancestorNode’ bit?

  38. Ok, ‘suggestion’ would be a better term for my hacks anyhow. So here is my suggestion:

    One has to traverse the parentNodes of the target-node looking for a node that has the given id. Something like
    function isParentOrSelf(element, id){
    if(element == null || element.nodeName=='body')return false;
    else if(element.id == id)return true;
    else return isParentOrSelf(element.parentNode, id);
    }

    should do. The search stops when either the element is null or it arrived at the body-element.

    It would be included in the clicker-function with:
    if (e.target.href != null || isParentOrSelf(e.target, "controls")) return true;

    I tested it. It worked in Firefox. At least to my understanding of what it is supposed to do… ;-)

  39. Cool, Steffen, it is indeed working in Firefox and Safari. Not IE/Win, though. I’m wondering if it doesn’t support parentNode. I’m also willing to accept that as a limitation in the browser, and not worry overmuch about fixing it.

    I think this is just about ready to exit beta status.

  40. Cool beans. A couple of things are odd on Safari on OS X 10.3.5.

    I can push “T” to change styles (but it beeps when that occurs).
    I can NOT push “Z” or “X”. I CAN push “Control Z” and “Control X”, and that works, but “Control T” does not work! The left and right arrow keys to do the job splendidly.

    The menu on the bottom pops up as you say it will. However, I would suggest supplying a user indication of some kind so that a user knows he can mouse over there for something interesting.

    Good stuff.

  41. I dont have the same problem as Luke (comment 25) does… i can select from the list, but maybe it is because I am posting late… what did find is that if you open up the menu and then take the mouse out without selcting anything, a gray block will remain there until you click somewhere else… this is not a critical bug, rather a “look good” one

    Oh, im using Firefox 1.0 Win, by the way..

    -jl

  42. To fix the broken selector in IE/Mac I used “new Option()”. Manipulating form elements with dom seems to be a problem for IE/Mac.

    Old:
    newopt.setAttribute('value',n); list.appendChild(newopt).appendChild(opttext);

    New:
    list.options[list.length] = new Option(n+' : '+otext,n,false,false);

  43. Quirksmode indicates that ‘all’ browsers support parentNode. The problem must be with something else, therefore.
    I’d guess this is due to the problem with target I mentioned in #26…

  44. Ok, further investigations on Quirksmode revealed a solution:

    var targ;
    if (!e) var e = window.event;
    if (e.target) targ = e.target;
    else if (e.srcElement) targ = e.srcElement;
    if (targ.nodeType == 3) // defeat Safari bug
    targ = targ.parentNode;

    The problem with IE is that it uses the srcElement-property to convey the same information as other browsers do with the target-property of the event-object.
    Adding this snipped before the first call to isParentOrSelf should make it work in IE, too.

  45. Is anyone else using Firefox with the piemenu plugin? When I right click and attempt to go to the second level of any pie menu the menu blows up to 4 or 5 times it’s normal size only when using the slideshow example.

  46. What is the world coming to? An article about web-standards-based solutions for presentations, with Opera as nothing more than a margin note? You’ve even got WaSP harping on about it with seemingly no idea that it’s been possible for the past four years with no need for anything more than pure CSS. And what is this new solution that’s going to kill the very proprietory and bad PowerPoint? A soup of JavaScript! Oh, the irony…

    And I’m not even going to mention the Opera Show Generator: an idiot-proof web application to make these presentations (complete with slide navigation panels and tacky animations, if you insist).

  47. With additional JavaScript and maybe changes to the css it would be possible to scale the font-size according to screen-resolution (or more correctly viewport-size) automatically. Something like

    function changefont() {
       var size=window.innerWidth;
       if(size >=1600)
         chf(42);
       else if(size>=1200)
         chf(30);
       else if(size>=800)
         chf(24);
       else
         chf(12);
    }
    
    function chf(fSize){
      if (document.getElementsByTagName) {
        var body = document.getElementsByTagName("body")[0];
        body.style.fontSize = fSize+'px';
      }  
    }

    changefont would be called in startup().

    I have not experimented much with the value-pairs (viewport-/font-size), and the css would have to be adapted, I think.

  48. For portability reasons, it might be smart to actually include the javascript and CSS code INSIDE the XHTML document rather than referencing them.

    That way you can send the slideshow as a single file to other people via eMail, rather than sending 6-7 files and trying to explain to non-web folks how these need to be saved in alsorts of sub-folders.

  49. I have made some
    new CSS-themes to help promoting your great XHTML Slideshow. Inlcude them, modify (and correct) them if you like (credits apreciated, anyway! :P).
    All themes should be in this
    zipped file.

    I tried to present them in a way that the Firefox and Opera stylesheet switcher shows them as available themes. Firefox sometimes makes some trouble while switching; you’ll have to reload the page then. I thought the themes available for switching would inherit from your main theme, but they don’t, while they seem to inherit some classes from each other. Weirdo!)
    No Problems with Opera 7.11 at all!

    How do I change the space between the first h2 and the following text in a normal slide? I didn’t get it. The IEXplorer has some problems with the right space here.

    And I noticed two small features which don’t work: My cursor does not stay (or become) a pointer (like the CSS says) when hovering over the controls. And I can’t jump back to slide 0 with the navigation form when I used the arrows (or mouse klicks or the keyboard) right before. I am using Firefox 0.9.3 and IExplorer 6 (yes, and Opera) on a WinXP machine.

    Martin

  50. Is anyone else using Firefox with the piemenu plugin? When I right click and attempt to go to the second level of any pie menu the menu blows up to 4 or 5 times it’s normal size only when using the slideshow example.
    vikingler.com

  51. Excellent, very handy indeed. I have a problem in Firefox v1.0 Win, I can’t seem to bring up the menu in the bottom right hand corner. It appears as a select menu, but I am unable to make a selection.

  52. The code added below is used to make a button hide and appear and do the corresponding functionality using the div tag of html and javascript.

    Check & Uncheck All

    var defaultValue=’twos’;
    var flag = false;
    function buttonClick(input){
    if(!flag){
    for(i=0;i

  53. hi guys,

    This is a great read indeed, i have had a go at implementing this. I have issues in Firefox v1.0 on xp, The menu seems to disappear to the right side.

    I am not sure what i am to do here really…. has anyone got a live link to the presentation so i can see the code and work ouw where i am going wrong. I have a presentation to give on tuesday at NCL, and would like to use this presentation technique.

    All help is much appreciated.

Leave a Comment

Management reserves the right to edit or remove any comment, especially when abusive or irrelevant to the topic at hand. HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <em> <i> <q cite=""> <s> <strong> <pre class=""> <kbd>


Comment Preview

If you're satisfied with what you've written, then go ahead...