Mercurial > hg > xemacs-beta
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/w3/w3-forms.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,586 @@ +;;; w3-forms.el,v --- Emacs-w3 forms parsing code for new display engine +;; Author: wmperry +;; Created: 1996/06/06 14:14:34 +;; Version: 1.51 +;; Keywords: faces, help, comm, data, languages + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Copyright (c) 1993, 1994, 1995 by William M. Perry (wmperry@spry.com) +;;; +;;; This file is not part of GNU Emacs, but the same permissions apply. +;;; +;;; GNU Emacs is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 2, or (at your option) +;;; any later version. +;;; +;;; GNU Emacs is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Emacs; see the file COPYING. If not, write to +;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; FORMS processing for html 2.0/3.0 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(eval-and-compile + (require 'w3-draw)) + +(require 'widget) + +(if (featurep 'mule) (fset 'string-width 'length)) + +;; These are things in later versions of the widget package that I don't +;; have yet. +(defun widget-at (pt) + (or (get-text-property pt 'button) + (get-text-property pt 'field))) + +;; A form entry area is a vector +;; [ type name default-value value maxlength options widget] +;; Where: +;; type = symbol defining what type of form entry area it is +;; (ie: file, radio) +;; name = the name of the form element +;; default-value = the value this started out with + +(defsubst w3-form-element-type (obj) (aref obj 0)) +(defsubst w3-form-element-name (obj) (aref obj 1)) +(defsubst w3-form-element-default-value (obj) (aref obj 2)) +(defsubst w3-form-element-value (obj) (aref obj 3)) +(defsubst w3-form-element-size (obj) (aref obj 4)) +(defsubst w3-form-element-maxlength (obj) (aref obj 5)) +(defsubst w3-form-element-options (obj) (aref obj 6)) +(defsubst w3-form-element-action (obj) (aref obj 7)) +(defsubst w3-form-element-widget (obj) (aref obj 8)) + +(defsubst w3-form-element-set-type (obj val) (aset obj 0 val)) +(defsubst w3-form-element-set-name (obj val) (aset obj 1 val)) +(defsubst w3-form-element-set-default-value (obj val) (aset obj 2 val)) +(defsubst w3-form-element-set-value (obj val) (aset obj 3 val)) +(defsubst w3-form-element-set-size (obj val) (aset obj 4 val)) +(defsubst w3-form-element-set-maxlength (obj val) (aset obj 5 val)) +(defsubst w3-form-element-set-options (obj val) (aset obj 6 val)) +(defsubst w3-form-element-set-action (obj val) (aset obj 7 val)) +(defsubst w3-form-element-set-widget (obj val) (aset obj 8 val)) + +;; The main function - this adds a single widget to the form +(defun w3-form-add-element (&rest args) + (let* ((widget nil) + (buffer-read-only nil) + (inhibit-read-only t) + (widget-creation-function nil) + (action (nth 6 args)) + (node (assoc action w3-form-elements)) + (name (or (nth 1 args) + (if (memq (nth 0 args) '(submit reset)) + nil + (symbol-name (nth 0 args))))) + (val (vector (nth 0 args) ; type + name ; name + (nth 5 args) ; default + (nth 2 args) ; value + (nth 3 args) ; size + (nth 4 args) ; maxlength + (nth 7 args) ; options + action + nil)) ; widget + ) + (setq widget-creation-function (or (get (car args) + 'w3-widget-creation-function) + 'w3-form-default-widget-creator) + widget (funcall widget-creation-function val + (cdr (nth 10 args)))) + (if node + (setcdr node (cons val (cdr node))) + (setq w3-form-elements (cons (cons action (list val)) w3-form-elements))) + (if (not widget) + nil + (w3-form-element-set-widget val widget) + (widget-put widget 'w3-form-data val)))) + +;; These properties tell the add-element function how to actually create +;; each type of widget. +(put 'checkbox 'w3-widget-creation-function 'w3-form-create-checkbox) +(put 'multiline 'w3-widget-creation-function 'w3-form-create-multiline) +(put 'radio 'w3-widget-creation-function 'w3-form-create-radio-button) +(put 'reset 'w3-widget-creation-function 'w3-form-create-submit-button) +(put 'submit 'w3-widget-creation-function 'w3-form-create-submit-button) +(put 'hidden 'w3-widget-creation-function 'ignore) +(put 'file 'w3-widget-creation-function 'w3-form-create-file-browser) +(put 'option 'w3-widget-creation-function 'w3-form-create-option-list) +(put 'keygen 'w3-widget-creation-function 'w3-form-create-keygen-list) +(put 'button 'w3-widget-creation-function 'w3-form-create-button) +(put 'image 'w3-widget-creation-function 'w3-form-create-image) + +(defun w3-form-create-checkbox (el face) + (widget-create 'checkbox :value-face face + (and (w3-form-element-default-value el) t))) + +(defun w3-form-create-radio-button (el face) + (let* ((name (w3-form-element-name el)) + (formobj (cdr (assoc name w3-form-radio-elements))) + (widget nil) + ) + (if formobj + (progn + (setq widget (w3-form-element-widget formobj)) + (widget-radio-add-item widget (list 'item :format "%t" :tag "")) + nil) + (setq widget (widget-create 'radio (list 'item :format "%t" :tag "")) + w3-form-radio-elements (cons (cons name el) + w3-form-radio-elements)) + widget))) + +(defun w3-form-create-button (el face) + ;; This handles dealing with the bogus Netscape 'button' input type + ;; that lots of places have been using to slap javascript shit onto + (let ((val (w3-form-element-value el))) + (if (or (not val) (string= val "")) + (setq val "Push Me")) + (widget-create 'push :notify 'ignore :button-face face val))) + +(defun w3-form-create-image (el face) + (let ((widget (widget-create 'push + :notify 'w3-form-submit/reset-callback + :value "Form-Image"))) + widget)) + +(defun w3-form-create-submit-button (el face) + (let ((val (w3-form-element-value el))) + (if (or (not val) (string= val "")) + (setq val (if (eq (w3-form-element-type el) 'submit) + "Submit" + "Reset"))) + (widget-create 'push :notify 'w3-form-submit/reset-callback + :button-face face val))) + +(defun w3-form-create-file-browser (el face) + (widget-create 'file :value-face face :value (w3-form-element-value el))) + + +(defvar w3-form-valid-key-sizes + '( + ("1024 (Premium)" . 1024) + ("896 (Regular)" . 896) + ("768 (Unleaded)" . 768) + ("512 (Low Grade)" . 512) + ("508 (Woos)" . 508) + ("256 (Test Grade)" . 256) + ) + "An assoc list of available key sizes and meaningful descriptions.") + +(defun w3-form-create-keygen-list (el face) + (let ((tmp w3-form-valid-key-sizes) + (longest 0) + (options nil)) + (while tmp + (if (> (length (caar tmp)) longest) + (setq longest (length (caar tmp)))) + (setq options (cons (list 'choice-item :tag (caar tmp) + :value (cdar tmp)) options) + tmp (cdr tmp))) + (apply 'widget-create 'choice :value 1024 + :tag "Key Length" + :size (1+ longest) + :value-face face + options))) + +(defun w3-form-create-option-list (el face) + (let ((widget (apply 'widget-create 'choice :value (w3-form-element-value el) + :tag "Choose" + :size (w3-form-element-size el) + :value-face face + (mapcar + (function + (lambda (x) + (list 'choice-item :format "%[%t%]" + :tag (car x) :value (cdr x)))) + (reverse (w3-form-element-options el)))))) + (widget-value-set widget (cdr-safe (assoc (w3-form-element-value el) + (w3-form-element-options el)))) + (goto-char (point-max)) + (skip-chars-backward "\r\n") + (delete-region (point) (point-max)) + widget)) + +;(defun w3-form-create-multiline (el face) +; ;; FIX THIS! - need to padd out with newlines or something... +; (widget-create 'field :value-face face (w3-form-element-value el))) + +(defun w3-form-create-multiline (el face) + (widget-create 'push :notify 'w3-do-text-entry "Multiline text area")) + +(defun w3-form-default-widget-creator (el face) + (widget-create 'link + :notify 'w3-form-default-button-callback + :size (w3-form-element-size el) + :tag (w3-truncate-string (w3-form-element-value el) + (w3-form-element-size el) ?_) + :value-face face + (w3-form-element-value el))) + +(defun w3-form-default-button-callback (widget &rest ignore) + (let* ((obj (widget-get widget 'w3-form-data)) + (typ (w3-form-element-type obj)) + (def (widget-value widget)) + (val nil) + ) + (case typ + (password + (setq val (funcall url-passwd-entry-func "Password: " def)) + (widget-put widget :tag (w3-truncate-string + (make-string (length val) ?*) + (w3-form-element-size obj) ?_))) + (otherwise + (setq val (read-string + (concat (capitalize (symbol-name typ)) ": ") def)) + (widget-put widget :tag (w3-truncate-string + val (w3-form-element-size obj) ?_)))) + (widget-value-set widget val)) + (apply 'w3-form-possibly-submit widget ignore)) + +(defun w3-truncate-string (str len &optional pad) + "Truncate string STR so that string-width of STR is not greater than LEN. + If width of the truncated string is less than LEN, and if a character PAD is + defined, add padding end of it." + (if (featurep 'mule) + (let ((cl (string-to-char-list str)) (n 0) (sw 0)) + (if (<= (string-width str) len) str + (while (<= (setq sw (+ (char-width (nth n cl)) sw)) len) + (setq n (1+ n))) + (string-match (make-string n ?.) str) + (setq str (substring str 0 (match-end 0)))) + (if pad (concat str (make-string (- len (string-width str)) pad)) str)) + (concat (if (> (length str) len) (substring str 0 len) str) + (if (or (null pad) (> (length str) len)) + "" + (make-string (- len (length str)) pad))))) + +(defun w3-form-possibly-submit (widget &rest ignore) + (let* ((formobj (widget-get widget 'w3-form-data)) + (ident (w3-form-element-action formobj)) + (widgets (w3-all-widgets ident)) + (text-fields 0) + (text-p nil)) + ;; + ;; Gack. Netscape auto-submits forms of one text field + ;; here we go through the list of widgets in this form and + ;; determine which are not submit/reset/button inputs. + ;; If the # == 1, then submit the form. + ;; + (while widgets + (setq text-fields (+ + text-fields + (case (w3-form-element-type (car widgets)) + ((submit reset image button) + 0) + (text + (setq text-p t) + 1) + (otherwise + 1))) + widgets (cdr widgets))) + (if (and (= text-fields 1) text-p) + (w3-submit-form ident)))) + +(defun w3-form-submit/reset-callback (widget &rest ignore) + (let* ((formobj (widget-get widget 'w3-form-data)) + (w3-submit-button formobj)) + (case (w3-form-element-type formobj) + (submit (w3-submit-form (w3-form-element-action formobj))) + (reset (w3-revert-form (w3-form-element-action formobj))) + (image (w3-submit-form (w3-form-element-action formobj))) + (otherwise + (error + "Impossible widget type %s triggered w3-form-submit/reset-callback" + (w3-form-element-type formobj)))))) + +(defun w3-do-text-entry (widget &rest ignore) + (let* ((data (list widget (current-buffer))) + (formobj (widget-get widget 'w3-form-data)) + (buff (get-buffer-create (format "Form Entry: %s" + (w3-form-element-name formobj))))) + (switch-to-buffer-other-window buff) + (indented-text-mode) + (erase-buffer) + (if (w3-form-element-value formobj) + (insert (w3-form-element-value formobj))) + (setq w3-current-last-buffer data) + (message "Press C-c C-c when finished with text entry.") + (local-set-key "\C-c\C-c" 'w3-finish-text-entry))) + +(defun w3-finish-text-entry () + (interactive) + (if w3-current-last-buffer + (let* ((widget (nth 0 w3-current-last-buffer)) + (formobj (widget-get widget 'w3-form-data)) + (buff (nth 1 w3-current-last-buffer)) + (valu (buffer-string)) + (inhibit-read-only t) + ) + (local-set-key "\C-c\C-c" 'undefined) + (kill-buffer (current-buffer)) + (condition-case () + (delete-window) + (error nil)) + (if (not (and buff (bufferp buff) (buffer-name buff))) + (message "Could not find the form buffer for this text!") + (switch-to-buffer buff) + (w3-form-element-set-value formobj valu))))) + +(defsubst w3-all-widgets (actn) + ;; Return a list of data entry widgets in form number ACTN + (cdr-safe (assoc actn w3-form-elements))) + +(defun w3-revert-form (actn) + (save-excursion + (let* ((formobjs (w3-all-widgets actn)) + (inhibit-read-only t) + deft type widget formobj) + (while formobjs + (setq formobj (car formobjs) + widget (w3-form-element-widget formobj) + formobjs (cdr formobjs) + deft (w3-form-element-default-value formobj) + type (w3-form-element-type formobj)) + (case type + ((submit reset image) nil) + (radio + ;; Ack - how!? + ) + (checkbox + (if deft + (widget-value-set widget t) + (widget-value-set widget nil))) + (file + (widget-value-set widget deft)) + (otherwise + (widget-value-set widget deft))))))) + +(defun w3-form-encode-helper (formobjs) + (let ( + (submit-button-data w3-submit-button) + formobj result widget temp type) + (while formobjs + (setq formobj (car formobjs) + type (w3-form-element-type formobj) + widget (w3-form-element-widget formobj) + formobjs (cdr formobjs) + temp (case type + (reset nil) + (image + (if (and (eq submit-button-data formobj) + (w3-form-element-name formobj)) + (setq result (append + (list + (cons + (concat (w3-form-element-name formobj) + ".x") "0") + (cons + (concat (w3-form-element-name formobj) + ".y") "0")) + result))) + nil) + (submit + (if (and (eq submit-button-data formobj) + (w3-form-element-name formobj)) + (cons (w3-form-element-name formobj) + (w3-form-element-value formobj)))) + (radio + ;; this is probably broken + (let ((x (widget-radio-chosen widget))) + (if (or (not x) + (not (eq x (w3-form-element-widget formobj)))) + nil + (cons (w3-form-element-name formobj) + (w3-form-element-value formobj))))) + (checkbox + (if (widget-value widget) + (cons (w3-form-element-name formobj) + (w3-form-element-value formobj)))) + (file + (let ((dat nil) + (fname (widget-value widget))) + (save-excursion + (set-buffer (get-buffer-create " *w3-temp*")) + (erase-buffer) + (setq dat + (condition-case () + (insert-file-contents-literally fname) + (error (concat "Error accessing " fname)))) + (cons (w3-form-element-name formobj) dat)))) + (option + (cons (w3-form-element-name formobj) + (widget-value widget))) + (keygen + (cons (w3-form-element-name formobj) + (format "Should create a %d bit RSA key" + (widget-value widget)))) + ((multiline hidden) + (cons (w3-form-element-name formobj) + (w3-form-element-value formobj))) + (otherwise + (cons (w3-form-element-name formobj) + (widget-value widget))))) + (if temp + (setq result (cons temp result)))) + result)) + +(defun w3-form-encode-make-mime-part (id data separator) + (concat separator "\nContent-id: " id + "\nContent-length: " (length data) + "\n\n" data)) + +(defun w3-form-encode-multipart/x-www-form-data (formobjs) + ;; Create a multipart form submission. + ;; Returns a cons of two strings. Car is the separator used. + ;; cdr is the body of the MIME message." + (let ((separator "---some-separator-for-www-form-data")) + (cons separator + (mapconcat + (function + (lambda (formobj) + (w3-form-encode-make-mime-part (car formobj) (cdr formobj) + separator))) + (w3-form-encode-helper formobjs) + "\n")))) + +(fset 'w3-form-encode-multipart/form-data + 'w3-form-encode-multipart/x-www-form-data) +(fset 'w3-form-encode- 'w3-form-encode-application/x-www-form-urlencoded) + +(defun w3-next-widget (pos) + (let* ((next (cond ((get-text-property pos 'button) + (next-single-property-change pos 'button)) + ((get-text-property pos 'field) + (next-single-property-change pos 'field)) + (t pos))) + (button (next-single-property-change next 'button)) + (field (next-single-property-change next 'field))) + (setq next + (cond + ((and button field) (min button field)) + (button button) + (field field) + (t nil))) + (and next + (or (get-text-property next 'button) + (get-text-property next 'field))))) + +(defun w3-form-encode (result &optional enctype) + "Create a string suitably encoded for a URL request." + (let ((func (intern (concat "w3-form-encode-" enctype)))) + (if (fboundp func) (funcall func result)))) + +(defun w3-form-encode-text/plain (result) + (let ((query "")) + (setq query + (mapconcat + (function + (lambda (widget) + (let ((nam (car widget)) + (val (cdr widget))) + (if (string-match "\n" nam) + (setq nam (mapconcat + (function + (lambda (x) + (if (= x ?\n) "," (char-to-string x)))) + nam ""))) + (concat nam " " val)))) + (w3-form-encode-helper result) "\n")) + query)) + +(defun w3-form-encode-application/x-gopher-query (result) + (concat "\t" (nth 5 (car result)))) + +(defconst w3-xwfu-acceptable-chars + (list + ?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 + ?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 + ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 + ?_;; BOGUS! This is for #!%#@!ing netscape compatibility + ) + "A list of characters that we do not have to escape in the media type +application/x-www/form-urlencoded") + +(defun w3-form-encode-xwfu (chunk) + "Escape characters in a string for application/x-www-form-urlencoded. +Blasphemous crap because someone didn't think %20 was good enough for encoding +spaces. Die Die Die." + (if (and (featurep 'mule) chunk) + (setq chunk (code-convert-string + chunk *internal* url-mule-retrieval-coding-system))) + (mapconcat + (function + (lambda (char) + (cond + ((= char ? ) "+") + ((memq char w3-xwfu-acceptable-chars) (char-to-string char)) + (t (format "%%%02x" char))))) + chunk "")) + +(defun w3-form-encode-application/x-www-form-urlencoded (result) + (mapconcat + (function + (lambda (data) + (concat (w3-form-encode-xwfu (car data)) "=" + (w3-form-encode-xwfu (cdr data))))) + (w3-form-encode-helper result) "&")) + +(defun w3-form-encode-application/x-w3-isindex (result) + (let* ((info (w3-form-encode-helper result)) + (query (cdr-safe (assoc "isindex" info)))) + (if query + (url-hexify-string query) + ""))) + +(defun w3-form-encode-application/gopher-ask-block (result) + (let ((query "")) + ;;; gopher+ will expect all the checkboxes/etc, even if they are + ;;; not turned on. Should still ignore RADIO boxes that are not + ;;; active though. + (while result + (if (and (not (and (string= (nth 2 (car result)) "RADIO") + (not (nth 6 (car result))))) + (not (member (nth 2 (car result)) '("SUBMIT" "RESET")))) + (setq query (format "%s\r\n%s" query (nth 5 (car result))))) + (setq result (cdr result))) + (concat query "\r\n.\r\n"))) + +(defun w3-submit-form (ident) + ;; Submit form entry fields matching ACTN as their action identifier. + (let* ((result (w3-all-widgets ident)) + (enctype (cdr (assq 'enctype ident))) + (query (w3-form-encode result enctype)) + (themeth (upcase (or (cdr (assq 'method ident)) "get"))) + (theurl (cdr (assq 'action ident)))) + (if (and (string= "GET" themeth) + (string-match "\\([^\\?]*\\)\\?" theurl)) + (setq theurl (url-match theurl 1))) + (cond + ((or (string= "POST" themeth) + (string= "PUT" themeth)) + (if (consp query) + (setq enctype (concat enctype "; separator=\"" + (substring (car query) 3 nil) + "\"") + query (cdr query))) + (let ((url-request-method themeth) + (url-request-data query) + (url-request-extra-headers + (cons (cons "Content-type" enctype) url-request-extra-headers))) + (w3-fetch theurl))) + ((string= "GET" themeth) + (let ((theurl (concat theurl "?" query))) + (w3-fetch theurl))) + (t + (w3-warn 'html (format "Unknown submit method: %s" themeth)) + (let ((theurl (concat theurl "?" query))) + (w3-fetch theurl)))))) + +(provide 'w3-forms)