Mercurial > hg > xemacs-beta
comparison lisp/w3/w3-speak.el @ 80:1ce6082ce73f r20-0b90
Import from CVS: tag r20-0b90
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:06:37 +0200 |
parents | 131b0175ea99 |
children | 6a378aca36af |
comparison
equal
deleted
inserted
replaced
79:5b0a5bbffab6 | 80:1ce6082ce73f |
---|---|
1 ;;; w3-speak.el --- Emacs-W3 speech interface | 1 ;;; w3-speak.el,v --- Emacs-W3 speech interface |
2 ;; Authors: wmperry and Raman | 2 ;; Author: wmperry |
3 ;; Created: 1996/07/09 14:08:09 | 3 ;; Original author: William Perry --<wmperry@cs.indiana.edu> |
4 ;; Version: 1.4 | 4 ;; Cloned from emacspeak-w3.el |
5 ;; Created: 1996/10/16 20:56:40 | |
6 ;; Version: 1.14 | |
5 ;; Keywords: hypermedia, speech | 7 ;; Keywords: hypermedia, speech |
6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 8 |
7 ;;{{{ Copyright | 9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
8 | 10 ;;; Copyright (c) 1996 by T.V. Raman (raman@adobe.com) |
9 ;;; Copyright (c) 1996 by William M. Perry (wmperry@cs.indiana.edu) | 11 ;;; Copyright (c) 1996 by William M. Perry (wmperry@spry.com) |
10 ;;; | 12 ;;; |
11 ;;; This file is not part of GNU Emacs, but the same permissions apply. | 13 ;;; This file is not part of GNU Emacs, but the same permissions apply. |
12 ;;; | 14 ;;; |
13 ;;; GNU Emacs is free software; you can redistribute it and/or modify | 15 ;;; GNU Emacs is free software; you can redistribute it and/or modify |
14 ;;; it under the terms of the GNU General Public License as published by | 16 ;;; it under the terms of the GNU General Public License as published by |
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 ;;; GNU General Public License for more details. | 23 ;;; GNU General Public License for more details. |
22 ;;; | 24 ;;; |
23 ;;; You should have received a copy of the GNU General Public License | 25 ;;; You should have received a copy of the GNU General Public License |
24 ;;; along with GNU Emacs; see the file COPYING. If not, write to | 26 ;;; along with GNU Emacs; see the file COPYING. If not, write to the |
25 ;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | 27 ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
28 ;;; Boston, MA 02111-1307, USA. | |
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
27 | 30 |
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 31 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
29 ;;; A replacement module for emacspeak-w3 that uses all the new functionality | 32 ;;; A replacement module for emacspeak-w3 that uses all the new functionality |
30 ;;; of Emacs-W3 3.0. | 33 ;;; of Emacs-W3 3.0. |
31 ;;; | 34 ;;; |
32 ;;; This file would not be possible without the help of | 35 ;;; This file would not be possible without the help of |
33 ;;; T.V. Raman (raman@adobe.com) and his continued efforts to make Emacs-W3 | 36 ;;; T.V. Raman (raman@adobe.com) and his continued efforts to make Emacs-W3 |
34 ;;; even remotely useful. :) | 37 ;;; even remotely useful. :) |
35 | 38 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
36 ;;}}} | 39 |
37 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 40 ;;; This conforms to http://www4.inria.fr/speech2.html |
38 ;;{{{ Required modules | |
39 | 41 |
40 (require 'widget) | 42 (require 'widget) |
41 (require 'w3-forms) | 43 (require 'w3-forms) |
42 (require 'advice) | 44 (require 'advice) |
43 | |
44 ;; This condition-case needs to be here or it completely chokes | 45 ;; This condition-case needs to be here or it completely chokes |
45 ;; byte-compilation for people who do not have Emacspeak installed. | 46 ;; byte-compilation for people who do not have Emacspeak installed. |
46 ;; *sigh* | 47 ;; *sigh* |
47 | |
48 (condition-case () | 48 (condition-case () |
49 (progn | 49 (progn |
50 (require 'emacspeak) | 50 (require 'emacspeak) |
51 (require 'dtk-voices) | 51 (require 'dtk-voices) |
52 (require 'dtk-css-speech) | |
53 (require 'emacspeak-speak) | 52 (require 'emacspeak-speak) |
54 (require 'emacspeak-sounds) | 53 (require 'emacspeak-sounds) |
55 (eval-when (compile) | 54 (eval-when (compile) |
56 (require 'emacspeak-fix-interactive))) | 55 (require 'emacspeak-fix-interactive))) |
57 (error (message "Emacspeak not found - speech will not work."))) | 56 (error (message "Emacspeak not found - speech will not work."))) |
58 | |
59 ;;}}} | |
60 | 57 |
61 | 58 |
62 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
63 ;;; How to get information summarizing a form field, so it can be spoken in | 60 ;;; How to get information summarizing a form field, so it can be spoken in |
64 ;;; a sane manner. | 61 ;;; a sane manner. |
74 (defsubst w3-speak-get-field-summarizer (type) | 71 (defsubst w3-speak-get-field-summarizer (type) |
75 "Retrieve function-name string for this voice" | 72 "Retrieve function-name string for this voice" |
76 (get type 'w3-speak-summarizer)) | 73 (get type 'w3-speak-summarizer)) |
77 | 74 |
78 ;;}}} | 75 ;;}}} |
76 ;;{{{ Associate summarizer functions for form fields | |
77 | |
78 (w3-speak-define-field-summarizer 'text) | |
79 (w3-speak-define-field-summarizer 'option) | |
80 (w3-speak-define-field-summarizer 'checkbox) | |
81 (w3-speak-define-field-summarizer 'reset) | |
82 (w3-speak-define-field-summarizer 'submit) | |
83 (w3-speak-define-field-summarizer 'button) | |
84 (w3-speak-define-field-summarizer 'radio) | |
85 (w3-speak-define-field-summarizer 'multiline) | |
86 (w3-speak-define-field-summarizer 'image) | |
87 | |
88 ;;}}} | |
89 | |
90 | |
79 ;;{{{ define the form field summarizer functions | 91 ;;{{{ define the form field summarizer functions |
80 | 92 |
81 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 93 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
82 ;;; Now actually define the summarizers | 94 ;;; Now actually define the summarizers |
83 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 95 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
90 (defun w3-speak-summarize-text-field (data) | 102 (defun w3-speak-summarize-text-field (data) |
91 "Summarize a text field given the field data." | 103 "Summarize a text field given the field data." |
92 (let ( | 104 (let ( |
93 (label (w3-speak-extract-form-field-label data)) | 105 (label (w3-speak-extract-form-field-label data)) |
94 (name (w3-form-element-name data)) | 106 (name (w3-form-element-name data)) |
95 (value (widget-value (w3-form-element-widget data)))) | 107 (value (widget-get (w3-form-element-widget data) :value))) |
96 (dtk-speak | 108 (dtk-speak |
97 (format "Text field %s %s " (or label (concat "called " name)) | 109 (format "Text field %s %s " (or label (concat "called " name)) |
98 (concat "set to " value))))) | 110 (concat "set to " value))))) |
99 | 111 |
100 (defun w3-speak-summarize-file-field (data) | 112 (defun w3-speak-summarize-multiline-field (data) |
101 "Summarize a f field of type file given the field data." | 113 "Summarize a text field given the field data." |
102 (let ( | |
103 (label (w3-speak-extract-form-field-label data)) | |
104 (name (w3-form-element-name data)) | |
105 (value (widget-value (w3-form-element-widget data)))) | |
106 (dtk-speak | |
107 (format "File field %s %s " (or label (concat "called " name)) | |
108 (concat "set to " value))))) | |
109 | |
110 (defun w3-speak-summarize-textarea-field (data) | |
111 "Summarize a textarea field given the field data." | |
112 (let ( | 114 (let ( |
113 (name (w3-form-element-name data)) | 115 (name (w3-form-element-name data)) |
114 (label (w3-speak-extract-form-field-label data)) | 116 (label (w3-speak-extract-form-field-label data)) |
115 (value (w3-form-element-value data))) | 117 (value (w3-form-element-value data))) |
116 (dtk-speak | 118 (dtk-speak |
158 (case type | 160 (case type |
159 (submit "Submit Form") | 161 (submit "Submit Form") |
160 (reset "Reset Form") | 162 (reset "Reset Form") |
161 (button "A Button")))))) | 163 (button "A Button")))))) |
162 | 164 |
163 (defalias 'w3-speak-summarize-reset-field 'w3-speak-summarize-submit-field) | 165 (defalias 'w3-speak-summarize-reset-field 'w3-speak-summarize-submit-field) |
164 (defalias 'w3-speak-summarize-button-field 'w3-speak-summarize-submit-field) | 166 (defalias 'w3-speak-summarize-button-field 'w3-speak-summarize-submit-field) |
165 | 167 |
166 (defun w3-speak-summarize-radio-field (data) | 168 (defun w3-speak-summarize-radio-field (data) |
167 "Summarize a radio field given the field data." | 169 "Summarize a radio field given the field data." |
168 (let ( | 170 (let ( |
173 (format "Radio button %s is %s" (or label name) (if checked | 175 (format "Radio button %s is %s" (or label name) (if checked |
174 "pressed" | 176 "pressed" |
175 "not pressed"))))) | 177 "not pressed"))))) |
176 | 178 |
177 ;;}}} | 179 ;;}}} |
178 ;;{{{ Associate summarizer functions for form fields | |
179 | |
180 (w3-speak-define-field-summarizer 'text) | |
181 (w3-speak-define-field-summarizer 'option) | |
182 (w3-speak-define-field-summarizer 'checkbox) | |
183 (w3-speak-define-field-summarizer 'reset) | |
184 (w3-speak-define-field-summarizer 'submit) | |
185 (w3-speak-define-field-summarizer 'button) | |
186 (w3-speak-define-field-summarizer 'radio) | |
187 (w3-speak-define-field-summarizer 'multiline) | |
188 (w3-speak-define-field-summarizer 'image) | |
189 (w3-speak-define-field-summarizer 'file) | |
190 | |
191 ;;}}} | |
192 | 180 |
193 ;;{{{ speaking form fields | 181 ;;{{{ speaking form fields |
194 | 182 |
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 183 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
196 ;;; Now for the guts | 184 ;;; Now for the guts |
197 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
198 (defun w3-speak-extract-form-field-information () | 186 (defun w3-speak-extract-form-field-information () |
204 "Summarizes field under point if any." | 192 "Summarizes field under point if any." |
205 (let* ((data (w3-speak-extract-form-field-information)) | 193 (let* ((data (w3-speak-extract-form-field-information)) |
206 (type (and data (w3-form-element-type data))) | 194 (type (and data (w3-form-element-type data))) |
207 (summarizer (and type (w3-speak-get-field-summarizer type)))) | 195 (summarizer (and type (w3-speak-get-field-summarizer type)))) |
208 (cond | 196 (cond |
209 ((and data summarizer (fboundp summarizer)) | 197 ((and data |
198 summarizer | |
199 (fboundp summarizer)) | |
210 (funcall summarizer data)) | 200 (funcall summarizer data)) |
211 (data | 201 (data |
212 (message "Please define a summarizer function for %s" type)) | 202 (message "Please define a summarizer function for %s" type)) |
213 (t nil)))) | 203 (t nil)))) |
214 | 204 |
215 ;;}}} | 205 ;;}}} |
216 | 206 |
217 ;;{{{ Movement notification | |
218 | |
219 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 207 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
220 ;;; Movement notification | 208 ;;; Movement notification |
221 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 209 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
222 (defadvice w3-scroll-up (after emacspeak pre act comp) | 210 (defadvice w3-scroll-up (after emacspeak pre act comp) |
223 "Provide auditory feedback" | 211 "Provide auditory feedback" |
224 (when (interactive-p) | 212 (when (interactive-p) |
225 (let ((start (point ))) | 213 (let ((start (point ))) |
226 (emacspeak-auditory-icon 'scroll) | 214 (emacspeak-auditory-icon 'scroll) |
227 (save-excursion | 215 (save-excursion |
228 (forward-line (window-height)) | 216 (forward-line (window-height)) |
229 (emacspeak-speak-region start (point )))))) | 217 (emacspeak-speak-region start (point )))))) |
230 | 218 |
231 (defadvice w3-follow-link (around emacspeak pre act) | 219 (defadvice w3-follow-link (around emacspeak pre act) |
232 "Provide feedback on what you did. " | 220 "Provide feedback on what you did. " |
233 (let ((data (w3-speak-extract-form-field-information)) | 221 (let ((data (emacspeak-w3-extract-form-field-information)) |
234 (form-field-p nil)) | 222 (form-field-p nil) |
223 (this-zone nil) | |
224 (opoint nil)) | |
235 (if data | 225 (if data |
236 (setq form-field-p t)) | 226 (setq form-field-p t |
227 opoint (point))) | |
237 ad-do-it | 228 ad-do-it |
238 (when form-field-p | 229 (when form-field-p |
239 (w3-speak-summarize-form-field) | 230 (w3-speak-summarize-form-field) |
240 (case (w3-form-element-type data) | 231 (case (w3-form-element-type data) |
241 ((radio checkbox button) | 232 ((radio checkbox) |
242 (emacspeak-auditory-icon 'button)) | 233 (emacspeak-auditory-icon 'button)) |
243 ((text textarea) | |
244 (emacspeak-auditory-icon 'close-object) | |
245 ;; fill in any others here | 234 ;; fill in any others here |
246 (otherwise | 235 (otherwise |
247 nil))) | 236 nil))) |
248 ad-return-value))) | 237 ad-return-value)) |
249 | 238 |
250 (defadvice w3-revert-form (after emacspeak pre act) | 239 (defadvice w3-revert-form (after emacspeak pre act) |
251 "Announce that you cleared the form. " | 240 "Announce that you cleared the form. " |
252 (dtk-speak "Cleared the form. ")) | 241 (dtk-speak "Cleared the form. ")) |
253 | 242 |
254 (defadvice w3-finish-text-entry (after emacspeak pre act ) | 243 (defadvice w3-finish-text-entry (after emacspeak pre act ) |
255 "Announce what the field was set to." | 244 "Announce what the field was set to." |
256 (when (interactive-p) | 245 (when (interactive-p) |
257 (w3-speak-summarize-form-field))) | 246 (w3-speak-summarize-form-field))) |
258 | 247 |
259 (defadvice widget-forward (after emacspeak pre act) | |
260 "Produce an auditory icon when moving forward. | |
261 If on a form field, then summarize it." | |
262 (declare (special emacspeak-lazy-message-time)) | |
263 (when (interactive-p) | |
264 (let ((emacspeak-lazy-message-time 0)) | |
265 (w3-speak-summarize-form-field) | |
266 (emacspeak-auditory-icon 'large-movement)))) | |
267 | |
268 (defadvice widget-backward (after emacspeak pre act) | |
269 "Produce an auditory icon when moving backward. | |
270 If on a form field, then summarize it." | |
271 (declare (special emacspeak-lazy-message-time)) | |
272 (when (interactive-p ) | |
273 (let ((emacspeak-lazy-message-time 0)) | |
274 (w3-speak-summarize-form-field) | |
275 (emacspeak-auditory-icon 'large-movement)))) | |
276 | |
277 (defadvice w3-start-of-document (after emacspeak pre act) | 248 (defadvice w3-start-of-document (after emacspeak pre act) |
278 "Produce an auditory icon. Also speak the first line. " | 249 "Produce an auditory icon. Also speak the first line. " |
279 (when (interactive-p) | 250 (when (interactive-p) |
280 (emacspeak-speak-line) | 251 (emacspeak-speak-line) |
281 (emacspeak-auditory-icon 'large-movement))) | 252 (emacspeak-auditory-icon 'large-movement))) |
282 | 253 |
283 (defadvice w3-end-of-document (after emacspeak pre act) | 254 (defadvice w3-end-of-document (after emacspeak pre act) |
284 "Produce an auditory icon. " | 255 "Produce an auditory icon. Also speak the first line." |
285 (when (interactive-p) | 256 (when (interactive-p) |
286 (emacspeak-speak-line) | 257 (emacspeak-speak-line) |
287 (emacspeak-auditory-icon 'large-movement))) | 258 (emacspeak-auditory-icon 'large-movement))) |
288 | 259 |
289 (defadvice w3-goto-last-buffer (after emacspeak pre act) | 260 (defadvice w3-goto-last-buffer (after emacspeak pre act) |
297 (when (interactive-p) | 268 (when (interactive-p) |
298 (emacspeak-auditory-icon 'close-object) | 269 (emacspeak-auditory-icon 'close-object) |
299 (emacspeak-speak-mode-line))) | 270 (emacspeak-speak-mode-line))) |
300 | 271 |
301 (defadvice w3-fetch (around emacspeak act comp ) | 272 (defadvice w3-fetch (around emacspeak act comp ) |
302 "First produce an auditory icon to indicate retrieval. After | 273 "First produce an auditory icon to indicate retrieval. |
303 retrieval, set voice-lock-mode to t after displaying the buffer, and | 274 After retrieval, |
304 then speak the mode-line. " | 275 set voice-lock-mode to t after displaying the buffer, |
276 and then speak the mode-line. " | |
305 (declare (special dtk-punctuation-mode)) | 277 (declare (special dtk-punctuation-mode)) |
306 (when (interactive-p) | 278 (emacspeak-auditory-icon 'select-object) |
307 (emacspeak-auditory-icon 'select-object) | 279 ad-do-it) |
308 ad-do-it | |
309 (set (make-local-variable 'voice-lock-mode) t) | |
310 (setq dtk-punctuation-mode "some") | |
311 (modify-syntax-entry 10 " ") | |
312 (emacspeak-auditory-icon 'open-object) | |
313 (emacspeak-speak-mode-line ))) | |
314 | |
315 ;;}}} | |
316 ;;{{{ top level | |
317 | 280 |
318 (defun w3-speak-mode-hook () | 281 (defun w3-speak-mode-hook () |
319 (set (make-local-variable 'voice-lock-mode) t) | 282 (set (make-local-variable 'voice-lock-mode) t) |
320 (setq dtk-punctuation-mode "some") | 283 (setq dtk-punctuation-mode "some") |
321 (emacspeak-auditory-icon 'open-object) | 284 (emacspeak-auditory-icon 'open-object) |
338 (remove-hook 'w3-mode-hook 'w3-speak-mode-hook)) | 301 (remove-hook 'w3-mode-hook 'w3-speak-mode-hook)) |
339 (setq w3-delimit-links nil | 302 (setq w3-delimit-links nil |
340 w3-delimit-emphasis nil) | 303 w3-delimit-emphasis nil) |
341 (add-hook 'w3-mode-hook 'w3-speak-mode-hook))) | 304 (add-hook 'w3-mode-hook 'w3-speak-mode-hook))) |
342 | 305 |
343 ;;}}} | 306 (defun w3-speak-browse-page () |
344 ;;{{{ make-local-hook | 307 "Browse a WWW page" |
345 | 308 (interactive) |
346 ;;; hope this is correct: | 309 (emacspeak-audio-annotate-paragraphs) |
347 (unless (fboundp 'make-local-hook) | 310 (emacspeak-execute-repeatedly 'forward-paragraph)) |
348 (defun make-local-hook (var) | 311 |
349 (make-variable-buffer-local var)) | 312 (declaim (special w3-mode-map)) |
350 ) | 313 (define-key w3-mode-map "." 'w3-speak-browse-page) |
351 | 314 |
352 ;;}}} | 315 (defvar url-speak-last-progress-indication 0 |
316 "Caches when we last produced a progress auditory icon") | |
317 | |
318 (defadvice url-lazy-message (around emacspeak pre act) | |
319 "Provide pleasant auditory feedback about progress" | |
320 (declare (special url-speak-last-progress-indication )) | |
321 (let ((now (nth 1 (current-time)))) | |
322 (when (> now | |
323 (+ 3 url-speak-last-progress-indication)) | |
324 (setq url-speak-last-progress-indication now) | |
325 (emacspeak-auditory-icon 'progress)))) | |
326 | |
353 (provide 'w3-speak) | 327 (provide 'w3-speak) |