diff --git a/doc/burial.png b/doc/burial.png new file mode 100644 index 0000000..58cfcd6 Binary files /dev/null and b/doc/burial.png differ diff --git a/scribblings/third-tutorial-files/burial.html.pm b/scribblings/third-tutorial-files/burial.html.pm new file mode 100644 index 0000000..e14baf7 --- /dev/null +++ b/scribblings/third-tutorial-files/burial.html.pm @@ -0,0 +1,17 @@ +#lang pollen + +◊h1{I. The Burial of the Dead} + +"You gave me hyacinths first a year ago; +They called me the hyacinth girl." +--- Yet when we came back, late, from the Hyacinth garden, +Your arms full, and your hair wet, I could not +Speak, and my eyes failed, I was neither +Living nor dead, and I knew nothing, +Looking into the heart of light, the silence. +◊em{Od' und leer das Meer.} + +Madame Sosostris, famous clairvoyante, +Had a bad cold, nevertheless +Is known to be the wisest woman in Europe, +With a wicked pack of cards. \ No newline at end of file diff --git a/scribblings/third-tutorial-files/chess.html.pm b/scribblings/third-tutorial-files/chess.html.pm new file mode 100644 index 0000000..ed12468 --- /dev/null +++ b/scribblings/third-tutorial-files/chess.html.pm @@ -0,0 +1,18 @@ +#lang pollen + +◊h1{II. A Game of Chess} + +And still she cried, and still the world pursues, +"Jug Jug" to dirty ears. +And other withered stumps of time +Were told upon the walls; staring forms +Leaned out, leaning, hushing the room enclosed. +Footsteps shuffled on the stair, +Under the firelight, under the brush, her hair +Spread out in fiery points +Glowed into words, then would be savagely still. + +"My nerves are bad to-night. Yes, bad. Stay with me. +Speak to me. Why do you never speak? Speak. +What are you thinking of? What thinking? What? +I never know what you are thinking. Think." \ No newline at end of file diff --git a/scribblings/third-tutorial-files/directory-require.rkt b/scribblings/third-tutorial-files/directory-require.rkt new file mode 100644 index 0000000..d5d4dc9 --- /dev/null +++ b/scribblings/third-tutorial-files/directory-require.rkt @@ -0,0 +1,7 @@ +#lang racket/base +(require pollen/decode txexpr) +(define (root . elements) + (make-txexpr 'root null (decode-elements elements + #:txexpr-elements-proc detect-paragraphs + #:string-proc (compose smart-quotes smart-dashes)))) +(provide root) \ No newline at end of file diff --git a/scribblings/third-tutorial-files/index.ptree b/scribblings/third-tutorial-files/index.ptree new file mode 100644 index 0000000..ddb3f58 --- /dev/null +++ b/scribblings/third-tutorial-files/index.ptree @@ -0,0 +1,5 @@ +#lang pollen + +burial.html +chess.html +sermon.html \ No newline at end of file diff --git a/scribblings/third-tutorial-files/sermon.html.pm b/scribblings/third-tutorial-files/sermon.html.pm new file mode 100644 index 0000000..1f644e6 --- /dev/null +++ b/scribblings/third-tutorial-files/sermon.html.pm @@ -0,0 +1,13 @@ +#lang pollen + +◊h1{III. The Fire Sermon} + +"Trams and dusty trees. +Highbury bore me. Richmond and Kew +Undid me. By Richmond I raised my knees +Supine on the floor of a narrow canoe." + +"My feet are at Moorgate, and my heart +Under my feet. After the event +He wept. He promised 'a new start.' +I made no comment. What should I resent?" \ No newline at end of file diff --git a/scribblings/third-tutorial-files/styles.css.pp b/scribblings/third-tutorial-files/styles.css.pp new file mode 100644 index 0000000..23c1dd5 --- /dev/null +++ b/scribblings/third-tutorial-files/styles.css.pp @@ -0,0 +1,31 @@ +#lang pollen + +◊(define inner 2) +◊(define edge (* inner 2)) +◊(define color "gray") +◊(define multiplier 1.3) + +body { + margin: ◊|edge|em; + border: ◊|inner|em double ◊|color|; + padding: ◊|inner|em; + font-size: ◊|multiplier|em; + line-height: ◊|multiplier|; +} + +h1 { + font-size: ◊|multiplier|em; +} + +#prev, #next { +position: fixed; + top: ◊|(/ edge 2)|em; +} + +#prev { + left: ◊|edge|em; +} + +#next { + right: ◊|edge|em; +} \ No newline at end of file diff --git a/scribblings/third-tutorial-files/template.html b/scribblings/third-tutorial-files/template.html new file mode 100644 index 0000000..092a5eb --- /dev/null +++ b/scribblings/third-tutorial-files/template.html @@ -0,0 +1,15 @@ + + + +◊select['h1 doc] by T. S. Eliot + + +◊->html[doc] +◊(define prev-page (previous here)) +◊when/block[prev-page]{ +} +◊(define next-page (next here)) +◊when/block[next-page]{ +} + + \ No newline at end of file diff --git a/scribblings/tutorial-third.scrbl b/scribblings/tutorial-third.scrbl index 7ca5660..0f8fd58 100644 --- a/scribblings/tutorial-third.scrbl +++ b/scribblings/tutorial-third.scrbl @@ -18,11 +18,11 @@ Now you're getting to the good stuff. In this tutorial, you'll use Pollen to pub @item{Attaching behavior to tag functions} -@item{the @racketfont{directory-require.rkt} file} +@item{the @tt{directory-require.rkt} file} @item{Using @racket[decode] with Pollen markup} -@item{@exec{raco pollen render} and @exec{raco pollen clone}} +@;item{@exec{raco pollen render} and @exec{raco pollen clone}} ] @@ -94,9 +94,9 @@ In so doing, Pollen markup becomes the source code of the book. Let's try it out @subsection{Creating a Pollen markup file} -We're going to use Pollen markup to make a file that will ultimately be HTML. So consistent with the authoring-mode workflow we learned in the @secref["second-tutorial"], we'll start with our desired output filename, @racketfont{article.html}, and then append the Pollen markup suffix, @racketfont{.pm}. +We're going to use Pollen markup to make a file that will ultimately be HTML. So consistent with the authoring-mode workflow we learned in the @secref["second-tutorial"], we'll start with our desired output filename, @tt{article.html}, and then append the Pollen markup suffix, @tt{.pm}. -In DrRacket, start a new file called @racketfont{article.html.pm} like so (BTW you can use any sample text you like): +In DrRacket, start a new file called @tt{article.html.pm} like so (BTW you can use any sample text you like): @fileblock["article.html.pm" @codeblock{ #lang pollen @@ -108,7 +108,7 @@ Consistent with usual authoring-mode policy, when you run this file, you'll get @repl-output{'(root "I want to attend RacketCon this year.")} -Remember, even though the first line of the file is @racketmodfont{#lang} @racketmodname[pollen] — same as the last tutorial — the new @racketfont{.pm} suffix signals that Pollen should interpret the source as Pollen markup. Look what happens if you goof up and put Markdown source in a Pollen markup file, like so: +Remember, even though the first line of the file is @racketmodfont{#lang} @racketmodname[pollen] — same as the last tutorial — the new @tt{.pm} suffix signals that Pollen should interpret the source as Pollen markup. Look what happens if you goof up and put Markdown source in a Pollen markup file, like so: @codeblock{ #lang pollen @@ -127,7 +127,7 @@ Restore the non-Markdown source, and let's continue. Pollen markup uses the same Pollen command syntax that we first saw in @secref["Adding_commands" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. Previously, we used this command syntax to invoke functions like @racket[define] and @racket[->html]. Pollen markup is used to invoke a special kind of function called a @italic{tag function}, which is a function that, by default, adds a tag to the text. -To see how this works, restore your @racketfont{article.html.pm} file to its original state: +To see how this works, restore your @tt{article.html.pm} file to its original state: @codeblock{ #lang pollen @@ -617,7 +617,7 @@ But it won't work when you try to run it in DrRacket or load it in the project s It would be fine, however, to call a different kind of @racket[plot] function that returned an SVG result, because any XML-ish data structure can be converted to an X-expression. -@margin-note{Super web nerds also know that binary data can be converted into XML-ish form by encoding the file as a base-64 data URL — but if you know what I'm talking about, then you don't need my help to try it.} +@margin-note{Super web nerds also know that binary data can be converted into XML-ish form by encoding the file as a base64 data URL — but if you know what I'm talking about, then you don't need my help to try it.} For functions that don't return a string or an X-expression, you can always make a conversion by hand. For instance, consider @racket[range], a Racket function that returns a list of integers: @@ -648,21 +648,21 @@ And get this output: @repl-output{'(root "A list of integers: " "0 1 2 3 4")} -@subsection[#:tag-prefix "tutorial-3"]{The @racketfont{directory-require.rkt} file} +@subsection[#:tag-prefix "tutorial-3"]{Using the @tt{directory-require.rkt} file} @(noskip-note) -As you get more comfortable attaching behavior to tags using tag functions, you'll likely want to create some functions that can be shared between multiple source files. The @racketfont{directory-require.rkt} file is a special file that is automatically imported by Pollen source files in the same directory. So every function and value provided by @racketfont{directory-require.rkt} can be used in these Pollen files. +As you get more comfortable attaching behavior to tags using tag functions, you'll likely want to create some functions that can be shared between multiple source files. The @tt{directory-require.rkt} file is a special file that is automatically imported by Pollen source files in the same directory. So every function and value provided by @tt{directory-require.rkt} can be used in these Pollen files. -First, using this file is not mandatory. You can always import functions and values from another file using @racket[require] (as seen in the previous section). The @racketfont{directory-require.rkt} is just meant to cure the tedium of importing the same file into every Pollen source file in your project. In a small project, not much tedium; in a large project, more. +First, using this file is not mandatory. You can always import functions and values from another file using @racket[require] (as seen in the previous section). The @tt{directory-require.rkt} is just meant to cure the tedium of importing the same file into every Pollen source file in your project. In a small project, not much tedium; in a large project, more. -Second, notice from the @racketfont{.rkt} suffix that @racketfont{directory-require.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the @racket[world:directory-require] value. +Second, notice from the @tt{.rkt} suffix that @tt{directory-require.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the @racket[world:directory-require] value. -Third, notice from the @racketfont{directory-} prefix that @racketfont{directory-require.rkt} is only used by Pollen source files @italic{in the same directory}. So if your project has source files nested inside a subdirectory, you'll need to explicitly create another @racketfont{directory-require.rkt} there and share the functions & values as needed. +Third, notice from the @tt{directory-} prefix that @tt{directory-require.rkt} is only used by Pollen source files @italic{in the same directory}. So if your project has source files nested inside a subdirectory, you'll need to explicitly create another @tt{directory-require.rkt} there and share the functions & values as needed. @margin-note{``Why not make this file visible throughout a project, rather than just a directory?'' Good idea, but I couldn't figure out how to do it without creating finicky new dependencies. If you have a better idea, I'm open to it.} -Let's see how this works in practice. In the same directory as @racketfont{article.html.pm}, create a new @racketfont{directory-require.rkt} file as follows: +Let's see how this works in practice. In the same directory as @tt{article.html.pm}, create a new @tt{directory-require.rkt} file as follows: @fileblock["directory-require.rkt" @codeblock{ #lang racket @@ -672,7 +672,7 @@ Let's see how this works in practice. In the same directory as @racketfont{artic Here we use the @racket[define] function (which we've seen before) to set @racket[author] equal to @racket["Trevor Goodchild"]. Note the final step: consistent with standard Racket rules, we have to explicitly @racket[provide] the new value so that other files can see it (unlike Python, things you @racket[define] in Racket are private by default, not public). -Then update good old @racketfont{article.html.pm}: +Then update good old @tt{article.html.pm}: @fileblock["article.html.pm" @codeblock{ #lang pollen @@ -696,7 +696,7 @@ Run this, and you'll get: @repl-output{'(root "The author is really " "Trevor Goodchild" "?")} -That's all there is to it. Everything provided by @racketfont{directory-require.rkt} is automatically available within each Pollen source file. +That's all there is to it. Everything provided by @tt{directory-require.rkt} is automatically available within each Pollen source file. You can include functions, including tag functions, the same way. For instance, add a function for @racket[em]: @@ -766,23 +766,26 @@ Instead, let's make a decoder that allows us to denote a linebreak with a single @fileblock["article.html.pm" @codeblock|{ #lang pollen -The first line of the first paragraph. +The first line of the 'first' paragraph. And a new line. -The second paragraph. +The second paragraph --- isn't it great. }|] But without a decoder, the newlines just get passed through: -@repl-output{'(root "The first line of the first paragraph." "\n" "And a new line." "\n" "\n" "The second paragraph.")} +@repl-output{'(root "The first line of the 'first' paragraph." "\n" "And a new line." "\n" "\n" "The second paragraph --- isn't it great.")} When this X-expression is converted to HTML, the newlines persist: -@repl-output{The first line of the first paragraph.\nAnd a new line.\n\nThe second paragraph.} +@repl-output{The first line of the 'first' paragraph.\nAnd a new line.\n\nThe second paragraph --- isn't it great.} But in HTML, raw newlines are displayed as a single space. So if you view this file in the project server, you'll see: -@terminal{@larger{@smaller{The first line of the first paragraph. And a new line. The second paragraph.}}} +@browser{ +The first line of the 'first' paragraph. And a new line. The second paragraph --- isn't it great. +} + Not what we want. @@ -799,10 +802,10 @@ Add a basic @racket[decode-elements] to the source file like so: ◊(define (root . elements) (make-txexpr 'root null (decode-elements elements))) -The first line of the first paragraph. +The first line of the 'first' paragraph. And a new line. -The second paragraph. +The second paragraph --- isn't it great. }|] The @racket[make-txexpr] function is a utility from the @racket[txexpr] package, which is installed with Pollen. It builds a new X-expression from a tag, attribute list, and list of elements. Here, we'll keep the tag name @tt{root}, leave the attributes as @tt{null}, and append our decoded list of elements. @@ -821,26 +824,29 @@ We change this by giving @racket[decode-elements] the name of a processing funct (make-txexpr 'root null (decode-elements elements #:txexpr-elements-proc detect-paragraphs))) -The first line of the first paragraph. +The first line of the 'first' paragraph. And a new line. -The second paragraph. +The second paragraph --- isn't it great. }|] Now, when we run the file, the X-expression has changed to include two @racket[p] tags and a @racket[br] tag: -@repl-output{'(root (p "The first line of the first paragraph." (br) "And a new line.") (p "The second paragraph."))} +@repl-output{'(root (p "The first line of the 'first' paragraph." (br) "And a new line.") (p "The second paragraph --- isn't it great."))} That means when we convert to HTML, we get the tags we need: -@repl-output{

The first line of the first paragraph.
And a new line.

The second paragraph.

} +@repl-output{

The first line of the 'first' paragraph.
And a new line.

The second paragraph --- isn't it great.

} So when we view this in the project server, the linebreaks and paragraph breaks are displayed correctly: -@terminal{@larger{@smaller{The first line of the first paragraph.@(linebreak) +@browser{ +The first line of the 'first' paragraph. And a new line. -@(linebreak)@(linebreak) -The second paragraph.}}} + +The second paragraph --- isn't it great. +} + Of course, in practice you wouldn't put your decoding function in a single source file. You'd make it available to all your source files by putting it in @tt{directory-require.rkt}. So let's do that now: @@ -850,7 +856,7 @@ Of course, in practice you wouldn't put your decoding function in a single sourc (define (root . elements) (make-txexpr 'root null (decode-elements elements #:txexpr-elements-proc detect-paragraphs))) -(provide (all-defined-out)) +(provide root) }] We'll also restore the source of @tt{article.html.pm} to its original, simplified state: @@ -858,24 +864,261 @@ We'll also restore the source of @tt{article.html.pm} to its original, simplifie @fileblock["article.html.pm" @codeblock|{ #lang pollen -The first line of the first paragraph. +The first line of the 'first' paragraph. And a new line. -The second paragraph. +The second paragraph --- isn't it great. }|] And the result in the project server will be the same: -@terminal{@larger{@smaller{The first line of the first paragraph.@(linebreak) +@browser{ +The first line of the 'first' paragraph. And a new line. -@(linebreak)@(linebreak) -The second paragraph.}}} +The second paragraph --- isn't it great. +} + +But wait, those straight quotes look terrible. Also, three hyphens for an em dash? Barbaric. -By the way, though decoding via the @tt{root} tag is the most likely usage scenario, you don't have to do it that way. Decoding is just a special kind of tag function. So you can make a decoder that only affects a certain tag within the page. Or you can make multiple decoders for different tags. The advantage of using a decoder with @tt{root} is that it can affect all the content, and it will be the last tag function that gets called. +Let's upgrade our decoder to take of those. Once again, we'll get lucky, because the @racket[decode] module provides two functions for the job: @racket[smart-quotes] and @racket[smart-dashes]. + +This time, however, we're going to attach them to another part of @racket[decode-elements]. Smart-quote and smart-dash conversion only needs to look at the strings within the X-expression. So instead of attaching these functions to the @racket[#:txexpr-elements-proc] argument of @racket[decode-elements], we'll attach them to @racket[#:string-proc], which lets us specify a function to apply to strings: + +@fileblock["directory-require.rkt" @codeblock{ +#lang racket/base +(require pollen/decode txexpr) +(define (root . elements) + (make-txexpr 'root null (decode-elements elements + #:txexpr-elements-proc detect-paragraphs + #:string-proc (compose smart-quotes smart-dashes)))) +(provide root) +}] + +Because @racket[#:string-proc] only accepts one function (not two), we need to use @racket[compose] to combine @racket[smart-quotes] and @racket[smart-dashes] into one (@racket[compose] will apply the last function, then the previous one, and so on to the left end of the list). + +Now, if we run @tt{article.html.pm} in DrRacket, we can see the effects of the new decoder functions. The quotes are curled, and the three hyphens become an em dash: + +@repl-output{'(root (p "The first line of the ‘first’ paragraph." (br) "And a new line.") (p "The second paragraph—isn’t it great."))} + +And of course, this shows up in the project server too: + +@browser{ +The first line of the ‘first’ paragraph. +And a new line. + +The second paragraph—isn’t it great. +} + +By the way, even though decoding via the @tt{root} tag is the most likely usage scenario, you don't have to do it that way. Decoding is just a special kind of tag function. So you can make a decoder that only affects a certain tag within the page. Or you can make multiple decoders for different tags. The advantage of using a decoder with @tt{root} is that it can affect all the content, and since it's attached to the root node, it will always be the last tag function that gets called. @section{Putting it all together} -[Coming soon] +For this final example, we'll combine what we've learned in the first three tutorials. Though this project is still simple, it summarizes all the major concepts of Pollen. + +It also provides a recipe you can adapt for your own projects, whether small or large. For instance, @italic{@link["http://practicaltypography.com"]{Butterick's Practical Typography}} follows this core structure. + +As we go through the ingredients, I'll review the purpose of each. Save these files into a single project directory with the project server running. + +@subsection{The @tt{directory-require.rkt} file} + +This file provides functions that are available to all Pollen source files in the same directory. It's written in standard Racket. The @tt{directory-require.rkt} file is optional — without it, your tags will just be treated as default tag functions. But you'll probably find it a convenient way to make tag functions available within your project, including a @racket[decode] function attached to @tt{root}. + +Here, we'll use the @tt{directory-require.rkt} we devised in the previous section to set up decoding for our source files: + +@fileblock["directory-require.rkt" @codeblock{ +#lang racket/base +(require pollen/decode txexpr) +(define (root . elements) + (make-txexpr 'root null (decode-elements elements + #:txexpr-elements-proc detect-paragraphs + #:string-proc (compose smart-quotes smart-dashes)))) +(provide root) +}] + + +@subsection{The template} + +When you're using Pollen authoring mode for your content — using either Markdown syntax, or Pollen markup — your source files will produce an X-expression. To convert this X-expression into a finished file, you need to use a template. + +By default, when Pollen finds a source file called @tt{filename.ext.pm} or @tt{filename.ext.pmd}, it will look for a template in your project directory called @tt{template.ext}, where @tt{.ext} is the matching output extension. + +In this project, we want to end up with HTML, so our source files will be called @tt{filename.html.pm}, and thus we need to make a @tt{template.html}. Let's use a modified version of the one we made in the second tutorial: + +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + + + +◊select['h1 doc] by T. S. Eliot + + +◊->html[doc] +◊(define prev-page (previous here)) +◊when/block[prev-page]{ +} +◊(define next-page (next here)) +◊when/block[next-page]{ +} + + +}] + +@subsection{The pagetree} + +A pagetree defines sequential and hierarchical relationships among a set of output files. The pagetree is used by the template to calculate navigational links (e.g., previous, next, up, etc.) A pagetree is optional — if you don't need navigation in your project, you don't need a pagetree. + +But in this project, we do want navigation. So we'll add an @tt{index.ptree} file like so: + +@fileblock["index.ptree" +@codeblock{ +#lang pollen + +burial.html +chess.html +sermon.html +}] + +@subsection{A CSS stylesheet using the preprocessor} + +Our template file above refers to a CSS file called @tt{styles.css}. When resolving linked files, the project server makes no distinction between static and dynamic files. If there's a static file called @tt{styles.css}, it will use that. + +Or, if you make a preprocessor source file called @tt{styles.css.pp}, it will be dynamically rendered into the requested @tt{styles.css} file. The preprocessor will operate on any file with the @tt{.pp} extension — so a preprocessor source called @tt{filename.ext.pp} will be rendered into @tt{filename.ext}. (The corollary is that preprocessor functionality can be added to @italic{any} kind of text-based file.) + +Preprocessor source files, like authoring source files, get access to everything in @tt{directory-require.rkt}, so you can share common functions and variables. + +Let's use an improved version of the dynamic CSS file we made in the first tutorial. + +@fileblock["styles.css.pp" +@codeblock{ +#lang pollen + +◊(define inner 2) +◊(define edge (* inner 2)) +◊(define color "gray") +◊(define multiplier 1.3) + +body { + margin: ◊|edge|em; + border: ◊|inner|em double ◊|color|; + padding: ◊|inner|em; + font-size: ◊|multiplier|em; + line-height: ◊|multiplier|; +} + +h1 { + font-size: ◊|multiplier|em; +} + +#prev, #next { + position: fixed; + top: ◊|(/ edge 2)|em; +} + +#prev { + left: ◊|edge|em; +} + +#next { + right: ◊|edge|em; +} +}] + +@subsection{The content source files using Pollen markup} + +With the scaffolding in place, we need the content. Our pagetree contains three output files — @tt{burial.html}, @tt{chess.html}, and @tt{sermon.html}. We're going to make these output files using Pollen markup. So we'll create three source files and name them by adding the @tt{.pm} source extension to each of the output names — thus @tt{burial.html.pm}, @tt{chess.html.pm}, and @tt{sermon.html.pm}, as follows (and with apologies to T. S. Eliot): + + +@fileblock["burial.html.pm" @codeblock[#:keep-lang-line? #f]{ +#lang scribble/text +#lang pollen + +◊h1{I. The Burial of the Dead} + +"You gave me hyacinths first a year ago; +They called me the hyacinth girl." +--- Yet when we came back, late, from the Hyacinth garden, +Your arms full, and your hair wet, I could not +Speak, and my eyes failed, I was neither +Living nor dead, and I knew nothing, +Looking into the heart of light, the silence. +◊em{Od' und leer das Meer.} + +Madame Sosostris, famous clairvoyante, +Had a bad cold, nevertheless +Is known to be the wisest woman in Europe, +With a wicked pack of cards. +}] + +@fileblock["chess.html.pm" @codeblock[#:keep-lang-line? #f]{ +#lang scribble/text +#lang pollen + +◊h1{II. A Game of Chess} + +And still she cried, and still the world pursues, +"Jug Jug" to dirty ears. +And other withered stumps of time +Were told upon the walls; staring forms +Leaned out, leaning, hushing the room enclosed. +Footsteps shuffled on the stair, +Under the firelight, under the brush, her hair +Spread out in fiery points +Glowed into words, then would be savagely still. + +"My nerves are bad to-night. Yes, bad. Stay with me. +Speak to me. Why do you never speak? Speak. +What are you thinking of? What thinking? What? +I never know what you are thinking. Think." +}] + +@fileblock["sermon.html.pm" @codeblock[#:keep-lang-line? #f]{ +#lang scribble/text +#lang pollen + +◊h1{III. The Fire Sermon} + +"Trams and dusty trees. +Highbury bore me. Richmond and Kew +Undid me. By Richmond I raised my knees +Supine on the floor of a narrow canoe." + +"My feet are at Moorgate, and my heart +Under my feet. After the event +He wept. He promised 'a new start.' +I made no comment. What should I resent?" +}] + +@subsection{The result} + +Now visit the project server and view @tt{burial.html}, which should look something like this (the box will expand to fit your browser window): + +@image/rp["burial.png" #:scale 0.8] + +Click the navigational links at the top to move between pages. You're encouraged to change the source files, the style sheet, the template, or @tt{directory-require.rkt}, and see how these changes immediately affect the page rendering in the project server. (You can also change the sequence of the pages in @tt{index.ptree}, but in that case, you'll need to restart the project server to see the change.) + +This page isn't a miracle of web design, but it shows you in one example: + +@itemlist[ + +@item{Pollen markup being decoded — paragraph breaks, linebreaks, smart quotes, smart dashes — with a @racket[decode] function attached to the @tt{root} node by @tt{directory-require.rkt};} + +@item{A dynamically-generated CSS file that computes positions for CSS elements using numerical values set up with @racket[define], and mathematical conversions thereof;} + +@item{Navigational links that appear and disappear as needed using conditional statements (@racket[when/block]) in @tt{template.html}, with the page sequence defined by @tt{index.ptree} and the names of the links being pulled from the @tt{h1} tag of each source file using @racket[select].} + +] + +@section{Third tutorial complete} + +OK, that was a humongous tutorial. Congratulations on making it through. + +But your reward is that you now understand all the core concepts of the Pollen publishing system, including the most important ones: the flexibility of Pollen markup, and the connection between tags and functions. + +Armed with this knowledge, you have everything you need to start doing useful things with Pollen. I hope you enjoy using it as much as I've enjoyed making it! + + +