#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]>