comparison lisp/w3/w3-forms.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; w3-forms.el,v --- Emacs-w3 forms parsing code for new display engine
2 ;; Author: wmperry
3 ;; Created: 1996/06/06 14:14:34
4 ;; Version: 1.51
5 ;; Keywords: faces, help, comm, data, languages
6
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; Copyright (c) 1993, 1994, 1995 by William M. Perry (wmperry@spry.com)
9 ;;;
10 ;;; This file is not part of GNU Emacs, but the same permissions apply.
11 ;;;
12 ;;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;;; it under the terms of the GNU General Public License as published by
14 ;;; the Free Software Foundation; either version 2, or (at your option)
15 ;;; any later version.
16 ;;;
17 ;;; GNU Emacs is distributed in the hope that it will be useful,
18 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;;; GNU General Public License for more details.
21 ;;;
22 ;;; You should have received a copy of the GNU General Public License
23 ;;; along with GNU Emacs; see the file COPYING. If not, write to
24 ;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;;; FORMS processing for html 2.0/3.0
29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30 (eval-and-compile
31 (require 'w3-draw))
32
33 (require 'widget)
34
35 (if (featurep 'mule) (fset 'string-width 'length))
36
37 ;; These are things in later versions of the widget package that I don't
38 ;; have yet.
39 (defun widget-at (pt)
40 (or (get-text-property pt 'button)
41 (get-text-property pt 'field)))
42
43 ;; A form entry area is a vector
44 ;; [ type name default-value value maxlength options widget]
45 ;; Where:
46 ;; type = symbol defining what type of form entry area it is
47 ;; (ie: file, radio)
48 ;; name = the name of the form element
49 ;; default-value = the value this started out with
50
51 (defsubst w3-form-element-type (obj) (aref obj 0))
52 (defsubst w3-form-element-name (obj) (aref obj 1))
53 (defsubst w3-form-element-default-value (obj) (aref obj 2))
54 (defsubst w3-form-element-value (obj) (aref obj 3))
55 (defsubst w3-form-element-size (obj) (aref obj 4))
56 (defsubst w3-form-element-maxlength (obj) (aref obj 5))
57 (defsubst w3-form-element-options (obj) (aref obj 6))
58 (defsubst w3-form-element-action (obj) (aref obj 7))
59 (defsubst w3-form-element-widget (obj) (aref obj 8))
60
61 (defsubst w3-form-element-set-type (obj val) (aset obj 0 val))
62 (defsubst w3-form-element-set-name (obj val) (aset obj 1 val))
63 (defsubst w3-form-element-set-default-value (obj val) (aset obj 2 val))
64 (defsubst w3-form-element-set-value (obj val) (aset obj 3 val))
65 (defsubst w3-form-element-set-size (obj val) (aset obj 4 val))
66 (defsubst w3-form-element-set-maxlength (obj val) (aset obj 5 val))
67 (defsubst w3-form-element-set-options (obj val) (aset obj 6 val))
68 (defsubst w3-form-element-set-action (obj val) (aset obj 7 val))
69 (defsubst w3-form-element-set-widget (obj val) (aset obj 8 val))
70
71 ;; The main function - this adds a single widget to the form
72 (defun w3-form-add-element (&rest args)
73 (let* ((widget nil)
74 (buffer-read-only nil)
75 (inhibit-read-only t)
76 (widget-creation-function nil)
77 (action (nth 6 args))
78 (node (assoc action w3-form-elements))
79 (name (or (nth 1 args)
80 (if (memq (nth 0 args) '(submit reset))
81 nil
82 (symbol-name (nth 0 args)))))
83 (val (vector (nth 0 args) ; type
84 name ; name
85 (nth 5 args) ; default
86 (nth 2 args) ; value
87 (nth 3 args) ; size
88 (nth 4 args) ; maxlength
89 (nth 7 args) ; options
90 action
91 nil)) ; widget
92 )
93 (setq widget-creation-function (or (get (car args)
94 'w3-widget-creation-function)
95 'w3-form-default-widget-creator)
96 widget (funcall widget-creation-function val
97 (cdr (nth 10 args))))
98 (if node
99 (setcdr node (cons val (cdr node)))
100 (setq w3-form-elements (cons (cons action (list val)) w3-form-elements)))
101 (if (not widget)
102 nil
103 (w3-form-element-set-widget val widget)
104 (widget-put widget 'w3-form-data val))))
105
106 ;; These properties tell the add-element function how to actually create
107 ;; each type of widget.
108 (put 'checkbox 'w3-widget-creation-function 'w3-form-create-checkbox)
109 (put 'multiline 'w3-widget-creation-function 'w3-form-create-multiline)
110 (put 'radio 'w3-widget-creation-function 'w3-form-create-radio-button)
111 (put 'reset 'w3-widget-creation-function 'w3-form-create-submit-button)
112 (put 'submit 'w3-widget-creation-function 'w3-form-create-submit-button)
113 (put 'hidden 'w3-widget-creation-function 'ignore)
114 (put 'file 'w3-widget-creation-function 'w3-form-create-file-browser)
115 (put 'option 'w3-widget-creation-function 'w3-form-create-option-list)
116 (put 'keygen 'w3-widget-creation-function 'w3-form-create-keygen-list)
117 (put 'button 'w3-widget-creation-function 'w3-form-create-button)
118 (put 'image 'w3-widget-creation-function 'w3-form-create-image)
119
120 (defun w3-form-create-checkbox (el face)
121 (widget-create 'checkbox :value-face face
122 (and (w3-form-element-default-value el) t)))
123
124 (defun w3-form-create-radio-button (el face)
125 (let* ((name (w3-form-element-name el))
126 (formobj (cdr (assoc name w3-form-radio-elements)))
127 (widget nil)
128 )
129 (if formobj
130 (progn
131 (setq widget (w3-form-element-widget formobj))
132 (widget-radio-add-item widget (list 'item :format "%t" :tag ""))
133 nil)
134 (setq widget (widget-create 'radio (list 'item :format "%t" :tag ""))
135 w3-form-radio-elements (cons (cons name el)
136 w3-form-radio-elements))
137 widget)))
138
139 (defun w3-form-create-button (el face)
140 ;; This handles dealing with the bogus Netscape 'button' input type
141 ;; that lots of places have been using to slap javascript shit onto
142 (let ((val (w3-form-element-value el)))
143 (if (or (not val) (string= val ""))
144 (setq val "Push Me"))
145 (widget-create 'push :notify 'ignore :button-face face val)))
146
147 (defun w3-form-create-image (el face)
148 (let ((widget (widget-create 'push
149 :notify 'w3-form-submit/reset-callback
150 :value "Form-Image")))
151 widget))
152
153 (defun w3-form-create-submit-button (el face)
154 (let ((val (w3-form-element-value el)))
155 (if (or (not val) (string= val ""))
156 (setq val (if (eq (w3-form-element-type el) 'submit)
157 "Submit"
158 "Reset")))
159 (widget-create 'push :notify 'w3-form-submit/reset-callback
160 :button-face face val)))
161
162 (defun w3-form-create-file-browser (el face)
163 (widget-create 'file :value-face face :value (w3-form-element-value el)))
164
165
166 (defvar w3-form-valid-key-sizes
167 '(
168 ("1024 (Premium)" . 1024)
169 ("896 (Regular)" . 896)
170 ("768 (Unleaded)" . 768)
171 ("512 (Low Grade)" . 512)
172 ("508 (Woos)" . 508)
173 ("256 (Test Grade)" . 256)
174 )
175 "An assoc list of available key sizes and meaningful descriptions.")
176
177 (defun w3-form-create-keygen-list (el face)
178 (let ((tmp w3-form-valid-key-sizes)
179 (longest 0)
180 (options nil))
181 (while tmp
182 (if (> (length (caar tmp)) longest)
183 (setq longest (length (caar tmp))))
184 (setq options (cons (list 'choice-item :tag (caar tmp)
185 :value (cdar tmp)) options)
186 tmp (cdr tmp)))
187 (apply 'widget-create 'choice :value 1024
188 :tag "Key Length"
189 :size (1+ longest)
190 :value-face face
191 options)))
192
193 (defun w3-form-create-option-list (el face)
194 (let ((widget (apply 'widget-create 'choice :value (w3-form-element-value el)
195 :tag "Choose"
196 :size (w3-form-element-size el)
197 :value-face face
198 (mapcar
199 (function
200 (lambda (x)
201 (list 'choice-item :format "%[%t%]"
202 :tag (car x) :value (cdr x))))
203 (reverse (w3-form-element-options el))))))
204 (widget-value-set widget (cdr-safe (assoc (w3-form-element-value el)
205 (w3-form-element-options el))))
206 (goto-char (point-max))
207 (skip-chars-backward "\r\n")
208 (delete-region (point) (point-max))
209 widget))
210
211 ;(defun w3-form-create-multiline (el face)
212 ; ;; FIX THIS! - need to padd out with newlines or something...
213 ; (widget-create 'field :value-face face (w3-form-element-value el)))
214
215 (defun w3-form-create-multiline (el face)
216 (widget-create 'push :notify 'w3-do-text-entry "Multiline text area"))
217
218 (defun w3-form-default-widget-creator (el face)
219 (widget-create 'link
220 :notify 'w3-form-default-button-callback
221 :size (w3-form-element-size el)
222 :tag (w3-truncate-string (w3-form-element-value el)
223 (w3-form-element-size el) ?_)
224 :value-face face
225 (w3-form-element-value el)))
226
227 (defun w3-form-default-button-callback (widget &rest ignore)
228 (let* ((obj (widget-get widget 'w3-form-data))
229 (typ (w3-form-element-type obj))
230 (def (widget-value widget))
231 (val nil)
232 )
233 (case typ
234 (password
235 (setq val (funcall url-passwd-entry-func "Password: " def))
236 (widget-put widget :tag (w3-truncate-string
237 (make-string (length val) ?*)
238 (w3-form-element-size obj) ?_)))
239 (otherwise
240 (setq val (read-string
241 (concat (capitalize (symbol-name typ)) ": ") def))
242 (widget-put widget :tag (w3-truncate-string
243 val (w3-form-element-size obj) ?_))))
244 (widget-value-set widget val))
245 (apply 'w3-form-possibly-submit widget ignore))
246
247 (defun w3-truncate-string (str len &optional pad)
248 "Truncate string STR so that string-width of STR is not greater than LEN.
249 If width of the truncated string is less than LEN, and if a character PAD is
250 defined, add padding end of it."
251 (if (featurep 'mule)
252 (let ((cl (string-to-char-list str)) (n 0) (sw 0))
253 (if (<= (string-width str) len) str
254 (while (<= (setq sw (+ (char-width (nth n cl)) sw)) len)
255 (setq n (1+ n)))
256 (string-match (make-string n ?.) str)
257 (setq str (substring str 0 (match-end 0))))
258 (if pad (concat str (make-string (- len (string-width str)) pad)) str))
259 (concat (if (> (length str) len) (substring str 0 len) str)
260 (if (or (null pad) (> (length str) len))
261 ""
262 (make-string (- len (length str)) pad)))))
263
264 (defun w3-form-possibly-submit (widget &rest ignore)
265 (let* ((formobj (widget-get widget 'w3-form-data))
266 (ident (w3-form-element-action formobj))
267 (widgets (w3-all-widgets ident))
268 (text-fields 0)
269 (text-p nil))
270 ;;
271 ;; Gack. Netscape auto-submits forms of one text field
272 ;; here we go through the list of widgets in this form and
273 ;; determine which are not submit/reset/button inputs.
274 ;; If the # == 1, then submit the form.
275 ;;
276 (while widgets
277 (setq text-fields (+
278 text-fields
279 (case (w3-form-element-type (car widgets))
280 ((submit reset image button)
281 0)
282 (text
283 (setq text-p t)
284 1)
285 (otherwise
286 1)))
287 widgets (cdr widgets)))
288 (if (and (= text-fields 1) text-p)
289 (w3-submit-form ident))))
290
291 (defun w3-form-submit/reset-callback (widget &rest ignore)
292 (let* ((formobj (widget-get widget 'w3-form-data))
293 (w3-submit-button formobj))
294 (case (w3-form-element-type formobj)
295 (submit (w3-submit-form (w3-form-element-action formobj)))
296 (reset (w3-revert-form (w3-form-element-action formobj)))
297 (image (w3-submit-form (w3-form-element-action formobj)))
298 (otherwise
299 (error
300 "Impossible widget type %s triggered w3-form-submit/reset-callback"
301 (w3-form-element-type formobj))))))
302
303 (defun w3-do-text-entry (widget &rest ignore)
304 (let* ((data (list widget (current-buffer)))
305 (formobj (widget-get widget 'w3-form-data))
306 (buff (get-buffer-create (format "Form Entry: %s"
307 (w3-form-element-name formobj)))))
308 (switch-to-buffer-other-window buff)
309 (indented-text-mode)
310 (erase-buffer)
311 (if (w3-form-element-value formobj)
312 (insert (w3-form-element-value formobj)))
313 (setq w3-current-last-buffer data)
314 (message "Press C-c C-c when finished with text entry.")
315 (local-set-key "\C-c\C-c" 'w3-finish-text-entry)))
316
317 (defun w3-finish-text-entry ()
318 (interactive)
319 (if w3-current-last-buffer
320 (let* ((widget (nth 0 w3-current-last-buffer))
321 (formobj (widget-get widget 'w3-form-data))
322 (buff (nth 1 w3-current-last-buffer))
323 (valu (buffer-string))
324 (inhibit-read-only t)
325 )
326 (local-set-key "\C-c\C-c" 'undefined)
327 (kill-buffer (current-buffer))
328 (condition-case ()
329 (delete-window)
330 (error nil))
331 (if (not (and buff (bufferp buff) (buffer-name buff)))
332 (message "Could not find the form buffer for this text!")
333 (switch-to-buffer buff)
334 (w3-form-element-set-value formobj valu)))))
335
336 (defsubst w3-all-widgets (actn)
337 ;; Return a list of data entry widgets in form number ACTN
338 (cdr-safe (assoc actn w3-form-elements)))
339
340 (defun w3-revert-form (actn)
341 (save-excursion
342 (let* ((formobjs (w3-all-widgets actn))
343 (inhibit-read-only t)
344 deft type widget formobj)
345 (while formobjs
346 (setq formobj (car formobjs)
347 widget (w3-form-element-widget formobj)
348 formobjs (cdr formobjs)
349 deft (w3-form-element-default-value formobj)
350 type (w3-form-element-type formobj))
351 (case type
352 ((submit reset image) nil)
353 (radio
354 ;; Ack - how!?
355 )
356 (checkbox
357 (if deft
358 (widget-value-set widget t)
359 (widget-value-set widget nil)))
360 (file
361 (widget-value-set widget deft))
362 (otherwise
363 (widget-value-set widget deft)))))))
364
365 (defun w3-form-encode-helper (formobjs)
366 (let (
367 (submit-button-data w3-submit-button)
368 formobj result widget temp type)
369 (while formobjs
370 (setq formobj (car formobjs)
371 type (w3-form-element-type formobj)
372 widget (w3-form-element-widget formobj)
373 formobjs (cdr formobjs)
374 temp (case type
375 (reset nil)
376 (image
377 (if (and (eq submit-button-data formobj)
378 (w3-form-element-name formobj))
379 (setq result (append
380 (list
381 (cons
382 (concat (w3-form-element-name formobj)
383 ".x") "0")
384 (cons
385 (concat (w3-form-element-name formobj)
386 ".y") "0"))
387 result)))
388 nil)
389 (submit
390 (if (and (eq submit-button-data formobj)
391 (w3-form-element-name formobj))
392 (cons (w3-form-element-name formobj)
393 (w3-form-element-value formobj))))
394 (radio
395 ;; this is probably broken
396 (let ((x (widget-radio-chosen widget)))
397 (if (or (not x)
398 (not (eq x (w3-form-element-widget formobj))))
399 nil
400 (cons (w3-form-element-name formobj)
401 (w3-form-element-value formobj)))))
402 (checkbox
403 (if (widget-value widget)
404 (cons (w3-form-element-name formobj)
405 (w3-form-element-value formobj))))
406 (file
407 (let ((dat nil)
408 (fname (widget-value widget)))
409 (save-excursion
410 (set-buffer (get-buffer-create " *w3-temp*"))
411 (erase-buffer)
412 (setq dat
413 (condition-case ()
414 (insert-file-contents-literally fname)
415 (error (concat "Error accessing " fname))))
416 (cons (w3-form-element-name formobj) dat))))
417 (option
418 (cons (w3-form-element-name formobj)
419 (widget-value widget)))
420 (keygen
421 (cons (w3-form-element-name formobj)
422 (format "Should create a %d bit RSA key"
423 (widget-value widget))))
424 ((multiline hidden)
425 (cons (w3-form-element-name formobj)
426 (w3-form-element-value formobj)))
427 (otherwise
428 (cons (w3-form-element-name formobj)
429 (widget-value widget)))))
430 (if temp
431 (setq result (cons temp result))))
432 result))
433
434 (defun w3-form-encode-make-mime-part (id data separator)
435 (concat separator "\nContent-id: " id
436 "\nContent-length: " (length data)
437 "\n\n" data))
438
439 (defun w3-form-encode-multipart/x-www-form-data (formobjs)
440 ;; Create a multipart form submission.
441 ;; Returns a cons of two strings. Car is the separator used.
442 ;; cdr is the body of the MIME message."
443 (let ((separator "---some-separator-for-www-form-data"))
444 (cons separator
445 (mapconcat
446 (function
447 (lambda (formobj)
448 (w3-form-encode-make-mime-part (car formobj) (cdr formobj)
449 separator)))
450 (w3-form-encode-helper formobjs)
451 "\n"))))
452
453 (fset 'w3-form-encode-multipart/form-data
454 'w3-form-encode-multipart/x-www-form-data)
455 (fset 'w3-form-encode- 'w3-form-encode-application/x-www-form-urlencoded)
456
457 (defun w3-next-widget (pos)
458 (let* ((next (cond ((get-text-property pos 'button)
459 (next-single-property-change pos 'button))
460 ((get-text-property pos 'field)
461 (next-single-property-change pos 'field))
462 (t pos)))
463 (button (next-single-property-change next 'button))
464 (field (next-single-property-change next 'field)))
465 (setq next
466 (cond
467 ((and button field) (min button field))
468 (button button)
469 (field field)
470 (t nil)))
471 (and next
472 (or (get-text-property next 'button)
473 (get-text-property next 'field)))))
474
475 (defun w3-form-encode (result &optional enctype)
476 "Create a string suitably encoded for a URL request."
477 (let ((func (intern (concat "w3-form-encode-" enctype))))
478 (if (fboundp func) (funcall func result))))
479
480 (defun w3-form-encode-text/plain (result)
481 (let ((query ""))
482 (setq query
483 (mapconcat
484 (function
485 (lambda (widget)
486 (let ((nam (car widget))
487 (val (cdr widget)))
488 (if (string-match "\n" nam)
489 (setq nam (mapconcat
490 (function
491 (lambda (x)
492 (if (= x ?\n) "," (char-to-string x))))
493 nam "")))
494 (concat nam " " val))))
495 (w3-form-encode-helper result) "\n"))
496 query))
497
498 (defun w3-form-encode-application/x-gopher-query (result)
499 (concat "\t" (nth 5 (car result))))
500
501 (defconst w3-xwfu-acceptable-chars
502 (list
503 ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z
504 ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z
505 ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
506 ?_;; BOGUS! This is for #!%#@!ing netscape compatibility
507 )
508 "A list of characters that we do not have to escape in the media type
509 application/x-www/form-urlencoded")
510
511 (defun w3-form-encode-xwfu (chunk)
512 "Escape characters in a string for application/x-www-form-urlencoded.
513 Blasphemous crap because someone didn't think %20 was good enough for encoding
514 spaces. Die Die Die."
515 (if (and (featurep 'mule) chunk)
516 (setq chunk (code-convert-string
517 chunk *internal* url-mule-retrieval-coding-system)))
518 (mapconcat
519 (function
520 (lambda (char)
521 (cond
522 ((= char ? ) "+")
523 ((memq char w3-xwfu-acceptable-chars) (char-to-string char))
524 (t (format "%%%02x" char)))))
525 chunk ""))
526
527 (defun w3-form-encode-application/x-www-form-urlencoded (result)
528 (mapconcat
529 (function
530 (lambda (data)
531 (concat (w3-form-encode-xwfu (car data)) "="
532 (w3-form-encode-xwfu (cdr data)))))
533 (w3-form-encode-helper result) "&"))
534
535 (defun w3-form-encode-application/x-w3-isindex (result)
536 (let* ((info (w3-form-encode-helper result))
537 (query (cdr-safe (assoc "isindex" info))))
538 (if query
539 (url-hexify-string query)
540 "")))
541
542 (defun w3-form-encode-application/gopher-ask-block (result)
543 (let ((query ""))
544 ;;; gopher+ will expect all the checkboxes/etc, even if they are
545 ;;; not turned on. Should still ignore RADIO boxes that are not
546 ;;; active though.
547 (while result
548 (if (and (not (and (string= (nth 2 (car result)) "RADIO")
549 (not (nth 6 (car result)))))
550 (not (member (nth 2 (car result)) '("SUBMIT" "RESET"))))
551 (setq query (format "%s\r\n%s" query (nth 5 (car result)))))
552 (setq result (cdr result)))
553 (concat query "\r\n.\r\n")))
554
555 (defun w3-submit-form (ident)
556 ;; Submit form entry fields matching ACTN as their action identifier.
557 (let* ((result (w3-all-widgets ident))
558 (enctype (cdr (assq 'enctype ident)))
559 (query (w3-form-encode result enctype))
560 (themeth (upcase (or (cdr (assq 'method ident)) "get")))
561 (theurl (cdr (assq 'action ident))))
562 (if (and (string= "GET" themeth)
563 (string-match "\\([^\\?]*\\)\\?" theurl))
564 (setq theurl (url-match theurl 1)))
565 (cond
566 ((or (string= "POST" themeth)
567 (string= "PUT" themeth))
568 (if (consp query)
569 (setq enctype (concat enctype "; separator=\""
570 (substring (car query) 3 nil)
571 "\"")
572 query (cdr query)))
573 (let ((url-request-method themeth)
574 (url-request-data query)
575 (url-request-extra-headers
576 (cons (cons "Content-type" enctype) url-request-extra-headers)))
577 (w3-fetch theurl)))
578 ((string= "GET" themeth)
579 (let ((theurl (concat theurl "?" query)))
580 (w3-fetch theurl)))
581 (t
582 (w3-warn 'html (format "Unknown submit method: %s" themeth))
583 (let ((theurl (concat theurl "?" query)))
584 (w3-fetch theurl))))))
585
586 (provide 'w3-forms)