Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / cssutils / stylesheets / medialist.py @ 475

History | View | Annotate | Download (9.37 KB)

1
"""MediaList implements DOM Level 2 Style Sheets MediaList.
2

3
TODO:
4
    - delete: maybe if deleting from all, replace *all* with all others?
5
    - is unknown media an exception?
6
"""
7
__all__ = ['MediaList']
8
__docformat__ = 'restructuredtext'
9
__version__ = '$Id$'
10

    
11
from cssutils.prodparser import *
12
from cssutils.helper import normalize, pushtoken
13
from cssutils.css import csscomment
14
from mediaquery import MediaQuery
15
import cssutils
16
import xml.dom
17

    
18
#class MediaList(cssutils.util.Base, cssutils.util.ListSeq):
19
class MediaList(cssutils.util._NewListBase): 
20
    """Provides the abstraction of an ordered collection of media,
21
    without defining or constraining how this collection is
22
    implemented.
23

24
    A single media in the list is an instance of :class:`MediaQuery`. 
25
    An empty list is the same as a list that contains the medium "all".
26

27
    New format with :class:`MediaQuery`::
28

29
        : S* [media_query [ ',' S* media_query ]* ]?
30

31

32
    """
33
    def __init__(self, mediaText=None, parentRule=None, readonly=False):
34
        """
35
        :param mediaText:
36
            Unicodestring of parsable comma separared media
37
            or a (Python) list of media.
38
        :param parentRule:
39
            CSSRule this medialist is used in, e.g. an @import or @media.
40
        :param readonly:
41
            Not used yet.
42
        """
43
        super(MediaList, self).__init__()
44
        self._wellformed = False
45

    
46
        if isinstance(mediaText, list):
47
            mediaText = u','.join(mediaText)
48

    
49
        self._parentRule = parentRule
50
        
51
        if mediaText:
52
            self.mediaText = mediaText
53
            
54
        self._readonly = readonly
55

    
56
    def __repr__(self):
57
        return "cssutils.stylesheets.%s(mediaText=%r)" % (self.__class__.__name__, self.mediaText)
58

    
59
    def __str__(self):
60
        return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (self.__class__.__name__, self.mediaText, id(self))
61

    
62

    
63
    def __iter__(self):
64
        for item in self._seq:
65
            if item.type == 'MediaQuery':
66
                yield item
67

    
68
    length = property(lambda self: len(list(self)),
69
        doc="The number of media in the list (DOM readonly).")
70

    
71

    
72
    def _getMediaText(self):
73
        return cssutils.ser.do_stylesheets_medialist(self)
74

    
75
    def _setMediaText(self, mediaText):
76
        """
77
        :param mediaText:
78
            simple value or comma-separated list of media
79

80
        :exceptions:
81
            - - :exc:`~xml.dom.SyntaxErr`:
82
              Raised if the specified string value has a syntax error and is
83
              unparsable.
84
            - - :exc:`~xml.dom.NoModificationAllowedErr`:
85
              Raised if this media list is readonly.
86
        """
87
        self._checkReadonly()
88

    
89

    
90
        mediaquery = lambda: Prod(name='MediaQueryStart',
91
                                  match=lambda t, v: t == 'IDENT' or v == u'(',
92
                                  toSeq=lambda t, tokens: ('MediaQuery', 
93
                                                           MediaQuery(pushtoken(t, tokens),
94
                                                                      _partof=True))
95
                                  )
96
        prods = Sequence(Sequence(PreDef.comment(parent=self),
97
                                  minmax=lambda: (0, None)
98
                                  ),
99
                         mediaquery(),
100
                         Sequence(PreDef.comma(toSeq=False),
101
                                  mediaquery(),
102
                                  minmax=lambda: (0, None))
103

    
104
                         )
105
        # parse
106
        ok, seq, store, unused = ProdParser().parse(mediaText,
107
                                                    u'MediaList',
108
                                                    prods, debug="ml")
109

    
110
        # each mq must be valid
111
        atleastone = False
112

    
113
        for item in seq:
114
            v = item.value
115
            if isinstance(v, MediaQuery):
116
               if not v.wellformed:
117
                   ok = False
118
                   break
119
               else:
120
                   atleastone = True
121

    
122
        # must be at least one value!
123
        if not atleastone:
124
            ok = False
125
            self._wellformed = ok
126
            self._log.error(u'MediaQuery: No content.',
127
                            error=xml.dom.SyntaxErr)
128

    
129
        self._wellformed = ok
130

    
131
        if ok: 
132
            mediaTypes = []
133
            finalseq = cssutils.util.Seq(readonly=False)
134
            commentseqonly = cssutils.util.Seq(readonly=False)
135
            for item in seq:
136
                # filter for doubles?
137
                if item.type == 'MediaQuery':
138
                    mediaType = item.value.mediaType
139
                    if mediaType:
140
                        if mediaType == u'all':
141
                            # remove anthing else and keep all+comments(!) only
142
                            finalseq = commentseqonly
143
                            finalseq.append(item)
144
                            break
145
                        elif mediaType in mediaTypes:
146
                            continue
147
                        else:
148
                            mediaTypes.append(mediaType)
149
                elif isinstance(item.value, cssutils.css.csscomment.CSSComment):
150
                    commentseqonly.append(item) 
151
                
152
                finalseq.append(item)
153

    
154
            self._setSeq(finalseq)
155

    
156
    mediaText = property(_getMediaText, _setMediaText,
157
        doc="The parsable textual representation of the media list.")
158

    
159
    def __prepareset(self, newMedium):
160
        # used by appendSelector and __setitem__
161
        self._checkReadonly()
162

    
163
        if not isinstance(newMedium, MediaQuery):
164
            newMedium = MediaQuery(newMedium)
165

    
166
        if newMedium.wellformed:
167
            return newMedium
168

    
169
    def __setitem__(self, index, newMedium):
170
        """Overwriting ListSeq.__setitem__
171

172
        Any duplicate items are **not yet** removed.
173
        """
174
        # TODO: remove duplicates?
175
        newMedium = self.__prepareset(newMedium)
176
        if newMedium:
177
            self._seq[index] = (newMedium, 'MediaQuery', None, None)
178

    
179
    def appendMedium(self, newMedium):
180
        """Add the `newMedium` to the end of the list. 
181
        If the `newMedium` is already used, it is first removed.
182
        
183
        :param newMedium:
184
            a string or a :class:`~cssutils.stylesheets.MediaQuery`
185
        :returns: Wellformedness of `newMedium`.
186
        :exceptions:
187
            - :exc:`~xml.dom.InvalidCharacterErr`:
188
              If the medium contains characters that are invalid in the
189
              underlying style language.
190
            - :exc:`~xml.dom.InvalidModificationErr`:
191
              If mediaText is "all" and a new medium is tried to be added.
192
              Exception is "handheld" which is set in any case (Opera does handle
193
              "all, handheld" special, this special case might be removed in the
194
              future).
195
            - :exc:`~xml.dom.NoModificationAllowedErr`:
196
              Raised if this list is readonly.
197
        """
198
        newMedium = self.__prepareset(newMedium)
199

    
200
        if newMedium:
201
            mts = [normalize(item.value.mediaType) for item in self]
202
            newmt = normalize(newMedium.mediaType)
203

    
204
            self._seq._readonly = False
205

    
206
            if u'all' in mts:
207
                self._log.info(u'MediaList: Ignoring new medium %r as already specified "all" (set ``mediaText`` instead).' % newMedium, error=xml.dom.InvalidModificationErr)
208
            
209
            elif newmt and newmt in mts:
210
                # might be empty
211
                self.deleteMedium(newmt)
212
                self._seq.append(newMedium, 'MediaQuery')
213

    
214
            else:
215
                if u'all' == newmt:
216
                    self._clearSeq()
217

    
218
                self._seq.append(newMedium, 'MediaQuery')
219

    
220
            self._seq._readonly = True
221

    
222
            return True
223

    
224
        else:
225
            return False
226

    
227
    def append(self, newMedium):
228
        "Same as :meth:`appendMedium`."
229
        self.appendMedium(newMedium)
230

    
231
    def deleteMedium(self, oldMedium):
232
        """Delete a medium from the list.
233

234
        :param oldMedium:
235
            delete this medium from the list.
236
        :exceptions:
237
            - :exc:`~xml.dom.NotFoundErr`:
238
              Raised if `oldMedium` is not in the list.
239
            - :exc:`~xml.dom.NoModificationAllowedErr`:
240
              Raised if this list is readonly.
241
        """
242
        self._checkReadonly()
243
        oldMedium = normalize(oldMedium)
244

    
245
        for i, mq in enumerate(self):
246
            if normalize(mq.value.mediaType) == oldMedium:
247
                del self[i]
248
                break
249
        else:
250
            self._log.error(u'"%s" not in this MediaList' % oldMedium,
251
                            error=xml.dom.NotFoundErr)
252

    
253
    def item(self, index):
254
        """Return the mediaType of the `index`'th element in the list.
255
        If `index` is greater than or equal to the number of media in the
256
        list, returns ``None``.
257
        """
258
        try:
259
            return self[index].mediaType
260
        except IndexError:
261
            return None
262

    
263
    parentRule = property(lambda self: self._parentRule,
264
                          doc=u"The CSSRule (e.g. an @media or @import rule "
265
                              u"this list is part of or None")
266
    
267
    wellformed = property(lambda self: self._wellformed)