#lang at-exp racket/base
(require scribble/html racket/dict (for-syntax racket/base syntax/name syntax/parse)
"utils.rkt" "resources.rkt" "private/roots.rkt")
(provide page page*
plain plain*
copyfile
symlink
(rename-out [mk-site site])
site?
site-dir
site-css-path
site-favicon-path
site-navbar
site-navbar-dynamic-js)
(define-for-syntax (process-contents who layouter stx xs)
(let loop ([xs xs] [kws '()] [id? #f])
(syntax-case xs ()
[(k v . xs) (keyword? (syntax-e #'k))
(loop #'xs (list* #'v #'k kws) (or id? (eq? '#:id (syntax-e #'k))))]
[_ (with-syntax ([layouter layouter]
[(x ...) (reverse kws)]
[(id ...)
(if id?
'()
(let ([name (or (syntax-property stx 'inferred-name)
(syntax-local-name))])
(if name (list '#:id `',name) '())))]
;; delay body, allow definitions
[body #`(λ () (begin/text #,@xs))])
#'(layouter id ... x ... body))])))
(define (get-path who id file sfx dir)
(define file*
(or file
(let ([f (and id (symbol->string (force id)))])
(cond [(and f (regexp-match #rx"[.]" f)) f]
[(and f sfx)
(string-append f (regexp-replace #rx"^[.]?" sfx "."))]
[else (error who "missing `#:file', or `#:id'~a"
(if sfx "" " and `#:suffix'"))]))))
(if dir (web-path dir file*) file*))
;; The following are not intended for direct use, see
;; `define+provide-context' below (it could be used with #f for the
;; directory if this ever gets used for a flat single directory web
;; page.)
;; for plain text files
(define-syntax (plain stx)
(syntax-case stx () [(_ . xs) (process-contents 'plain #'plain* stx #'xs)]))
(define (plain* #:site site
#:id [id #f] #:suffix [suffix #f]
#:file [file #f]
#:referrer [referrer values]
#:newline [newline? #t]
content)
(resource/referrer (get-path 'plain id file suffix (site-dir site))
(file-writer output (list content (and newline? "\n")))
referrer))
;; page layout function
(define-syntax (page stx)
(syntax-case stx () [(_ . xs) (process-contents 'page #'page* stx #'xs)]))
(define (page* #:site site
#:id [id #f]
#:file [file #f]
;; if this is true, return only the html -- don't create
;; a resource -- therefore no file is made, and no links
;; to it can be made (useful only for stub templates)
#:html-only [html-only? #f]
#:title [label (if id
(let* ([id (format "~a" (force id))]
[id (regexp-replace #rx"^.*/" id "")]
[id (regexp-replace #rx"-" id " ")])
(string-titlecase id))
(error 'page "missing `#:id' or `#:title'"))]
#:link-title [linktitle label]
#:window-title [wintitle @list{Racket: @label}]
;; can be #f (default), 'full: full page (and no div),
;; otherwise, a css width
#:width [width #f]
#:description [description #f] ; for a meta tag
#:extra-headers [extra-headers #f]
#:extra-body-attrs [body-attrs #f]
#:referrer [referrer
(λ (url . more)
(a href: url (if (null? more) linktitle more)))]
;; will be used instead of `this' to determine navbar highlights
#:part-of [part-of #f]
content0)
(define dir (site-dir site))
(define (page)
(define desc
(and description (meta name: 'description content: description)))
(define resources (site-resources site))
(define header
(let ([headers (resources 'headers)]
[extras (if (and extra-headers desc)
(list desc "\n" extra-headers)
(or desc extra-headers))])
(if extras (list headers "\n" extras) headers)))
(define navbar ((resources 'make-navbar) (or part-of this)))
(define content
(list navbar "\n"
(case width
[(full) content0]
[(#f) (div class: 'bodycontent content0)]
[else (div class: 'bodycontent style: @list{width: @|width|@";"}
content0)])
(resources 'postamble)))
@list{@resources['preamble]
@html{@||
@head{@||
@title{@wintitle}
@header
@||}
@(if body-attrs
(apply body `(,@body-attrs ,content))
(body content))}
@||})
(define this (and (not html-only?)
(resource/referrer (get-path 'page id file "html" dir)
(file-writer output-xml page)
referrer)))
(when this (pages->part-of this (or part-of this)))
(or this page))
;; maps pages to their parts, so symbolic values can be used to determine it
(define pages->part-of
(let ([t (make-hasheq)])
(case-lambda [(page) (hash-ref t page page)]
[(page part-of) (hash-set! t page part-of)])))
(define (list-ref* l n d)
(if ((length l) . > . n)
(list-ref l n)
d))
(define ((navbar-content logo columns page-style?))
(define (icon name) @i[class: name]{})
(define (row . content) (apply div class: "row" content))
(define main-promise (resource "www/" #f))
@row{
@(if page-style?
@a[class: "toggle" gumby-trigger: "#nav1 > .row > ul" href: "#"]{
@icon{icon-menu}}
'())
@a[class: "five columns logo" href: (url-of main-promise)]{
@img[class: "logo" src: logo width: "198" height: "60" alt: "Racket"]}
@ul[class: "five columns"]{
@li{@(list-ref* columns 0 "")}
@li{@(list-ref* columns 1 "")}
@li{@(list-ref* columns 2 "")}
@li{@(list-ref* columns 3 "")}}})
(define ((navbar-maker logo columns page-style?) this)
@div[class: "navbar gumby-content" gumby-fixed: "top" id: "nav1"]{
@((navbar-content logo columns page-style?))})
(define gumby-preamble
@list{
@; paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
@comment{[if lt IE 7]>