;;; Copyright 2002, 2003 Andreas Bernauer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; input-fields ;;; defines input-fields for surflets (define *input-field-trigger* `*input-field*) ;; GET-BINDINGS?: Transformer will get all bindings of request, not ;; only the one concerning the input-field. (define-record-type input-field :input-field (real-make-input-field name transformer html-tree get-bindings?) real-input-field? (name input-field-name) (transformer input-field-transformer) (attributes input-field-attributes) (html-tree input-field-html-tree) (get-bindings? input-field-get-bindings?)) (define-record-discloser :input-field (lambda (input-field) (list 'input-field (input-field-name input-field)))) ;; Have to do a trick to get around with SSAX: input-field is a list ;; whose first element is *input-field-trigger* and the last (next) one ;; is a real input-field. (define (input-field? input-field) (and (pair? input-field) (eq? *input-field-trigger* (car input-field)) (real-input-field? (cadr input-field)))) (define generate-input-field-name generate-unique-name) (define identity (lambda (a) a)) ;; See note at input-field? for reasons for the list. (define (make-input-field name transformer html-tree) (list *input-field-trigger* (real-make-input-field name transformer html-tree #f))) (define (make-higher-input-field transformer html-tree) (list *input-field-trigger* (real-make-input-field #f transformer html-tree #t))) (define (make-text-input-field . maybe-further-attributes) (let ((name (generate-input-field-name "text"))) (optionals maybe-further-attributes ((default-text string?) (attributes sxml-attribute?)) (make-input-field name identity `(input (@ (type "text") (name ,name) ,(and default-text `(value ,default-text)) ;; this will insert a list, but ;; XML->HTML doesn't care about it ,(and attributes (cdr attributes)) )))))) (define make-number-input-field (let ((number-input-field-transformer (lambda (string) (or (string->number string) (error "wrong type"))) )) (lambda maybe-further-attributes (let ((name (generate-input-field-name "number"))) (optionals maybe-further-attributes ((default (lambda (a) (or (number? a) (string-or-symbol? a)))) (attributes sxml-attribute?)) (make-input-field name number-input-field-transformer `(input (@ (type "text") (name ,name) ,(and default `(value ,default)) ,(and attributes (cdr attributes)))))))))) (define (make-password-input-field . maybe-further-attributes) (let ((name (generate-input-field-name "password"))) (optionals maybe-further-attributes ((attributes sxml-attribute?)) (make-input-field name identity `(input (@ (type "password") (name ,name) ,(and attributes (cdr attributes)))))))) (define (make-textarea-input-field . maybe-further-attributes) (let ((name (generate-input-field-name "textarea"))) (optionals maybe-further-attributes ((default-text string?) (attributes sxml-attribute?)) (make-input-field name identity `(textarea (@ (type "textarea") (name ,name) ,(and attributes (cdr attributes))) ,(and default-text)))))) ;(make-select-input-fields '("this" "that" "those") '(@ ((id "sushi")))) ;(make-select-input-fields '("this" ("that" '(@ (selected))) "those")) ;; dropdown: (size 1) ;; multiple choice: (multiple) ;; preselected option: (selected) ;; changed return value: (value new-value) ;; returns a select input field with several options (define make-select-input-field (let ((make-multiple-transformer (lambda (name) (lambda (bindings) (map cdr (filter (lambda (binding) (equal? (car binding) name)) bindings)))))) (lambda (options . maybe-further-attributes) (optionals maybe-further-attributes ((multiple? boolean?) (attributes sxml-attribute?)) (let* ((name (generate-input-field-name "select")) (sxml-options (map (lambda (option) (cond ((string-or-symbol? option) (list 'option option)) ((list? option) (cond ((null? (cdr option)) `(option ,option)) ((sxml-attribute? (cdr option)) ; w/attribs? `(option ,(cdr option) ,(car option))) (else (error "not an attribute" (cdr option))))) (else (error "not an option" option)))) options)) (sxml `(select (@ ((name ,name) ,(if multiple? '(multiple) '()) ,(and attributes (cdr attributes)))) #\newline ,sxml-options))) (if multiple? (make-higher-input-field (make-multiple-transformer name) sxml) (make-input-field name identity sxml))))))) ;; returns a *list* of radio buttons (define (make-radio-input-fields values . maybe-further-attributes) (let ((name (generate-input-field-name "radio"))) (optionals maybe-further-attributes ((attributes sxml-attribute?)) (map (lambda (value) (let ((value-value (if (pair? value) (car value) value)) (value-attributes (if (pair? value) (if (sxml-attribute? (cdr value)) (cddr value) (error "not an attribute" cdr value)) #f))) (make-input-field name (lambda (select) select) `(input (@ ((type "radio") (name ,name) (value ,value-value) ,(and value-attributes) ,(and attributes (cdr attributes)))))))) values)))) ;; returns a checkbox input field (define (make-checkbox-input-field . maybe-further-attributes) (let* ((name (generate-input-field-name "checkbox"))) (optionals maybe-further-attributes ((checked? boolean?) (value (lambda (a) (or (string? a) (number? a) (symbol? a)))) (attributes sxml-attribute?)) (make-input-field name (lambda (value) (or (string=? value "on") value)) `(input (@ ((type "checkbox") (name ,name) ,(if value `(value ,value) '()) ,(if checked? '(checked) '()) ,(and attributes (cdr attributes))))))))) (define (make-hidden-input-field value . maybe-further-attributes) (let ((name (generate-input-field-name "hidden"))) (optionals maybe-further-attributes ((attributes sxml-attribute?)) (make-input-field name identity `(input (@ (type "hidden") (name ,name) (value ,value) ,(and attributes (cdr attributes)))))))) (define (make-button type name button-caption attributes) (make-input-field name identity `(input (@ (type ,type) (name ,name) ,(and button-caption `(value ,button-caption)) ,(and attributes (cdr attributes)))))) (define (string-or-symbol? a) (or (string? a) (symbol? a))) (define (make-submit-button . maybe-further-attributes) (optionals maybe-further-attributes ((button-caption string-or-symbol?) (attributes sxml-attribute?)) (make-button "submit" (generate-input-field-name "submit") button-caption attributes))) (define (make-reset-button . maybe-further-attributes) (optionals maybe-further-attributes ((button-caption string-or-symbol?) (attributes sxml-attribute?)) (make-button "reset" (generate-input-field-name "reset") button-caption attributes))) (define (make-image-button image-source . maybe-further-attributes) (optionals maybe-further-attributes ((attributes sxml-attribute?)) (make-button "image" (generate-input-field-name "imgbtn") #f `(@ (src ,image-source) ,@(if attributes (cdr attributes) '()))))) ;; : '(input-field . ) ;; : #{Input-field "name"} (define (raw-input-field-value input-field bindings) (let ((input-field (cadr input-field))) (cond ((input-field-get-bindings? input-field) ((input-field-transformer input-field) bindings)) ((real-input-field-binding input-field bindings) => (lambda (binding) ((input-field-transformer input-field) (cdr binding)))) (else (error "no such input-field" input-field bindings))))) ;; Trys to get a value for INPUT-FIELD in BINDINGS. If this fails ;; (i.e. RAW-INPUT-FIELD-VALUE returns an error), the default-value is ;; returned. The default-value defaults to #f. NOTE: If you do this ;; with input-fields whose valid values may be the same as the default ;; value, you cannot determine by the result if there was such a value ;; or not. Keep in mind, that RAW-INPUT-FIELD-VALUE returns also an ;; error, if there was not such an input field. This makes ;; INPUT-FIELD-VALUE working with checkbox input fields because they ;; miss if they are not checked. (define (input-field-value input-field bindings . maybe-default) (let ((default (:optional maybe-default #f))) (with-fatal-error-handler (lambda (condition more) default) (raw-input-field-value input-field bindings)))) (define (real-input-field-binding input-field bindings) (assoc (input-field-name input-field) bindings)) (define (input-field-binding input-field bindings) (real-input-field-binding (cadr input-field) bindings))