Initial Commit
[packages] / xemacs-packages / psgml / psgml-html.el
1 ;;; psgml-html.el --- HTML mode in conjunction with PSGML
2
3 ;; Copyright (C) 1994 Nelson Minar.
4 ;; Copyright (C) 1995 Nelson Minar and Ulrik Dickow.
5 ;; Copyright (C) 1996 Ben Wing.
6
7 ;; This file is part of XEmacs.
8
9 ;; XEmacs is free software; you can redistribute it and/or modify it
10 ;; under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; XEmacs is distributed in the hope that it will be useful, but
15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 ;; General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
21 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 ;; MA 02111-1307, USA.
23
24 ;;; Synched up with: FSF 19.30.
25
26 ;;; Author: Ben Wing.
27
28 ;;; Commentary:
29
30 ; Parts were taken from html-helper-mode and from code by Alastair Burt.
31
32 ; If you'd like to use the hm--html-minor-mode together with this
33 ; mode, you have to put the following line to your ~/.emacs:
34 ;       (add-hook 'html-mode-hook 'hm--html-minor-mode)
35
36 ;;; Code:
37
38 (defvar html-auto-sgml-entity-conversion nil
39   "*Control automatic SGML entity to ISO-8859-1 conversion.")
40
41 (provide 'psgml-html)
42 (require 'psgml)
43 (require 'derived)
44 (when html-auto-sgml-entity-conversion
45   (require 'iso-sgml))
46 (require 'tempo)                        ;essential part of html-helper-mode
47
48 (eval-when-compile
49   (require 'browse-url)
50   (require 'font-lock)
51   (require 'imenu)
52   (require 'sendmail))
53
54 ;;{{{ user variables
55
56 (defgroup html nil
57   "HyperText Markup Language"
58   :group 'sgml)
59
60 (defgroup psgml-html nil
61   "HTML mode in conjunction with PSGML"
62   :tag "Psgml Html"
63   :prefix "html-helper-"
64   :prefix "psgml-html-"
65   :group 'html
66   :group 'hypermedia
67   :group 'psgml)
68
69 (defcustom html-helper-address-string
70   (concat "<a href=\"mailto:"
71           (or user-mail-address
72               (concat (user-login-name) "@"
73                       (or mail-host-address (system-name))))
74           "\">" (user-full-name) "</a>")
75   "*The default author string of each file."
76   :type 'string
77   :group 'psgml-html)
78
79 (defcustom html-helper-htmldtd-version "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
80   "*Version of HTML DTD you're using."
81   :type 'string
82   :group 'psgml-html)
83
84 (defcustom html-helper-do-write-file-hooks t
85   "*If not nil, then modify `local-write-file-hooks' to do timestamps."
86   :type 'boolean
87   :group 'psgml-html)
88
89 (defcustom html-helper-build-new-buffer t
90   "*If not nil, then insert `html-helper-new-buffer-strings' for new buffers."
91   :type 'boolean
92   :group 'psgml-html)
93
94 (defcustom html-helper-timestamp-hook 'html-helper-default-insert-timestamp
95   "*Hook called for timestamp insertion.
96 Override this for your own timestamp styles."
97   :type 'hook
98   :group 'psgml-html)
99
100 ;; strings you might want to change
101
102 (defcustom html-helper-new-buffer-template
103   '(html-helper-htmldtd-version
104     "<html>\n"
105     "  <head>\n"
106     "    <title>" (p "Document Title: " title) "</title>\n"
107     "  </head>\n"
108     "\n"
109     "  <body>\n"
110     "    <h1>" (s title) "</h1>\n\n"
111     p
112     "\n\n    <hr>\n"
113     "    <address>" html-helper-address-string "</address>\n"
114     (html-helper-return-created-string)
115     html-helper-timestamp-start
116     html-helper-timestamp-end
117     "\n  </body>\n</html>\n")
118   "*Template for new buffers.
119 Inserted by `html-helper-insert-new-buffer-strings' if
120 `html-helper-build-new-buffer' is set to t"
121   :type 'sexp
122   :group 'psgml-html)
123
124 (defcustom html-helper-timestamp-start "<!-- hhmts start -->\n"
125   "*Start delimiter for timestamps.
126 Everything between `html-helper-timestamp-start' and
127 `html-helper-timestamp-end' will be deleted and replaced with the output
128 of the functions `html-helper-timestamp-hook' if
129 `html-helper-do-write-file-hooks' is t"
130   :type 'string
131   :group 'psgml-html)
132
133 (defcustom html-helper-timestamp-end "<!-- hhmts end -->"
134   "*End delimiter for timestamps.
135 Everything between `html-helper-timestamp-start' and
136 `html-helper-timestamp-end' will be deleted and replaced with the output
137 of the function `html-helper-insert-timestamp' if
138 `html-helper-do-write-file-hooks' is t"
139   :type 'string
140   :group 'psgml-html)
141
142 ;; control over what types of tags to load. By default, we load all the
143 ;; ones we know of.
144
145 (defcustom html-helper-types-to-install
146   '(anchor header logical phys list textel entity image head form table
147            special)
148   "*List of tag types to install when html-helper-mode is first loaded.
149 If you want to not install some type of tag, override this variable.
150 Order is significant: menus go in this order."
151   :type '(repeat symbol)
152   :group 'psgml-html)
153
154 (defcustom html-helper-use-expert-menu nil
155   "*If not nil, then use the full HTML menu."
156   :type 'boolean
157   :group 'psgml-html)
158
159 (defcustom html-helper-user-menu nil
160   "*Extra items to put in the HTML expert menu.
161 The value of this symbol is appended to the beginning of the expert
162 menu that is handed off to easymenu for definition. It should be a
163 list of vectors or lists which themselves are vectors (for submenus)."
164   :type 'sexp
165   :group 'psgml-html)
166
167 (defgroup psgml-html-faces nil
168   "Faces for highlighting HTML."
169   :prefix "html-helper-"
170   :group 'faces
171   :group 'psgml-html)
172
173 (defface html-helper-bold-face '((t (:bold t)))
174   "*Face for highlighting bold text."
175   :group 'psgml-html-faces)
176
177 (defface html-helper-italic-face '((t (:italic t)))
178   "*Face for highlighting italic text."
179   :group 'psgml-html-faces)
180
181 (defface html-helper-underline-face '((t (:underline t)))
182   "*Face for highlighting underlined text."
183   :group 'psgml-html-faces)
184
185 (defface html-helper-strikethrough-face '((t (:strikethru t)))
186   "*Face for highlighting strikethrough text."
187   :group 'psgml-html-faces)
188
189 ;; Could be underline, but it looks ugly at line breaks.
190 (defface html-helper-link-face '((t (:foreground "blue")))
191   "*Face for highlighting link text."
192   :group 'psgml-html-faces)
193
194 (defface html-helper-significant-tag-face '((t (:foreground "salmon")))
195   "*Face for highlighting html, body, head, form, input and img tags."
196   :group 'psgml-html-faces)
197
198 ;;}}} end of user variables
199 ;;{{{ type based keymap and menu variable and function setup
200
201 ;; html-helper-mode has a concept of "type" of tags. Each type is a
202 ;; list of tags that all go together in one keymap and one menu.
203 ;; Types can be added to the system after html-helper has been loaded,
204 ;; briefly by doing html-helper-add-type-to-alist, then
205 ;; html-helper-install-type, then html-helper-add-tag (for each tag)
206 ;; then html-helper-rebuild-menu. See the mode documentation for more detail.
207
208 (defconst html-helper-type-alist nil
209   "Alist: type of tag -> keymap, keybinding, menu, menu string.
210 Add to this with `html-helper-add-type-to-alist'.")
211
212 ;;{{{ accessor functions for html-helper-type-alist
213
214 (defun html-helper-keymap-for (type)
215   "Accessor function for alist: for type, return keymap or nil"
216   (nth 0 (cdr-safe (assq type html-helper-type-alist))))
217
218 (defun html-helper-key-for (type)
219   "Accessor function for alist: for type, return keybinding or nil"
220   (nth 1 (cdr-safe (assq type html-helper-type-alist))))
221
222 (defun html-helper-menu-for (type)
223   "Accessor function for alist: for type, return menu or nil"
224   (nth 2 (cdr-safe (assq type html-helper-type-alist))))
225
226 (defun html-helper-menu-string-for (type)
227   "Accessor function for alist: for type, return menustring or nil"
228   (nth 3 (cdr-safe (assq type html-helper-type-alist))))
229
230 (defun html-helper-normalized-menu-for (type)
231   "Helper function for building menus from submenus: add on string to menu."
232   (cons (html-helper-menu-string-for type)
233         (eval (html-helper-menu-for type))))
234
235 ;;}}}
236
237 (define-derived-mode html-mode sgml-mode "HTML"
238   "Major mode for editing HTML documents.
239 This is based on PSGML mode, and has a sophisticated SGML parser in it.
240 It knows how to properly indent HTML/SGML documents, and it can do
241   a form of document validation (use \\[sgml-next-trouble-spot] to find
242   the next error in your document).
243 Commands beginning with C-z insert various types of HTML tags
244   (prompting for the required information); to iconify or suspend,
245   use C-z C-z.
246 To literally insert special characters such as < and &, use C-c followed
247   by the character.
248 Use \\[sgml-insert-end-tag] to insert the proper closing tag.
249 Use \\[sgml-edit-attributes] to edit the attributes for a tag.
250 Use \\[sgml-show-context] to show the current HTML context.
251
252 More specifically:
253 \\{html-mode-map}
254 "
255   (make-local-variable 'sgml-declaration)
256   (make-local-variable 'sgml-default-doctype-name)
257   (setq sgml-declaration             (expand-file-name "html.decl"
258                                                        sgml-data-directory)
259         sgml-default-doctype-name    "HTML"
260         sgml-always-quote-attributes t
261         sgml-indent-step             2
262         sgml-indent-data             t
263         sgml-inhibit-indent-tags     '("pre")
264         sgml-minimize-attributes     nil
265         sgml-omittag                 t
266         sgml-shorttag                t)
267
268   ;; font-lock setup for various emacsen: XEmacs, Emacs 19.29+, Emacs <19.29.
269   ;; By Ulrik Dickow <dickow@nbi.dk>.  (Last update: 05-Sep-1995).
270   (cond (running-xemacs ; XEmacs/Lucid
271          (put major-mode 'font-lock-keywords-case-fold-search t))
272         ;; XEmacs (19.13, at least) guesses the rest correctly.
273         ;; If any older XEmacsen don't, then tell me.
274         ;;
275         ((string-lessp "19.28.89" emacs-version) ; Emacs 19.29 and later
276          (make-local-variable 'font-lock-defaults)
277          (setq font-lock-defaults '(html-font-lock-keywords t t)))
278         ;;
279         (t ; Emacs 19.28 and older
280          (make-local-variable 'font-lock-keywords-case-fold-search)
281          (make-local-variable 'font-lock-keywords)
282          (make-local-variable 'font-lock-no-comments)
283          (setq font-lock-keywords-case-fold-search t)
284          (setq font-lock-keywords html-font-lock-keywords)
285          (setq font-lock-no-comments t)))
286
287   (if html-helper-do-write-file-hooks
288       (add-hook 'local-write-file-hooks 'html-helper-update-timestamp))
289
290   (if (and html-helper-build-new-buffer (zerop (buffer-size)))
291       (html-helper-insert-new-buffer-strings))
292
293   (set (make-local-variable 'sgml-custom-markup)
294        '(("<A>" "<A HREF=\"\">\r</a>")))
295
296   ;; Set up the syntax table.
297   (modify-syntax-entry ?< "(>" html-mode-syntax-table)
298   (modify-syntax-entry ?> ")<" html-mode-syntax-table)
299   (modify-syntax-entry ?\" ".   " html-mode-syntax-table)
300   (modify-syntax-entry ?\\ ".   " html-mode-syntax-table)
301   (modify-syntax-entry ?'  "w   " html-mode-syntax-table)
302
303   (tempo-use-tag-list 'html-helper-tempo-tags html-helper-completion-finder)
304   (set (make-local-variable 'imenu-create-index-function)
305        'html-helper-imenu-index)
306   (set (make-local-variable 'imenu-sort-function) nil) ; sorting the
307                                                        ; menu defeats
308                                                        ; the purpose
309
310   ; sigh ...  need to call this now to get things working.
311   (sgml-build-custom-menus)
312   ;; (add-submenu nil sgml-html-menu "SGML")
313   (setq sgml-menu-name "HTML")
314   (easy-menu-add sgml-html-menu)
315   (html-helper-rebuild-menu)
316   (unless (featurep 'infodock)
317     (delete-menu-item '("SGML"))))
318
319 (defvar html-helper-imenu-regexp
320   "\\s-*<h\\([1-9]\\)[^\n<>]*>\\(<[^\n<>]*>\\)*\\s-*\\([^\n<>]*\\)"
321   "*A regular expression matching a head line to be added to the menu.
322 The first `match-string' should be a number from 1-9.
323 The second `match-string' matches extra tags and is ignored.
324 The third `match-string' will be the used in the menu.")
325
326 ;; Make an index for imenu
327 (defun html-helper-imenu-index ()
328   "Return an table of contents for an html buffer for use with Imenu."
329   (let ((space ?\ ) ; a char
330         (toc-index '())
331         toc-str)
332     (save-excursion
333       (goto-char (point-min))
334       (while (re-search-forward html-helper-imenu-regexp nil t)
335         (setq toc-str
336               (concat
337                (make-string
338                 (* 2 (- (string-to-number (match-string 1)) 1))
339                 space)
340                (match-string 3)))
341         (beginning-of-line)
342         (setq toc-index (cons (cons toc-str (point)) toc-index))
343         (end-of-line)))
344     (nreverse toc-index)))
345
346 (defun html-helper-add-type-to-alist (type)
347   "Add a type specification to the alist.
348 The spec goes (type . (keymap-symbol keyprefix menu-symbol menu-string)).
349 See code for an example."
350   (setq html-helper-type-alist (cons type html-helper-type-alist)))
351
352 ;; Here are the types provided by html-helper-mode.
353 (mapcar 'html-helper-add-type-to-alist
354   '((entity  . (nil nil html-helper-entity-menu "Insert Character Entities"))
355     (textel  . (nil nil html-helper-textel-menu "Insert Text Elements"))
356     (head    . (html-helper-head-map "\C-zw" html-helper-head-menu "Insert Structural Elements"))
357     (header  . (html-helper-base-map "\C-z" html-helper-header-menu "Insert Headers"))
358     (anchor  . (html-helper-base-map "\C-z" html-helper-anchor-menu "Insert Hyperlinks"))
359     (logical . (html-helper-base-map "\C-z" html-helper-logical-menu "Insert Logical Styles"))
360     (phys    . (html-helper-base-map "\C-z" html-helper-phys-menu "Insert Physical Styles"))
361     (list    . (html-helper-list-map "\C-zl" html-helper-list-menu "Insert List Elements"))
362     (form    . (html-helper-form-map "\C-zf" html-helper-form-menu "Insert Form Elements"))
363     (table   . (html-helper-table-map "\C-zt" html-helper-table-menu "Insert Table Elements"))
364     (image   . (html-helper-image-map "\C-zm" html-helper-image-menu "Insert Inlined Images"))
365     (special . (html-helper-base-map "\C-z" html-helper-special-menu "Insert Specials"))))
366
367 ;; Once html-helper-mode is aware of a type, it can then install the
368 ;; type: arrange for keybindings, menus, etc.
369
370 (defconst html-helper-installed-types nil
371   "The types that have been installed (used when building menus).
372 There is no support for removing a type once it has been installed.")
373
374 (defun html-helper-install-type (type)
375   "Install a new tag type: add it to the keymap, menu structures, etc.
376 For this to work, the type must first have been added to the list of types
377 with html-helper-add-type-to-alist."
378   (setq html-helper-installed-types (cons type html-helper-installed-types))
379   (let ((keymap (html-helper-keymap-for type))
380         (key (html-helper-key-for type))
381         (menu (html-helper-menu-for type))
382         (menu-string (html-helper-menu-string-for type)))
383     (and key
384          (progn
385            (set keymap nil)
386            (define-prefix-command keymap)
387            (define-key html-mode-map key keymap)))
388     (and menu
389          (progn
390            (set menu nil)))))
391
392 ;; install the default types.
393 (mapcar 'html-helper-install-type html-helper-types-to-install)
394
395 ;;}}}
396
397 ;;{{{ html-helper-add-tag function for building basic tags
398
399 (defvar html-helper-tempo-tags nil
400   "List of tags used in completion.")
401
402 ;; this while loop is awfully Cish
403 ;; isn't there an emacs lisp function to do this?
404 (defun html-helper-string-to-symbol (input-string)
405   "Given a string, downcase it and replace spaces with -.
406 We use this to turn menu entries into good symbols for functions.
407 It's not entirely successful, but fortunately emacs lisp is forgiving."
408   (let* ((s (downcase input-string))
409          (l (1- (length s))))
410     (while (>= l 0)
411       (if (char-equal (aref s l) ?\ )
412           (aset s l ?\-))
413       (setq l (1- l)))
414     (concat "html-" s)))
415
416
417 (defun html-helper-add-tag (l)
418   "Add a new tag to html-helper-mode.
419 Builds a tempo-template for the tag and puts it into the
420 appropriate keymap if a key is requested. Format:
421 `(html-helper-add-tag '(type keybinding completion-tag menu-name template doc)'"
422   (let* ((type (car l))
423          (keymap (html-helper-keymap-for type))
424          (menu (html-helper-menu-for type))
425          (key (nth 1 l))
426          (completer (nth 2 l))
427          (name (nth 3 l))
428          (tag (nth 4 l))
429          (doc (nth 5 l))
430          (command (tempo-define-template (html-helper-string-to-symbol name)
431                                          tag completer doc
432                                          'html-helper-tempo-tags)))
433
434     (if (null (memq type html-helper-installed-types))    ;type loaded?
435         t                                                 ;no, do nothing.
436       (if (stringp key)                                   ;bind key somewhere?
437           (if keymap                                      ;special keymap?
438               (define-key (eval keymap) key command)      ;t:   bind to prefix
439             (define-key html-mode-map key command))       ;nil: bind to global
440         t)
441       (if menu                                            ;is there a menu?
442           (set menu                                       ;good, cons it in
443                (cons (vector name command t) (eval menu))))
444       )))
445
446 ;;}}}
447
448 ;;{{{ most of the HTML tags
449
450 ;; These tags are an attempt to be HTML 3.2 compliant
451 ;; For reference see <URL:http://www.w3.org/TR/REC-html32.html>
452
453 ;; order here is significant: within a tag type, menus and mode help
454 ;; go in the reverse order of what you see here. Sorry about that, it's
455 ;; not easy to fix.
456
457 (mapcar
458  'html-helper-add-tag
459  '(
460    ;;entities
461    (entity  "\C-c#"   "&#"              "Ascii Code"     ("&#" (r "Ascii: ") ";"))
462    (entity  "\C-c\""  "&quot;"          "Quotation mark" ("&quot;"))
463    (entity  "\C-c$"   "&reg;"           "Registered"     ("&reg;"))
464    (entity  "\C-c@"   "&copy;"          "Copyright"      ("&copy;"))
465    (entity  "\C-c-"   "&shy;"           "Soft Hyphen"    ("&shy;"))
466    (entity  "\C-c "   "&nbsp;"          "Nonbreaking Space" ("&nbsp;"))
467    (entity  "\C-c&"   "&amp;"           "Ampersand"      ("&amp;"))
468    (entity  "\C-c>"   "&gt;"            "Greater Than"   ("&gt;"))
469    (entity  "\C-c<"   "&lt;"            "Less Than"      ("&lt;"))
470
471    ;; logical styles
472    (logical "v"       "<div"            "Text Division"  ("<div align=\"" (r "Alignment: ") "\">" (r "Text: ") "</div>"))
473    (logical "n"       "<center>"        "Center"         ("<center>" (r "Text: ") "</center>"))
474    (logical "q"       "<blockquote>"    "Blockquote"     ("<blockquote>" (r "Quote: ") "</blockquote>"))
475    (logical "c"       "<code>"          "Code"           ("<code>" (r "Code: ") "</code>"))
476    (logical "x"       "<samp>"          "Sample"         ("<samp>" (r "Sample code") "</samp>"))
477    (logical "r"       "<cite>"          "Citation"       ("<cite>" (r "Citation: ") "</cite>"))
478    (logical "k"       "<kbd>"           "Keyboard Input" ("<kbd>" (r "Keyboard: ") "</kbd>"))
479    (logical "v"       "<var>"           "Variable"       ("<var>" (r "Variable: ") "</var>"))
480    (logical "d"       "<dfn>"           "Definition"     ("<dfn>" (r "Definition: ") "</dfn>"))
481    (logical "a"       "<address>"       "Address"        ("<address>" r "</address>"))
482    (logical "e"       "<em>"            "Emphasized"     ("<em>" (r "Text: ") "</em>"))
483    (logical "s"       "<strong>"        "Strong"         ("<strong>" (r "Text: ") "</strong>"))
484    (logical "p"       "<pre>"           "Preformatted"   ("<pre>" (r "Text: ") "</pre>"))
485
486    ;;physical styles
487    (phys    "p"       "<sup>"           "Superscript"    ("<sup>" (r "Text: ") "</sup>"))
488    (phys    "u"       "<sub>"           "Subscript"      ("<sub>" (r "Text: ") "</sub>"))
489    (phys    "s"       "<small>"         "Small"          ("<small>" (r "Text: ") "</small>"))
490    (phys    "g"       "<big>"           "Big"            ("<big>" (r "Text: ") "</big>"))
491    (phys    "-"       "<strike>"        "Strikethru"     ("<strike>" (r "Text: ") "</strike>"))
492    (phys    "u"       "<u>"             "Underline"      ("<u>" (r "Text: ") "</u>"))
493    (phys    "o"       "<i>"             "Italic"         ("<i>" (r "Text: ") "</i>"))
494    (phys    "b"       "<b>"             "Bold"           ("<b>" (r "Text: ") "</b>"))
495    (phys    "t"       "<tt>"            "Fixed"          ("<tt>" (r "Text: ") "</tt>"))
496
497    ;;headers
498    (header  "6"       "<h6>"            "Header 6"       ("<h6>" (r "Header: ") "</h6>"))
499    (header  "5"       "<h5>"            "Header 5"       ("<h5>" (r "Header: ") "</h5>"))
500    (header  "4"       "<h4>"            "Header 4"       ("<h4>" (r "Header: ") "</h4>"))
501    (header  "3"       "<h3>"            "Header 3"       ("<h3>" (r "Header: ") "</h3>"))
502    (header  "2"       "<h2>"            "Header 2"       ("<h2>" (r "Header: ") "</h2>"))
503    (header  "1"       "<h1>"            "Header 1"       ("<h1>" (r "Header: ") "</h1>"))
504
505    ;; forms
506    (form    "o"       "<option>"        "Option"         (& "<option>" > ))
507    (form    "v"       "<option value"   "Option with Value"  (& "<option value=\"" (r "Value: ") "\">" >))
508    (form    "s"       "<select"         "Selections"     ("<select name=\"" (p "Name: ") "\">\n<option>" > "\n</select>")"<select")
509    (form    "z"       "<input"          "Reset Form"     ("<input type=\"RESET\" value=\"" (p "Reset button text: ") "\">"))
510    (form    "b"       "<input"          "Submit Form"    ("<input type=\"SUBMIT\" value=\"" (p "Submit button text: ") "\">"))
511    (form    "i"       "<input"          "Image Field"    ("<input type=\"IMAGE\" name=\"" (p "Name: ") "\" src=\"" (p "Image URL: ") "\">"))
512    (form    "h"       "<input"          "Hidden Field"   ("<input type=\"HIDDEN\" name=\"" (p "Name: ") "\" value=\"" (p "Value: ") "\">"))
513    (form    "p"       "<textarea"       "Text Area"      ("<textarea name=\"" (p "Name: ") "\" rows=\"" (p "Rows: ") "\" cols=\"" (p "Columns: ") "\">" r "</textarea>"))
514    (form    "c"       "<input"          "Checkbox"       ("<input type=\"CHECKBOX\" name=\"" (p "Name: ") "\">"))
515    (form    "r"       "<input"          "Radiobutton"    ("<input type=\"RADIO\" name=\"" (p "Name: ") "\">"))
516    (form    "t"       "<input"          "Text Field"     ("<input type=\"TEXT\" name=\"" (p "Name: ") "\" size=\"" (p "Size: ") "\">"))
517    (form    "f"       "<form"           "Form"           ("<form action=\"" (p "Action: ") "\" method=\"" (p "Method: ") "\">\n</form>\n"))
518
519    ;;tables
520    (table   "d"       "<td>"            "Data cell"      ("<td>"))
521    (table   "h"       "<th>"            "Header"         ("<th>"))
522    (table   "r"       "<tr>"            "Row"            ("<tr>"))
523    (table   "t"       "<table>"         "Table"          ("<table>\n<tr>\n</table>\n"))
524
525    ;;lists
526    (list    "t"       "<dt>"            "Definition Item" (& "<dt>" > (p "Term: ") "\n<dd>" > (r "Definition: ")))
527    (list    "l"       "<li>"            "List Item"      (& "<li>" > (r "Item: ")))
528    (list    "r"       "<dir>"           "DirectoryList"  (& "<dir>" > "\n<li>" > (r "Item: ") "\n</dir>" >))
529    (list    "m"       "<menu>"          "Menu List"      (& "<menu>" > "\n<li>" > (r "Item: ") "\n</menu>" >))
530    (list    "o"       "<ol>"            "Ordered List"   (& "<ol>" > "\n<li>" > (r "Item: ") "\n</ol>" >))
531    (list    "d"       "<dl>"            "Definition List" (& "<dl>" > "\n<dt>" > (p "Term: ") "\n<dd>" > (r "Definition: ") "\n</dl>" >))
532    (list    "u"       "<ul>"            "Unordered List" (& "<ul>" > "\n<li>" > (r "Item: ") "\n</ul>" >))
533
534    ;;anchors
535    (anchor  "n"       "<a name="        "Link Target"    ("<a name=\"" (p "Anchor name: ") "\">" (r "Anchor text: ") "</a>"))
536    (anchor  "h"       "<a href="        "Hyperlink"      ("<a href=\"" (p "URL: ") "\">" (r "Anchor text: ") "</a>"))
537
538    ;;graphics
539    (image   "m"       "<map name="      "Image map"      ("<map name=\"" (r "Map name: ") "\">"))
540    (image   "a"       nil               "Aligned Image"  ("<img align=\"" (r "Alignment: ") "\" src=\"" (r "Image URL: ") "\">"))
541    (image   "i"       "<img src="       "Image"          ("<img src=\"" (r "Image URL: ") "\">"))
542    (image   "e"       "<img align="     "Aligned Image With Alt. Text"  ("<img align=\"" (r "Alignment: ") "\" src=\"" (r "Image URL: ") "\" alt=\"" (r "Text URL: ") "\">"))
543    (image   "t"       "<img alt="       "Image With Alternate Text"     ("<img alt=\"" (r "Text URL: ") "\" src=\"" (r "Image URL: ") "\">"))
544
545    ;;specials
546    (special "a"       "<applet code="   "Applet"         ("<applet code=\"" (r "Applet class: ") "\" width=" (r "Applet width: ") " height=" (r "Applet height: ") ">"))
547    (special "b"       "<basefont size=" "Base font size" ("<basefont size=" (r "Font size: ") ">"))
548    (special "c"       "<font color="    "Font color"     ("<font color=\"" (r "Color: ") "\">" (r "Text: ") "</font>"))
549    (special "s"       "<font size="     "Font size"      ("<font size=" (r "Font size: ") ">" (r "Text: ") "</font>"))
550
551    ;;text elements
552    (textel  "\C-c="   nil               "Horizontal Line" (& "<hr>\n"))
553    (textel  "\C-c\C-m" nil              "Line Break"     ("<br>\n"))
554    (textel  "\e\C-m"  nil               "Paragraph"      ("<p>" (progn (sgml-indent-line) nil) "\n"))
555
556    ;;head elements
557    (head    "H"       "<head>"          "Head"           ("<head>\n" "</head>\n"))
558    (head    "B"       "<body>"          "Body"           ("<body>\n" "</body>\n"))
559    (head    "i"       "<isindex>"       "Isindex"        ("<isindex>\n"))
560    (head    "n"       "<nextid>"        "Nextid"         ("<nextid>\n"))
561    (head    "h"       "<meta http-equiv=" "HTTP Equivalent" ("<meta http-equiv=\"" (p "Equivalent: ") "\" content=\"" (r "Content: ") "\">\n"))
562    (head    "m"       "<meta name="     "Meta Name"      ("<meta name=\"" (p "Name: ") "\" content=\"" (r "Content: ") "\">\n"))
563    (head    "l"       "<link"           "Link"           ("<link href=\"" p "\">"))
564    (head    "s"       "<script>"        "Script"         ("<script>"))
565    (head    "y"       "<style>"         "Style"          ("<style>"))
566    (head    "b"       "<base"           "Base"           ("<base href=\"" r "\">"))
567    (head    "t"       "<title>"         "Title"          ("<title>" (r "Document title: ") "</title>"))
568    ))
569
570 ;;}}}
571 ;;{{{ html-helper-smart-insert-item
572
573 ;; there are two different kinds of items in HTML - those in regular
574 ;; lists <li> and those in dictionaries <dt>..<dd>
575 ;; This command will insert the appropriate one depending on context.
576
577 (defun html-helper-smart-insert-item (&optional arg)
578   "Insert a new item, either in a regular list or a dictionary."
579   (interactive "*P")
580   (let ((case-fold-search t))
581     (if
582         (save-excursion
583           (re-search-backward "<li>\\|<dt>\\|<ul>\\|<ol>\\|<dd>\\|<menu>\\|<dir>\\|<dl>" nil t)
584           (looking-at "<dt>\\|<dl>\\|<dd>"))
585         (tempo-template-html-definition-item arg)
586       (tempo-template-html-list-item arg))))
587
588 ;; special keybindings in the prefix maps (not in the list of tags)
589 (and (boundp 'html-helper-base-map)
590      (define-key html-helper-base-map "i" 'html-helper-smart-insert-item))
591
592 (define-key html-mode-map "\C-z\C-z" 'suspend-or-iconify-emacs)
593 (define-key html-mode-map "\C-zg" 'html-insert-mailto-reference-from-click)
594
595 ;; and, special menu bindings
596 (and (boundp 'html-helper-list-menu)
597      (setq html-helper-list-menu
598            (cons '["List Item" html-helper-smart-insert-item t] html-helper-list-menu)))
599
600 ;;}}}
601
602 ;;{{{ menu support
603
604 ;; menus are built for easymenu. html-helper-add-tag builds
605 ;; submenus based on tag type, the expert menu code lumps them
606 ;; together into one list and calls easy-menu-define
607
608 (defun html-helper-rebuild-menu nil
609   "Rebuild and install the HTML menu (using `easy-menu-define').
610 If `html-helper-use-expert-menu' is nil, then just use a novice menu."
611   (let ((menu (html-helper-expert-menu)))
612     (easy-menu-remove menu)
613     (easy-menu-add menu html-mode-map)))
614
615 (defun html-helper-toggle-expert-menu (&optional arg)
616   "Toggle full HTML menus. Optional arg acts like minor-mode args."
617   (interactive "P")
618   (setq html-helper-use-expert-menu
619         (if (null arg) (not html-helper-use-expert-menu)
620           (> (prefix-numeric-value arg) 0)))
621   (html-helper-rebuild-menu))
622
623 ;; Expert menus: consed up out of html-helper-installed-types
624 (defun html-helper-expert-menu ()
625   "This menu is based on the current value of `html-helper-installed-types'.
626 This function can be called again, it redoes the entire menu."
627   ;; Start with the user-provided menu stuff
628   (let ((html-helper-mode-menu html-helper-user-menu))
629     ;; Now cons in the browse-url functions
630     (if (fboundp 'browse-url-of-file)
631         (setq html-helper-mode-menu
632               (cons '["Load this Buffer in Browser" browse-url-of-file t]
633                     html-helper-mode-menu)))
634     (setq html-helper-mode-menu
635           (cons (vector "Browse URL at point"
636                         (when (and (boundp 'browse-url-browser-function)
637                                    browse-url-browser-function)
638                           ;; If the value is not a function it should be a list of pairs
639                           ;; (REGEXP . FUNCTION)
640                           (cond
641                            ((functionp browse-url-browser-function))
642                            ((listp browse-url-browser-function)
643                             (dolist (elt browse-url-browser-function)
644                               (when (not (string-match "mail" (symbol-name (cdr-safe elt))))
645                                 (return (cdr elt)))))))
646                         t)
647                 html-helper-mode-menu))
648
649     ;; cons in the timestamp delimiters
650     (setq html-helper-mode-menu
651           (cons '["Insert Timestamp Delimiter"
652                   html-helper-insert-timestamp-delimiter-at-point t]
653                 html-helper-mode-menu))
654
655     ;; now cons up the main menu out of the submenus
656     (mapcar
657      (function (lambda (type)
658                  (setq html-helper-mode-menu
659                        (cons (html-helper-normalized-menu-for type)
660                              html-helper-mode-menu))))
661      html-helper-installed-types)
662
663     ;; now tack on our name
664     (setq html-helper-mode-menu (cons "Insert" html-helper-mode-menu))
665
666     ;; special mode keys
667     (define-key html-mode-map (kbd "<M-iso-left-tab>") 'tempo-complete-tag)
668     ;;("\M-\C-f" tempo-forward-mark)
669     ;;("\M-\C-b" tempo-backward-mark)
670
671     html-helper-mode-menu))
672
673 ;;}}}
674
675 ;;{{{ patterns for font-lock
676
677 ; Old patterns from html-mode.el
678 ;(defvar html-font-lock-keywords
679 ;  (list
680 ;   '("\\(<[^>]*>\\)+" . font-lock-comment-face)
681 ;   '("[Hh][Rr][Ee][Ff]=\"\\([^\"]*\\)\"" 1 font-lock-string-face t)
682 ;   '("[Ss][Rr][Cc]=\"\\([^\"]*\\)\"" 1 font-lock-string-face t))
683 ;  "Patterns to highlight in HTML buffers.")
684
685 ;; By Ulrik Dickow <dickow@nbi.dk>.
686 ;;
687 ;; Originally aimed at Emacs 19.29.  Later on disabled syntactic fontification
688 ;; and reordered regexps completely, to be compatible with XEmacs (it doesn't
689 ;; understand OVERRIDE=`keep').
690 ;;
691 ;; We make an effort on handling nested tags intelligently.
692
693 ;;;###autoload
694 (defvar html-font-lock-keywords
695   (let (;; Titles and H1's, like function defs.
696         ;;   We allow for HTML 3.0 attributes, like `<h1 align=center>'.
697         (tword "\\(h1\\|title\\)\\([ \t\n]+[^>]+\\)?")
698         ;; Names of tags to boldify.
699         (bword "\\(b\\|h[2-4]\\|strong\\)\\([ \t\n]+[^>]+\\)?")
700         ;; Names of tags to italify.
701         (iword "\\(address\\|cite\\|em\\|i\\|var\\)\\([ \t\n]+[^>]+\\)?")
702         ;; Regexp to match shortest sequence that surely isn't a bold end.
703         ;; We simplify a bit by extending "</strong>" to "</str.*".
704         ;; Do similarly for non-italic and non-title ends.
705         (not-bend (concat "\\([^<]\\|<\\([^/]\\|/\\([^bhs]\\|"
706                           "b[^>]\\|"
707                           "h\\([^2-4]\\|[2-4][^>]\\)\\|"
708                           "s\\([^t]\\|t[^r]\\)\\)\\)\\)"))
709         (not-iend (concat "\\([^<]\\|<\\([^/]\\|/\\([^aceiv]\\|"
710                           "a\\([^d]\\|d[^d]\\)\\|"
711                           "c\\([^i]\\|i[^t]\\)\\|"
712                           "e\\([^m]\\|m[^>]\\)\\|"
713                           "i[^>]\\|"
714                           "v\\([^a]\\|a[^r]\\)\\)\\)\\)"))
715         (not-tend (concat "\\([^<]\\|<\\([^/]\\|/\\([^ht]\\|"
716                           "h[^1]\\|t\\([^i]\\|i[^t]\\)\\)\\)\\)")))
717     (list ; Avoid use of `keep', since XEmacs will treat it the same as `t'.
718      ;; First fontify the text of a HREF anchor.  It may be overridden later.
719      ;; Anchors in headings will be made bold, for instance.
720      '("<a\\s-+href[^>]*>\\([^>]+\\)</a>"
721        1 html-helper-link-face t)
722      ;; Tag pairs like <b>...</b> etc.
723      ;; Cunning repeated fontification to handle common cases of overlap.
724      ;; Bold complex --- possibly with arbitrary other non-bold stuff inside.
725      (list (concat "<" bword ">\\(" not-bend "*\\)</\\1>")
726            3 'html-helper-bold-face t)
727      ;; Italic complex --- possibly with arbitrary non-italic kept inside.
728      (list (concat "<" iword ">\\(" not-iend "*\\)</\\1>")
729            3 'html-helper-italic-face t)
730      ;; Bold simple --- first fontify bold regions with no tags inside.
731      (list (concat "<" bword ">\\("  "[^<]"  "*\\)</\\1>")
732            3 'html-helper-bold-face t)
733      ;; Any tag, general rule, just after bold/italic stuff.
734      '("\\(<[^>]*>\\)" 1 font-lock-type-face t)
735      ;; Titles and level 1 headings (anchors do sometimes appear in h1's)
736      (list (concat "<" tword ">\\(" not-tend "*\\)</\\1>")
737            3 'font-lock-function-name-face t)
738      ;; Underline is rarely used. Only handle it when no tags inside.
739      '("<u>\\([^<]*\\)</u>" 1 html-helper-underline-face t)
740      ;; Ditto for strikethrough.
741      '("<strike>\\([^<]*\\)</strike>" 1 html-helper-strikethrough-face t)
742      ;; Forms, anchors & images (also fontify strings inside)
743      '("\\(<\\(form\\|i\\(mg\\|nput\\)\\)\\>[^>]*>\\)"
744        1 html-helper-significant-tag-face t)
745      '("</a>" 0 font-lock-keyword-face t)
746      '("\\(<a\\b[^>]*>\\)" 1 font-lock-keyword-face t)
747      '("=[ \t\n]*\\(\"[^\"]+\"\\)" 1 font-lock-string-face t)
748      ;; Large-scale structure keywords (like "program" in Fortran).
749      ;;   "<html>" "</html>" "<body>" "</body>" "<head>" "</head>" "</form>"
750      '("</?\\(body\\|form\\|h\\(ead\\|tml\\)\\)>"
751        0 html-helper-significant-tag-face t)
752      ;; HTML special characters
753      '("&[^;\n]*;" 0 font-lock-string-face t)
754      ;; SGML things like <!DOCTYPE ...> with possible <!ENTITY...> inside.
755      '("\\(<![a-z]+\\>[^<>]*\\(<[^>]*>[^<>]*\\)*>\\)"
756        1 font-lock-comment-face t)
757      ;; Comments: <!-- ... -->. They traditionally override anything else.
758      ;; It's complicated 'cause we won't allow "-->" inside a comment, and
759      ;; font-lock colours the *longest* possible match of the regexp.
760      '("\\(<!--\\([^-]\\|-[^-]\\|--[^>]\\)*-->\\)"
761        1 font-lock-comment-face t)))
762     "Additional expressions to highlight in HTML mode.")
763
764 (put 'html-mode 'font-lock-defaults '(html-font-lock-keywords nil t))
765
766 ;;}}}
767
768 ;;{{{ patterns for hilit19
769
770 ;; Define some useful highlighting patterns for the hilit19 package.
771 ;; These will activate only if hilit19 has already been loaded.
772 ;; Thanks to <dickow@nbi.dk> for some pattern suggestions
773
774 (if (featurep 'hilit19)
775     (hilit-set-mode-patterns
776      'html-helper-mode
777      '(("<!--" "-->" comment)
778        ("<![a-z]+\\>[^<>]*\\(<[^>]*>[^<>]*\\)*>" nil comment) ;<!DOCTYPE ...>
779        ("<title>" "</title>" defun)
780        ("<h[1-6]>" "</h[1-6]>" bold) ;only colour inside tag
781        ("<a\\b" ">" define)
782        ("</a>" nil define)
783        ("<img\\b" ">" include)
784        ("<option\\|</?select\\|<input\\|</?form\\|</?textarea" ">" include)
785        ;; First <i> highlighting just handles unnested tags, then do nesting
786        ("<i>[^<]*</i>" nil italic)
787        ("<b>" "</b>" bold)
788        ("<i>" "</i>" italic)
789        ("<u>" "</u>" underline)
790        ("&[^;\n]*;" nil string)
791        ("<" ">" keyword))
792      nil 'case-insensitive)
793   nil)
794
795 ;;}}}
796
797 ;;{{{ completion finder for tempo
798
799 ;; The regexp finds everything between the last < or & and point,
800 ;; which is good enough to match the tags HTML might complete.
801 (defvar html-helper-completion-finder "\\(\\(<\\|&\\).*\\)\\="
802   "Passed to tempo-use-tag-list, used to find tags to complete.")
803
804 ;;}}}
805
806 ;;{{{ timestamps
807
808 (defun html-helper-update-timestamp ()
809   "Basic function for updating timestamps.
810 It finds the timestamp in the buffer by looking for
811 `html-helper-timestamp-start', deletes all text up to
812 `html-helper-timestamp-end', and runs `html-helper-timestamp-hook' which
813 will should insert an appropriate timestamp in the buffer."
814   (save-excursion
815     (goto-char (point-max))
816     (if (not (search-backward html-helper-timestamp-start nil t))
817         (message "timestamp delimiter start was not found")
818       (let ((ts-start (+ (point) (length html-helper-timestamp-start)))
819             (ts-end (if (search-forward html-helper-timestamp-end nil t)
820                         (- (point) (length html-helper-timestamp-end))
821                       nil)))
822         (if (not ts-end)
823             (message "timestamp delimiter end was not found. Type C-c C-t to insert one.")
824           (delete-region ts-start ts-end)
825           (goto-char ts-start)
826           (run-hooks 'html-helper-timestamp-hook)))))
827   nil)
828
829 (defun html-helper-return-created-string ()
830   "Return a \"Created:\" string."
831   (let ((time (current-time-string)))
832     (concat "<!-- Created: "
833             (substring time 0 20)
834             (nth 1 (current-time-zone))
835             " "
836             (substring time -4)
837             " -->\n")))
838
839 (defun html-helper-default-insert-timestamp ()
840   "Default timestamp insertion function."
841   (let ((time (current-time-string)))
842     (insert "Last modified: "
843             (substring time 0 20)
844             (nth 1 (current-time-zone))
845             " "
846             (substring time -4)
847             "\n")))
848
849 (defun html-helper-insert-timestamp-delimiter-at-point ()
850   "Simple function that inserts timestamp delimiters at point.
851 Useful for adding timestamps to existing buffers."
852   (interactive)
853   (insert html-helper-timestamp-start)
854   (insert html-helper-timestamp-end))
855
856 ;;}}}
857
858 (defun mail-address-at-point (pos &optional buffer)
859   "Return a list (NAME ADDRESS) of the address at POS in BUFFER."
860   (or buffer (setq buffer (current-buffer)))
861   (let (beg end)
862     (save-excursion
863       (set-buffer buffer)
864       (save-excursion
865         (goto-char pos)
866         (or (re-search-forward "[\n,]" nil t)
867             (error "Can't find address at position"))
868         (backward-char)
869         (setq end (point))
870         (or (re-search-backward "[\n,:]" nil t)
871             (error "Can't find address at position"))
872         (forward-char)
873         (re-search-forward "[ \t]*" nil t)
874         (setq beg (point))
875         (mail-extract-address-components (buffer-substring beg end))))))
876
877 (defun html-insert-mailto-reference-from-click ()
878   "Insert a mailto: reference for the clicked-on e-mail address."
879   (interactive)
880   (let (event)
881     (message "Click on a mail address:")
882     (save-excursion
883       (setq event (next-command-event))
884       (or (mouse-event-p event)
885           (error "Aborted.")))
886     (let ((lis (mail-address-at-point (event-closest-point event)
887                                       (event-buffer event))))
888       (insert "<a href=\"mailto:" (car (cdr lis)) "\">"
889               (or (car lis) (car (cdr lis))) "</a>"))))
890
891 ;;;###autoload
892 (defun html-quote-region (begin end)
893   "\"Quote\" any characters in the region that have special HTML meanings.
894 This converts <'s, >'s, and &'s into the HTML commands necessary to
895 get those characters to appear literally in the output."
896   (interactive "r")
897   (save-excursion
898     (goto-char begin)
899     (while (search-forward "&" end t)
900       (forward-char -1)
901       (delete-char 1)
902       (insert "&amp;")
903       (setq end (+ 4 end)))
904     (goto-char begin)
905     (while (search-forward "<" end t)
906       (forward-char -1)
907       (delete-char 1)
908       (insert "&lt;")
909       (setq end (+ 3 end)))
910     (goto-char begin)
911     (while (search-forward ">" end t)
912       (forward-char -1)
913       (delete-char 1)
914       (insert "&gt;")
915       (setq end (+ 3 end)))))
916
917 ;;{{{ html-helper-insert-new-buffer-strings
918
919 (tempo-define-template "html-skeleton" html-helper-new-buffer-template
920                        nil
921                        "Insert a skeleton for a HTML document")
922
923 (defun html-helper-insert-new-buffer-strings ()
924   "Insert `html-helper-new-buffer-strings'."
925   (tempo-template-html-skeleton))
926
927 ;;}}}
928
929 ;;;###autoload
930 (autoload 'html-mode "psgml-html" "HTML mode." t)
931
932 (defvar sgml-html-menu
933   (cons "HTML"
934         (append '(["View in default browser" browse-url-of-buffer t]
935                   ["View in Firefox" sgml-html-netscape-file
936                    (buffer-file-name (current-buffer))]
937                   ["View in Konqueror" sgml-html-kfm-file
938                    (buffer-file-name (current-buffer))]
939                   ["View in W3" w3-preview-this-buffer t]
940                   "---"
941                   ["HTML-Quote Region" html-quote-region t]
942                   "---")
943                 (cdr sgml-main-menu))))
944
945 (defun sgml-html-netscape-file ()
946   "Preview the file for the current buffer in Firefox."
947   (interactive)
948   (browse-url-firefox
949    (concat "file:" (buffer-file-name (current-buffer)))))
950
951 (defun sgml-html-kfm-file ()
952   "Preview the file for the current buffer in Konqueror."
953   (interactive)
954   (browse-url-kde
955    (concat "file:" (buffer-file-name (current-buffer)))))
956
957 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.[sj]?html?\\'" . html-mode))
958 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.jsp\\'" . html-mode))
959
960 ;;; end of psgml-html.el