#lang racket
(require "tfield.rkt")
(require web-server/servlet
web-server/servlet-env
web-server/templates
xml)
(define (render-full/string tf cont-url)
(define title (tfield-label tf))
(define text
(match (tfield/function-text tf)
[(? string? s) s]
[xs (apply string-append (map xexpr->string xs))]))
(include-template "htdocs/template.html"))
(define (render*/edit tfs parent)
(map (λ(t) (render/edit t parent)) tfs))
(define (render/edit tf parent)
(define parent-not-oneof? (not (tfield/oneof? parent)))
(define parent-not-oneof/listof? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define (input-text-of name value [disabled? #f])
`(input ([type "text"] [name ,name] [id ,name]
[value ,value] ,@(if disabled? `([disabled "disabled"]) '()))))
(match tf
[(tfield/const label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-constant) (and label? label)
(input-text-of name (format "~a" value) #t) error)]
[(tfield/boolean label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-boolean)
(and label? `(label ([for ,name]) ,label))
`(input ([type "checkbox"] [name ,name]
[id ,name] ,@(if value `([checked "checked"]) '())))
error)]
[(tfield/number label name error value raw-value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-number) (and label? label)
(input-text-of name (or raw-value "")) error)]
[(tfield/symbol label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-symbol) (and label? label)
(input-text-of name (if value (symbol->string value) ""))
error)]
[(tfield/string label name error value non-empty?)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-string) (and label? label)
(input-text-of name (or value "")) error)]
[(? tfield/file? _) (render-file/edit tf parent)]
[(? tfield/struct? _) (render-struct/edit tf parent)]
[(? tfield/oneof? _) (render-oneof/edit tf parent)]
[(? tfield/listof? _) (render-listof/edit tf parent)]
[(tfield/function title name error text func args result)
"blah"]
[_ (error (object-name render/edit)
(format "somehow got an unknown field type: ~a" tf))]))
(define (colonize str)
(if (equal? str "") str (string-append str ": ")))
(define ((div-wrapper name classes) inner)
`(div (,@(if name `([id ,(format "~a-div" name)]) '())
[class ,(string-join (map symbol->string classes) " ")])
,@(if (xexpr? inner) (list inner) inner)))
(define (nest-level tf/name)
(format "nest~a" (add1 (remainder (sub1 (depth-of tf/name)) 3))))
(define (render-basic/edit name classes label input-elt error)
((div-wrapper name `(tfield tfield-basic ,@classes))
(list (if label `(span ([class "label"]) ,(colonize label)) "")
input-elt
(if error `(span ([class "error"]) ,error) ""))))
(define (render-file/edit tf parent)
(define label? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(match tf
[(tfield/file label name error file-name mime-type temp-path)
(define inner-content
(cond
[(not file-name) `(input ([type "file"] [name ,name] [id ,name]))]
[(not temp-path) `(span ([class "uploading"])
(img ([src "file-loading.gif"] [alt "file loading"]))
"Uploading " ,file-name "... ")]
[else
`(span ([class "uploaded"])
(input ([type "hidden"] [id ,name] [name ,name]
[value ,file-name]))
,(file-view-link name file-name)
" " ,(file-clear-link name file-name)
)]))
(render-basic/edit name '(tfield-file) (and label? label)
inner-content error)]))
(define (render-listof/edit tf parent)
(match tf
[(tfield/listof label name error base elts non-empty?)
((div-wrapper name `(tfield tfield-listof))
`(fieldset ([class ,(nest-level tf)])
(legend ,label)
,(if error `(div ([class "error error-listof"]) ,error) "")
(input ([type "hidden"] [name ,name] [id ,name]
[value ,(format "~a" (length elts))]))
(ol ([id ,(string-append name "-ol")] [class "tfield-listof sortable"])
,@(map (curry render-listof-item/edit tf) elts)
(li ([class "nosort"])
(button ([id ,(string-append name "-addbtn")]
[class "addbtn"] [type "button"])
"Add " ,(tfield-label base))))))]))
(define (render-listof-item/edit tf/listof e)
(define elt-name (tfield-name e))
`(li ([id ,(string-append elt-name "-li")])
(div ([class "listof-item"])
(div ([class "li-handle"])
(span ([class "ui-icon ui-icon-arrowthick-2-n-s"]) ""))
(button ([id ,(string-append elt-name "-delbtn")]
[class "delbtn"] [type "button"]) "")
(div ([class "listof-data"])
,(render/edit e tf/listof))
(div ([style "clear: both;"]) ""))))
(define (render-oneof/edit tf parent)
(match tf
[(tfield/oneof label name error options chosen)
(define label? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define selected-tf (and chosen (list-ref options chosen)))
(define fieldset? (and selected-tf (or (tfield/struct? selected-tf)
(tfield/listof? selected-tf))))
(define select-elt (render-select-element/edit tf))
(define label-span
`(span ,(if (and label? label)
`(label ([for ,name]) ,label " ") "") ,select-elt))
(cond
[(not selected-tf)
(render-basic/edit name '(tfield-oneof) label-span empty error)]
[fieldset?
((div-wrapper name `(tfield tfield-oneof))
`(fieldset ([class ,(nest-level tf)])
(legend ,label-span)
,(if error `(div ([class "error"]) ,error) "")
,(render/edit selected-tf tf)))]
[else
(render-basic/edit name '(tfield-oneof) label-span
(render/edit selected-tf tf) error)])]))
(define (render-select-element/edit tf/o)
(define name (tfield-name tf/o))
(define opts (tfield/oneof-options tf/o))
(define chosen (tfield/oneof-chosen tf/o))
`(select ([name ,name] [id ,name] [class "tfield-oneof"])
(option ([value "-"]
,@(if chosen '() '([selected "selected"]))) "-")
,@(map (λ(tflabel i)
`(option ([value ,(number->string i)]
,@(if (and chosen (= i chosen))
'([selected "selected"]) '())) ,tflabel))
(map tfield-label opts)
(build-list (length opts) values))))
(define (render-struct/edit tf parent)
(match tf
[(tfield/struct label name error constr args)
(define arg/content
`(ul ([class "tfield-structure"])
,@(map (λ(a) `(li ,(render/edit a tf))) args)))
(define wrapper
(if (tfield/oneof? parent)
((div-wrapper name '(tfield tfield-structure))
(list (if error `(div ([class "error"]) ,error) "")
arg/content))
((div-wrapper name '(tfield tfield-structure))
`(fieldset ([class ,(nest-level name)])
,(if label `(legend ,label) "")
,(if error `(div ([class "error"]) ,error) "")
,arg/content)) ))
wrapper]))
(define (render*/disp tfs parent [save-file #f])
(map (λ(t) (render/disp t parent save-file)) tfs))
(define (render/disp tf parent [save-file #f])
(define parent-not-oneof? (not (tfield/oneof? parent)))
(define parent-not-oneof/listof? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define parent-not-listof? (not (tfield/listof? parent)))
(match tf
[(tfield/const label name error value)
(render-basic/disp name '(tfield-constant) #f label)]
[(tfield/boolean label name error value)
(render-basic/disp name '(tfield-boolean) (and parent-not-listof? label)
(if value "YES" "NO"))]
[(tfield/number label name error value raw-value)
(render-basic/disp name '(tfield-number) (and parent-not-listof? label)
(or raw-value "-"))]
[(tfield/symbol label name error value)
(render-basic/disp name '(tfield-symbol) (and parent-not-listof? label)
(or (and value (symbol->string value)) "-"))]
[(tfield/string label name error value non-empty?)
(render-basic/disp name '(tfield-string) (and parent-not-listof? label)
(if (equal? value "") "-"
(or value "-")))]
[(tfield/file label name error file-name mime-type temp-path)
(render-basic/disp name '(tfield-file) (and parent-not-listof? label)
(file-view-link name file-name save-file))]
[(tfield/struct label name error constr args)
((div-wrapper #f '(tfield tfield-structure))
`(fieldset (legend ,(colonize label))
(ul ,@(map (λ(a) `(li ,a)) (render*/disp args tf save-file)))))]
[(tfield/oneof label name error options chosen)
(define selected-tf (and chosen (list-ref options chosen)))
(cond
[(not selected-tf)
((div-wrapper #f `(tfield tfield-oneof))
`(span "(" ,label " not selected)"))]
[else
((div-wrapper #f `(tfield tfield-oneof))
(render/disp selected-tf tf save-file))])]
[(tfield/listof label name error base elts non-empty?)
((div-wrapper #f `(tfield tfield-listof))
`(fieldset (legend ,(colonize label))
,(if (empty? elts)
"(empty)"
`(ol ,@(map (λ(a) `(li ,a)) (render*/disp elts tf save-file))))))]
[(tfield/function title name error text func args result)
`(span () "")]
[_ (error (object-name render/disp)
(format "somehow got an unknown field type: ~a" tf))]))
(define (render-basic/disp name classes label content)
((div-wrapper #f `(tfield tfield-basic ,@classes))
(list (if label `(span ([class "label"]) ,(colonize label)) "")
`(span ,@(if (xexpr? content) (list content) content)))))
(define (file-view-link name file-name [save-file #f])
(define handler
(if save-file
(format "viewFile($(this), '~a', '~a'); return false;" name save-file)
(format "viewFile($(this), '~a'); return false;" name)))
(if file-name
`(a ([href ,file-name] [class "filelink"] [onClick ,handler]) ,file-name
(input ([type "hidden"] [class "id"] [value ,name]))
,@(if save-file `((input ([type "hidden"] [class "savefile"]
[value ,save-file]))) '()))
"-"))
(define (file-clear-link name file-name)
`(button
([id ,(format "~a-clearbtn" name)] [class "fileclear"]
[type "button"]
[onClick ,(format "clearFile('~a'); return false;" name)])
"remove"))
(provide render-full/string
render*/edit
render/edit
render-listof-item/edit
render*/disp
render/disp)