! Copyright (C) 2008 Slava Pestov ! See http://factorcode.org/license.txt for BSD license. USING: html.elements http.server.validators accessors namespaces kernel io math.parser assocs classes words classes.tuple arrays sequences io.files http.server.templating.fhtml http.server.actions splitting mirrors hashtables fry continuations math ; IN: http.server.components SYMBOL: components TUPLE: component id required default ; : component ( name -- component ) dup components get at [ ] [ "No such component: " prepend throw ] ?if ; GENERIC: validate* ( value component -- result ) GENERIC: render-view* ( value component -- ) GENERIC: render-edit* ( value component -- ) GENERIC: render-error* ( reason value component -- ) SYMBOL: values : value values get at ; : set-value values get set-at ; : validate ( value component -- result ) '[ , over empty? [ [ default>> [ v-default ] when* ] [ required>> [ v-required ] when ] bi ] [ validate* ] if ] with-validator ; : render-view ( component -- ) [ id>> value ] [ render-view* ] bi ; : render-error ( error -- ) write ; : render-edit ( component -- ) dup id>> value dup validation-error? [ [ reason>> ] [ value>> ] bi rot render-error* ] [ swap [ default>> or ] keep render-edit* ] if ; : ( id class -- component ) \ component construct-empty swap construct-delegate swap >>id ; inline ! Forms TUPLE: form view-template edit-template components ; :
( id -- form ) form V{ } clone >>components ; : add-field ( form component -- form ) dup id>> pick components>> set-at ; : with-form ( form quot -- ) >r components>> components r> with-variable ; inline : set-defaults ( form -- ) [ components get [ swap values get [ swap default>> or ] change-at ] assoc-each ] with-form ; : view-form ( form -- ) dup view-template>> '[ , run-template ] with-form ; : edit-form ( form -- ) dup edit-template>> '[ , run-template ] with-form ; : validate-param ( id component -- ) [ [ params get at ] [ validate ] bi* ] [ drop set-value ] 2bi ; : (validate-form) ( form -- error? ) [ validation-failed? off components get [ validate-param ] assoc-each validation-failed? get ] with-form ; : validate-form ( form -- ) (validate-form) [ validation-failed ] when ; : blank-values H{ } clone values set ; : from-tuple values set ; : values-tuple values get mirror-object ; ! ! ! ! Canned components: for simple applications and prototyping ! ! ! : render-input ( value component type -- ) > [ =id ] [ =name ] bi =value input/> ; ! Hidden fields TUPLE: hidden ; : ( component -- component ) hidden construct-delegate ; M: hidden render-view* 2drop ; M: hidden render-edit* >r dup number? [ number>string ] when r> "hidden" render-input ; ! String input fields TUPLE: string min-length max-length ; : ( id -- component ) string ; M: string validate* [ v-one-line ] [ [ min-length>> [ v-min-length ] when* ] [ max-length>> [ v-max-length ] when* ] bi ] bi* ; M: string render-view* drop write ; M: string render-edit* "text" render-input ; M: string render-error* "text" render-input render-error ; ! Username fields TUPLE: username ; : ( id -- component ) username construct-delegate 2 >>min-length 20 >>max-length ; M: username validate* delegate validate* v-one-word ; ! E-mail fields TUPLE: email ; : ( id -- component ) email construct-delegate 5 >>min-length 60 >>max-length ; M: email validate* delegate validate* dup empty? [ v-email ] unless ; ! Password fields TUPLE: password ; : ( id -- component ) password construct-delegate 6 >>min-length 60 >>max-length ; M: password validate* delegate validate* v-one-word ; M: password render-edit* >r drop f r> "password" render-input ; M: password render-error* render-edit* render-error ; ! Number fields TUPLE: number min-value max-value integer ; : ( id -- component ) number ; M: number validate* [ v-number ] [ [ integer>> [ v-integer ] when ] [ min-value>> [ v-min-value ] when* ] [ max-value>> [ v-max-value ] when* ] tri ] bi* ; M: number render-view* drop number>string write ; M: number render-edit* >r number>string r> "text" render-input ; M: number render-error* "text" render-input render-error ; ! Text areas TUPLE: text ; : ( id -- component ) text ; M: text validate* drop ; M: text render-view* drop write ; : render-textarea ; M: text render-edit* render-textarea ; M: text render-error* render-textarea render-error ; ! Simple captchas TUPLE: captcha ; : ( id -- component ) captcha construct-delegate ; M: captcha validate* drop v-captcha ;