472 lines
17 KiB
Scheme
472 lines
17 KiB
Scheme
|
;;; SUrflets' input fields
|
||
|
;;; Copyright 2002, 2003 Andreas Bernauer
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; Functions to create simple input fields
|
||
|
|
||
|
;; The interface for input-fields does not prescribe what the type of
|
||
|
;; attributes has to be. We choose a record here.
|
||
|
|
||
|
(define-record-type input-field-attributes :input-field-attributes
|
||
|
(make-input-field-attributes default others)
|
||
|
input-field-attributes?
|
||
|
(default input-field-attributes-default set-input-field-attributes-default!)
|
||
|
(others input-field-attributes-others set-input-field-attributes-others!))
|
||
|
|
||
|
;; A simple input-field is a prototype for other input-fields.
|
||
|
;; REPORTED-TYPE is the type of the input-field in HTML, TYPE the
|
||
|
;; internal referenced type and TRANSFORMER the function that
|
||
|
;; translates the HTTP-string of the request into a scheme value.
|
||
|
(define (simple-input-field-maker reported-type type default-pred transformer)
|
||
|
(lambda maybe-further-attributes
|
||
|
(let ((name (generate-input-field-name type)))
|
||
|
(optionals maybe-further-attributes
|
||
|
((default default-pred)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-input-field name type
|
||
|
transformer
|
||
|
(make-input-field-attributes
|
||
|
(and default `(value ,default))
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
(simple-input-field-maker-html-tree-maker
|
||
|
reported-type))))))
|
||
|
|
||
|
(define (simple-input-field-maker-html-tree-maker reported-type)
|
||
|
(lambda (input-field)
|
||
|
(let ((attributes (input-field-attributes input-field)))
|
||
|
`(input (@ (type ,reported-type)
|
||
|
(name ,(input-field-name input-field))
|
||
|
,(input-field-attributes-default attributes)
|
||
|
,(input-field-attributes-others attributes))))))
|
||
|
|
||
|
(define (make-simple-input-field-default-setter default-pred? wrap?)
|
||
|
(lambda (input-field value)
|
||
|
(if (default-pred? value)
|
||
|
(set-input-field-attributes-default!
|
||
|
(input-field-attributes input-field)
|
||
|
(if wrap? `(value ,value) value))
|
||
|
(error "Default value must be a number or a string or a symbol."
|
||
|
value))
|
||
|
(touch-input-field! input-field)))
|
||
|
|
||
|
(define (string-or-symbol? thing)
|
||
|
(or (string? thing) (symbol? thing)))
|
||
|
(define simple-default? string-or-symbol?)
|
||
|
|
||
|
(define set-simple-input-field-default!
|
||
|
(make-simple-input-field-default-setter simple-default? #t))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; Text input field
|
||
|
(define make-text-input-field
|
||
|
(simple-input-field-maker "text" "text"
|
||
|
simple-default? identity))
|
||
|
(define set-text-input-field-value! set-simple-input-field-default!)
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; Number input field
|
||
|
(define (number-input-field-default? value)
|
||
|
(or (number? value)
|
||
|
(simple-default? value)))
|
||
|
(define (number-input-field-transformer string)
|
||
|
(or (string->number string)
|
||
|
(error "wrong type")))
|
||
|
(define make-number-input-field
|
||
|
(simple-input-field-maker "text" "number"
|
||
|
number-input-field-default?
|
||
|
number-input-field-transformer))
|
||
|
(define set-number-input-field-value!
|
||
|
(make-simple-input-field-default-setter number-input-field-default? #t))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; hidden input-field
|
||
|
;; Little workaraound, as a hidden input-field needs a value. This
|
||
|
;; value is propagated in the slot "default value".
|
||
|
(define make-hidden-input-field
|
||
|
(let ((hidden-input-field-generator
|
||
|
(simple-input-field-maker "hidden" "text"
|
||
|
simple-default? identity)))
|
||
|
(lambda (value . maybe-further-attributes)
|
||
|
(apply hidden-input-field-generator
|
||
|
(cons value maybe-further-attributes)))))
|
||
|
(define set-hidden-input-field-value!
|
||
|
set-simple-input-field-default!)
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; Password input field
|
||
|
(define make-password-input-field
|
||
|
(simple-input-field-maker "password" "password"
|
||
|
simple-default? identity))
|
||
|
(define set-password-input-field-value!
|
||
|
set-simple-input-field-default!)
|
||
|
|
||
|
;;; That's it for simple input fields.
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; Textarea input field
|
||
|
(define (make-textarea-input-field . maybe-further-attributes)
|
||
|
(let ((name (generate-input-field-name "textarea")))
|
||
|
(optionals maybe-further-attributes
|
||
|
((default-text simple-default?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-input-field
|
||
|
name "textarea"
|
||
|
identity
|
||
|
(make-input-field-attributes
|
||
|
(and default-text)
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
make-textarea-input-field-html-tree))))
|
||
|
|
||
|
(define (make-textarea-input-field-html-tree input-field)
|
||
|
(let ((attributes (input-field-attributes input-field)))
|
||
|
`(textarea (@ (type "textarea")
|
||
|
(name ,(input-field-name input-field))
|
||
|
,(input-field-attributes-others attributes))
|
||
|
,(input-field-attributes-default attributes))))
|
||
|
|
||
|
(define set-textarea-input-field-value!
|
||
|
(let ((textarea-default-setter!
|
||
|
(make-simple-input-field-default-setter simple-default? #f)))
|
||
|
(lambda (textarea-input-field value)
|
||
|
(textarea-default-setter! textarea-input-field value))))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; Selection input field
|
||
|
|
||
|
;; sel-if == select-input-field
|
||
|
;(make-select-input-fields '("this" "that" "those") '(@ ((id "sushi"))))
|
||
|
;(make-select-input-fields '("this" ("that" '(@ (selected))) "those"))
|
||
|
;; dropdown: (size 1)
|
||
|
|
||
|
;;; A selection input field shows a list of options that can be
|
||
|
;;; selected. For this purpose, we introduce an sel-if-option record,
|
||
|
;;; that contains all the information for each option. This is
|
||
|
;;; justified by the fact, that the options list is in HTML seperated,
|
||
|
;;; too.
|
||
|
(define-record-type sel-if-option :sel-if-option
|
||
|
(really-make-sel-if-option tag value selected? attributes)
|
||
|
sel-if-option?
|
||
|
(tag sel-if-option-tag)
|
||
|
(value sel-if-option-value)
|
||
|
(selected? sel-if-option-selected? set-sel-if-option-selected?!)
|
||
|
(attributes sel-if-option-attributes set-sel-if-option-attributes!))
|
||
|
|
||
|
(define (make-sel-if-option tag value selected? attributes)
|
||
|
(if (string? tag)
|
||
|
(really-make-sel-if-option tag value selected?
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
(error "Select-input-field-option's tag must be a string." tag)))
|
||
|
|
||
|
;; Constructor for valued select input-field option.
|
||
|
(define (make-annotated-sel-if-option tag value . maybe-attributes)
|
||
|
(optionals maybe-attributes
|
||
|
((selected? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-sel-if-option tag value selected? attributes)))
|
||
|
|
||
|
;; Constructor for a simple select input-field option (not annotated).
|
||
|
(define (make-simple-sel-if-option tag . maybe-attributes)
|
||
|
(optionals maybe-attributes
|
||
|
((selected? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-sel-if-option tag 'ignored selected? attributes)))
|
||
|
|
||
|
(define-record-discloser :sel-if-option
|
||
|
(lambda (sel-if-option)
|
||
|
(list 'select-input-field-option
|
||
|
(sel-if-option-tag sel-if-option)
|
||
|
(sel-if-option-value sel-if-option)
|
||
|
(sel-if-option-selected? sel-if-option)
|
||
|
(sel-if-option-attributes sel-if-option)
|
||
|
)))
|
||
|
|
||
|
;; Selecting / Unselecting of an option in an select input-field,
|
||
|
;; chosen by tag.
|
||
|
(define (select-sel-if-option! tag sel-if)
|
||
|
(set-select-input-field-option-selected?! tag sel-if #t))
|
||
|
|
||
|
(define (unselect-sel-if-option! tag sel-if)
|
||
|
(set-select-input-field-option-selected?! tag sel-if #f))
|
||
|
|
||
|
(define (set-select-input-field-option-selected?! tag sel-if selected?)
|
||
|
(let ((options (input-field-attributes-default
|
||
|
(input-field-attributes sel-if))))
|
||
|
(if (number? tag)
|
||
|
(set-sel-if-option-selected?! (list-ref options tag)
|
||
|
selected?)
|
||
|
(let lp ((options options))
|
||
|
(if (null? options)
|
||
|
(error "No such option" tag sel-if)
|
||
|
(if (tag=sel-if-option? tag (car options))
|
||
|
(set-sel-if-option-selected?! (car options) selected?)
|
||
|
(lp (cdr options))))))
|
||
|
(touch-input-field! sel-if)))
|
||
|
|
||
|
;; Find sel-if-option in a list by its tag.
|
||
|
(define (tag=sel-if-option? tag sel-if-option)
|
||
|
(string=? tag (sel-if-option-tag sel-if-option)))
|
||
|
|
||
|
(define (find-sel-if-option tag sel-if-options)
|
||
|
(cond ((member/srfi-1 tag sel-if-options tag=sel-if-option?)
|
||
|
=> car)
|
||
|
(else #f)))
|
||
|
|
||
|
(define (find-sel-if-option-value tag sel-if-options)
|
||
|
(cond ((find-sel-if-option tag sel-if-options)
|
||
|
=> sel-if-option-value)
|
||
|
(else #f)))
|
||
|
|
||
|
;; To be compatible with previous versions of MAKE-SELECT-INPUT-FIELD,
|
||
|
;; we accept also a simple list as an option-list. New programs should
|
||
|
;; use sel-if-options-list (easily createable with
|
||
|
;; (map make-simple-sel-if-option option-list))
|
||
|
(define (tolerate-old-sel-if-options sel-if-options)
|
||
|
(if (and (list? sel-if-options)
|
||
|
(every sel-if-option? sel-if-options))
|
||
|
sel-if-options
|
||
|
(map make-simple-sel-if-option sel-if-options)))
|
||
|
|
||
|
(define (make-select-input-field sel-if-options . maybe-further-attributes)
|
||
|
(really-make-select-input-field (tolerate-old-sel-if-options sel-if-options)
|
||
|
#f
|
||
|
maybe-further-attributes))
|
||
|
|
||
|
(define (make-annotated-select-input-field sel-if-options .
|
||
|
maybe-further-attributes)
|
||
|
(really-make-select-input-field sel-if-options #t maybe-further-attributes))
|
||
|
|
||
|
(define (really-make-select-input-field sel-if-options annotated?
|
||
|
maybe-further-attributes)
|
||
|
(let ((real-sel-if-options (tolerate-old-sel-if-options sel-if-options)))
|
||
|
(optionals maybe-further-attributes
|
||
|
((multiple? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(let ((name (generate-input-field-name "select")))
|
||
|
(if multiple?
|
||
|
(make-multiple-select-input-field name sel-if-options
|
||
|
annotated? attributes)
|
||
|
(make-single-select-input-field name sel-if-options
|
||
|
annotated? attributes))))))
|
||
|
|
||
|
;; internal
|
||
|
(define (make-multiple-select-input-field name sel-if-options
|
||
|
annotated? attributes)
|
||
|
(make-multi-input-field name "mult-select"
|
||
|
(make-sel-if-multiple-transformer name sel-if-options
|
||
|
annotated?)
|
||
|
(make-input-field-attributes
|
||
|
sel-if-options
|
||
|
(list '(multiple)
|
||
|
(sxml-attribute-attributes attributes)))
|
||
|
make-sel-if-html-tree))
|
||
|
|
||
|
;; internal
|
||
|
(define (make-single-select-input-field name sel-if-options
|
||
|
annotated? attributes)
|
||
|
(make-input-field name "select"
|
||
|
(if annotated?
|
||
|
(lambda (tag)
|
||
|
(cond ((find-sel-if-option-value tag sel-if-options)
|
||
|
=> identity)
|
||
|
(else (error "no such option." tag))))
|
||
|
identity)
|
||
|
(make-input-field-attributes
|
||
|
sel-if-options
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
make-sel-if-html-tree))
|
||
|
|
||
|
(define (make-sel-if-multiple-transformer name sel-if-options annotated?)
|
||
|
(lambda (bindings)
|
||
|
(let ((tags (map cdr
|
||
|
(filter (lambda (binding)
|
||
|
(equal? (car binding) name))
|
||
|
bindings))))
|
||
|
(filter-map (if annotated?
|
||
|
(lambda (tag)
|
||
|
(find-sel-if-option-value tag sel-if-options))
|
||
|
(lambda (tag)
|
||
|
(if (find-sel-if-option tag sel-if-options)
|
||
|
tag
|
||
|
#f)))
|
||
|
tags))))
|
||
|
|
||
|
(define (make-sel-if-html-tree sel-if)
|
||
|
(let ((attributes (input-field-attributes sel-if)))
|
||
|
`(select (@ (name ,(input-field-name sel-if))
|
||
|
,(input-field-attributes-others attributes))
|
||
|
#\newline
|
||
|
,@(make-sel-if-options-html-tree
|
||
|
(input-field-attributes-default attributes)))))
|
||
|
|
||
|
(define (make-sel-if-options-html-tree sel-if-options)
|
||
|
(map (lambda (sel-if-option)
|
||
|
`(option (@ ,(and (sel-if-option-selected? sel-if-option) '(selected))
|
||
|
,(sel-if-option-attributes sel-if-option))
|
||
|
,(sel-if-option-tag sel-if-option)))
|
||
|
sel-if-options))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; radio input-fields
|
||
|
;; Because grouped radio input-fields must use the same name, we
|
||
|
;; cannot just return one radio input-field object, but we must
|
||
|
;; generate several ones with the same name.
|
||
|
(define (make-radio-input-field-group)
|
||
|
(let ((name (generate-input-field-name "radio")))
|
||
|
(lambda (value-string . maybe-further-attributes)
|
||
|
(optionals maybe-further-attributes
|
||
|
((checked? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-input-field name "radio"
|
||
|
identity
|
||
|
(make-input-field-attributes
|
||
|
(if checked? '(checked) #f)
|
||
|
(list `(value ,value-string)
|
||
|
(sxml-attribute-attributes attributes)))
|
||
|
radio-input-field-html-tree-maker)))))
|
||
|
|
||
|
(define (make-annotated-radio-input-field-group)
|
||
|
(let* ((name (generate-input-field-name "radio"))
|
||
|
(value-table (make-integer-table))
|
||
|
(transformer (make-radio-input-field-transformer value-table)))
|
||
|
(lambda (value . maybe-further-attributes)
|
||
|
(optionals maybe-further-attributes
|
||
|
((checked? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(let ((number (generate-unique-number)))
|
||
|
(table-set! value-table number value)
|
||
|
(make-input-field name "radio"
|
||
|
transformer
|
||
|
(make-input-field-attributes
|
||
|
(if checked? '(checked) #f)
|
||
|
(list`(value ,(number->string number))
|
||
|
(sxml-attribute-attributes attributes)))
|
||
|
radio-input-field-html-tree-maker))))))
|
||
|
|
||
|
(define (make-radio-input-fields values . maybe-further-attributes)
|
||
|
(optionals maybe-further-attributes
|
||
|
((attributes sxml-attribute?))
|
||
|
(let ((radio-gen (make-annotated-radio-input-field-group)))
|
||
|
(map (lambda (value)
|
||
|
(if attributes
|
||
|
(radio-gen value attributes)
|
||
|
(radio-gen value)))
|
||
|
values))))
|
||
|
|
||
|
|
||
|
(define (make-radio-input-field-transformer value-table)
|
||
|
(lambda (tag)
|
||
|
(cond
|
||
|
((string->number tag) =>
|
||
|
(lambda (number)
|
||
|
(let ((value (table-ref value-table number)))
|
||
|
(if value
|
||
|
value
|
||
|
(error "Unknown tag number for radio input-field" tag)))))
|
||
|
(else
|
||
|
(error "Unknown tag number for radio input-field" tag)))))
|
||
|
|
||
|
(define (radio-input-field-html-tree-maker radio-input-field)
|
||
|
(let* ((attributes (input-field-attributes radio-input-field)))
|
||
|
`(input (@ ((type "radio")
|
||
|
(name ,(input-field-name radio-input-field))
|
||
|
,(input-field-attributes-default attributes)
|
||
|
,(input-field-attributes-others attributes))))))
|
||
|
|
||
|
(define (set-input-field-checked?! input-field checked?)
|
||
|
(let ((attributes (input-field-attributes input-field)))
|
||
|
(set-input-field-attributes-default!
|
||
|
attributes
|
||
|
(if checked? '(checked) #f))
|
||
|
(touch-input-field! input-field)))
|
||
|
|
||
|
(define set-radio-input-field-checked?! set-input-field-checked?!)
|
||
|
(define (check-radio-input-field! radio)
|
||
|
(set-radio-input-field-checked?! radio #t))
|
||
|
(define (uncheck-radio-input-field! radio)
|
||
|
(set-radio-input-field-checked?! radio #f))
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; checkbox input-field
|
||
|
(define (make-checkbox-input-field . maybe-further-attributes)
|
||
|
(really-make-checkbox-input-field #t
|
||
|
checkbox-transformer
|
||
|
maybe-further-attributes))
|
||
|
|
||
|
(define (make-annotated-checkbox-input-field value . maybe-further-attributes)
|
||
|
(really-make-checkbox-input-field value
|
||
|
(make-checkbox-transformer value)
|
||
|
maybe-further-attributes))
|
||
|
|
||
|
(define (really-make-checkbox-input-field value transformer attributes)
|
||
|
(let ((name (generate-input-field-name "checkbox")))
|
||
|
(optionals attributes
|
||
|
((checked? boolean?)
|
||
|
(attributes sxml-attribute?))
|
||
|
(make-input-field name "checkbox"
|
||
|
transformer
|
||
|
(make-input-field-attributes
|
||
|
(if checked? '(checked) #f)
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
checkbox-input-field-html-tree-maker))))
|
||
|
|
||
|
(define (make-checkbox-transformer value)
|
||
|
(lambda (tag)
|
||
|
(if (string=? tag "on")
|
||
|
value
|
||
|
#f)))
|
||
|
|
||
|
(define checkbox-transformer
|
||
|
(make-checkbox-transformer #t))
|
||
|
|
||
|
(define (checkbox-input-field-html-tree-maker cb-if)
|
||
|
(let ((attributes (input-field-attributes cb-if)))
|
||
|
`(input (@ ((type "checkbox")
|
||
|
(name ,(input-field-name cb-if))
|
||
|
,(input-field-attributes-default attributes)
|
||
|
,(input-field-attributes-others attributes))))))
|
||
|
|
||
|
(define set-checkbox-input-field-checked?! set-input-field-checked?!)
|
||
|
(define (check-checkbox-input-field! checkbox)
|
||
|
(set-checkbox-input-field-checked?! checkbox #t))
|
||
|
(define (uncheck-checkbox-input-field! checkbox)
|
||
|
(set-checkbox-input-field-checked?! checkbox #f))
|
||
|
;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; button input-fields
|
||
|
(define (make-button type name button-caption attributes)
|
||
|
(make-input-field name type
|
||
|
identity
|
||
|
(make-input-field-attributes
|
||
|
(and button-caption `(value ,button-caption))
|
||
|
(sxml-attribute-attributes attributes))
|
||
|
make-button-html-tree))
|
||
|
|
||
|
(define (make-button-html-tree button-input-field)
|
||
|
(let ((attributes (input-field-attributes button-input-field)))
|
||
|
`(input (@ (type ,(input-field-type button-input-field))
|
||
|
(name ,(input-field-name button-input-field))
|
||
|
,(input-field-attributes-default attributes)
|
||
|
,(input-field-attributes-others attributes)))))
|
||
|
|
||
|
(define (make-submit-button . maybe-further-attributes)
|
||
|
(optionals maybe-further-attributes
|
||
|
((button-caption string?)
|
||
|
(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?)
|
||
|
(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)
|
||
|
,@(sxml-attribute-attributes attributes)))))
|
||
|
|
||
|
|
||
|
|
||
|
;;EOF
|