comparison lisp/w3/css.el @ 14:9ee227acff29 r19-15b90

Import from CVS: tag r19-15b90
author cvs
date Mon, 13 Aug 2007 08:48:42 +0200
parents
children 0293115a14e9
comparison
equal deleted inserted replaced
13:13c6d0aaafe5 14:9ee227acff29
1 ;;; css.el -- Cascading Style Sheet parser
2 ;; Author: wmperry
3 ;; Created: 1996/12/26 16:49:58
4 ;; Version: 1.18
5 ;; Keywords:
6
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; Copyright (c) 1996 by William M. Perry (wmperry@cs.indiana.edu)
9 ;;; Copyright (c) 1996 Free Software Foundation, Inc.
10 ;;;
11 ;;; This file is not part of GNU Emacs, but the same permissions apply.
12 ;;;
13 ;;; 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
15 ;;; the Free Software Foundation; either version 2, or (at your option)
16 ;;; any later version.
17 ;;;
18 ;;; GNU Emacs is distributed in the hope that it will be useful,
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;;; GNU General Public License for more details.
22 ;;;
23 ;;; 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 the
25 ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;;; Boston, MA 02111-1307, USA.
27 ;;;
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
29
30 (eval-and-compile
31 (require 'cl)
32 (require 'font)
33 )
34
35 ;; CBI = Cant Be Implemented - due to limitations in emacs/xemacs
36 ;; NYI = Not Yet Implemented - due to limitations of space/time
37 ;; NYPI = Not Yet Partially Implemented - possible partial support, eventually
38
39 (defconst css-properties
40 '(;; Property name Inheritable? Type of data
41 [font-family nil string-list]
42 [font-style nil string]
43 [font-variant nil symbol-list]
44 [font-weight nil weight]
45 [font-size nil length]
46 [font nil font]
47 [color nil color]
48 [background nil color]
49 [word-spacing nil length] ; CBI
50 [letter-spacing nil length] ; CBI
51 [text-decoration nil symbol-list]
52 [vertical-align nil symbol] ; CBI
53 [text-transform nil string]
54 [text-align t symbol]
55 [text-indent t length] ; NYI
56 [line-height t length] ; CBI
57 [margin nil margin]
58 [margin-left nil margin]
59 [margin-right nil margin]
60 [margin-top nil margin]
61 [margin-bottom nil margin]
62 [padding nil padding]
63 [padding-left nil padding]
64 [padding-right nil padding]
65 [padding-top nil padding]
66 [padding-bottom nil padding]
67 [border nil border]
68 [border-left nil border]
69 [border-right nil border]
70 [border-top nil border]
71 [border-bottom nil border]
72 [width nil length] ; NYPI
73 [height nil length] ; NYPI
74 [float nil symbol]
75 [clear nil symbol]
76 [display nil symbol]
77 [list-style t symbol] ;!! can't specify 'inside|outside'
78 [white-space t symbol]
79
80 ;; These are for specifying speech properties
81 [voice-family t string]
82 [gain t integer]
83 [left-volume t integer]
84 [right-volume t integer]
85 [pitch t integer]
86 [pitch-range t integer]
87 [stress t integer]
88 [richness t integer]
89 )
90 "A description of the various CSS properties and how to interpret them.")
91
92 (mapcar
93 (lambda (entry)
94 (put (aref entry 0) 'css-inherit (aref entry 1))
95 (put (aref entry 0) 'css-type (aref entry 2)))
96 css-properties)
97
98 (defconst css-weights
99 '(nil ;never used
100 :extra-light
101 :light
102 :demi-light
103 :medium
104 :normal
105 :demi-bold
106 :bold
107 :extra-bold
108 )
109 "List of CSS font weights.")
110
111 (defvar css-syntax-table
112 (copy-syntax-table emacs-lisp-mode-syntax-table)
113 "The syntax table for parsing stylesheets")
114
115 (modify-syntax-entry ?' "\"" css-syntax-table)
116 (modify-syntax-entry ?` "\"" css-syntax-table)
117 (modify-syntax-entry ?{ "(" css-syntax-table)
118 (modify-syntax-entry ?} ")" css-syntax-table)
119
120 (eval-when-compile
121 (defvar css-scratch-val nil)
122 (defvar css-scratch-id nil)
123 (defvar css-scratch-class nil)
124 (defvar css-scratch-possibles nil)
125 (defvar css-scratch-current nil)
126 (defvar css-scratch-classes nil)
127 (defvar css-scratch-class-match nil)
128 (defvar css-scratch-current-rule nil)
129 (defvar css-scratch-current-value nil)
130 )
131
132 (defconst css-running-xemacs
133 (string-match "XEmacs" (emacs-version))
134 "Whether we are running in XEmacs or not.")
135
136 (defvar css-ie-compatibility t
137 "Whether we want to do Internet Explorer 3.0 compatible parsing of
138 CSS stylesheets.")
139
140 (defsubst css-replace-regexp (regexp to-string)
141 (goto-char (point-min))
142 (while (re-search-forward regexp nil t)
143 (replace-match to-string t nil)))
144
145 (defun css-contextual-match (rule stack)
146 (let ((ancestor)
147 (p-args)
148 (p-class)
149 (matched t))
150 (while rule
151 (setq ancestor (assq (caar rule) stack))
152 (if (not ancestor)
153 (setq rule nil
154 matched nil)
155 (setq p-args (cdr ancestor)
156 p-class (or (cdr-safe (assq 'class p-args)) t))
157 (if (not (equal p-class (cdar rule)))
158 (setq matched nil
159 rule nil)))
160 (setq rule (cdr rule)))
161 matched))
162
163 (defsubst css-get-internal (tag args)
164 (declare (special tag sheet element-stack default))
165 (setq css-scratch-id (or (cdr-safe (assq 'id args))
166 (cdr-safe (assq 'name args)))
167 css-scratch-class (or (cdr-safe (assq 'class args)) t)
168 css-scratch-possibles (cl-gethash tag sheet))
169 (while css-scratch-possibles
170 (setq css-scratch-current (car css-scratch-possibles)
171 css-scratch-current-rule (car css-scratch-current)
172 css-scratch-current-value (cdr css-scratch-current)
173 css-scratch-classes (if (listp (car css-scratch-current-rule))
174 (cdar css-scratch-current-rule)
175 (cdr css-scratch-current-rule))
176 css-scratch-class-match t
177 css-scratch-possibles (cdr css-scratch-possibles))
178 (if (eq t css-scratch-classes)
179 (setq css-scratch-classes nil))
180 (if (eq t css-scratch-class)
181 (setq css-scratch-class nil))
182 (while css-scratch-classes
183 (if (not (member (pop css-scratch-classes) css-scratch-class))
184 (setq css-scratch-class-match nil
185 css-scratch-classes nil)))
186 (cond
187 ((and (listp (car css-scratch-current-rule)) css-scratch-class-match)
188 ;; Contextual!
189 (setq css-scratch-current-rule (cdr css-scratch-current-rule))
190 (if (css-contextual-match css-scratch-current-rule element-stack)
191 (setq css-scratch-val
192 (append css-scratch-val css-scratch-current-value)))
193 )
194 (css-scratch-class-match
195 (setq css-scratch-val (append css-scratch-val css-scratch-current-value))
196 )
197 (t
198 nil))
199 )
200 )
201
202 (defsubst css-get (tag args &optional sheet element-stack)
203 (setq css-scratch-val nil
204 css-scratch-class (or (cdr-safe (assq 'class args)) t))
205
206 ;; check for things without the class
207 (if (listp css-scratch-class)
208 (css-get-internal tag nil))
209
210 ;; check for global class values
211 (css-get-internal '*document args)
212
213 ;; Now check for things with the class - they will be stuck on the front
214 ;; of the list, which will mean we do the right thing
215 (css-get-internal tag args)
216
217 ;; Defaults are up to the calling application to provide
218 css-scratch-val)
219
220 (defun css-ancestor-get (info ancestors sheet)
221 ;; Inheritable property, check ancestors
222 (let (cur)
223 (while ancestors
224 (setq cur (car ancestors)
225 css-scratch-val (css-get info (car cur) (cdr cur) sheet)
226 ancestors (if css-scratch-val nil (cdr ancestors)))))
227 css-scratch-val)
228
229 (defun css-split-selector (tag)
230 ;; Return a list
231 (cond
232 ((string-match " " tag) ; contextual
233 (let ((tags (split-string tag "[ \t]+"))
234 (result nil))
235 (while tags
236 (setq result (cons (css-split-selector (car tags)) result)
237 tags (cdr tags)))
238 result))
239 ((string-match "[:\\.]" tag)
240 (let ((tag (if (= (match-beginning 0) 0)
241 '*document
242 (intern (downcase (substring tag 0 (match-beginning 0))))))
243 (rest (substring tag (match-beginning 0) nil))
244 (classes nil))
245 (while (string-match "^[:\\.][^:\\.]+" rest)
246 (if (= ?. (aref rest 0))
247 (setq classes (cons (substring rest 1 (match-end 0)) classes))
248 (setq classes (cons (substring rest 0 (match-end 0)) classes)))
249 (setq rest (substring rest (match-end 0) nil)))
250 (setq classes (sort classes 'string-lessp))
251 (cons tag classes)))
252 ((string-match "^#" tag) ; id selector
253 (cons '*document tag))
254 (t
255 (cons (intern (downcase tag)) t)
256 )
257 )
258 )
259
260 (defun css-applies-to (st nd)
261 (let ((results nil)
262 (save-pos nil))
263 (narrow-to-region st nd)
264 (goto-char st)
265 (skip-chars-forward " \t\r\n")
266 (while (not (eobp))
267 (setq save-pos (point))
268 (skip-chars-forward "^,")
269 (skip-chars-backward " \r\t\n")
270 (setq results (cons (css-split-selector
271 (buffer-substring save-pos (point))) results))
272 (skip-chars-forward ", \t\r\n"))
273 (widen)
274 results))
275
276 (defun css-split-font-shorthand (font)
277 ;; [<font-weight> || <font-style>]? <font-size> [ / <line-height> ]? <font-family>
278 (let (weight size height family retval)
279 (if (not (string-match " *\\([0-9.]+[^ /]+\\)" font))
280 (error "Malformed font shorthand: %s" font))
281 (setq weight (if (/= 0 (match-beginning 0))
282 (substring font 0 (match-beginning 0)))
283 size (match-string 1 font)
284 font (substring font (match-end 0) nil))
285 (if (string-match " */ *\\([^ ]+\\) *" font)
286 ;; they specified a line-height as well
287 (setq height (match-string 1 font)
288 family (substring font (match-end 0) nil))
289 (if (string-match "^[ \t]+" font)
290 (setq family (substring font (match-end 0) nil))
291 (setq family font)))
292 (if weight (setq retval (cons (cons 'font-weight weight) retval)))
293 (if size (setq retval (cons (cons 'font-size size) retval)))
294 (if height (setq retval (cons (cons 'line-height height) retval)))
295 (if family (setq retval (cons (cons 'font-family family) retval)))
296 retval))
297
298 (defun css-expand-length (spec)
299 (cond
300 ((not (stringp spec)) spec)
301 ((string-equal spec "auto") nil)
302 ((string-match "\([0-9]+\)%" spec) ; A percentage
303 nil)
304 ((string-match "\([0-9]+\)e[mn]" spec) ; Character based
305 (string-to-int (substring spec (match-beginning 1) (match-end 1))))
306 (t
307 (truncate (font-spatial-to-canonical spec)))
308 )
309 )
310
311 (defsubst css-unhex-char (x)
312 (if (> x ?9)
313 (if (>= x ?a)
314 (+ 10 (- x ?a))
315 (+ 10 (- x ?A)))
316 (- x ?0)))
317
318 (defsubst css-pow (x n)
319 (apply '* (make-list n x)))
320
321 (defun css-unhex (x)
322 (let ((ord (length x))
323 (rval 0))
324 (while (> ord 0)
325 (setq rval (+ rval
326 (* (css-pow 16 (- (length x) ord))
327 (css-unhex-char (aref x (1- ord)))))
328 ord (1- ord)))
329 rval))
330
331 (defun css-expand-color (color)
332 (cond
333 ((string-match "^#" color)
334 (let (r g b)
335 (cond
336 ((string-match "^#...$" color)
337 ;; 3-char rgb spec, expand out to six chars by replicating
338 ;; digits, not adding zeros.
339 (setq r (css-unhex (make-string 2 (aref color 1)))
340 g (css-unhex (make-string 2 (aref color 2)))
341 b (css-unhex (make-string 2 (aref color 3)))))
342 ((string-match "^#\\(..\\)\\(..\\)\\(..\\)$" color)
343 (setq r (css-unhex (match-string 1 color))
344 g (css-unhex (match-string 2 color))
345 b (css-unhex (match-string 3 color))))
346 (t
347 (setq color (substring color 1))
348 (let* ((n (/ (length color) 3))
349 (max (float (css-pow 16 n))))
350 (setq r (css-unhex (substring color 0 n))
351 g (css-unhex (substring color n (* n 2)))
352 b (css-unhex (substring color (* n 2) (* n 3)))
353 r (round (* (/ r max) 255))
354 g (round (* (/ g max) 255))
355 b (round (* (/ b max) 255))))))
356 (setq color (vector 'rgb r g b))))
357 ((string-match "^rgb *( *\\([0-9]+\\)[, ]+\\([0-9]+\\)[, ]+\\([0-9]+\\) *) *$" color)
358 ;; rgb(r,g,b) 0 - 255, cutting off at 255
359 (setq color (vector
360 'rgb
361 (min (string-to-int (match-string 1 color)) 255)
362 (min (string-to-int (match-string 2 color)) 255)
363 (min (string-to-int (match-string 3 color)) 255))))
364 ((string-match "^rgb *( *\\([0-9]+\\) *%[, ]+\\([0-9]+\\) *%[, ]+\\([0-9]+\\) *% *) *$" color)
365 ;; rgb(r%,g%,b%) 0 - 100%, cutting off at 100%
366 (let ((r (min (string-to-number (match-string 1 color)) 100.0))
367 (g (min (string-to-number (match-string 2 color)) 100.0))
368 (b (min (string-to-number (match-string 3 color)) 100.0)))
369 (setq r (round (* r 2.55))
370 g (round (* g 2.55))
371 b (round (* b 2.55))
372 color (vector 'rgb r g b))))
373 ((string-match "url *(\\([^ )]+\\) *)" color)
374 ;; A picture in the background
375 (let ((pixmap (match-string 1 color))
376 (attributes nil))
377 (setq color (concat (substring color 0 (match-beginning 0))
378 (substring color (match-end 0) nil))
379 attributes (split-string color " "))
380 )
381 )
382 (t
383 ;; Hmmm... pass it through unmangled and hope the underlying
384 ;; windowing system can handle it.
385 )
386 )
387 color
388 )
389
390 (defun css-expand-value (type value)
391 (case type
392 ((symbol integer) ; Read it in
393 (setq value (read (downcase value))))
394 (symbol-list
395 (setq value (downcase value)
396 value (split-string value "[ ,]+")
397 value (mapcar 'intern value)))
398 (string-list
399 (setq value (split-string value " *, *")))
400 (color ; A color, possibly with URLs
401 (setq value (css-expand-color value)))
402 (length ; Pixels, picas, ems, etc.
403 (setq value (css-expand-length value)))
404 (font ; Font shorthand
405 (setq value (css-split-font-shorthand value)))
406 ((margin padding) ; length|percentage|auto {1,4}
407 (setq value (split-string value "[ ,]+"))
408 (if (/= 1 (length value))
409 ;; More than one value - a shortcut
410 (let* ((top (intern (format "%s-top" type)))
411 (bottom (intern (format "%s-bottom" type)))
412 (left (intern (format "%s-left" type)))
413 (right (intern (format "%s-right" type))))
414 (setq top (cons top (css-expand-length (nth 0 value)))
415 right (cons right (css-expand-length (nth 1 value)))
416 bottom (cons bottom (css-expand-length (nth 2 value)))
417 left (cons left (css-expand-length (nth 3 value)))
418 value (list top right bottom left)))
419 (setq value (css-expand-length (car value)))))
420 (border
421 (cond
422 ((member (downcase value) '("none" "dotted" "dashed" "solid"
423 "double" "groove" "ridge" "inset" "outset"))
424 (setq value (intern (downcase value))))
425 ((string-match "^[0-9]+" value)
426 (setq value (font-spatial-to-canonical value)))
427 (t nil)))
428 (weight ; normal|bold|bolder|lighter|[1-9]00
429 (if (string-match "^[0-9]+" value)
430 (setq value (/ (read value) 100)
431 value (or (nth value css-weights) :bold))
432 (setq value (intern (downcase (concat ":" value))))))
433 (otherwise ; Leave it as is
434 t)
435 )
436 value
437 )
438
439 (defun css-parse-args (st &optional nd)
440 ;; Return an assoc list of attribute/value pairs from a CSS style entry
441 (let (
442 name ; From name=
443 value ; its value
444 results ; Assoc list of results
445 name-pos ; Start of XXXX= position
446 val-pos ; Start of value position
447 )
448 (save-excursion
449 (if (stringp st)
450 (progn
451 (set-buffer (get-buffer-create " *css-style-temp*"))
452 (set-syntax-table css-syntax-table)
453 (erase-buffer)
454 (insert st)
455 (setq st (point-min)
456 nd (point-max)))
457 (set-syntax-table css-syntax-table))
458 (save-restriction
459 (narrow-to-region st nd)
460 (goto-char (point-min))
461 (while (not (eobp))
462 (skip-chars-forward ";, \n\t")
463 (setq name-pos (point))
464 (skip-chars-forward "^ \n\t:=,;")
465 (downcase-region name-pos (point))
466 (setq name (intern (buffer-substring name-pos (point))))
467 (skip-chars-forward " \t\n")
468 (if (not (eq (char-after (point)) ?:)) ; There is no value
469 (setq value nil)
470 (skip-chars-forward " \t\n:")
471 (setq val-pos (point)
472 value
473 (cond
474 ((or (= (or (char-after val-pos) 0) ?\")
475 (= (or (char-after val-pos) 0) ?'))
476 (buffer-substring (1+ val-pos)
477 (condition-case ()
478 (prog2
479 (forward-sexp 1)
480 (1- (point))
481 (skip-chars-forward "\""))
482 (error
483 (skip-chars-forward "^ \t\n")
484 (point)))))
485 (t
486 (buffer-substring val-pos
487 (progn
488 (if css-ie-compatibility
489 (skip-chars-forward "^;")
490 (skip-chars-forward "^,;"))
491 (skip-chars-backward " \t")
492 (point)))))))
493 (setq value (css-expand-value (get name 'css-type) value))
494 (if (eq (get name 'css-type) 'font)
495 (setq results (append value results))
496 (setq results (cons (cons name value) results)))
497 (skip-chars-forward ";, \n\t"))
498 results))))
499
500 (defun css-handle-import ()
501 (let ((url nil)
502 (save-pos (point)))
503 (if (looking-at "'\"")
504 (condition-case ()
505 (forward-sexp 1)
506 (error (skip-chars-forward "^ \t\r\n;")))
507 (skip-chars-forward "^ \t\r\n;"))
508 (setq url (url-expand-file-name (buffer-substring save-pos (point))))
509 (skip-chars-forward "\"; \t\r\n")
510 (setq save-pos (point))
511 (let ((url-working-buffer (generate-new-buffer-name " *styleimport*"))
512 (url-mime-accept-string
513 "text/css ; level=2")
514 (sheet nil))
515 (save-excursion
516 (set-buffer (get-buffer-create url-working-buffer))
517 (setq url-be-asynchronous nil)
518 (url-retrieve url)
519 (css-clean-buffer)
520 (setq sheet (buffer-string))
521 (set-buffer-modified-p nil)
522 (kill-buffer (current-buffer)))
523 (insert sheet)
524 (goto-char save-pos))))
525
526 (defun css-clean-buffer ()
527 ;; Nuke comments, etc.
528 (goto-char (point-min))
529 (let ((save-pos nil))
530 (while (search-forward "/*" nil t)
531 (setq save-pos (- (point) 2))
532 (delete-region save-pos
533 (if (search-forward "*/" nil t)
534 (point)
535 (end-of-line)
536 (point)))))
537 (goto-char (point-min))
538 (delete-matching-lines "^[ \t\r]*$") ; Nuke blank lines
539 (css-replace-regexp "^[ \t\r]+" "") ; Nuke whitespace at beg. of line
540 (css-replace-regexp "[ \t\r]+$" "") ; Nuke whitespace at end of line
541 (goto-char (point-min)))
542
543 (defun css-active-device-types (&optional device)
544 (let ((types (list 'normal 'default (if css-running-xemacs 'xemacs 'emacs)))
545 (type (device-type device)))
546 (cond
547 ((featurep 'emacspeak)
548 (setq types (cons 'speech types)))
549 ((eq type 'tty)
550 (if (and (fboundp 'tty-color-list)
551 (/= 0 (length (tty-color-list))))
552 (setq types (cons 'ansi-tty types))
553 (setq types (cons 'tty types))))
554 ((eq 'color (device-class))
555 (if (not (device-bitplanes))
556 (setq types (cons 'color types))
557 (setq types
558 (append
559 (list (intern (format "%dbit-color"
560 (device-bitplanes)))
561 (intern (format "%dbit"
562 (device-bitplanes)))
563 'color) types))
564 (if (= 24 (device-bitplanes))
565 (setq types (cons 'truecolor types)))))
566 ((eq 'grayscale (device-class))
567 (setq types (append (list (intern (format "%dbit-grayscale"
568 (device-bitplanes)))
569 'grayscale)
570 types)))
571 ((eq 'mono (device-class))
572 (setq types (append (list 'mono 'monochrome) types)))
573 (t
574 (setq types (cons 'unknown types))))
575 types))
576
577 (defmacro css-rule-specificity-internal (rule)
578 (`
579 (progn
580 (setq tmp (cdr (, rule)))
581 (if (listp tmp)
582 (while tmp
583 (if (= ?# (aref (car tmp) 0))
584 (incf a)
585 (incf b))
586 (setq tmp (cdr tmp)))))))
587
588 (defsubst css-specificity (rule)
589 ;; To find specificity, according to the september 1996 CSS draft
590 ;; a = # of ID attributes in the selector
591 ;; b = # of class attributes in the selector
592 ;; c = # of tag names in the selector
593 (let ((a 0) (b 0) (c 0) cur tmp)
594 (if (not (listp (car rule)))
595 (css-rule-specificity-internal rule)
596 (setq c (length rule))
597 (while rule
598 (css-rule-specificity-internal (pop rule))))
599 (+ (* 100 a) (* 10 b) c)
600 )
601 )
602
603 (defun css-copy-stylesheet (sheet)
604 (let ((new (make-hash-table :size (hash-table-count sheet))))
605 (cl-maphash
606 (function
607 (lambda (k v)
608 (cl-puthash k (copy-tree v) new))) sheet)
609 new))
610
611 (defsubst css-store-rule (attrs applies-to)
612 (declare (special sheet))
613 (let (rules cur tag node)
614 (while applies-to
615 (setq cur (pop applies-to)
616 tag (car cur))
617 (if (listp tag)
618 (setq tag (car tag)))
619 (setq rules (cl-gethash tag sheet))
620 (cond
621 ((null rules)
622 ;; First rule for this tag. Create new ruleset
623 (cl-puthash tag (list (cons cur attrs)) sheet))
624 ((setq node (assoc cur rules))
625 ;; Similar rule already exists, splice in our information
626 (setcdr node (append attrs (cdr node))))
627 (t
628 ;; First rule for this particular combination of tag/ancestors/class.
629 ;; Slap it onto the existing set of rules and push back into sheet.
630 (setq rules (cons (cons cur attrs) rules))
631 (cl-puthash tag rules sheet))
632 )
633 )
634 )
635 )
636
637 (defun css-parse (fname &optional string inherit)
638 (let (
639 (url-mime-accept-string
640 "text/css ; level=2")
641 (save-pos nil)
642 (applies-to nil) ; List of tags to apply style to
643 (attrs nil) ; List of name/value pairs
644 (att nil)
645 (cur nil)
646 (val nil)
647 (device-type nil)
648 (active-device-types (css-active-device-types (selected-device)))
649 (sheet inherit))
650 (if (not sheet)
651 (setq sheet (make-hash-table :size 13 :test 'eq)))
652 (save-excursion
653 (set-buffer (get-buffer-create
654 (generate-new-buffer-name " *style*")))
655 (set-syntax-table css-syntax-table)
656 (erase-buffer)
657 (if fname (url-insert-file-contents fname))
658 (goto-char (point-max))
659 (if string (insert string))
660 (css-clean-buffer)
661 (goto-char (point-min))
662 (while (not (eobp))
663 (setq save-pos (point))
664 (cond
665 ;; *sigh* SGML comments are being used to 'hide' data inlined
666 ;; with the <style> tag from older browsers.
667 ((or (looking-at "<!--+") ; begin
668 (looking-at "--+>")) ; end
669 (goto-char (match-end 0)))
670 ;; C++ style comments, and we are doing IE compatibility
671 ((and (looking-at "//") css-ie-compatibility)
672 (end-of-line))
673 ;; Pre-Processor directives
674 ((looking-at "[ \t\r]*@\\([^ \t\r\n]\\)")
675 (let ((directive nil))
676 (skip-chars-forward " @\t\r") ; Past any leading whitespace
677 (setq save-pos (point))
678 (skip-chars-forward "^ \t\r\n") ; Past the @ directive
679 (downcase-region save-pos (point))
680 (setq directive (buffer-substring save-pos (point)))
681 (skip-chars-forward " \t\r") ; Past any trailing whitespace
682 (setq save-pos (point))
683 (cond
684 ((string= directive "import")
685 (css-handle-import))
686 (t
687 (message "Unknown directive in stylesheet: @%s" directive)))))
688 ;; Giving us some output device information
689 ((looking-at "[ \t\r]*:\\([^: \n]+\\):")
690 (downcase-region (match-beginning 1) (match-end 1))
691 (setq device-type (intern (buffer-substring (match-beginning 1)
692 (match-end 1))))
693 (goto-char (match-end 0))
694 (if (not (memq device-type active-device-types))
695 ;; Not applicable to us... skip the info
696 (progn
697 (if (re-search-forward ":[^:{ ]*:" nil t)
698 (goto-char (match-beginning 0))
699 (goto-char (point-max))))))
700 ;; Default is to treat it like a stylesheet declaration
701 (t
702 (skip-chars-forward "^{")
703 ;;(downcase-region save-pos (point))
704 (setq applies-to (css-applies-to save-pos (point)))
705 (skip-chars-forward "^{")
706 (setq save-pos (point))
707 (condition-case ()
708 (forward-sexp 1)
709 (error (goto-char (point-max))))
710 (end-of-line)
711 (skip-chars-backward "\r}")
712 (subst-char-in-region save-pos (point) ?\n ? )
713 (subst-char-in-region save-pos (point) ?\r ? )
714 ;; This is for not choking on garbage at the end of the buffer.
715 ;; I get bit by this every once in a while when going through my
716 ;; socks gateway.
717 (if (eobp)
718 nil
719 (setq attrs (css-parse-args (1+ save-pos) (point)))
720 (skip-chars-forward "}\r\n")
721 (css-store-rule attrs applies-to))
722 )
723 )
724 (skip-chars-forward " \t\r\n"))
725 (set-buffer-modified-p nil)
726 (kill-buffer (current-buffer)))
727 sheet)
728 )
729
730 ;; Tools for pretty-printing an existing stylesheet.
731 (defun css-rule-name (rule)
732 (cond
733 ((listp (car rule)) ; Contextual
734 (mapconcat 'css-rule-name
735 (reverse rule) " "))
736 ((listp (cdr rule)) ; More than one class
737 (let ((classes (cdr rule))
738 (rval (symbol-name (car rule))))
739 (while classes
740 (setq rval (concat rval
741 (if (= (aref (car classes) 0) ?:)
742 (pop classes)
743 (concat "." (pop classes))))))
744 rval))
745 (t
746 (symbol-name (car rule)))))
747
748 (defun css-display (sheet)
749 (with-output-to-temp-buffer "CSS Stylesheet"
750 (set-buffer standard-output)
751 (indented-text-mode)
752 (insert "# Stylesheet auto-regenerated by css.el\n#\n"
753 "# This is a mixture of the default stylesheet and any\n"
754 "# styles specified by the document. The rules are in no\n"
755 "# particular order.\n\n")
756 (let (tmp cur goal-col)
757 (cl-maphash
758 (function
759 (lambda (k v)
760 (while v
761 (setq cur (pop v))
762 (insert (css-rule-name (car cur)))
763 (insert " { ")
764 (setq goal-col (point))
765 (insert "\n")
766 ;; Display the rules
767 (setq tmp (cdr cur))
768 (let (prop val)
769 (while tmp
770 (setq prop (caar tmp)
771 val (cdar tmp)
772 tmp (cdr tmp))
773 (case (get prop 'css-type)
774 (symbol-list
775 (setq val (mapconcat 'symbol-name val ",")))
776 (weight
777 (setq val (substring (symbol-name val) 1 nil)))
778 (otherwise
779 nil)
780 )
781 (insert (format " %s: %s;\n" prop val))))
782 (insert "}\n\n");
783 )))
784 sheet))))
785
786 (provide 'css)