1 /*!
  2  * wef.cssParser
  3  * Copyright (c) 2011 Pablo Escalada
  4  * MIT Licensed
  5  *
  6  * Uses a lightly modified version of JSCSSP
  7  * by Daniel Glazman <daniel.glazman@disruptive-innovations.com>
  8  * licensed under MPL 1.1/GPL 2.0/LGPL 2.1
  9  *
 10  * Changelog:
 11  * - Add module pattern to jscsp
 12  * - Change const declarations to var for IE9 compatibility
 13  */
 14 (function (wef) {
 15     /**#nocode+*/
 16 
 17     /* ***** BEGIN LICENSE BLOCK *****
 18      * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 19      *
 20      * The contents of this file are subject to the Mozilla Public License Version
 21      * 1.1 (the "License"); you may not use this file except in compliance with
 22      * the License. You may obtain a copy of the License at
 23      * http://www.mozilla.org/MPL/
 24      *
 25      * Software distributed under the License is distributed on an "AS IS" basis,
 26      * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 27      * for the specific language governing rights and limitations under the
 28      * License.
 29      *
 30      * The Original Code is mozilla.org code.
 31      *
 32      * The Initial Developer of the Original Code is
 33      * Netscape Communications Corporation.
 34      * Portions created by the Initial Developer are Copyright (C) 1998
 35      * the Initial Developer. All Rights Reserved.
 36      *
 37      * Contributor(s):
 38      *   emk <VYV03354@nifty.ne.jp>
 39      *   Daniel Glazman <glazman@netscape.com>
 40      *   L. David Baron <dbaron@dbaron.org>
 41      *   Boris Zbarsky <bzbarsky@mit.edu>
 42      *   Mats Palmgren <mats.palmgren@bredband.net>
 43      *   Christian Biesinger <cbiesinger@web.de>
 44      *   Jeff Walden <jwalden+code@mit.edu>
 45      *   Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>, Collabora Ltd.
 46      *   Siraj Razick <siraj.razick@collabora.co.uk>, Collabora Ltd.
 47      *   Daniel Glazman <daniel.glazman@disruptive-innovations.com>
 48      *
 49      * Alternatively, the contents of this file may be used under the terms of
 50      * either of the GNU General Public License Version 2 or later (the "GPL"),
 51      * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 52      * in which case the provisions of the GPL or the LGPL are applicable instead
 53      * of those above. If you wish to allow use of your version of this file only
 54      * under the terms of either the GPL or the LGPL, and not to allow others to
 55      * use your version of this file under the terms of the MPL, indicate your
 56      * decision by deleting the provisions above and replace them with the notice
 57      * and other provisions required by the GPL or the LGPL. If you do not delete
 58      * the provisions above, a recipient may use your version of this file under
 59      * the terms of any one of the MPL, the GPL or the LGPL.
 60      *
 61      * ***** END LICENSE BLOCK ***** */
 62 
 63     /* FROM http://peter.sh/data/vendor-prefixed-css.php?js=1 */
 64 
 65     var kENGINES = [
 66         "webkit", "presto", "trident", "generic"
 67     ];
 68 
 69     var kCSS_VENDOR_VALUES = {
 70         "-moz-box":{"webkit":"-webkit-box", "presto":"", "trident":"", "generic":"box" },
 71         "-moz-inline-box":{"webkit":"-webkit-inline-box", "presto":"", "trident":"", "generic":"inline-box" },
 72         "-moz-initial":{"webkit":"", "presto":"", "trident":"", "generic":"initial" },
 73         "-moz-linear-gradient":{"webkit20110101":FilterLinearGradientForOutput,
 74             "webkit":FilterLinearGradientForOutput,
 75             "presto":"",
 76             "trident":"",
 77             "generic":FilterLinearGradientForOutput },
 78         "-moz-radial-gradient":{"webkit20110101":FilterRadialGradientForOutput,
 79             "webkit":FilterRadialGradientForOutput,
 80             "presto":"",
 81             "trident":"",
 82             "generic":FilterRadialGradientForOutput },
 83         "-moz-repeating-linear-gradient":{"webkit20110101":"",
 84             "webkit":FilterRepeatingGradientForOutput,
 85             "presto":"",
 86             "trident":"",
 87             "generic":FilterRepeatingGradientForOutput },
 88         "-moz-repeating-radial-gradient":{"webkit20110101":"",
 89             "webkit":FilterRepeatingGradientForOutput,
 90             "presto":"",
 91             "trident":"",
 92             "generic":FilterRepeatingGradientForOutput }
 93     };
 94 
 95     var kCSS_VENDOR_PREFIXES = {"lastUpdate":1304175007, "properties":[
 96         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-accelerator", "status":"P"},
 97         {"gecko":"", "webkit":"", "presto":"-wap-accesskey", "trident":"", "status":""},
 98         {"gecko":"-moz-animation", "webkit":"-webkit-animation", "presto":"", "trident":"", "status":"WD"},
 99         {"gecko":"-moz-animation-delay", "webkit":"-webkit-animation-delay", "presto":"", "trident":"", "status":"WD"},
100         {"gecko":"-moz-animation-direction", "webkit":"-webkit-animation-direction", "presto":"", "trident":"", "status":"WD"},
101         {"gecko":"-moz-animation-duration", "webkit":"-webkit-animation-duration", "presto":"", "trident":"", "status":"WD"},
102         {"gecko":"-moz-animation-fill-mode", "webkit":"-webkit-animation-fill-mode", "presto":"", "trident":"", "status":"ED"},
103         {"gecko":"-moz-animation-iteration-count", "webkit":"-webkit-animation-iteration-count", "presto":"", "trident":"", "status":"WD"},
104         {"gecko":"-moz-animation-name", "webkit":"-webkit-animation-name", "presto":"", "trident":"", "status":"WD"},
105         {"gecko":"-moz-animation-play-state", "webkit":"-webkit-animation-play-state", "presto":"", "trident":"", "status":"WD"},
106         {"gecko":"-moz-animation-timing-function", "webkit":"-webkit-animation-timing-function", "presto":"", "trident":"", "status":"WD"},
107         {"gecko":"-moz-appearance", "webkit":"-webkit-appearance", "presto":"", "trident":"", "status":"CR"},
108         {"gecko":"", "webkit":"-webkit-backface-visibility", "presto":"", "trident":"", "status":"WD"},
109         {"gecko":"background-clip", "webkit":"-webkit-background-clip", "presto":"background-clip", "trident":"background-clip", "status":"WD"},
110         {"gecko":"", "webkit":"-webkit-background-composite", "presto":"", "trident":"", "status":""},
111         {"gecko":"-moz-background-inline-policy", "webkit":"", "presto":"", "trident":"", "status":"P"},
112         {"gecko":"background-origin", "webkit":"-webkit-background-origin", "presto":"background-origin", "trident":"background-origin", "status":"WD"},
113         {"gecko":"", "webkit":"background-position-x", "presto":"", "trident":"-ms-background-position-x", "status":""},
114         {"gecko":"", "webkit":"background-position-y", "presto":"", "trident":"-ms-background-position-y", "status":""},
115         {"gecko":"background-size", "webkit":"-webkit-background-size", "presto":"background-size", "trident":"background-size", "status":"WD"},
116         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-behavior", "status":""},
117         {"gecko":"-moz-binding", "webkit":"", "presto":"", "trident":"", "status":"P"},
118         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-block-progression", "status":""},
119         {"gecko":"", "webkit":"-webkit-border-after", "presto":"", "trident":"", "status":"ED"},
120         {"gecko":"", "webkit":"-webkit-border-after-color", "presto":"", "trident":"", "status":"ED"},
121         {"gecko":"", "webkit":"-webkit-border-after-style", "presto":"", "trident":"", "status":"ED"},
122         {"gecko":"", "webkit":"-webkit-border-after-width", "presto":"", "trident":"", "status":"ED"},
123         {"gecko":"", "webkit":"-webkit-border-before", "presto":"", "trident":"", "status":"ED"},
124         {"gecko":"", "webkit":"-webkit-border-before-color", "presto":"", "trident":"", "status":"ED"},
125         {"gecko":"", "webkit":"-webkit-border-before-style", "presto":"", "trident":"", "status":"ED"},
126         {"gecko":"", "webkit":"-webkit-border-before-width", "presto":"", "trident":"", "status":"ED"},
127         {"gecko":"-moz-border-bottom-colors", "webkit":"", "presto":"", "trident":"", "status":"P"},
128         {"gecko":"border-bottom-left-radius", "webkit":"-webkit-border-bottom-left-radius", "presto":"border-bottom-left-radius", "trident":"border-bottom-left-radius", "status":"WD"},
129         {"gecko":"", "webkit":"-webkit-border-bottom-left-radius = border-bottom-left-radius", "presto":"", "trident":"", "status":""},
130         {"gecko":"border-bottom-right-radius", "webkit":"-webkit-border-bottom-right-radius", "presto":"border-bottom-right-radius", "trident":"border-bottom-right-radius", "status":"WD"},
131         {"gecko":"", "webkit":"-webkit-border-bottom-right-radius = border-bottom-right-radius", "presto":"", "trident":"", "status":""},
132         {"gecko":"-moz-border-end", "webkit":"-webkit-border-end", "presto":"", "trident":"", "status":"ED"},
133         {"gecko":"-moz-border-end-color", "webkit":"-webkit-border-end-color", "presto":"", "trident":"", "status":"ED"},
134         {"gecko":"-moz-border-end-style", "webkit":"-webkit-border-end-style", "presto":"", "trident":"", "status":"ED"},
135         {"gecko":"-moz-border-end-width", "webkit":"-webkit-border-end-width", "presto":"", "trident":"", "status":"ED"},
136         {"gecko":"", "webkit":"-webkit-border-fit", "presto":"", "trident":"", "status":""},
137         {"gecko":"", "webkit":"-webkit-border-horizontal-spacing", "presto":"", "trident":"", "status":""},
138         {"gecko":"-moz-border-image", "webkit":"-webkit-border-image", "presto":"-o-border-image", "trident":"", "status":"WD"},
139         {"gecko":"-moz-border-left-colors", "webkit":"", "presto":"", "trident":"", "status":"P"},
140         {"gecko":"border-radius", "webkit":"-webkit-border-radius", "presto":"border-radius", "trident":"border-radius", "status":"WD"},
141         {"gecko":"-moz-border-right-colors", "webkit":"", "presto":"", "trident":"", "status":"P"},
142         {"gecko":"-moz-border-start", "webkit":"-webkit-border-start", "presto":"", "trident":"", "status":"ED"},
143         {"gecko":"-moz-border-start-color", "webkit":"-webkit-border-start-color", "presto":"", "trident":"", "status":"ED"},
144         {"gecko":"-moz-border-start-style", "webkit":"-webkit-border-start-style", "presto":"", "trident":"", "status":"ED"},
145         {"gecko":"-moz-border-start-width", "webkit":"-webkit-border-start-width", "presto":"", "trident":"", "status":"ED"},
146         {"gecko":"-moz-border-top-colors", "webkit":"", "presto":"", "trident":"", "status":"P"},
147         {"gecko":"border-top-left-radius", "webkit":"-webkit-border-top-left-radius", "presto":"border-top-left-radius", "trident":"border-top-left-radius", "status":"WD"},
148         {"gecko":"", "webkit":"-webkit-border-top-left-radius = border-top-left-radius", "presto":"", "trident":"", "status":""},
149         {"gecko":"border-top-right-radius", "webkit":"-webkit-border-top-right-radius", "presto":"border-top-right-radius", "trident":"border-top-right-radius", "status":"WD"},
150         {"gecko":"", "webkit":"-webkit-border-top-right-radius = border-top-right-radius", "presto":"", "trident":"", "status":""},
151         {"gecko":"", "webkit":"-webkit-border-vertical-spacing", "presto":"", "trident":"", "status":""},
152         {"gecko":"-moz-box-align", "webkit":"-webkit-box-align", "presto":"", "trident":"-ms-box-align", "status":"WD"},
153         {"gecko":"-moz-box-direction", "webkit":"-webkit-box-direction", "presto":"", "trident":"-ms-box-direction", "status":"WD"},
154         {"gecko":"-moz-box-flex", "webkit":"-webkit-box-flex", "presto":"", "trident":"-ms-box-flex", "status":"WD"},
155         {"gecko":"", "webkit":"-webkit-box-flex-group", "presto":"", "trident":"", "status":"WD"},
156         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-box-line-progression", "status":""},
157         {"gecko":"", "webkit":"-webkit-box-lines", "presto":"", "trident":"-ms-box-lines", "status":"WD"},
158         {"gecko":"-moz-box-ordinal-group", "webkit":"-webkit-box-ordinal-group", "presto":"", "trident":"-ms-box-ordinal-group", "status":"WD"},
159         {"gecko":"-moz-box-orient", "webkit":"-webkit-box-orient", "presto":"", "trident":"-ms-box-orient", "status":"WD"},
160         {"gecko":"-moz-box-pack", "webkit":"-webkit-box-pack", "presto":"", "trident":"-ms-box-pack", "status":"WD"},
161         {"gecko":"", "webkit":"-webkit-box-reflect", "presto":"", "trident":"", "status":""},
162         {"gecko":"box-shadow", "webkit":"-webkit-box-shadow", "presto":"box-shadow", "trident":"box-shadow", "status":"WD"},
163         {"gecko":"-moz-box-sizing", "webkit":"box-sizing", "presto":"box-sizing", "trident":"", "status":"CR"},
164         {"gecko":"", "webkit":"-webkit-box-sizing = box-sizing", "presto":"", "trident":"", "status":""},
165         {"gecko":"", "webkit":"-epub-caption-side = caption-side", "presto":"", "trident":"", "status":""},
166         {"gecko":"", "webkit":"-webkit-color-correction", "presto":"", "trident":"", "status":""},
167         {"gecko":"", "webkit":"-webkit-column-break-after", "presto":"", "trident":"", "status":""},
168         {"gecko":"", "webkit":"-webkit-column-break-before", "presto":"", "trident":"", "status":""},
169         {"gecko":"", "webkit":"-webkit-column-break-inside", "presto":"", "trident":"", "status":""},
170         {"gecko":"-moz-column-count", "webkit":"-webkit-column-count", "presto":"column-count", "trident":"column-count", "status":"CR"},
171         {"gecko":"-moz-column-gap", "webkit":"-webkit-column-gap", "presto":"column-gap", "trident":"column-gap", "status":"CR"},
172         {"gecko":"-moz-column-rule", "webkit":"-webkit-column-rule", "presto":"column-rule", "trident":"column-rule", "status":"CR"},
173         {"gecko":"-moz-column-rule-color", "webkit":"-webkit-column-rule-color", "presto":"column-rule-color", "trident":"column-rule-color", "status":"CR"},
174         {"gecko":"-moz-column-rule-style", "webkit":"-webkit-column-rule-style", "presto":"column-rule-style", "trident":"column-rule-style", "status":"CR"},
175         {"gecko":"-moz-column-rule-width", "webkit":"-webkit-column-rule-width", "presto":"column-rule-width", "trident":"column-rule-width", "status":"CR"},
176         {"gecko":"", "webkit":"-webkit-column-span", "presto":"column-span", "trident":"column-span", "status":"CR"},
177         {"gecko":"-moz-column-width", "webkit":"-webkit-column-width", "presto":"column-width", "trident":"column-width", "status":"CR"},
178         {"gecko":"", "webkit":"-webkit-columns", "presto":"columns", "trident":"columns", "status":"CR"},
179         {"gecko":"", "webkit":"-webkit-dashboard-region", "presto":"-apple-dashboard-region", "trident":"", "status":""},
180         {"gecko":"filter", "webkit":"", "presto":"filter", "trident":"-ms-filter", "status":""},
181         {"gecko":"-moz-float-edge", "webkit":"", "presto":"", "trident":"", "status":"P"},
182         {"gecko":"", "webkit":"", "presto":"-o-focus-opacity", "trident":"", "status":""},
183         {"gecko":"-moz-font-feature-settings", "webkit":"", "presto":"", "trident":"", "status":""},
184         {"gecko":"-moz-font-language-override", "webkit":"", "presto":"", "trident":"", "status":""},
185         {"gecko":"", "webkit":"-webkit-font-size-delta", "presto":"", "trident":"", "status":""},
186         {"gecko":"", "webkit":"-webkit-font-smoothing", "presto":"", "trident":"", "status":""},
187         {"gecko":"-moz-force-broken-image-icon", "webkit":"", "presto":"", "trident":"", "status":""},
188         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-column", "status":"WD"},
189         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-column-align", "status":"WD"},
190         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-column-span", "status":"WD"},
191         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-columns", "status":"WD"},
192         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-layer", "status":"WD"},
193         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-row", "status":"WD"},
194         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-row-align", "status":"WD"},
195         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-row-span", "status":"WD"},
196         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-grid-rows", "status":"WD"},
197         {"gecko":"", "webkit":"-webkit-highlight", "presto":"", "trident":"", "status":""},
198         {"gecko":"", "webkit":"-webkit-hyphenate-character", "presto":"", "trident":"", "status":"WD"},
199         {"gecko":"", "webkit":"-webkit-hyphenate-limit-after", "presto":"", "trident":"", "status":""},
200         {"gecko":"", "webkit":"-webkit-hyphenate-limit-before", "presto":"", "trident":"", "status":""},
201         {"gecko":"", "webkit":"-webkit-hyphens", "presto":"", "trident":"", "status":"WD"},
202         {"gecko":"", "webkit":"-epub-hyphens = -webkit-hyphens", "presto":"", "trident":"", "status":""},
203         {"gecko":"-moz-image-region", "webkit":"", "presto":"", "trident":"", "status":"P"},
204         {"gecko":"ime-mode", "webkit":"", "presto":"", "trident":"-ms-ime-mode", "status":""},
205         {"gecko":"", "webkit":"", "presto":"-wap-input-format", "trident":"", "status":""},
206         {"gecko":"", "webkit":"", "presto":"-wap-input-required", "trident":"", "status":""},
207         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-interpolation-mode", "status":""},
208         {"gecko":"", "webkit":"", "presto":"-xv-interpret-as", "trident":"", "status":""},
209         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-flow", "status":""},
210         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-grid", "status":""},
211         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-grid-char", "status":""},
212         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-grid-line", "status":""},
213         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-grid-mode", "status":""},
214         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-layout-grid-type", "status":""},
215         {"gecko":"", "webkit":"-webkit-line-box-contain", "presto":"", "trident":"", "status":""},
216         {"gecko":"", "webkit":"-webkit-line-break", "presto":"", "trident":"-ms-line-break", "status":""},
217         {"gecko":"", "webkit":"-webkit-line-clamp", "presto":"", "trident":"", "status":""},
218         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-line-grid-mode", "status":""},
219         {"gecko":"", "webkit":"", "presto":"-o-link", "trident":"", "status":""},
220         {"gecko":"", "webkit":"", "presto":"-o-link-source", "trident":"", "status":""},
221         {"gecko":"", "webkit":"-webkit-locale", "presto":"", "trident":"", "status":""},
222         {"gecko":"", "webkit":"-webkit-logical-height", "presto":"", "trident":"", "status":"ED"},
223         {"gecko":"", "webkit":"-webkit-logical-width", "presto":"", "trident":"", "status":"ED"},
224         {"gecko":"", "webkit":"-webkit-margin-after", "presto":"", "trident":"", "status":"ED"},
225         {"gecko":"", "webkit":"-webkit-margin-after-collapse", "presto":"", "trident":"", "status":""},
226         {"gecko":"", "webkit":"-webkit-margin-before", "presto":"", "trident":"", "status":"ED"},
227         {"gecko":"", "webkit":"-webkit-margin-before-collapse", "presto":"", "trident":"", "status":""},
228         {"gecko":"", "webkit":"-webkit-margin-bottom-collapse", "presto":"", "trident":"", "status":""},
229         {"gecko":"", "webkit":"-webkit-margin-collapse", "presto":"", "trident":"", "status":""},
230         {"gecko":"-moz-margin-end", "webkit":"-webkit-margin-end", "presto":"", "trident":"", "status":"ED"},
231         {"gecko":"-moz-margin-start", "webkit":"-webkit-margin-start", "presto":"", "trident":"", "status":"ED"},
232         {"gecko":"", "webkit":"-webkit-margin-top-collapse", "presto":"", "trident":"", "status":""},
233         {"gecko":"", "webkit":"-webkit-marquee", "presto":"", "trident":"", "status":""},
234         {"gecko":"", "webkit":"", "presto":"-wap-marquee-dir", "trident":"", "status":""},
235         {"gecko":"", "webkit":"-webkit-marquee-direction", "presto":"", "trident":"", "status":"WD"},
236         {"gecko":"", "webkit":"-webkit-marquee-increment", "presto":"", "trident":"", "status":""},
237         {"gecko":"", "webkit":"", "presto":"-wap-marquee-loop", "trident":"", "status":"WD"},
238         {"gecko":"", "webkit":"-webkit-marquee-repetition", "presto":"", "trident":"", "status":""},
239         {"gecko":"", "webkit":"-webkit-marquee-speed", "presto":"-wap-marquee-speed", "trident":"", "status":"WD"},
240         {"gecko":"", "webkit":"-webkit-marquee-style", "presto":"-wap-marquee-style", "trident":"", "status":"WD"},
241         {"gecko":"mask", "webkit":"-webkit-mask", "presto":"mask", "trident":"", "status":""},
242         {"gecko":"", "webkit":"-webkit-mask-attachment", "presto":"", "trident":"", "status":""},
243         {"gecko":"", "webkit":"-webkit-mask-box-image", "presto":"", "trident":"", "status":""},
244         {"gecko":"", "webkit":"-webkit-mask-clip", "presto":"", "trident":"", "status":""},
245         {"gecko":"", "webkit":"-webkit-mask-composite", "presto":"", "trident":"", "status":""},
246         {"gecko":"", "webkit":"-webkit-mask-image", "presto":"", "trident":"", "status":""},
247         {"gecko":"", "webkit":"-webkit-mask-origin", "presto":"", "trident":"", "status":""},
248         {"gecko":"", "webkit":"-webkit-mask-position", "presto":"", "trident":"", "status":""},
249         {"gecko":"", "webkit":"-webkit-mask-position-x", "presto":"", "trident":"", "status":""},
250         {"gecko":"", "webkit":"-webkit-mask-position-y", "presto":"", "trident":"", "status":""},
251         {"gecko":"", "webkit":"-webkit-mask-repeat", "presto":"", "trident":"", "status":""},
252         {"gecko":"", "webkit":"-webkit-mask-repeat-x", "presto":"", "trident":"", "status":""},
253         {"gecko":"", "webkit":"-webkit-mask-repeat-y", "presto":"", "trident":"", "status":""},
254         {"gecko":"", "webkit":"-webkit-mask-size", "presto":"", "trident":"", "status":""},
255         {"gecko":"", "webkit":"-webkit-match-nearest-mail-blockquote-color", "presto":"", "trident":"", "status":""},
256         {"gecko":"", "webkit":"-webkit-max-logical-height", "presto":"", "trident":"", "status":""},
257         {"gecko":"", "webkit":"-webkit-max-logical-width", "presto":"", "trident":"", "status":"ED"},
258         {"gecko":"", "webkit":"-webkit-min-logical-height", "presto":"", "trident":"", "status":"ED"},
259         {"gecko":"", "webkit":"-webkit-min-logical-width", "presto":"", "trident":"", "status":"ED"},
260         {"gecko":"", "webkit":"", "presto":"-o-mini-fold", "trident":"", "status":""},
261         {"gecko":"", "webkit":"-webkit-nbsp-mode", "presto":"", "trident":"", "status":"P"},
262         {"gecko":"", "webkit":"", "presto":"-o-object-fit", "trident":"", "status":"ED"},
263         {"gecko":"", "webkit":"", "presto":"-o-object-position", "trident":"", "status":"ED"},
264         {"gecko":"opacity", "webkit":"-webkit-opacity", "presto":"opacity", "trident":"opacity", "status":"WD"},
265         {"gecko":"", "webkit":"-webkit-opacity = opacity", "presto":"", "trident":"", "status":""},
266         {"gecko":"-moz-outline-radius", "webkit":"", "presto":"", "trident":"", "status":"P"},
267         {"gecko":"-moz-outline-radius-bottomleft", "webkit":"", "presto":"", "trident":"", "status":"P"},
268         {"gecko":"-moz-outline-radius-bottomright", "webkit":"", "presto":"", "trident":"", "status":"P"},
269         {"gecko":"-moz-outline-radius-topleft", "webkit":"", "presto":"", "trident":"", "status":"P"},
270         {"gecko":"-moz-outline-radius-topright", "webkit":"", "presto":"", "trident":"", "status":"P"},
271         {"gecko":"overflow-x", "webkit":"overflow-x", "presto":"overflow-x", "trident":"-ms-overflow-x", "status":"WD"},
272         {"gecko":"overflow-y", "webkit":"overflow-y", "presto":"overflow-y", "trident":"-ms-overflow-y", "status":"WD"},
273         {"gecko":"", "webkit":"-webkit-padding-after", "presto":"", "trident":"", "status":"ED"},
274         {"gecko":"", "webkit":"-webkit-padding-before", "presto":"", "trident":"", "status":"ED"},
275         {"gecko":"-moz-padding-end", "webkit":"-webkit-padding-end", "presto":"", "trident":"", "status":"ED"},
276         {"gecko":"-moz-padding-start", "webkit":"-webkit-padding-start", "presto":"", "trident":"", "status":"ED"},
277         {"gecko":"", "webkit":"-webkit-perspective", "presto":"", "trident":"", "status":"WD"},
278         {"gecko":"", "webkit":"-webkit-perspective-origin", "presto":"", "trident":"", "status":"WD"},
279         {"gecko":"", "webkit":"-webkit-perspective-origin-x", "presto":"", "trident":"", "status":""},
280         {"gecko":"", "webkit":"-webkit-perspective-origin-y", "presto":"", "trident":"", "status":""},
281         {"gecko":"", "webkit":"", "presto":"-xv-phonemes", "trident":"", "status":""},
282         {"gecko":"", "webkit":"-webkit-rtl-ordering", "presto":"", "trident":"", "status":"P"},
283         {"gecko":"-moz-script-level", "webkit":"", "presto":"", "trident":"", "status":""},
284         {"gecko":"-moz-script-min-size", "webkit":"", "presto":"", "trident":"", "status":""},
285         {"gecko":"-moz-script-size-multiplier", "webkit":"", "presto":"", "trident":"", "status":""},
286         {"gecko":"", "webkit":"", "presto":"scrollbar-3dlight-color", "trident":"-ms-scrollbar-3dlight-color", "status":"P"},
287         {"gecko":"", "webkit":"", "presto":"scrollbar-arrow-color", "trident":"-ms-scrollbar-arrow-color", "status":"P"},
288         {"gecko":"", "webkit":"", "presto":"scrollbar-base-color", "trident":"-ms-scrollbar-base-color", "status":"P"},
289         {"gecko":"", "webkit":"", "presto":"scrollbar-darkshadow-color", "trident":"-ms-scrollbar-darkshadow-color", "status":"P"},
290         {"gecko":"", "webkit":"", "presto":"scrollbar-face-color", "trident":"-ms-scrollbar-face-color", "status":"P"},
291         {"gecko":"", "webkit":"", "presto":"scrollbar-highlight-color", "trident":"-ms-scrollbar-highlight-color", "status":"P"},
292         {"gecko":"", "webkit":"", "presto":"scrollbar-shadow-color", "trident":"-ms-scrollbar-shadow-color", "status":"P"},
293         {"gecko":"", "webkit":"", "presto":"scrollbar-track-color", "trident":"-ms-scrollbar-track-color", "status":"P"},
294         {"gecko":"-moz-stack-sizing", "webkit":"", "presto":"", "trident":"", "status":"P"},
295         {"gecko":"", "webkit":"-webkit-svg-shadow", "presto":"", "trident":"", "status":""},
296         {"gecko":"-moz-tab-size", "webkit":"", "presto":"-o-tab-size", "trident":"", "status":""},
297         {"gecko":"", "webkit":"", "presto":"-o-table-baseline", "trident":"", "status":""},
298         {"gecko":"", "webkit":"-webkit-tap-highlight-color", "presto":"", "trident":"", "status":"P"},
299         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-text-align-last", "status":"WD"},
300         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-text-autospace", "status":"WD"},
301         {"gecko":"-moz-text-blink", "webkit":"", "presto":"", "trident":"", "status":""},
302         {"gecko":"", "webkit":"-webkit-text-combine", "presto":"", "trident":"", "status":""},
303         {"gecko":"", "webkit":"-epub-text-combine = -webkit-text-combine", "presto":"", "trident":"", "status":""},
304         {"gecko":"-moz-text-decoration-color", "webkit":"", "presto":"", "trident":"", "status":""},
305         {"gecko":"-moz-text-decoration-line", "webkit":"", "presto":"", "trident":"", "status":""},
306         {"gecko":"-moz-text-decoration-style", "webkit":"", "presto":"", "trident":"", "status":""},
307         {"gecko":"", "webkit":"-webkit-text-decorations-in-effect", "presto":"", "trident":"", "status":""},
308         {"gecko":"", "webkit":"-webkit-text-emphasis", "presto":"", "trident":"", "status":""},
309         {"gecko":"", "webkit":"-epub-text-emphasis = -webkit-text-emphasis", "presto":"", "trident":"", "status":""},
310         {"gecko":"", "webkit":"-webkit-text-emphasis-color", "presto":"", "trident":"", "status":""},
311         {"gecko":"", "webkit":"-epub-text-emphasis-color = -webkit-text-emphasis-color", "presto":"", "trident":"", "status":""},
312         {"gecko":"", "webkit":"-webkit-text-emphasis-position", "presto":"", "trident":"", "status":""},
313         {"gecko":"", "webkit":"-webkit-text-emphasis-style", "presto":"", "trident":"", "status":""},
314         {"gecko":"", "webkit":"-epub-text-emphasis-style = -webkit-text-emphasis-style", "presto":"", "trident":"", "status":""},
315         {"gecko":"", "webkit":"-webkit-text-fill-color", "presto":"", "trident":"", "status":"P"},
316         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-text-justify", "status":"WD"},
317         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-text-kashida-space", "status":"P"},
318         {"gecko":"", "webkit":"-webkit-text-orientation", "presto":"", "trident":"", "status":""},
319         {"gecko":"", "webkit":"-epub-text-orientation = -webkit-text-orientation", "presto":"", "trident":"", "status":""},
320         {"gecko":"", "webkit":"text-overflow", "presto":"text-overflow", "trident":"-ms-text-overflow", "status":"WD"},
321         {"gecko":"", "webkit":"-webkit-text-security", "presto":"", "trident":"", "status":"P"},
322         {"gecko":"", "webkit":"-webkit-text-size-adjust", "presto":"", "trident":"-ms-text-size-adjust", "status":""},
323         {"gecko":"", "webkit":"-webkit-text-stroke", "presto":"", "trident":"", "status":"P"},
324         {"gecko":"", "webkit":"-webkit-text-stroke-color", "presto":"", "trident":"", "status":"P"},
325         {"gecko":"", "webkit":"-webkit-text-stroke-width", "presto":"", "trident":"", "status":"P"},
326         {"gecko":"", "webkit":"-epub-text-transform = text-transform", "presto":"", "trident":"", "status":""},
327         {"gecko":"", "webkit":"", "presto":"", "trident":"-ms-text-underline-position", "status":"P"},
328         {"gecko":"", "webkit":"-webkit-touch-callout", "presto":"", "trident":"", "status":"P"},
329         {"gecko":"-moz-transform", "webkit":"-webkit-transform", "presto":"-o-transform", "trident":"-ms-transform", "status":"WD"},
330         {"gecko":"-moz-transform-origin", "webkit":"-webkit-transform-origin", "presto":"-o-transform-origin", "trident":"-ms-transform-origin", "status":"WD"},
331         {"gecko":"", "webkit":"-webkit-transform-origin-x", "presto":"", "trident":"", "status":"P"},
332         {"gecko":"", "webkit":"-webkit-transform-origin-y", "presto":"", "trident":"", "status":"P"},
333         {"gecko":"", "webkit":"-webkit-transform-origin-z", "presto":"", "trident":"", "status":"P"},
334         {"gecko":"", "webkit":"-webkit-transform-style", "presto":"", "trident":"", "status":"WD"},
335         {"gecko":"-moz-transition", "webkit":"-webkit-transition", "presto":"-o-transition", "trident":"", "status":"WD"},
336         {"gecko":"-moz-transition-delay", "webkit":"-webkit-transition-delay", "presto":"-o-transition-delay", "trident":"", "status":"WD"},
337         {"gecko":"-moz-transition-duration", "webkit":"-webkit-transition-duration", "presto":"-o-transition-duration", "trident":"", "status":"WD"},
338         {"gecko":"-moz-transition-property", "webkit":"-webkit-transition-property", "presto":"-o-transition-property", "trident":"", "status":"WD"},
339         {"gecko":"-moz-transition-timing-function", "webkit":"-webkit-transition-timing-function", "presto":"-o-transition-timing-function", "trident":"", "status":"WD"},
340         {"gecko":"", "webkit":"-webkit-user-drag", "presto":"", "trident":"", "status":"P"},
341         {"gecko":"-moz-user-focus", "webkit":"", "presto":"", "trident":"", "status":"P"},
342         {"gecko":"-moz-user-input", "webkit":"", "presto":"", "trident":"", "status":"P"},
343         {"gecko":"-moz-user-modify", "webkit":"-webkit-user-modify", "presto":"", "trident":"", "status":"P"},
344         {"gecko":"-moz-user-select", "webkit":"-webkit-user-select", "presto":"", "trident":"", "status":"P"},
345         {"gecko":"", "webkit":"", "presto":"-xv-voice-balance", "trident":"", "status":""},
346         {"gecko":"", "webkit":"", "presto":"-xv-voice-duration", "trident":"", "status":""},
347         {"gecko":"", "webkit":"", "presto":"-xv-voice-pitch", "trident":"", "status":""},
348         {"gecko":"", "webkit":"", "presto":"-xv-voice-pitch-range", "trident":"", "status":""},
349         {"gecko":"", "webkit":"", "presto":"-xv-voice-rate", "trident":"", "status":""},
350         {"gecko":"", "webkit":"", "presto":"-xv-voice-stress", "trident":"", "status":""},
351         {"gecko":"", "webkit":"", "presto":"-xv-voice-volume", "trident":"", "status":""},
352         {"gecko":"-moz-window-shadow", "webkit":"", "presto":"", "trident":"", "status":"P"},
353         {"gecko":"", "webkit":"word-break", "presto":"", "trident":"-ms-word-break", "status":"WD"},
354         {"gecko":"", "webkit":"-epub-word-break = word-break", "presto":"", "trident":"", "status":""},
355         {"gecko":"word-wrap", "webkit":"word-wrap", "presto":"word-wrap", "trident":"-ms-word-wrap", "status":"WD"},
356         {"gecko":"", "webkit":"-webkit-writing-mode", "presto":"writing-mode", "trident":"-ms-writing-mode", "status":"ED"},
357         {"gecko":"", "webkit":"-epub-writing-mode = -webkit-writing-mode", "presto":"", "trident":"", "status":""},
358         {"gecko":"", "webkit":"zoom", "presto":"", "trident":"-ms-zoom", "status":""}
359     ]};
360 
361     var kCSS_PREFIXED_VALUE = [
362         {"gecko":"-moz-box", "webkit":"-moz-box", "presto":"", "trident":"", "generic":"box"}
363     ];
364 
365     var CssInspector = {
366 
367         mVENDOR_PREFIXES:null,
368 
369         kEXPORTS_FOR_GECKO:true,
370         kEXPORTS_FOR_WEBKIT:true,
371         kEXPORTS_FOR_PRESTO:true,
372         kEXPORTS_FOR_TRIDENT:true,
373 
374         cleanPrefixes:function () {
375             this.mVENDOR_PREFIXES = null;
376         },
377 
378         prefixesForProperty:function (aProperty) {
379             if (!this.mVENDOR_PREFIXES) {
380 
381                 this.mVENDOR_PREFIXES = {};
382                 for (var i = 0; i < kCSS_VENDOR_PREFIXES.properties.length; i++) {
383                     var p = kCSS_VENDOR_PREFIXES.properties[i];
384                     if (p.gecko && (p.webkit || p.presto || p.trident)) {
385                         var o = {};
386                         if (this.kEXPORTS_FOR_GECKO) {
387                             o[p.gecko] = true;
388                         }
389                         if (this.kEXPORTS_FOR_WEBKIT && p.webkit) {
390                             o[p.webkit] = true;
391                         }
392                         if (this.kEXPORTS_FOR_PRESTO && p.presto) {
393                             o[p.presto] = true;
394                         }
395                         if (this.kEXPORTS_FOR_TRIDENT && p.trident) {
396                             o[p.trident] = true;
397                         }
398                         this.mVENDOR_PREFIXES[p.gecko] = [];
399                         for (var j in o) {
400                             this.mVENDOR_PREFIXES[p.gecko].push(j)
401                         }
402                     }
403                 }
404             }
405             if (aProperty in this.mVENDOR_PREFIXES) {
406                 return this.mVENDOR_PREFIXES[aProperty].sort();
407             }
408             return null;
409         },
410 
411         parseColorStop:function (parser, token) {
412             var color = parser.parseColor(token);
413             var position = "";
414             if (!color) {
415                 return null;
416             }
417             token = parser.getToken(true, true);
418             if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
419                 position = token.value;
420                 token = parser.getToken(true, true);
421             }
422             return { color:color, position:position }
423         },
424 
425         parseGradient:function (parser, token) {
426             var isRadial = false;
427             var gradient = { isRepeating:false };
428             if (token.isNotNull()) {
429                 if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) {
430                     if (token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) {
431                         gradient.isRadial = true;
432                     }
433                     if (token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) {
434                         gradient.isRepeating = true;
435                     }
436 
437 
438                     token = parser.getToken(true, true);
439                     var haveGradientLine = false;
440                     var foundHorizPosition = false;
441                     var haveAngle = false;
442 
443                     if (token.isAngle()) {
444                         gradient.angle = token.value;
445                         haveGradientLine = true;
446                         haveAngle = true;
447                         token = parser.getToken(true, true);
448                     }
449 
450                     if (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")) {
451                         haveGradientLine = true;
452                         if (token.isLength() || token.isIdent("left") || token.isIdent("right")) {
453                             foundHorizPosition = true;
454                         }
455                         gradient.position = token.value;
456                         token = parser.getToken(true, true);
457                     }
458 
459                     if (haveGradientLine) {
460                         if (!haveAngle && token.isAngle()) { // we have an angle here
461                             gradient.angle = token.value;
462                             haveAngle = true;
463                             token = parser.getToken(true, true);
464                         }
465 
466                         else if (token.isLength() || (foundHorizPosition && (token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom"))) || (!foundHorizPosition && (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")))) {
467                             gradient.position = ("position" in gradient) ? gradient.position + " " : "";
468                             gradient.position += token.value;
469                             token = parser.getToken(true, true);
470                         }
471 
472                         if (!haveAngle && token.isAngle()) { // we have an angle here
473                             gradient.angle = token.value;
474                             haveAngle = true;
475                             token = parser.getToken(true, true);
476                         }
477 
478                         // we must find a comma here
479                         if (!token.isSymbol(",")) {
480                             return null;
481                         }
482                         token = parser.getToken(true, true);
483                     }
484 
485                     // ok... Let's deal with the rest now
486                     if (gradient.isRadial) {
487                         if (token.isIdent("circle") || token.isIdent("ellipse")) {
488                             gradient.shape = token.value;
489                             token = parser.getToken(true, true);
490                         }
491                         if (token.isIdent("closest-side") || token.isIdent("closest-corner") || token.isIdent("farthest-side") || token.isIdent("farthest-corner") || token.isIdent("contain") || token.isIdent("cover")) {
492                             gradient.size = token.value;
493                             token = parser.getToken(true, true);
494                         }
495                         if (!("shape" in gradient) && (token.isIdent("circle") || token.isIdent("ellipse"))) {
496                             // we can still have the second value...
497                             gradient.shape = token.value;
498                             token = parser.getToken(true, true);
499                         }
500                         if ((("shape" in gradient) || ("size" in gradient)) && !token.isSymbol(",")) {
501                             return null;
502                         } else if (("shape" in gradient) || ("size" in gradient)) {
503                             token = parser.getToken(true, true);
504                         }
505                     }
506 
507                     // now color stops...
508                     var stop1 = this.parseColorStop(parser, token);
509                     if (!stop1) {
510                         return null;
511                     }
512                     token = parser.currentToken();
513                     if (!token.isSymbol(",")) {
514                         return null;
515                     }
516                     token = parser.getToken(true, true);
517                     var stop2 = this.parseColorStop(parser, token);
518                     if (!stop2) {
519                         return null;
520                     }
521                     token = parser.currentToken();
522                     if (token.isSymbol(",")) {
523                         token = parser.getToken(true, true);
524                     }
525                     // ok we have at least two color stops
526                     gradient.stops = [stop1, stop2];
527                     while (!token.isSymbol(")")) {
528                         var colorstop = this.parseColorStop(parser, token);
529                         if (!colorstop) {
530                             return null;
531                         }
532                         token = parser.currentToken();
533                         if (!token.isSymbol(")") && !token.isSymbol(",")) {
534                             return null;
535                         }
536                         if (token.isSymbol(",")) {
537                             token = parser.getToken(true, true);
538                         }
539                         gradient.stops.push(colorstop);
540                     }
541                     return gradient;
542                 }
543             }
544             return null;
545         },
546 
547         parseBoxShadows:function (aString) {
548             var parser = new CSSParser();
549             parser._init();
550             parser.mPreserveWS = false;
551             parser.mPreserveComments = false;
552             parser.mPreservedTokens = [];
553             parser.mScanner.init(aString);
554 
555             var shadows = [];
556             var token = parser.getToken(true, true);
557             var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px", spreadRadius = "0px";
558             var inset = false;
559             while (token.isNotNull()) {
560                 if (token.isIdent("none")) {
561                     shadows.push({ none:true });
562                     token = parser.getToken(true, true);
563                 } else {
564                     if (token.isIdent('inset')) {
565                         inset = true;
566                         token = parser.getToken(true, true);
567                     }
568 
569                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
570                         var offsetX = token.value;
571                         token = parser.getToken(true, true);
572                     } else {
573                         return [];
574                     }
575 
576                     if (!inset && token.isIdent('inset')) {
577                         inset = true;
578                         token = parser.getToken(true, true);
579                     }
580 
581                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
582                         var offsetX = token.value;
583                         token = parser.getToken(true, true);
584                     } else {
585                         return [];
586                     }
587 
588                     if (!inset && token.isIdent('inset')) {
589                         inset = true;
590                         token = parser.getToken(true, true);
591                     }
592 
593                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
594                         var blurRadius = token.value;
595                         token = parser.getToken(true, true);
596                     }
597 
598                     if (!inset && token.isIdent('inset')) {
599                         inset = true;
600                         token = parser.getToken(true, true);
601                     }
602 
603                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
604                         var spreadRadius = token.value;
605                         token = parser.getToken(true, true);
606                     }
607 
608                     if (!inset && token.isIdent('inset')) {
609                         inset = true;
610                         token = parser.getToken(true, true);
611                     }
612 
613                     if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) {
614                         var color = parser.parseColor(token);
615                         token = parser.getToken(true, true);
616                     }
617 
618                     if (!inset && token.isIdent('inset')) {
619                         inset = true;
620                         token = parser.getToken(true, true);
621                     }
622 
623                     shadows.push({ none:false,
624                                      color:color,
625                                      offsetX:offsetX, offsetY:offsetY,
626                                      blurRadius:blurRadius,
627                                      spreadRadius:spreadRadius });
628 
629                     if (token.isSymbol(",")) {
630                         inset = false;
631                         color = "";
632                         blurRadius = "0px";
633                         spreadRadius = "0px"
634                         offsetX = "0px";
635                         offsetY = "0px";
636                         token = parser.getToken(true, true);
637                     } else if (!token.isNotNull()) {
638                         return shadows;
639                     } else {
640                         return [];
641                     }
642                 }
643             }
644             return shadows;
645         },
646 
647         parseTextShadows:function (aString) {
648             var parser = new CSSParser();
649             parser._init();
650             parser.mPreserveWS = false;
651             parser.mPreserveComments = false;
652             parser.mPreservedTokens = [];
653             parser.mScanner.init(aString);
654 
655             var shadows = [];
656             var token = parser.getToken(true, true);
657             var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px";
658             while (token.isNotNull()) {
659                 if (token.isIdent("none")) {
660                     shadows.push({ none:true });
661                     token = parser.getToken(true, true);
662                 } else {
663                     if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) {
664                         var color = parser.parseColor(token);
665                         token = parser.getToken(true, true);
666                     }
667                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
668                         var offsetX = token.value;
669                         token = parser.getToken(true, true);
670                     } else {
671                         return [];
672                     }
673                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
674                         var offsetY = token.value;
675                         token = parser.getToken(true, true);
676                     } else {
677                         return [];
678                     }
679                     if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) {
680                         var blurRadius = token.value;
681                         token = parser.getToken(true, true);
682                     }
683                     if (!color && (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent())) {
684                         var color = parser.parseColor(token);
685                         token = parser.getToken(true, true);
686                     }
687 
688                     shadows.push({ none:false,
689                                      color:color,
690                                      offsetX:offsetX, offsetY:offsetY,
691                                      blurRadius:blurRadius });
692 
693                     if (token.isSymbol(",")) {
694                         color = "";
695                         blurRadius = "0px";
696                         offsetX = "0px";
697                         offsetY = "0px";
698                         token = parser.getToken(true, true);
699                     } else if (!token.isNotNull()) {
700                         return shadows;
701                     } else {
702                         return [];
703                     }
704                 }
705             }
706             return shadows;
707         },
708 
709         parseBackgroundImages:function (aString) {
710             var parser = new CSSParser();
711             parser._init();
712             parser.mPreserveWS = false;
713             parser.mPreserveComments = false;
714             parser.mPreservedTokens = [];
715             parser.mScanner.init(aString);
716 
717             var backgrounds = [];
718             var token = parser.getToken(true, true);
719             while (token.isNotNull()) {
720                 /*if (token.isFunction("rgb(") ||
721                  token.isFunction("rgba(") ||
722                  token.isFunction("hsl(") ||
723                  token.isFunction("hsla(") ||
724                  token.isSymbol("#") ||
725                  token.isIdent()) {
726                  var color = parser.parseColor(token);
727                  backgrounds.push( { type: "color", value: color });
728                  token = parser.getToken(true, true);
729                  }
730                  else */
731                 if (token.isFunction("url(")) {
732                     token = parser.getToken(true, true);
733                     var urlContent = parser.parseURL(token);
734                     backgrounds.push({ type:"image", value:"url(" + urlContent });
735                     token = parser.getToken(true, true);
736                 } else if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) {
737                     var gradient = this.parseGradient(parser, token);
738                     backgrounds.push({ type:gradient.isRadial ? "radial-gradient" : "linear-gradient", value:gradient });
739                     token = parser.getToken(true, true);
740                 } else {
741                     return null;
742                 }
743                 if (token.isSymbol(",")) {
744                     token = parser.getToken(true, true);
745                     if (!token.isNotNull()) {
746                         return null;
747                     }
748                 }
749             }
750             return backgrounds;
751         },
752 
753         serializeGradient:function (gradient) {
754             var s = gradient.isRadial ? (gradient.isRepeating ? "-moz-repeating-radial-gradient(" : "-moz-radial-gradient(" ) : (gradient.isRepeating ? "-moz-repeating-linear-gradient(" : "-moz-linear-gradient(" );
755             if (gradient.angle || gradient.position) {
756                 s += (gradient.angle ? gradient.angle + " " : "") + (gradient.position ? gradient.position : "") + ", ";
757             }
758             if (gradient.isRadial && (gradient.shape || gradient.size)) {
759                 s += (gradient.shape ? gradient.shape : "") + " " + (gradient.size ? gradient.size : "") + ", ";
760             }
761             for (var i = 0; i < gradient.stops.length; i++) {
762                 var colorstop = gradient.stops[i];
763                 s += colorstop.color + (colorstop.position ? " " + colorstop.position : "");
764                 if (i != gradient.stops.length - 1) {
765                     s += ", ";
766                 }
767             }
768             s += ")";
769             return s;
770         },
771 
772         parseBorderImage:function (aString) {
773             var parser = new CSSParser();
774             parser._init();
775             parser.mPreserveWS = false;
776             parser.mPreserveComments = false;
777             parser.mPreservedTokens = [];
778             parser.mScanner.init(aString);
779 
780             var borderImage = {url:"", offsets:[], widths:[], sizes:[]};
781             var token = parser.getToken(true, true);
782             if (token.isFunction("url(")) {
783                 token = parser.getToken(true, true);
784                 var urlContent = parser.parseURL(token);
785                 if (urlContent) {
786                     borderImage.url = urlContent.substr(0, urlContent.length - 1).trim();
787                     if ((borderImage.url[0] == '"' && borderImage.url[borderImage.url.length - 1] == '"') || (borderImage.url[0] == "'" && borderImage.url[borderImage.url.length - 1] == "'")) {
788                         borderImage.url = borderImage.url.substr(1, borderImage.url.length - 2);
789                     }
790                 } else {
791                     return null;
792                 }
793             } else {
794                 return null;
795             }
796 
797             token = parser.getToken(true, true);
798             if (token.isNumber() || token.isPercentage()) {
799                 borderImage.offsets.push(token.value);
800             } else {
801                 return null;
802             }
803             var i;
804             for (i = 0; i < 3; i++) {
805                 token = parser.getToken(true, true);
806                 if (token.isNumber() || token.isPercentage()) {
807                     borderImage.offsets.push(token.value);
808                 } else {
809                     break;
810                 }
811             }
812             if (i == 3) {
813                 token = parser.getToken(true, true);
814             }
815 
816             if (token.isSymbol("/")) {
817                 token = parser.getToken(true, true);
818                 if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) {
819                     borderImage.widths.push(token.value);
820                 } else {
821                     return null;
822                 }
823 
824                 for (var i = 0; i < 3; i++) {
825                     token = parser.getToken(true, true);
826                     if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) {
827                         borderImage.widths.push(token.value);
828                     } else {
829                         break;
830                     }
831                 }
832                 if (i == 3) {
833                     token = parser.getToken(true, true);
834                 }
835             }
836 
837             for (var i = 0; i < 2; i++) {
838                 if (token.isIdent("stretch") || token.isIdent("repeat") || token.isIdent("round")) {
839                     borderImage.sizes.push(token.value);
840                 } else if (!token.isNotNull()) {
841                     return borderImage;
842                 } else {
843                     return null;
844                 }
845                 token = parser.getToken(true, true);
846             }
847             if (!token.isNotNull()) {
848                 return borderImage;
849             }
850 
851             return null;
852         },
853 
854         parseMediaQuery:function (aString) {
855             var kCONSTRAINTS = {
856                 "width":true,
857                 "min-width":true,
858                 "max-width":true,
859                 "height":true,
860                 "min-height":true,
861                 "max-height":true,
862                 "device-width":true,
863                 "min-device-width":true,
864                 "max-device-width":true,
865                 "device-height":true,
866                 "min-device-height":true,
867                 "max-device-height":true,
868                 "orientation":true,
869                 "aspect-ratio":true,
870                 "min-aspect-ratio":true,
871                 "max-aspect-ratio":true,
872                 "device-aspect-ratio":true,
873                 "min-device-aspect-ratio":true,
874                 "max-device-aspect-ratio":true,
875                 "color":true,
876                 "min-color":true,
877                 "max-color":true,
878                 "color-index":true,
879                 "min-color-index":true,
880                 "max-color-index":true,
881                 "monochrome":true,
882                 "min-monochrome":true,
883                 "max-monochrome":true,
884                 "resolution":true,
885                 "min-resolution":true,
886                 "max-resolution":true,
887                 "scan":true,
888                 "grid":true
889             };
890             var parser = new CSSParser();
891             parser._init();
892             parser.mPreserveWS = false;
893             parser.mPreserveComments = false;
894             parser.mPreservedTokens = [];
895             parser.mScanner.init(aString);
896 
897             var m = {amplifier:"", medium:"", constraints:[]};
898             var token = parser.getToken(true, true);
899 
900             if (token.isIdent("all") || token.isIdent("aural") || token.isIdent("braille") || token.isIdent("handheld") || token.isIdent("print") || token.isIdent("projection") || token.isIdent("screen") || token.isIdent("tty") || token.isIdent("tv")) {
901                 m.medium = token.value;
902                 token = parser.getToken(true, true);
903             } else if (token.isIdent("not") || token.isIdent("only")) {
904                 m.amplifier = token.value;
905                 token = parser.getToken(true, true);
906                 if (token.isIdent("all") || token.isIdent("aural") || token.isIdent("braille") || token.isIdent("handheld") || token.isIdent("print") || token.isIdent("projection") || token.isIdent("screen") || token.isIdent("tty") || token.isIdent("tv")) {
907                     m.medium = token.value;
908                     token = parser.getToken(true, true);
909                 } else {
910                     return null;
911                 }
912             }
913 
914             if (m.medium) {
915                 if (!token.isNotNull()) {
916                     return m;
917                 }
918                 if (token.isIdent("and")) {
919                     token = parser.getToken(true, true);
920                 } else {
921                     return null;
922                 }
923             }
924 
925             while (token.isSymbol("(")) {
926                 token = parser.getToken(true, true);
927                 if (token.isIdent() && (token.value in kCONSTRAINTS)) {
928                     var constraint = token.value;
929                     token = parser.getToken(true, true);
930                     if (token.isSymbol(":")) {
931                         token = parser.getToken(true, true);
932                         var values = [];
933                         while (!token.isSymbol(")")) {
934                             values.push(token.value);
935                             token = parser.getToken(true, true);
936                         }
937                         if (token.isSymbol(")")) {
938                             m.constraints.push({constraint:constraint, value:values});
939                             token = parser.getToken(true, true);
940                             if (token.isNotNull()) {
941                                 if (token.isIdent("and")) {
942                                     token = parser.getToken(true, true);
943                                 } else {
944                                     return null;
945                                 }
946                             } else {
947                                 return m;
948                             }
949                         } else {
950                             return null;
951                         }
952                     } else if (token.isSymbol(")")) {
953                         m.constraints.push({constraint:constraint, value:null});
954                         token = parser.getToken(true, true);
955                         if (token.isNotNull()) {
956                             if (token.isIdent("and")) {
957                                 token = parser.getToken(true, true);
958                             } else {
959                                 return null;
960                             }
961                         } else {
962                             return m;
963                         }
964                     } else {
965                         return null;
966                     }
967                 } else {
968                     return null;
969                 }
970             }
971             return m;
972         }
973 
974     };
975 
976 
977     /************************************************************/
978     /************************** JSCSSP **************************/
979     /************************************************************/
980 
981     var CSS_ESCAPE = '\\';
982 
983     var IS_HEX_DIGIT = 1;
984     var START_IDENT = 2;
985     var IS_IDENT = 4;
986     var IS_WHITESPACE = 8;
987 
988     var W = IS_WHITESPACE;
989     var I = IS_IDENT;
990     var S = START_IDENT;
991     var SI = IS_IDENT | START_IDENT;
992     var XI = IS_IDENT | IS_HEX_DIGIT;
993     var XSI = IS_IDENT | START_IDENT | IS_HEX_DIGIT;
994 
995     function CSSScanner(aString) {
996         this.init(aString);
997     }
998 
999     CSSScanner.prototype = {
1000 
1001         kLexTable:[
1002             //                                     TAB LF      FF  CR
1003             0, 0, 0, 0, 0, 0, 0, 0, 0, W, W, 0, W, W, 0, 0, //
1004             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // SPC !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
1005             W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, I, 0, 0, // 0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
1006             XI, XI, XI, XI, XI, XI, XI, XI, XI, XI, 0, 0, 0, 0, 0, 0, // @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1007             0, XSI, XSI, XSI, XSI, XSI, XSI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
1008             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, 0, S, 0, 0, SI, // `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1009             0, XSI, XSI, XSI, XSI, XSI, XSI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~
1010             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, 0, 0, 0, 0, 0, //
1011             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
1012             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //     ¡   ¢   £   ¤   ¥   ¦   §   ¨   ©   ª   «   ¬   ­   ®   ¯
1013             0, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // °   ±   ²   ³   ´   µ   ¶   ·   ¸   ¹   º   »   ¼   ½   ¾   ¿
1014             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // À   Á   Â   Ã   Ä   Å   Æ   Ç   È   É   Ê   Ë   Ì   Í   Î   Ï
1015             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // Ð   Ñ   Ò   Ó   Ô   Õ   Ö   ×   Ø   Ù   Ú   Û   Ü   Ý   Þ   ß
1016             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // à   á   â   ã   ä   å   æ   ç   è   é   ê   ë   ì   í   î   ï
1017             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, // ð   ñ   ò   ó   ô   õ   ö   ÷   ø   ù   ú   û   ü   ý   þ   ÿ
1018             SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI
1019         ],
1020 
1021         kHexValues:{
1022             "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9,
1023             "a":10, "b":11, "c":12, "d":13, "e":14, "f":15
1024         },
1025 
1026         mString:"",
1027         mPos:0,
1028         mPreservedPos:[],
1029 
1030         init:function (aString) {
1031             this.mString = aString;
1032             this.mPos = 0;
1033             this.mPreservedPos = [];
1034         },
1035 
1036         getCurrentPos:function () {
1037             return this.mPos;
1038         },
1039 
1040         getAlreadyScanned:function () {
1041             return this.mString.substr(0, this.mPos);
1042         },
1043 
1044         preserveState:function () {
1045             this.mPreservedPos.push(this.mPos);
1046         },
1047 
1048         restoreState:function () {
1049             if (this.mPreservedPos.length) {
1050                 this.mPos = this.mPreservedPos.pop();
1051             }
1052         },
1053 
1054         forgetState:function () {
1055             if (this.mPreservedPos.length) {
1056                 this.mPreservedPos.pop();
1057             }
1058         },
1059 
1060         read:function () {
1061             if (this.mPos < this.mString.length) {
1062                 return this.mString.charAt(this.mPos++);
1063             }
1064             return -1;
1065         },
1066 
1067         peek:function () {
1068             if (this.mPos < this.mString.length) {
1069                 return this.mString.charAt(this.mPos);
1070             }
1071             return -1;
1072         },
1073 
1074         isHexDigit:function (c) {
1075             var code = c.charCodeAt(0);
1076             return (code < 256 && (this.kLexTable[code] & IS_HEX_DIGIT) != 0);
1077         },
1078 
1079         isIdentStart:function (c) {
1080             var code = c.charCodeAt(0);
1081             return (code >= 256 || (this.kLexTable[code] & START_IDENT) != 0);
1082         },
1083 
1084         startsWithIdent:function (aFirstChar, aSecondChar) {
1085             var code = aFirstChar.charCodeAt(0);
1086             return this.isIdentStart(aFirstChar) || (aFirstChar == "-" && this.isIdentStart(aSecondChar));
1087         },
1088 
1089         isIdent:function (c) {
1090             var code = c.charCodeAt(0);
1091             return (code >= 256 || (this.kLexTable[code] & IS_IDENT) != 0);
1092         },
1093 
1094         pushback:function () {
1095             this.mPos--;
1096         },
1097 
1098         nextHexValue:function () {
1099             var c = this.read();
1100             if (c == -1 || !this.isHexDigit(c)) {
1101                 return new jscsspToken(jscsspToken.NULL_TYPE, null);
1102             }
1103             var s = c;
1104             c = this.read();
1105             while (c != -1 && this.isHexDigit(c)) {
1106                 s += c;
1107                 c = this.read();
1108             }
1109             if (c != -1) {
1110                 this.pushback();
1111             }
1112             return new jscsspToken(jscsspToken.HEX_TYPE, s);
1113         },
1114 
1115         gatherEscape:function () {
1116             var c = this.peek();
1117             if (c == -1) {
1118                 return "";
1119             }
1120             if (this.isHexDigit(c)) {
1121                 var code = 0;
1122                 for (var i = 0; i < 6; i++) {
1123                     c = this.read();
1124                     if (this.isHexDigit(c)) {
1125                         code = code * 16 + this.kHexValues[c.toLowerCase()];
1126                     } else if (!this.isHexDigit(c) && !this.isWhiteSpace(c)) {
1127                         this.pushback();
1128                         break;
1129                     } else {
1130                         break;
1131                     }
1132                 }
1133                 if (i == 6) {
1134                     c = this.peek();
1135                     if (this.isWhiteSpace(c)) {
1136                         this.read();
1137                     }
1138                 }
1139                 return String.fromCharCode(code);
1140             }
1141             c = this.read();
1142             if (c != "\n") {
1143                 return c;
1144             }
1145             return "";
1146         },
1147 
1148         gatherIdent:function (c) {
1149             var s = "";
1150             if (c == CSS_ESCAPE) {
1151                 s += this.gatherEscape();
1152             } else {
1153                 s += c;
1154             }
1155             c = this.read();
1156             while (c != -1 && (this.isIdent(c) || c == CSS_ESCAPE)) {
1157                 if (c == CSS_ESCAPE) {
1158                     s += this.gatherEscape();
1159                 } else {
1160                     s += c;
1161                 }
1162                 c = this.read();
1163             }
1164             if (c != -1) {
1165                 this.pushback();
1166             }
1167             return s;
1168         },
1169 
1170         parseIdent:function (c) {
1171             var value = this.gatherIdent(c);
1172             var nextChar = this.peek();
1173             if (nextChar == "(") {
1174                 value += this.read();
1175                 return new jscsspToken(jscsspToken.FUNCTION_TYPE, value);
1176             }
1177             return new jscsspToken(jscsspToken.IDENT_TYPE, value);
1178         },
1179 
1180         isDigit:function (c) {
1181             return (c >= '0') && (c <= '9');
1182         },
1183 
1184         parseComment:function (c) {
1185             var s = c;
1186             while ((c = this.read()) != -1) {
1187                 s += c;
1188                 if (c == "*") {
1189                     c = this.read();
1190                     if (c == -1) {
1191                         break;
1192                     }
1193                     if (c == "/") {
1194                         s += c;
1195                         break;
1196                     }
1197                     this.pushback();
1198                 }
1199             }
1200             return new jscsspToken(jscsspToken.COMMENT_TYPE, s);
1201         },
1202 
1203         parseNumber:function (c) {
1204             var s = c;
1205             var foundDot = false;
1206             while ((c = this.read()) != -1) {
1207                 if (c == ".") {
1208                     if (foundDot) {
1209                         break;
1210                     } else {
1211                         s += c;
1212                         foundDot = true;
1213                     }
1214                 } else if (this.isDigit(c)) {
1215                     s += c;
1216                 } else {
1217                     break;
1218                 }
1219             }
1220 
1221             if (c != -1 && this.startsWithIdent(c, this.peek())) { // DIMENSION
1222                 var unit = this.gatherIdent(c);
1223                 s += unit;
1224                 return new jscsspToken(jscsspToken.DIMENSION_TYPE, s, unit);
1225             } else if (c == "%") {
1226                 s += "%";
1227                 return new jscsspToken(jscsspToken.PERCENTAGE_TYPE, s);
1228             } else if (c != -1) {
1229                 this.pushback();
1230             }
1231             return new jscsspToken(jscsspToken.NUMBER_TYPE, s);
1232         },
1233 
1234         parseString:function (aStop) {
1235             var s = aStop;
1236             var previousChar = aStop;
1237             var c;
1238             while ((c = this.read()) != -1) {
1239                 if (c == aStop && previousChar != CSS_ESCAPE) {
1240                     s += c;
1241                     break;
1242                 } else if (c == CSS_ESCAPE) {
1243                     c = this.peek();
1244                     if (c == -1) {
1245                         break;
1246                     } else if (c == "\n" || c == "\r" || c == "\f") {
1247                         d = c;
1248                         c = this.read();
1249                         // special for Opera that preserves \r\n...
1250                         if (d == "\r") {
1251                             c = this.peek();
1252                             if (c == "\n") {
1253                                 c = this.read();
1254                             }
1255                         }
1256                     } else {
1257                         s += this.gatherEscape();
1258                         c = this.peek();
1259                     }
1260                 } else if (c == "\n" || c == "\r" || c == "\f") {
1261                     break;
1262                 } else {
1263                     s += c;
1264                 }
1265 
1266                 previousChar = c;
1267             }
1268             return new jscsspToken(jscsspToken.STRING_TYPE, s);
1269         },
1270 
1271         isWhiteSpace:function (c) {
1272             var code = c.charCodeAt(0);
1273             return code < 256 && (this.kLexTable[code] & IS_WHITESPACE) != 0;
1274         },
1275 
1276         eatWhiteSpace:function (c) {
1277             var s = c;
1278             while ((c = this.read()) != -1) {
1279                 if (!this.isWhiteSpace(c)) {
1280                     break;
1281                 }
1282                 s += c;
1283             }
1284             if (c != -1) {
1285                 this.pushback();
1286             }
1287             return s;
1288         },
1289 
1290         parseAtKeyword:function (c) {
1291             return new jscsspToken(jscsspToken.ATRULE_TYPE, this.gatherIdent(c));
1292         },
1293 
1294         nextToken:function () {
1295             var c = this.read();
1296             if (c == -1) {
1297                 return new jscsspToken(jscsspToken.NULL_TYPE, null);
1298             }
1299 
1300             if (this.startsWithIdent(c, this.peek())) {
1301                 return this.parseIdent(c);
1302             }
1303 
1304             if (c == '@') {
1305                 var nextChar = this.read();
1306                 if (nextChar != -1) {
1307                     var followingChar = this.peek();
1308                     this.pushback();
1309                     if (this.startsWithIdent(nextChar, followingChar)) {
1310                         return this.parseAtKeyword(c);
1311                     }
1312                 }
1313             }
1314 
1315             if (c == "." || c == "+" || c == "-") {
1316                 var nextChar = this.peek();
1317                 if (this.isDigit(nextChar)) {
1318                     return this.parseNumber(c);
1319                 } else if (nextChar == "." && c != ".") {
1320                     firstChar = this.read();
1321                     var secondChar = this.peek();
1322                     this.pushback();
1323                     if (this.isDigit(secondChar)) {
1324                         return this.parseNumber(c);
1325                     }
1326                 }
1327             }
1328             if (this.isDigit(c)) {
1329                 return this.parseNumber(c);
1330             }
1331 
1332             if (c == "'" || c == '"') {
1333                 return this.parseString(c);
1334             }
1335 
1336             if (this.isWhiteSpace(c)) {
1337                 var s = this.eatWhiteSpace(c);
1338 
1339                 return new jscsspToken(jscsspToken.WHITESPACE_TYPE, s);
1340             }
1341 
1342             if (c == "|" || c == "~" || c == "^" || c == "$" || c == "*") {
1343                 var nextChar = this.read();
1344                 if (nextChar == "=") {
1345                     switch (c) {
1346                         case "~" :
1347                             return new jscsspToken(jscsspToken.INCLUDES_TYPE, "~=");
1348                         case "|" :
1349                             return new jscsspToken(jscsspToken.DASHMATCH_TYPE, "|=");
1350                         case "^" :
1351                             return new jscsspToken(jscsspToken.BEGINSMATCH_TYPE, "^=");
1352                         case "$" :
1353                             return new jscsspToken(jscsspToken.ENDSMATCH_TYPE, "$=");
1354                         case "*" :
1355                             return new jscsspToken(jscsspToken.CONTAINSMATCH_TYPE, "*=");
1356                         default :
1357                             break;
1358                     }
1359                 } else if (nextChar != -1) {
1360                     this.pushback();
1361                 }
1362             }
1363 
1364             if (c == "/" && this.peek() == "*") {
1365                 return this.parseComment(c);
1366             }
1367 
1368             return new jscsspToken(jscsspToken.SYMBOL_TYPE, c);
1369         }
1370     };
1371 
1372     function CSSParser(aString) {
1373         this.mToken = null;
1374         this.mLookAhead = null;
1375         this.mScanner = new CSSScanner(aString);
1376 
1377         this.mPreserveWS = true;
1378         this.mPreserveComments = true;
1379 
1380         this.mPreservedTokens = [];
1381 
1382         this.mError = null;
1383     }
1384 
1385     CSSParser.prototype = {
1386 
1387         _init:function () {
1388             this.mToken = null;
1389             this.mLookAhead = null;
1390         },
1391 
1392         kINHERIT:"inherit",
1393 
1394         kBORDER_WIDTH_NAMES:{
1395             "thin":true,
1396             "medium":true,
1397             "thick":true
1398         },
1399 
1400         kBORDER_STYLE_NAMES:{
1401             "none":true,
1402             "hidden":true,
1403             "dotted":true,
1404             "dashed":true,
1405             "solid":true,
1406             "double":true,
1407             "groove":true,
1408             "ridge":true,
1409             "inset":true,
1410             "outset":true
1411         },
1412 
1413         kCOLOR_NAMES:{
1414             "transparent":true,
1415 
1416             "black":true,
1417             "silver":true,
1418             "gray":true,
1419             "white":true,
1420             "maroon":true,
1421             "red":true,
1422             "purple":true,
1423             "fuchsia":true,
1424             "green":true,
1425             "lime":true,
1426             "olive":true,
1427             "yellow":true,
1428             "navy":true,
1429             "blue":true,
1430             "teal":true,
1431             "aqua":true,
1432 
1433             "aliceblue":true,
1434             "antiquewhite":true,
1435             "aqua":true,
1436             "aquamarine":true,
1437             "azure":true,
1438             "beige":true,
1439             "bisque":true,
1440             "black":true,
1441             "blanchedalmond":true,
1442             "blue":true,
1443             "blueviolet":true,
1444             "brown":true,
1445             "burlywood":true,
1446             "cadetblue":true,
1447             "chartreuse":true,
1448             "chocolate":true,
1449             "coral":true,
1450             "cornflowerblue":true,
1451             "cornsilk":true,
1452             "crimson":true,
1453             "cyan":true,
1454             "darkblue":true,
1455             "darkcyan":true,
1456             "darkgoldenrod":true,
1457             "darkgray":true,
1458             "darkgreen":true,
1459             "darkgrey":true,
1460             "darkkhaki":true,
1461             "darkmagenta":true,
1462             "darkolivegreen":true,
1463             "darkorange":true,
1464             "darkorchid":true,
1465             "darkred":true,
1466             "darksalmon":true,
1467             "darkseagreen":true,
1468             "darkslateblue":true,
1469             "darkslategray":true,
1470             "darkslategrey":true,
1471             "darkturquoise":true,
1472             "darkviolet":true,
1473             "deeppink":true,
1474             "deepskyblue":true,
1475             "dimgray":true,
1476             "dimgrey":true,
1477             "dodgerblue":true,
1478             "firebrick":true,
1479             "floralwhite":true,
1480             "forestgreen":true,
1481             "fuchsia":true,
1482             "gainsboro":true,
1483             "ghostwhite":true,
1484             "gold":true,
1485             "goldenrod":true,
1486             "gray":true,
1487             "green":true,
1488             "greenyellow":true,
1489             "grey":true,
1490             "honeydew":true,
1491             "hotpink":true,
1492             "indianred":true,
1493             "indigo":true,
1494             "ivory":true,
1495             "khaki":true,
1496             "lavender":true,
1497             "lavenderblush":true,
1498             "lawngreen":true,
1499             "lemonchiffon":true,
1500             "lightblue":true,
1501             "lightcoral":true,
1502             "lightcyan":true,
1503             "lightgoldenrodyellow":true,
1504             "lightgray":true,
1505             "lightgreen":true,
1506             "lightgrey":true,
1507             "lightpink":true,
1508             "lightsalmon":true,
1509             "lightseagreen":true,
1510             "lightskyblue":true,
1511             "lightslategray":true,
1512             "lightslategrey":true,
1513             "lightsteelblue":true,
1514             "lightyellow":true,
1515             "lime":true,
1516             "limegreen":true,
1517             "linen":true,
1518             "magenta":true,
1519             "maroon":true,
1520             "mediumaquamarine":true,
1521             "mediumblue":true,
1522             "mediumorchid":true,
1523             "mediumpurple":true,
1524             "mediumseagreen":true,
1525             "mediumslateblue":true,
1526             "mediumspringgreen":true,
1527             "mediumturquoise":true,
1528             "mediumvioletred":true,
1529             "midnightblue":true,
1530             "mintcream":true,
1531             "mistyrose":true,
1532             "moccasin":true,
1533             "navajowhite":true,
1534             "navy":true,
1535             "oldlace":true,
1536             "olive":true,
1537             "olivedrab":true,
1538             "orange":true,
1539             "orangered":true,
1540             "orchid":true,
1541             "palegoldenrod":true,
1542             "palegreen":true,
1543             "paleturquoise":true,
1544             "palevioletred":true,
1545             "papayawhip":true,
1546             "peachpuff":true,
1547             "peru":true,
1548             "pink":true,
1549             "plum":true,
1550             "powderblue":true,
1551             "purple":true,
1552             "red":true,
1553             "rosybrown":true,
1554             "royalblue":true,
1555             "saddlebrown":true,
1556             "salmon":true,
1557             "sandybrown":true,
1558             "seagreen":true,
1559             "seashell":true,
1560             "sienna":true,
1561             "silver":true,
1562             "skyblue":true,
1563             "slateblue":true,
1564             "slategray":true,
1565             "slategrey":true,
1566             "snow":true,
1567             "springgreen":true,
1568             "steelblue":true,
1569             "tan":true,
1570             "teal":true,
1571             "thistle":true,
1572             "tomato":true,
1573             "turquoise":true,
1574             "violet":true,
1575             "wheat":true,
1576             "white":true,
1577             "whitesmoke":true,
1578             "yellow":true,
1579             "yellowgreen":true,
1580 
1581             "activeborder":true,
1582             "activecaption":true,
1583             "appworkspace":true,
1584             "background":true,
1585             "buttonface":true,
1586             "buttonhighlight":true,
1587             "buttonshadow":true,
1588             "buttontext":true,
1589             "captiontext":true,
1590             "graytext":true,
1591             "highlight":true,
1592             "highlighttext":true,
1593             "inactiveborder":true,
1594             "inactivecaption":true,
1595             "inactivecaptiontext":true,
1596             "infobackground":true,
1597             "infotext":true,
1598             "menu":true,
1599             "menutext":true,
1600             "scrollbar":true,
1601             "threeddarkshadow":true,
1602             "threedface":true,
1603             "threedhighlight":true,
1604             "threedlightshadow":true,
1605             "threedshadow":true,
1606             "window":true,
1607             "windowframe":true,
1608             "windowtext":true
1609         },
1610 
1611         kLIST_STYLE_TYPE_NAMES:{
1612             "decimal":true,
1613             "decimal-leading-zero":true,
1614             "lower-roman":true,
1615             "upper-roman":true,
1616             "georgian":true,
1617             "armenian":true,
1618             "lower-latin":true,
1619             "lower-alpha":true,
1620             "upper-latin":true,
1621             "upper-alpha":true,
1622             "lower-greek":true,
1623 
1624             "disc":true,
1625             "circle":true,
1626             "square":true,
1627             "none":true,
1628 
1629             /* CSS 3 */
1630             "box":true,
1631             "check":true,
1632             "diamond":true,
1633             "hyphen":true,
1634 
1635             "lower-armenian":true,
1636             "cjk-ideographic":true,
1637             "ethiopic-numeric":true,
1638             "hebrew":true,
1639             "japanese-formal":true,
1640             "japanese-informal":true,
1641             "simp-chinese-formal":true,
1642             "simp-chinese-informal":true,
1643             "syriac":true,
1644             "tamil":true,
1645             "trad-chinese-formal":true,
1646             "trad-chinese-informal":true,
1647             "upper-armenian":true,
1648             "arabic-indic":true,
1649             "binary":true,
1650             "bengali":true,
1651             "cambodian":true,
1652             "khmer":true,
1653             "devanagari":true,
1654             "gujarati":true,
1655             "gurmukhi":true,
1656             "kannada":true,
1657             "lower-hexadecimal":true,
1658             "lao":true,
1659             "malayalam":true,
1660             "mongolian":true,
1661             "myanmar":true,
1662             "octal":true,
1663             "oriya":true,
1664             "persian":true,
1665             "urdu":true,
1666             "telugu":true,
1667             "tibetan":true,
1668             "upper-hexadecimal":true,
1669             "afar":true,
1670             "ethiopic-halehame-aa-et":true,
1671             "ethiopic-halehame-am-et":true,
1672             "amharic-abegede":true,
1673             "ehiopic-abegede-am-et":true,
1674             "cjk-earthly-branch":true,
1675             "cjk-heavenly-stem":true,
1676             "ethiopic":true,
1677             "ethiopic-abegede":true,
1678             "ethiopic-abegede-gez":true,
1679             "hangul-consonant":true,
1680             "hangul":true,
1681             "hiragana-iroha":true,
1682             "hiragana":true,
1683             "katakana-iroha":true,
1684             "katakana":true,
1685             "lower-norwegian":true,
1686             "oromo":true,
1687             "ethiopic-halehame-om-et":true,
1688             "sidama":true,
1689             "ethiopic-halehame-sid-et":true,
1690             "somali":true,
1691             "ethiopic-halehame-so-et":true,
1692             "tigre":true,
1693             "ethiopic-halehame-tig":true,
1694             "tigrinya-er-abegede":true,
1695             "ethiopic-abegede-ti-er":true,
1696             "tigrinya-et":true,
1697             "ethiopic-halehame-ti-et":true,
1698             "upper-greek":true,
1699             "asterisks":true,
1700             "footnotes":true,
1701             "circled-decimal":true,
1702             "circled-lower-latin":true,
1703             "circled-upper-latin":true,
1704             "dotted-decimal":true,
1705             "double-circled-decimal":true,
1706             "filled-circled-decimal":true,
1707             "parenthesised-decimal":true,
1708             "parenthesised-lower-latin":true
1709         },
1710 
1711         reportError:function (aMsg) {
1712             this.mError = aMsg;
1713         },
1714 
1715         consumeError:function () {
1716             var e = this.mError;
1717             this.mError = null;
1718             return e;
1719         },
1720 
1721         currentToken:function () {
1722             return this.mToken;
1723         },
1724 
1725         getHexValue:function () {
1726             this.mToken = this.mScanner.nextHexValue();
1727             return this.mToken;
1728         },
1729 
1730         getToken:function (aSkipWS, aSkipComment) {
1731             if (this.mLookAhead) {
1732                 this.mToken = this.mLookAhead;
1733                 this.mLookAhead = null;
1734                 return this.mToken;
1735             }
1736 
1737             this.mToken = this.mScanner.nextToken();
1738             while (this.mToken && ((aSkipWS && this.mToken.isWhiteSpace()) || (aSkipComment && this.mToken.isComment()))) {
1739                 this.mToken = this.mScanner.nextToken();
1740             }
1741             return this.mToken;
1742         },
1743 
1744         lookAhead:function (aSkipWS, aSkipComment) {
1745             var preservedToken = this.mToken;
1746             this.mScanner.preserveState();
1747             var token = this.getToken(aSkipWS, aSkipComment);
1748             this.mScanner.restoreState();
1749             this.mToken = preservedToken;
1750 
1751             return token;
1752         },
1753 
1754         ungetToken:function () {
1755             this.mLookAhead = this.mToken;
1756         },
1757 
1758         addUnknownAtRule:function (aSheet, aString) {
1759             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
1760             var blocks = [];
1761             var token = this.getToken(false, false);
1762             while (token.isNotNull()) {
1763                 aString += token.value;
1764                 if (token.isSymbol(";") && !blocks.length) {
1765                     break;
1766                 } else if (token.isSymbol("{") || token.isSymbol("(") || token.isSymbol("[") || token.type == "function") {
1767                     blocks.push(token.isFunction() ? "(" : token.value);
1768                 } else if (token.isSymbol("}") || token.isSymbol(")") || token.isSymbol("]")) {
1769                     if (blocks.length) {
1770                         var ontop = blocks[blocks.length - 1];
1771                         if ((token.isSymbol("}") && ontop == "{") || (token.isSymbol(")") && ontop == "(") || (token.isSymbol("]") && ontop == "[")) {
1772                             blocks.pop();
1773                             if (!blocks.length && token.isSymbol("}")) {
1774                                 break;
1775                             }
1776                         }
1777                     }
1778                 }
1779                 token = this.getToken(false, false);
1780             }
1781 
1782             this.addUnknownRule(aSheet, aString, currentLine);
1783         },
1784 
1785         addUnknownRule:function (aSheet, aString, aCurrentLine) {
1786             var errorMsg = this.consumeError();
1787             var rule = new jscsspErrorRule(errorMsg);
1788             rule.currentLine = aCurrentLine;
1789             rule.parsedCssText = aString;
1790             rule.parentStyleSheet = aSheet;
1791             aSheet.cssRules.push(rule);
1792         },
1793 
1794         addWhitespace:function (aSheet, aString) {
1795             var rule = new jscsspWhitespace();
1796             rule.parsedCssText = aString;
1797             rule.parentStyleSheet = aSheet;
1798             aSheet.cssRules.push(rule);
1799         },
1800 
1801         addComment:function (aSheet, aString) {
1802             var rule = new jscsspComment();
1803             rule.parsedCssText = aString;
1804             rule.parentStyleSheet = aSheet;
1805             aSheet.cssRules.push(rule);
1806         },
1807 
1808         parseCharsetRule:function (aToken, aSheet) {
1809             var s = aToken.value;
1810             var token = this.getToken(false, false);
1811             s += token.value;
1812             if (token.isWhiteSpace(" ")) {
1813                 token = this.getToken(false, false);
1814                 s += token.value;
1815                 if (token.isString()) {
1816                     var encoding = token.value;
1817                     token = this.getToken(false, false);
1818                     s += token.value;
1819                     if (token.isSymbol(";")) {
1820                         var rule = new jscsspCharsetRule();
1821                         rule.encoding = encoding;
1822                         rule.parsedCssText = s;
1823                         rule.parentStyleSheet = aSheet;
1824                         aSheet.cssRules.push(rule);
1825                         return true;
1826                     } else {
1827                         this.reportError(kCHARSET_RULE_MISSING_SEMICOLON);
1828                     }
1829                 } else {
1830                     this.reportError(kCHARSET_RULE_CHARSET_IS_STRING);
1831                 }
1832             } else {
1833                 this.reportError(kCHARSET_RULE_MISSING_WS);
1834             }
1835 
1836             this.addUnknownAtRule(aSheet, s);
1837             return false;
1838         },
1839 
1840         parseImportRule:function (aToken, aSheet) {
1841             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
1842             var s = aToken.value;
1843             this.preserveState();
1844             var token = this.getToken(true, true);
1845             var media = [];
1846             var href = "";
1847             if (token.isString()) {
1848                 href = token.value;
1849                 s += " " + href;
1850             } else if (token.isFunction("url(")) {
1851                 token = this.getToken(true, true);
1852                 var urlContent = this.parseURL(token);
1853                 if (urlContent) {
1854                     href = "url(" + urlContent;
1855                     s += " " + href;
1856                 }
1857             } else {
1858                 this.reportError(kIMPORT_RULE_MISSING_URL);
1859             }
1860 
1861             if (href) {
1862                 token = this.getToken(true, true);
1863                 while (token.isIdent()) {
1864                     s += " " + token.value;
1865                     media.push(token.value);
1866                     token = this.getToken(true, true);
1867                     if (!token) {
1868                         break;
1869                     }
1870                     if (token.isSymbol(",")) {
1871                         s += ",";
1872                     } else if (token.isSymbol(";")) {
1873                         break;
1874                     } else {
1875                         break;
1876                     }
1877                     token = this.getToken(true, true);
1878                 }
1879 
1880                 if (!media.length) {
1881                     media.push("all");
1882                 }
1883 
1884                 if (token.isSymbol(";")) {
1885                     s += ";"
1886                     this.forgetState();
1887                     var rule = new jscsspImportRule();
1888                     rule.currentLine = currentLine;
1889                     rule.parsedCssText = s;
1890                     rule.href = href;
1891                     rule.media = media;
1892                     rule.parentStyleSheet = aSheet;
1893                     aSheet.cssRules.push(rule);
1894                     return true;
1895                 }
1896             }
1897 
1898             this.restoreState();
1899             this.addUnknownAtRule(aSheet, "@import");
1900             return false;
1901         },
1902 
1903         parseVariablesRule:function (token, aSheet) {
1904             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
1905             var s = token.value;
1906             var declarations = [];
1907             var valid = false;
1908             this.preserveState();
1909             token = this.getToken(true, true);
1910             var media = [];
1911             var foundMedia = false;
1912             while (token.isNotNull()) {
1913                 if (token.isIdent()) {
1914                     foundMedia = true;
1915                     s += " " + token.value;
1916                     media.push(token.value);
1917                     token = this.getToken(true, true);
1918                     if (token.isSymbol(",")) {
1919                         s += ",";
1920                     } else {
1921                         if (token.isSymbol("{")) {
1922                             this.ungetToken();
1923                         } else {
1924                             // error...
1925                             token.type = jscsspToken.NULL_TYPE;
1926                             break;
1927                         }
1928                     }
1929                 } else if (token.isSymbol("{")) {
1930                     break;
1931                 } else if (foundMedia) {
1932                     token.type = jscsspToken.NULL_TYPE;
1933                     // not a media list
1934                     break;
1935                 }
1936                 token = this.getToken(true, true);
1937             }
1938 
1939             if (token.isSymbol("{")) {
1940                 s += " {";
1941                 token = this.getToken(true, true);
1942                 while (true) {
1943                     if (!token.isNotNull()) {
1944                         valid = true;
1945                         break;
1946                     }
1947                     if (token.isSymbol("}")) {
1948                         s += "}";
1949                         valid = true;
1950                         break;
1951                     } else {
1952                         var d = this.parseDeclaration(token, declarations, true, false, aSheet);
1953                         s += ((d && declarations.length) ? " " : "") + d;
1954                     }
1955                     token = this.getToken(true, false);
1956                 }
1957             }
1958             if (valid) {
1959                 this.forgetState();
1960                 var rule = new jscsspVariablesRule();
1961                 rule.currentLine = currentLine;
1962                 rule.parsedCssText = s;
1963                 rule.declarations = declarations;
1964                 rule.media = media;
1965                 rule.parentStyleSheet = aSheet;
1966                 aSheet.cssRules.push(rule)
1967                 return true;
1968             }
1969             this.restoreState();
1970             return false;
1971         },
1972 
1973         parseNamespaceRule:function (aToken, aSheet) {
1974             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
1975             var s = aToken.value;
1976             var valid = false;
1977             this.preserveState();
1978             var token = this.getToken(true, true);
1979             if (token.isNotNull()) {
1980                 var prefix = "";
1981                 var url = "";
1982                 if (token.isIdent()) {
1983                     prefix = token.value;
1984                     s += " " + prefix;
1985                     token = this.getToken(true, true);
1986                 }
1987                 if (token) {
1988                     var foundURL = false;
1989                     if (token.isString()) {
1990                         foundURL = true;
1991                         url = token.value;
1992                         s += " " + url;
1993                     } else if (token.isFunction("url(")) {
1994                         // get a url here...
1995                         token = this.getToken(true, true);
1996                         var urlContent = this.parseURL(token);
1997                         if (urlContent) {
1998                             url += "url(" + urlContent;
1999                             foundURL = true;
2000                             s += " " + urlContent;
2001                         }
2002                     }
2003                 }
2004                 if (foundURL) {
2005                     token = this.getToken(true, true);
2006                     if (token.isSymbol(";")) {
2007                         s += ";";
2008                         this.forgetState();
2009                         var rule = new jscsspNamespaceRule();
2010                         rule.currentLine = currentLine;
2011                         rule.parsedCssText = s;
2012                         rule.prefix = prefix;
2013                         rule.url = url;
2014                         rule.parentStyleSheet = aSheet;
2015                         aSheet.cssRules.push(rule);
2016                         return true;
2017                     }
2018                 }
2019 
2020             }
2021             this.restoreState();
2022             this.addUnknownAtRule(aSheet, "@namespace");
2023             return false;
2024         },
2025 
2026         parseFontFaceRule:function (aToken, aSheet) {
2027             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
2028             var s = aToken.value;
2029             var valid = false;
2030             var descriptors = [];
2031             this.preserveState();
2032             var token = this.getToken(true, true);
2033             if (token.isNotNull()) {
2034                 // expecting block start
2035                 if (token.isSymbol("{")) {
2036                     s += " " + token.value;
2037                     var token = this.getToken(true, false);
2038                     while (true) {
2039                         if (token.isSymbol("}")) {
2040                             s += "}";
2041                             valid = true;
2042                             break;
2043                         } else {
2044                             var d = this.parseDeclaration(token, descriptors, false, false, aSheet);
2045                             s += ((d && descriptors.length) ? " " : "") + d;
2046                         }
2047                         token = this.getToken(true, false);
2048                     }
2049                 }
2050             }
2051             if (valid) {
2052                 this.forgetState();
2053                 var rule = new jscsspFontFaceRule();
2054                 rule.currentLine = currentLine;
2055                 rule.parsedCssText = s;
2056                 rule.descriptors = descriptors;
2057                 rule.parentStyleSheet = aSheet;
2058                 aSheet.cssRules.push(rule)
2059                 return true;
2060             }
2061             this.restoreState();
2062             return false;
2063         },
2064 
2065         parsePageRule:function (aToken, aSheet) {
2066             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
2067             var s = aToken.value;
2068             var valid = false;
2069             var declarations = [];
2070             this.preserveState();
2071             var token = this.getToken(true, true);
2072             var pageSelector = "";
2073             if (token.isSymbol(":") || token.isIdent()) {
2074                 if (token.isSymbol(":")) {
2075                     pageSelector = ":";
2076                     token = this.getToken(false, false);
2077                 }
2078                 if (token.isIdent()) {
2079                     pageSelector += token.value;
2080                     s += " " + pageSelector;
2081                     token = this.getToken(true, true);
2082                 }
2083             }
2084             if (token.isNotNull()) {
2085                 // expecting block start
2086                 if (token.isSymbol("{")) {
2087                     s += " " + token.value;
2088                     var token = this.getToken(true, false);
2089                     while (true) {
2090                         if (token.isSymbol("}")) {
2091                             s += "}";
2092                             valid = true;
2093                             break;
2094                         } else {
2095                             var d = this.parseDeclaration(token, declarations, true, true, aSheet);
2096                             s += ((d && declarations.length) ? " " : "") + d;
2097                         }
2098                         token = this.getToken(true, false);
2099                     }
2100                 }
2101             }
2102             if (valid) {
2103                 this.forgetState();
2104                 var rule = new jscsspPageRule();
2105                 rule.currentLine = currentLine;
2106                 rule.parsedCssText = s;
2107                 rule.pageSelector = pageSelector;
2108                 rule.declarations = declarations;
2109                 rule.parentStyleSheet = aSheet;
2110                 aSheet.cssRules.push(rule)
2111                 return true;
2112             }
2113             this.restoreState();
2114             return false;
2115         },
2116 
2117         parseDefaultPropertyValue:function (token, aDecl, aAcceptPriority, descriptor, aSheet) {
2118             var valueText = "";
2119             var blocks = [];
2120             var foundPriority = false;
2121             var values = [];
2122             while (token.isNotNull()) {
2123 
2124                 if ((token.isSymbol(";") || token.isSymbol("}") || token.isSymbol("!")) && !blocks.length) {
2125                     if (token.isSymbol("}")) {
2126                         this.ungetToken();
2127                     }
2128                     break;
2129                 }
2130 
2131                 if (token.isIdent(this.kINHERIT)) {
2132                     if (values.length) {
2133                         return "";
2134                     } else {
2135                         valueText = this.kINHERIT;
2136                         var value = new jscsspVariable(kJscsspINHERIT_VALUE, aSheet);
2137                         values.push(value);
2138                         token = this.getToken(true, true);
2139                         break;
2140                     }
2141                 } else if (token.isSymbol("{") || token.isSymbol("(") || token.isSymbol("[")) {
2142                     blocks.push(token.value);
2143                 } else if (token.isSymbol("}") || token.isSymbol("]")) {
2144                     if (blocks.length) {
2145                         var ontop = blocks[blocks.length - 1];
2146                         if ((token.isSymbol("}") && ontop == "{") || (token.isSymbol(")") && ontop == "(") || (token.isSymbol("]") && ontop == "[")) {
2147                             blocks.pop();
2148                         }
2149                     }
2150                 }
2151                 // XXX must find a better way to store individual values
2152                 // probably a |values: []| field holding dimensions, percentages
2153                 // functions, idents, numbers and symbols, in that order.
2154                 if (token.isFunction()) {
2155                     if (token.isFunction("var(")) {
2156                         token = this.getToken(true, true);
2157                         if (token.isIdent()) {
2158                             var name = token.value;
2159                             token = this.getToken(true, true);
2160                             if (token.isSymbol(")")) {
2161                                 var value = new jscsspVariable(kJscsspVARIABLE_VALUE, aSheet);
2162                                 valueText += "var(" + name + ")";
2163                                 value.name = name;
2164                                 values.push(value);
2165                             } else {
2166                                 return "";
2167                             }
2168                         } else {
2169                             return "";
2170                         }
2171                     } else {
2172                         var fn = token.value;
2173                         token = this.getToken(false, true);
2174                         var arg = this.parseFunctionArgument(token);
2175                         if (arg) {
2176                             valueText += fn + arg;
2177                             var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet);
2178                             value.value = fn + arg;
2179                             values.push(value);
2180                         } else {
2181                             return "";
2182                         }
2183                     }
2184                 } else if (token.isSymbol("#")) {
2185                     var color = this.parseColor(token);
2186                     if (color) {
2187                         valueText += color;
2188                         var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet);
2189                         value.value = color;
2190                         values.push(value);
2191                     } else {
2192                         return "";
2193                     }
2194                 } else if (!token.isWhiteSpace() && !token.isSymbol(",")) {
2195                     var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet);
2196                     value.value = token.value;
2197                     values.push(value);
2198                     valueText += token.value;
2199                 } else {
2200                     valueText += token.value;
2201                 }
2202                 token = this.getToken(false, true);
2203             }
2204             if (values.length && valueText) {
2205                 this.forgetState();
2206                 aDecl.push(this._createJscsspDeclarationFromValuesArray(descriptor, values, valueText));
2207                 return valueText;
2208             }
2209             return "";
2210         },
2211 
2212         parseMarginOrPaddingShorthand:function (token, aDecl, aAcceptPriority, aProperty) {
2213             var top = null;
2214             var bottom = null;
2215             var left = null;
2216             var right = null;
2217 
2218             var values = [];
2219             while (true) {
2220 
2221                 if (!token.isNotNull()) {
2222                     break;
2223                 }
2224 
2225                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2226                     if (token.isSymbol("}")) {
2227                         this.ungetToken();
2228                     }
2229                     break;
2230                 }
2231 
2232                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2233                     values.push(token.value);
2234                     token = this.getToken(true, true);
2235                     break;
2236                 }
2237 
2238                 else if (token.isDimension() || token.isNumber("0") || token.isPercentage() || token.isIdent("auto")) {
2239                     values.push(token.value);
2240                 } else {
2241                     return "";
2242                 }
2243 
2244                 token = this.getToken(true, true);
2245             }
2246 
2247             var count = values.length;
2248             switch (count) {
2249                 case 1:
2250                     top = values[0];
2251                     bottom = top;
2252                     left = top;
2253                     right = top;
2254                     break;
2255                 case 2:
2256                     top = values[0];
2257                     bottom = top;
2258                     left = values[1];
2259                     right = left;
2260                     break;
2261                 case 3:
2262                     top = values[0];
2263                     left = values[1];
2264                     right = left;
2265                     bottom = values[2];
2266                     break;
2267                 case 4:
2268                     top = values[0];
2269                     right = values[1];
2270                     bottom = values[2];
2271                     left = values[3];
2272                     break;
2273                 default:
2274                     return "";
2275             }
2276             this.forgetState();
2277             aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-top", top));
2278             aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-right", right));
2279             aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-bottom", bottom));
2280             aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-left", left));
2281             return top + " " + right + " " + bottom + " " + left;
2282         },
2283 
2284         parseBorderColorShorthand:function (token, aDecl, aAcceptPriority) {
2285             var top = null;
2286             var bottom = null;
2287             var left = null;
2288             var right = null;
2289 
2290             var values = [];
2291             while (true) {
2292 
2293                 if (!token.isNotNull()) {
2294                     break;
2295                 }
2296 
2297                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2298                     if (token.isSymbol("}")) {
2299                         this.ungetToken();
2300                     }
2301                     break;
2302                 }
2303 
2304                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2305                     values.push(token.value);
2306                     token = this.getToken(true, true);
2307                     break;
2308                 }
2309 
2310                 else {
2311                     var color = this.parseColor(token);
2312                     if (color) {
2313                         values.push(color);
2314                     } else {
2315                         return "";
2316                     }
2317                 }
2318 
2319                 token = this.getToken(true, true);
2320             }
2321 
2322             var count = values.length;
2323             switch (count) {
2324                 case 1:
2325                     top = values[0];
2326                     bottom = top;
2327                     left = top;
2328                     right = top;
2329                     break;
2330                 case 2:
2331                     top = values[0];
2332                     bottom = top;
2333                     left = values[1];
2334                     right = left;
2335                     break;
2336                 case 3:
2337                     top = values[0];
2338                     left = values[1];
2339                     right = left;
2340                     bottom = values[2];
2341                     break;
2342                 case 4:
2343                     top = values[0];
2344                     right = values[1];
2345                     bottom = values[2];
2346                     left = values[3];
2347                     break;
2348                 default:
2349                     return "";
2350             }
2351             this.forgetState();
2352             aDecl.push(this._createJscsspDeclarationFromValue("border-top-color", top));
2353             aDecl.push(this._createJscsspDeclarationFromValue("border-right-color", right));
2354             aDecl.push(this._createJscsspDeclarationFromValue("border-bottom-color", bottom));
2355             aDecl.push(this._createJscsspDeclarationFromValue("border-left-color", left));
2356             return top + " " + right + " " + bottom + " " + left;
2357         },
2358 
2359         parseCueShorthand:function (token, declarations, aAcceptPriority) {
2360             var before = "";
2361             var after = "";
2362 
2363             var values = [];
2364             var values = [];
2365             while (true) {
2366 
2367                 if (!token.isNotNull()) {
2368                     break;
2369                 }
2370 
2371                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2372                     if (token.isSymbol("}")) {
2373                         this.ungetToken();
2374                     }
2375                     break;
2376                 }
2377 
2378                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2379                     values.push(token.value);
2380                 }
2381 
2382                 else if (token.isIdent("none")) {
2383                     values.push(token.value);
2384                 }
2385 
2386                 else if (token.isFunction("url(")) {
2387                     var token = this.getToken(true, true);
2388                     var urlContent = this.parseURL(token);
2389                     if (urlContent) {
2390                         values.push("url(" + urlContent);
2391                     } else {
2392                         return "";
2393                     }
2394                 } else {
2395                     return "";
2396                 }
2397 
2398                 token = this.getToken(true, true);
2399             }
2400 
2401             var count = values.length;
2402             switch (count) {
2403                 case 1:
2404                     before = values[0];
2405                     after = before;
2406                     break;
2407                 case 2:
2408                     before = values[0];
2409                     after = values[1];
2410                     break;
2411                 default:
2412                     return "";
2413             }
2414             this.forgetState();
2415             aDecl.push(this._createJscsspDeclarationFromValue("cue-before", before));
2416             aDecl.push(this._createJscsspDeclarationFromValue("cue-after", after));
2417             return before + " " + after;
2418         },
2419 
2420         parsePauseShorthand:function (token, declarations, aAcceptPriority) {
2421             var before = "";
2422             var after = "";
2423 
2424             var values = [];
2425             var values = [];
2426             while (true) {
2427 
2428                 if (!token.isNotNull()) {
2429                     break;
2430                 }
2431 
2432                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2433                     if (token.isSymbol("}")) {
2434                         this.ungetToken();
2435                     }
2436                     break;
2437                 }
2438 
2439                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2440                     values.push(token.value);
2441                 }
2442 
2443                 else if (token.isDimensionOfUnit("ms") || token.isDimensionOfUnit("s") || token.isPercentage() || token.isNumber("0")) {
2444                     values.push(token.value);
2445                 } else {
2446                     return "";
2447                 }
2448 
2449                 token = this.getToken(true, true);
2450             }
2451 
2452             var count = values.length;
2453             switch (count) {
2454                 case 1:
2455                     before = values[0];
2456                     after = before;
2457                     break;
2458                 case 2:
2459                     before = values[0];
2460                     after = values[1];
2461                     break;
2462                 default:
2463                     return "";
2464             }
2465             this.forgetState();
2466             aDecl.push(this._createJscsspDeclarationFromValue("pause-before", before));
2467             aDecl.push(this._createJscsspDeclarationFromValue("pause-after", after));
2468             return before + " " + after;
2469         },
2470 
2471         parseBorderWidthShorthand:function (token, aDecl, aAcceptPriority) {
2472             var top = null;
2473             var bottom = null;
2474             var left = null;
2475             var right = null;
2476 
2477             var values = [];
2478             while (true) {
2479 
2480                 if (!token.isNotNull()) {
2481                     break;
2482                 }
2483 
2484                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2485                     if (token.isSymbol("}")) {
2486                         this.ungetToken();
2487                     }
2488                     break;
2489                 }
2490 
2491                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2492                     values.push(token.value);
2493                 }
2494 
2495                 else if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in this.kBORDER_WIDTH_NAMES)) {
2496                     values.push(token.value);
2497                 } else {
2498                     return "";
2499                 }
2500 
2501                 token = this.getToken(true, true);
2502             }
2503 
2504             var count = values.length;
2505             switch (count) {
2506                 case 1:
2507                     top = values[0];
2508                     bottom = top;
2509                     left = top;
2510                     right = top;
2511                     break;
2512                 case 2:
2513                     top = values[0];
2514                     bottom = top;
2515                     left = values[1];
2516                     right = left;
2517                     break;
2518                 case 3:
2519                     top = values[0];
2520                     left = values[1];
2521                     right = left;
2522                     bottom = values[2];
2523                     break;
2524                 case 4:
2525                     top = values[0];
2526                     right = values[1];
2527                     bottom = values[2];
2528                     left = values[3];
2529                     break;
2530                 default:
2531                     return "";
2532             }
2533             this.forgetState();
2534             aDecl.push(this._createJscsspDeclarationFromValue("border-top-width", top));
2535             aDecl.push(this._createJscsspDeclarationFromValue("border-right-width", right));
2536             aDecl.push(this._createJscsspDeclarationFromValue("border-bottom-width", bottom));
2537             aDecl.push(this._createJscsspDeclarationFromValue("border-left-width", left));
2538             return top + " " + right + " " + bottom + " " + left;
2539         },
2540 
2541         parseBorderStyleShorthand:function (token, aDecl, aAcceptPriority) {
2542             var top = null;
2543             var bottom = null;
2544             var left = null;
2545             var right = null;
2546 
2547             var values = [];
2548             while (true) {
2549 
2550                 if (!token.isNotNull()) {
2551                     break;
2552                 }
2553 
2554                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2555                     if (token.isSymbol("}")) {
2556                         this.ungetToken();
2557                     }
2558                     break;
2559                 }
2560 
2561                 else if (!values.length && token.isIdent(this.kINHERIT)) {
2562                     values.push(token.value);
2563                 }
2564 
2565                 else if (token.isIdent() && token.value in this.kBORDER_STYLE_NAMES) {
2566                     values.push(token.value);
2567                 } else {
2568                     return "";
2569                 }
2570 
2571                 token = this.getToken(true, true);
2572             }
2573 
2574             var count = values.length;
2575             switch (count) {
2576                 case 1:
2577                     top = values[0];
2578                     bottom = top;
2579                     left = top;
2580                     right = top;
2581                     break;
2582                 case 2:
2583                     top = values[0];
2584                     bottom = top;
2585                     left = values[1];
2586                     right = left;
2587                     break;
2588                 case 3:
2589                     top = values[0];
2590                     left = values[1];
2591                     right = left;
2592                     bottom = values[2];
2593                     break;
2594                 case 4:
2595                     top = values[0];
2596                     right = values[1];
2597                     bottom = values[2];
2598                     left = values[3];
2599                     break;
2600                 default:
2601                     return "";
2602             }
2603             this.forgetState();
2604             aDecl.push(this._createJscsspDeclarationFromValue("border-top-style", top));
2605             aDecl.push(this._createJscsspDeclarationFromValue("border-right-style", right));
2606             aDecl.push(this._createJscsspDeclarationFromValue("border-bottom-style", bottom));
2607             aDecl.push(this._createJscsspDeclarationFromValue("border-left-style", left));
2608             return top + " " + right + " " + bottom + " " + left;
2609         },
2610 
2611         parseBorderEdgeOrOutlineShorthand:function (token, aDecl, aAcceptPriority, aProperty) {
2612             var bWidth = null;
2613             var bStyle = null;
2614             var bColor = null;
2615 
2616             while (true) {
2617                 if (!token.isNotNull()) {
2618                     break;
2619                 }
2620 
2621                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2622                     if (token.isSymbol("}")) {
2623                         this.ungetToken();
2624                     }
2625                     break;
2626                 }
2627 
2628                 else if (!bWidth && !bStyle && !bColor && token.isIdent(this.kINHERIT)) {
2629                     bWidth = this.kINHERIT;
2630                     bStyle = this.kINHERIT;
2631                     bColor = this.kINHERIT;
2632                 }
2633 
2634                 else if (!bWidth && (token.isDimension() || (token.isIdent() && token.value in this.kBORDER_WIDTH_NAMES) || token.isNumber("0"))) {
2635                     bWidth = token.value;
2636                 }
2637 
2638                 else if (!bStyle && (token.isIdent() && token.value in this.kBORDER_STYLE_NAMES)) {
2639                     bStyle = token.value;
2640                 }
2641 
2642                 else {
2643                     var color = (aProperty == "outline" && token.isIdent("invert")) ? "invert" : this.parseColor(token);
2644                     if (!bColor && color) {
2645                         bColor = color;
2646                     } else {
2647                         return "";
2648                     }
2649                 }
2650                 token = this.getToken(true, true);
2651             }
2652 
2653             // create the declarations
2654             this.forgetState();
2655             bWidth = bWidth ? bWidth : "medium";
2656             bStyle = bStyle ? bStyle : "none";
2657             bColor = bColor ? bColor : "-moz-initial";
2658 
2659             function addPropertyToDecl(aSelf, aDecl, property, w, s, c) {
2660                 aDecl.push(aSelf._createJscsspDeclarationFromValue(property + "-width", w));
2661                 aDecl.push(aSelf._createJscsspDeclarationFromValue(property + "-style", s));
2662                 aDecl.push(aSelf._createJscsspDeclarationFromValue(property + "-color", c));
2663             }
2664 
2665             if (aProperty == "border") {
2666                 addPropertyToDecl(this, aDecl, "border-top", bWidth, bStyle, bColor);
2667                 addPropertyToDecl(this, aDecl, "border-right", bWidth, bStyle, bColor);
2668                 addPropertyToDecl(this, aDecl, "border-bottom", bWidth, bStyle, bColor);
2669                 addPropertyToDecl(this, aDecl, "border-left", bWidth, bStyle, bColor);
2670             } else {
2671                 addPropertyToDecl(this, aDecl, aProperty, bWidth, bStyle, bColor);
2672             }
2673             return bWidth + " " + bStyle + " " + bColor;
2674         },
2675 
2676         parseBackgroundShorthand:function (token, aDecl, aAcceptPriority) {
2677             var kHPos = {"left":true, "right":true };
2678             var kVPos = {"top":true, "bottom":true };
2679             var kPos = {"left":true, "right":true, "top":true, "bottom":true, "center":true};
2680 
2681             var bgColor = null;
2682             var bgRepeat = null;
2683             var bgAttachment = null;
2684             var bgImage = null;
2685             var bgPosition = null;
2686 
2687             while (true) {
2688 
2689                 if (!token.isNotNull()) {
2690                     break;
2691                 }
2692 
2693                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2694                     if (token.isSymbol("}")) {
2695                         this.ungetToken();
2696                     }
2697                     break;
2698                 }
2699 
2700                 else if (!bgColor && !bgRepeat && !bgAttachment && !bgImage && !bgPosition && token.isIdent(this.kINHERIT)) {
2701                     bgColor = this.kINHERIT;
2702                     bgRepeat = this.kINHERIT;
2703                     bgAttachment = this.kINHERIT;
2704                     bgImage = this.kINHERIT;
2705                     bgPosition = this.kINHERIT;
2706                 }
2707 
2708                 else {
2709                     if (!bgAttachment && (token.isIdent("scroll") || token.isIdent("fixed"))) {
2710                         bgAttachment = token.value;
2711                     }
2712 
2713                     else if (!bgPosition && ((token.isIdent() && token.value in kPos) || token.isDimension() || token.isNumber("0") || token.isPercentage())) {
2714                         bgPosition = token.value;
2715                         token = this.getToken(true, true);
2716                         if (token.isDimension() || token.isNumber("0") || token.isPercentage()) {
2717                             bgPosition += " " + token.value;
2718                         } else if (token.isIdent() && token.value in kPos) {
2719                             if ((bgPosition in kHPos && token.value in kHPos) || (bgPosition in kVPos && token.value in kVPos)) {
2720                                 return "";
2721                             }
2722                             bgPosition += " " + token.value;
2723                         } else {
2724                             this.ungetToken();
2725                             bgPosition += " center";
2726                         }
2727                     }
2728 
2729                     else if (!bgRepeat && (token.isIdent("repeat") || token.isIdent("repeat-x") || token.isIdent("repeat-y") || token.isIdent("no-repeat"))) {
2730                         bgRepeat = token.value;
2731                     }
2732 
2733                     else if (!bgImage && (token.isFunction("url(") || token.isIdent("none"))) {
2734                         bgImage = token.value;
2735                         if (token.isFunction("url(")) {
2736                             token = this.getToken(true, true);
2737                             var url = this.parseURL(token); // TODO
2738                             if (url) {
2739                                 bgImage += url;
2740                             } else {
2741                                 return "";
2742                             }
2743                         }
2744                     }
2745 
2746                     else if (!bgImage && (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient("))) {
2747                         var gradient = CssInspector.parseGradient(this, token);
2748                         if (gradient) {
2749                             bgImage = CssInspector.serializeGradient(gradient);
2750                         } else {
2751                             return "";
2752                         }
2753                     }
2754 
2755                     else {
2756                         var color = this.parseColor(token);
2757                         if (!bgColor && color) {
2758                             bgColor = color;
2759                         } else {
2760                             return "";
2761                         }
2762                     }
2763 
2764                 }
2765 
2766                 token = this.getToken(true, true);
2767             }
2768 
2769             // create the declarations
2770             this.forgetState();
2771             bgColor = bgColor ? bgColor : "transparent";
2772             bgImage = bgImage ? bgImage : "none";
2773             bgRepeat = bgRepeat ? bgRepeat : "repeat";
2774             bgAttachment = bgAttachment ? bgAttachment : "scroll";
2775             bgPosition = bgPosition ? bgPosition : "top left";
2776 
2777             aDecl.push(this._createJscsspDeclarationFromValue("background-color", bgColor));
2778             aDecl.push(this._createJscsspDeclarationFromValue("background-image", bgImage));
2779             aDecl.push(this._createJscsspDeclarationFromValue("background-repeat", bgRepeat));
2780             aDecl.push(this._createJscsspDeclarationFromValue("background-attachment", bgAttachment));
2781             aDecl.push(this._createJscsspDeclarationFromValue("background-position", bgPosition));
2782             return bgColor + " " + bgImage + " " + bgRepeat + " " + bgAttachment + " " + bgPosition;
2783         },
2784 
2785         parseListStyleShorthand:function (token, aDecl, aAcceptPriority) {
2786             var kPosition = { "inside":true, "outside":true };
2787 
2788             var lType = null;
2789             var lPosition = null;
2790             var lImage = null;
2791 
2792             while (true) {
2793 
2794                 if (!token.isNotNull()) {
2795                     break;
2796                 }
2797 
2798                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2799                     if (token.isSymbol("}")) {
2800                         this.ungetToken();
2801                     }
2802                     break;
2803                 }
2804 
2805                 else if (!lType && !lPosition && !lImage && token.isIdent(this.kINHERIT)) {
2806                     lType = this.kINHERIT;
2807                     lPosition = this.kINHERIT;
2808                     lImage = this.kINHERIT;
2809                 }
2810 
2811                 else if (!lType && (token.isIdent() && token.value in this.kLIST_STYLE_TYPE_NAMES)) {
2812                     lType = token.value;
2813                 }
2814 
2815                 else if (!lPosition && (token.isIdent() && token.value in kPosition)) {
2816                     lPosition = token.value;
2817                 }
2818 
2819                 else if (!lImage && token.isFunction("url")) {
2820                     token = this.getToken(true, true);
2821                     var urlContent = this.parseURL(token);
2822                     if (urlContent) {
2823                         lImage = "url(" + urlContent;
2824                     } else {
2825                         return "";
2826                     }
2827                 } else if (!token.isIdent("none")) {
2828                     return "";
2829                 }
2830 
2831                 token = this.getToken(true, true);
2832             }
2833 
2834             // create the declarations
2835             this.forgetState();
2836             lType = lType ? lType : "none";
2837             lImage = lImage ? lImage : "none";
2838             lPosition = lPosition ? lPosition : "outside";
2839 
2840             aDecl.push(this._createJscsspDeclarationFromValue("list-style-type", lType));
2841             aDecl.push(this._createJscsspDeclarationFromValue("list-style-position", lPosition));
2842             aDecl.push(this._createJscsspDeclarationFromValue("list-style-image", lImage));
2843             return lType + " " + lPosition + " " + lImage;
2844         },
2845 
2846         parseFontShorthand:function (token, aDecl, aAcceptPriority) {
2847             var kStyle = {"italic":true, "oblique":true };
2848             var kVariant = {"small-caps":true };
2849             var kWeight = { "bold":true, "bolder":true, "lighter":true,
2850                 "100":true, "200":true, "300":true, "400":true,
2851                 "500":true, "600":true, "700":true, "800":true,
2852                 "900":true };
2853             var kSize = { "xx-small":true, "x-small":true, "small":true, "medium":true,
2854                 "large":true, "x-large":true, "xx-large":true,
2855                 "larger":true, "smaller":true };
2856             var kValues = { "caption":true, "icon":true, "menu":true, "message-box":true, "small-caption":true, "status-bar":true };
2857             var kFamily = { "serif":true, "sans-serif":true, "cursive":true, "fantasy":true, "monospace":true };
2858 
2859             var fStyle = null;
2860             var fVariant = null;
2861             var fWeight = null;
2862             var fSize = null;
2863             var fLineHeight = null;
2864             var fFamily = "";
2865             var fSystem = null;
2866             var fFamilyValues = [];
2867 
2868             var normalCount = 0;
2869             while (true) {
2870 
2871                 if (!token.isNotNull()) {
2872                     break;
2873                 }
2874 
2875                 if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2876                     if (token.isSymbol("}")) {
2877                         this.ungetToken();
2878                     }
2879                     break;
2880                 }
2881 
2882                 else if (!fStyle && !fVariant && !fWeight && !fSize && !fLineHeight && !fFamily && !fSystem && token.isIdent(this.kINHERIT)) {
2883                     fStyle = this.kINHERIT;
2884                     fVariant = this.kINHERIT;
2885                     fWeight = this.kINHERIT;
2886                     fSize = this.kINHERIT;
2887                     fLineHeight = this.kINHERIT;
2888                     fFamily = this.kINHERIT;
2889                     fSystem = this.kINHERIT;
2890                 }
2891 
2892                 else {
2893                     if (!fSystem && (token.isIdent() && token.value in kValues)) {
2894                         fSystem = token.value;
2895                         break;
2896                     }
2897 
2898                     else {
2899                         if (!fStyle && token.isIdent() && (token.value in kStyle)) {
2900                             fStyle = token.value;
2901                         }
2902 
2903                         else if (!fVariant && token.isIdent() && (token.value in kVariant)) {
2904                             fVariant = token.value;
2905                         }
2906 
2907                         else if (!fWeight && (token.isIdent() || token.isNumber()) && (token.value in kWeight)) {
2908                             fWeight = token.value;
2909                         }
2910 
2911                         else if (!fSize && ((token.isIdent() && (token.value in kSize)) || token.isDimension() || token.isPercentage())) {
2912                             fSize = token.value;
2913                             var token = this.getToken(false, false);
2914                             if (token.isSymbol("/")) {
2915                                 token = this.getToken(false, false);
2916                                 if (!fLineHeight && (token.isDimension() || token.isNumber() || token.isPercentage())) {
2917                                     fLineHeight = token.value;
2918                                 } else {
2919                                     return "";
2920                                 }
2921                             } else {
2922                                 this.ungetToken();
2923                             }
2924                         }
2925 
2926                         else if (token.isIdent("normal")) {
2927                             normalCount++;
2928                             if (normalCount > 3) {
2929                                 return "";
2930                             }
2931                         }
2932 
2933                         else if (!fFamily && // *MUST* be last to be tested here
2934                                  (token.isString() || token.isIdent())) {
2935                             var lastWasComma = false;
2936                             while (true) {
2937                                 if (!token.isNotNull()) {
2938                                     break;
2939                                 } else if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) {
2940                                     this.ungetToken();
2941                                     break;
2942                                 } else if (token.isIdent() && token.value in kFamily) {
2943                                     var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, null);
2944                                     value.value = token.value;
2945                                     fFamilyValues.push(value);
2946                                     fFamily += token.value;
2947                                     break;
2948                                 } else if (token.isString() || token.isIdent()) {
2949                                     var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, null);
2950                                     value.value = token.value;
2951                                     fFamilyValues.push(value);
2952                                     fFamily += token.value;
2953                                     lastWasComma = false;
2954                                 } else if (!lastWasComma && token.isSymbol(",")) {
2955                                     fFamily += ", ";
2956                                     lastWasComma = true;
2957                                 } else {
2958                                     return "";
2959                                 }
2960                                 token = this.getToken(true, true);
2961                             }
2962                         }
2963 
2964                         else {
2965                             return "";
2966                         }
2967                     }
2968 
2969                 }
2970 
2971                 token = this.getToken(true, true);
2972             }
2973 
2974             // create the declarations
2975             this.forgetState();
2976             if (fSystem) {
2977                 aDecl.push(this._createJscsspDeclarationFromValue("font", fSystem));
2978                 return fSystem;
2979             }
2980             fStyle = fStyle ? fStyle : "normal";
2981             fVariant = fVariant ? fVariant : "normal";
2982             fWeight = fWeight ? fWeight : "normal";
2983             fSize = fSize ? fSize : "medium";
2984             fLineHeight = fLineHeight ? fLineHeight : "normal";
2985             fFamily = fFamily ? fFamily : "-moz-initial";
2986 
2987             aDecl.push(this._createJscsspDeclarationFromValue("font-style", fStyle));
2988             aDecl.push(this._createJscsspDeclarationFromValue("font-variant", fVariant));
2989             aDecl.push(this._createJscsspDeclarationFromValue("font-weight", fWeight));
2990             aDecl.push(this._createJscsspDeclarationFromValue("font-size", fSize));
2991             aDecl.push(this._createJscsspDeclarationFromValue("line-height", fLineHeight));
2992             aDecl.push(this._createJscsspDeclarationFromValuesArray("font-family", fFamilyValues, fFamily));
2993             return fStyle + " " + fVariant + " " + fWeight + " " + fSize + "/" + fLineHeight + " " + fFamily;
2994         },
2995 
2996         _createJscsspDeclaration:function (property, value) {
2997             var decl = new jscsspDeclaration();
2998             decl.property = property;
2999             decl.value = this.trim11(value);
3000             decl.parsedCssText = property + ": " + value + ";";
3001             return decl;
3002         },
3003 
3004         _createJscsspDeclarationFromValue:function (property, valueText) {
3005             var decl = new jscsspDeclaration();
3006             decl.property = property;
3007             var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, null);
3008             value.value = valueText;
3009             decl.values = [value];
3010             decl.valueText = valueText;
3011             decl.parsedCssText = property + ": " + valueText + ";";
3012             return decl;
3013         },
3014 
3015         _createJscsspDeclarationFromValuesArray:function (property, values, valueText) {
3016             var decl = new jscsspDeclaration();
3017             decl.property = property;
3018             decl.values = values;
3019             decl.valueText = valueText;
3020             decl.parsedCssText = property + ": " + valueText + ";";
3021             return decl;
3022         },
3023 
3024         parseURL:function (token) {
3025             var value = "";
3026             if (token.isString()) {
3027                 value += token.value;
3028                 token = this.getToken(true, true);
3029             } else {
3030                 while (true) {
3031                     if (!token.isNotNull()) {
3032                         this.reportError(kURL_EOF);
3033                         return "";
3034                     }
3035                     if (token.isWhiteSpace()) {
3036                         nextToken = this.lookAhead(true, true);
3037                         // if next token is not a closing parenthesis, that's an error
3038                         if (!nextToken.isSymbol(")")) {
3039                             this.reportError(kURL_WS_INSIDE);
3040                             token = this.currentToken();
3041                             break;
3042                         }
3043                     }
3044                     if (token.isSymbol(")")) {
3045                         break;
3046                     }
3047                     value += token.value;
3048                     token = this.getToken(false, false);
3049                 }
3050             }
3051 
3052             if (token.isSymbol(")")) {
3053                 return value + ")";
3054             }
3055             return "";
3056         },
3057 
3058         parseFunctionArgument:function (token) {
3059             var value = "";
3060             if (token.isString()) {
3061                 value += token.value;
3062                 token = this.getToken(true, true);
3063             } else {
3064                 var parenthesis = 1;
3065                 while (true) {
3066                     if (!token.isNotNull()) {
3067                         return "";
3068                     }
3069                     if (token.isFunction() || token.isSymbol("(")) {
3070                         parenthesis++;
3071                     }
3072                     if (token.isSymbol(")")) {
3073                         parenthesis--;
3074                         if (!parenthesis) {
3075                             break;
3076                         }
3077                     }
3078                     value += token.value;
3079                     token = this.getToken(false, false);
3080                 }
3081             }
3082 
3083             if (token.isSymbol(")")) {
3084                 return value + ")";
3085             }
3086             return "";
3087         },
3088 
3089         parseColor:function (token) {
3090             var color = "";
3091             if (token.isFunction("rgb(") || token.isFunction("rgba(")) {
3092                 color = token.value;
3093                 var isRgba = token.isFunction("rgba(")
3094                 token = this.getToken(true, true);
3095                 if (!token.isNumber() && !token.isPercentage()) {
3096                     return "";
3097                 }
3098                 color += token.value;
3099                 token = this.getToken(true, true);
3100                 if (!token.isSymbol(",")) {
3101                     return "";
3102                 }
3103                 color += ", ";
3104 
3105                 token = this.getToken(true, true);
3106                 if (!token.isNumber() && !token.isPercentage()) {
3107                     return "";
3108                 }
3109                 color += token.value;
3110                 token = this.getToken(true, true);
3111                 if (!token.isSymbol(",")) {
3112                     return "";
3113                 }
3114                 color += ", ";
3115 
3116                 token = this.getToken(true, true);
3117                 if (!token.isNumber() && !token.isPercentage()) {
3118                     return "";
3119                 }
3120                 color += token.value;
3121 
3122                 if (isRgba) {
3123                     token = this.getToken(true, true);
3124                     if (!token.isSymbol(",")) {
3125                         return "";
3126                     }
3127                     color += ", ";
3128 
3129                     token = this.getToken(true, true);
3130                     if (!token.isNumber()) {
3131                         return "";
3132                     }
3133                     color += token.value;
3134                 }
3135 
3136                 token = this.getToken(true, true);
3137                 if (!token.isSymbol(")")) {
3138                     return "";
3139                 }
3140                 color += token.value;
3141             }
3142 
3143             else if (token.isFunction("hsl(") || token.isFunction("hsla(")) {
3144                 color = token.value;
3145                 var isHsla = token.isFunction("hsla(")
3146                 token = this.getToken(true, true);
3147                 if (!token.isNumber()) {
3148                     return "";
3149                 }
3150                 color += token.value;
3151                 token = this.getToken(true, true);
3152                 if (!token.isSymbol(",")) {
3153                     return "";
3154                 }
3155                 color += ", ";
3156 
3157                 token = this.getToken(true, true);
3158                 if (!token.isPercentage()) {
3159                     return "";
3160                 }
3161                 color += token.value;
3162                 token = this.getToken(true, true);
3163                 if (!token.isSymbol(",")) {
3164                     return "";
3165                 }
3166                 color += ", ";
3167 
3168                 token = this.getToken(true, true);
3169                 if (!token.isPercentage()) {
3170                     return "";
3171                 }
3172                 color += token.value;
3173 
3174                 if (isHsla) {
3175                     token = this.getToken(true, true);
3176                     if (!token.isSymbol(",")) {
3177                         return "";
3178                     }
3179                     color += ", ";
3180 
3181                     token = this.getToken(true, true);
3182                     if (!token.isNumber()) {
3183                         return "";
3184                     }
3185                     color += token.value;
3186                 }
3187 
3188                 token = this.getToken(true, true);
3189                 if (!token.isSymbol(")")) {
3190                     return "";
3191                 }
3192                 color += token.value;
3193             }
3194 
3195             else if (token.isIdent() && (token.value in this.kCOLOR_NAMES)) {
3196                 color = token.value;
3197             }
3198 
3199             else if (token.isSymbol("#")) {
3200                 token = this.getHexValue();
3201                 if (!token.isHex()) {
3202                     return "";
3203                 }
3204                 var length = token.value.length;
3205                 if (length != 3 && length != 6) {
3206                     return "";
3207                 }
3208                 if (token.value.match(/[a-fA-F0-9]/g).length != length) {
3209                     return "";
3210                 }
3211                 color = "#" + token.value;
3212             }
3213             return color;
3214         },
3215 
3216         parseDeclaration:function (aToken, aDecl, aAcceptPriority, aExpandShorthands, aSheet) {
3217             this.preserveState();
3218             var blocks = [];
3219             if (aToken.isIdent()) {
3220                 var descriptor = aToken.value.toLowerCase();
3221                 var token = this.getToken(true, true);
3222                 if (token.isSymbol(":")) {
3223                     var token = this.getToken(true, true);
3224 
3225                     var value = "";
3226                     var declarations = [];
3227                     if (aExpandShorthands) {
3228                         switch (descriptor) {
3229                             case "background":
3230                                 value = this.parseBackgroundShorthand(token, declarations, aAcceptPriority);
3231                                 break;
3232                             case "margin":
3233                             case "padding":
3234                                 value = this.parseMarginOrPaddingShorthand(token, declarations, aAcceptPriority, descriptor);
3235                                 break;
3236                             case "border-color":
3237                                 value = this.parseBorderColorShorthand(token, declarations, aAcceptPriority);
3238                                 break;
3239                             case "border-style":
3240                                 value = this.parseBorderStyleShorthand(token, declarations, aAcceptPriority);
3241                                 break;
3242                             case "border-width":
3243                                 value = this.parseBorderWidthShorthand(token, declarations, aAcceptPriority);
3244                                 break;
3245                             case "border-top":
3246                             case "border-right":
3247                             case "border-bottom":
3248                             case "border-left":
3249                             case "border":
3250                             case "outline":
3251                                 value = this.parseBorderEdgeOrOutlineShorthand(token, declarations, aAcceptPriority, descriptor);
3252                                 break;
3253                             case "cue":
3254                                 value = this.parseCueShorthand(token, declarations, aAcceptPriority);
3255                                 break;
3256                             case "pause":
3257                                 value = this.parsePauseShorthand(token, declarations, aAcceptPriority);
3258                                 break;
3259                             case "font":
3260                                 value = this.parseFontShorthand(token, declarations, aAcceptPriority);
3261                                 break;
3262                             case "list-style":
3263                                 value = this.parseListStyleShorthand(token, declarations, aAcceptPriority);
3264                                 break;
3265                             default:
3266                                 value = this.parseDefaultPropertyValue(token, declarations, aAcceptPriority, descriptor, aSheet);
3267                                 break;
3268                         }
3269                     } else {
3270                         value = this.parseDefaultPropertyValue(token, declarations, aAcceptPriority, descriptor, aSheet);
3271                     }
3272                     token = this.currentToken();
3273                     if (value) // no error above
3274                     {
3275                         var priority = false;
3276                         if (token.isSymbol("!")) {
3277                             token = this.getToken(true, true);
3278                             if (token.isIdent("important")) {
3279                                 priority = true;
3280                                 token = this.getToken(true, true);
3281                                 if (token.isSymbol(";") || token.isSymbol("}")) {
3282                                     if (token.isSymbol("}")) {
3283                                         this.ungetToken();
3284                                     }
3285                                 } else {
3286                                     return "";
3287                                 }
3288                             } else {
3289                                 return "";
3290                             }
3291                         } else if (token.isNotNull() && !token.isSymbol(";") && !token.isSymbol("}")) {
3292                             return "";
3293                         }
3294                         for (var i = 0; i < declarations.length; i++) {
3295                             declarations[i].priority = priority;
3296                             aDecl.push(declarations[i]);
3297                         }
3298                         return descriptor + ": " + value + ";";
3299                     }
3300                 }
3301             } else if (aToken.isComment()) {
3302                 if (this.mPreserveComments) {
3303                     this.forgetState();
3304                     var comment = new jscsspComment();
3305                     comment.parsedCssText = aToken.value;
3306                     aDecl.push(comment);
3307                 }
3308                 return aToken.value;
3309             }
3310 
3311             // we have an error here, let's skip it
3312             this.restoreState();
3313             var s = aToken.value;
3314             blocks = [];
3315             var token = this.getToken(false, false);
3316             while (token.isNotNull()) {
3317                 s += token.value;
3318                 if ((token.isSymbol(";") || token.isSymbol("}")) && !blocks.length) {
3319                     if (token.isSymbol("}")) {
3320                         this.ungetToken();
3321                     }
3322                     break;
3323                 } else if (token.isSymbol("{") || token.isSymbol("(") || token.isSymbol("[") || token.isFunction()) {
3324                     blocks.push(token.isFunction() ? "(" : token.value);
3325                 } else if (token.isSymbol("}") || token.isSymbol(")") || token.isSymbol("]")) {
3326                     if (blocks.length) {
3327                         var ontop = blocks[blocks.length - 1];
3328                         if ((token.isSymbol("}") && ontop == "{") || (token.isSymbol(")") && ontop == "(") || (token.isSymbol("]") && ontop == "[")) {
3329                             blocks.pop();
3330                         }
3331                     }
3332                 }
3333                 token = this.getToken(false, false);
3334             }
3335             return "";
3336         },
3337 
3338         parseMediaRule:function (aToken, aSheet) {
3339             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
3340             var s = aToken.value;
3341             var valid = false;
3342             var mediaRule = new jscsspMediaRule();
3343             mediaRule.currentLine = currentLine;
3344             this.preserveState();
3345             var token = this.getToken(true, true);
3346             var foundMedia = false;
3347             while (token.isNotNull()) {
3348                 if (token.isIdent()) {
3349                     foundMedia = true;
3350                     s += " " + token.value;
3351                     mediaRule.media.push(token.value);
3352                     token = this.getToken(true, true);
3353                     if (token.isSymbol(",")) {
3354                         s += ",";
3355                     } else {
3356                         if (token.isSymbol("{")) {
3357                             this.ungetToken();
3358                         } else {
3359                             // error...
3360                             token.type = jscsspToken.NULL_TYPE;
3361                             break;
3362                         }
3363                     }
3364                 } else if (token.isSymbol("{")) {
3365                     break;
3366                 } else if (foundMedia) {
3367                     token.type = jscsspToken.NULL_TYPE;
3368                     // not a media list
3369                     break;
3370                 }
3371                 token = this.getToken(true, true);
3372             }
3373             if (token.isSymbol("{") && mediaRule.media.length) {
3374                 // ok let's parse style rules now...
3375                 s += " { ";
3376                 token = this.getToken(true, false);
3377                 while (token.isNotNull()) {
3378                     if (token.isComment() && this.mPreserveComments) {
3379                         s += " " + token.value;
3380                         var comment = new jscsspComment();
3381                         comment.parsedCssText = token.value;
3382                         mediaRule.cssRules.push(comment);
3383                     } else if (token.isSymbol("}")) {
3384                         valid = true;
3385                         break;
3386                     } else {
3387                         var r = this.parseStyleRule(token, mediaRule, true);
3388                         if (r) {
3389                             s += r;
3390                         }
3391                     }
3392                     token = this.getToken(true, false);
3393                 }
3394             }
3395             if (valid) {
3396                 this.forgetState();
3397                 mediaRule.parsedCssText = s;
3398                 aSheet.cssRules.push(mediaRule);
3399                 return true;
3400             }
3401             this.restoreState();
3402             return false;
3403         },
3404 
3405         trim11:function (str) {
3406             str = str.replace(/^\s+/, '');
3407             for (var i = str.length - 1; i >= 0; i--) {
3408                 if (/\S/.test(str.charAt(i))) { // XXX charat
3409                     str = str.substring(0, i + 1);
3410                     break;
3411                 }
3412             }
3413             return str;
3414         },
3415 
3416         parseStyleRule:function (aToken, aOwner, aIsInsideMediaRule) {
3417             var currentLine = CountLF(this.mScanner.getAlreadyScanned());
3418             this.preserveState();
3419             // first let's see if we have a selector here...
3420             var selector = this.parseSelector(aToken, false);
3421             var valid = false;
3422             var declarations = [];
3423             if (selector) {
3424                 selector = this.trim11(selector.selector);
3425                 var s = selector;
3426                 var token = this.getToken(true, true);
3427                 if (token.isSymbol("{")) {
3428                     s += " { ";
3429                     var token = this.getToken(true, false);
3430                     while (true) {
3431                         if (!token.isNotNull()) {
3432                             valid = true;
3433                             break;
3434                         }
3435                         if (token.isSymbol("}")) {
3436                             s += "}";
3437                             valid = true;
3438                             break;
3439                         } else {
3440                             var d = this.parseDeclaration(token, declarations, true, true, aOwner);
3441                             s += ((d && declarations.length) ? " " : "") + d;
3442                         }
3443                         token = this.getToken(true, false);
3444                     }
3445                 }
3446             } else {
3447                 // selector is invalid so the whole rule is invalid with it
3448             }
3449 
3450             if (valid) {
3451                 var rule = new jscsspStyleRule();
3452                 rule.currentLine = currentLine;
3453                 rule.parsedCssText = s;
3454                 rule.declarations = declarations;
3455                 rule.mSelectorText = selector;
3456                 if (aIsInsideMediaRule) {
3457                     rule.parentRule = aOwner;
3458                 } else {
3459                     rule.parentStyleSheet = aOwner;
3460                 }
3461                 aOwner.cssRules.push(rule);
3462                 return s;
3463             }
3464             this.restoreState();
3465             s = this.currentToken().value;
3466             this.addUnknownAtRule(aOwner, s);
3467             return "";
3468         },
3469 
3470         parseSelector:function (aToken, aParseSelectorOnly) {
3471             var s = "";
3472             var specificity = {a:0, b:0, c:0, d:0}; // CSS 2.1 section 6.4.3
3473             var isFirstInChain = true;
3474             var token = aToken;
3475             var valid = false;
3476             var combinatorFound = false;
3477             while (true) {
3478                 if (!token.isNotNull()) {
3479                     if (aParseSelectorOnly) {
3480                         return {selector:s, specificity:specificity };
3481                     }
3482                     return "";
3483                 }
3484 
3485                 if (!aParseSelectorOnly && token.isSymbol("{")) {
3486                     // end of selector
3487                     valid = !combinatorFound;
3488                     // don't unget if invalid since addUnknownRule is going to restore state anyway
3489                     if (valid) {
3490                         this.ungetToken();
3491                     }
3492                     break;
3493                 }
3494 
3495                 if (token.isSymbol(",")) { // group of selectors
3496                     s += token.value;
3497                     isFirstInChain = true;
3498                     combinatorFound = false;
3499                     token = this.getToken(false, true);
3500                     continue;
3501                 }
3502                 // now combinators and grouping...
3503                 else if (!combinatorFound && (token.isWhiteSpace() || token.isSymbol(">") || token.isSymbol("+") || token.isSymbol("~"))) {
3504                     if (token.isWhiteSpace()) {
3505                         s += " ";
3506                         var nextToken = this.lookAhead(true, true);
3507                         if (!nextToken.isNotNull()) {
3508                             if (aParseSelectorOnly) {
3509                                 return {selector:s, specificity:specificity };
3510                             }
3511                             return "";
3512                         }
3513                         if (nextToken.isSymbol(">") || nextToken.isSymbol("+") || nextToken.isSymbol("~")) {
3514                             token = this.getToken(true, true);
3515                             s += token.value + " ";
3516                             combinatorFound = true;
3517                         }
3518                     } else {
3519                         s += token.value;
3520                         combinatorFound = true;
3521                     }
3522                     isFirstInChain = true;
3523                     token = this.getToken(true, true);
3524                     continue;
3525                 } else {
3526                     var simpleSelector = this.parseSimpleSelector(token, isFirstInChain, true);
3527                     if (!simpleSelector) {
3528                         break;
3529                     } // error
3530                     s += simpleSelector.selector;
3531                     specificity.b += simpleSelector.specificity.b;
3532                     specificity.c += simpleSelector.specificity.c;
3533                     specificity.d += simpleSelector.specificity.d;
3534                     isFirstInChain = false;
3535                     combinatorFound = false;
3536                 }
3537 
3538                 token = this.getToken(false, true);
3539             }
3540 
3541             if (valid) {
3542                 return {selector:s, specificity:specificity };
3543             }
3544             return "";
3545         },
3546 
3547         isPseudoElement:function (aIdent) {
3548             switch (aIdent) {
3549                 case "first-letter":
3550                 case "first-line":
3551                 case "before":
3552                 case "after":
3553                 case "marker":
3554                     return true;
3555                     break;
3556                 default:
3557                     return false;
3558                     break;
3559             }
3560         },
3561 
3562         parseSimpleSelector:function (token, isFirstInChain, canNegate) {
3563             var s = "";
3564             var specificity = {a:0, b:0, c:0, d:0}; // CSS 2.1 section 6.4.3
3565 
3566             if (isFirstInChain && (token.isSymbol("*") || token.isSymbol("|") || token.isIdent())) {
3567                 // type or universal selector
3568                 if (token.isSymbol("*") || token.isIdent()) {
3569                     // we don't know yet if it's a prefix or a universal
3570                     // selector
3571                     s += token.value;
3572                     var isIdent = token.isIdent();
3573                     token = this.getToken(false, true);
3574                     if (token.isSymbol("|")) {
3575                         // it's a prefix
3576                         s += token.value;
3577                         token = this.getToken(false, true);
3578                         if (token.isIdent() || token.isSymbol("*")) {
3579                             // ok we now have a type element or universal
3580                             // selector
3581                             s += token.value;
3582                             if (token.isIdent()) {
3583                                 specificity.d++;
3584                             }
3585                         } else
3586                         // oops that's an error...
3587                         {
3588                             return null;
3589                         }
3590                     } else {
3591                         this.ungetToken();
3592                         if (isIdent) {
3593                             specificity.d++;
3594                         }
3595                     }
3596                 } else if (token.isSymbol("|")) {
3597                     s += token.value;
3598                     token = this.getToken(false, true);
3599                     if (token.isIdent() || token.isSymbol("*")) {
3600                         s += token.value;
3601                         if (token.isIdent()) {
3602                             specificity.d++;
3603                         }
3604                     } else
3605                     // oops that's an error
3606                     {
3607                         return null;
3608                     }
3609                 }
3610             }
3611 
3612             else if (token.isSymbol(".") || token.isSymbol("#")) {
3613                 var isClass = token.isSymbol(".");
3614                 s += token.value;
3615                 token = this.getToken(false, true);
3616                 if (token.isIdent()) {
3617                     s += token.value;
3618                     if (isClass) {
3619                         specificity.c++;
3620                     } else {
3621                         specificity.b++;
3622                     }
3623                 } else {
3624                     return null;
3625                 }
3626             }
3627 
3628             else if (token.isSymbol(":")) {
3629                 s += token.value;
3630                 token = this.getToken(false, true);
3631                 if (token.isSymbol(":")) {
3632                     s += token.value;
3633                     token = this.getToken(false, true);
3634                 }
3635                 if (token.isIdent()) {
3636                     s += token.value;
3637                     if (this.isPseudoElement(token.value)) {
3638                         specificity.d++;
3639                     } else {
3640                         specificity.c++;
3641                     }
3642                 } else if (token.isFunction()) {
3643                     s += token.value;
3644                     if (token.isFunction(":not(")) {
3645                         if (!canNegate) {
3646                             return null;
3647                         }
3648                         token = this.getToken(true, true);
3649                         var simpleSelector = this.parseSimpleSelector(token, isFirstInChain, false);
3650                         if (!simpleSelector) {
3651                             return null;
3652                         } else {
3653                             s += simpleSelector.selector;
3654                             token = this.getToken(true, true);
3655                             if (token.isSymbol(")")) {
3656                                 s += ")";
3657                             } else {
3658                                 return null;
3659                             }
3660                         }
3661                         specificity.c++;
3662                     } else {
3663                         while (true) {
3664                             token = this.getToken(false, true);
3665                             if (token.isSymbol(")")) {
3666                                 s += ")";
3667                                 break;
3668                             } else {
3669                                 s += token.value;
3670                             }
3671                         }
3672                         specificity.c++;
3673                     }
3674                 } else {
3675                     return null;
3676                 }
3677 
3678             } else if (token.isSymbol("[")) {
3679                 s += "[";
3680                 token = this.getToken(true, true);
3681                 if (token.isIdent() || token.isSymbol("*")) {
3682                     s += token.value;
3683                     var nextToken = this.getToken(true, true);
3684                     if (token.isSymbol("|")) {
3685                         s += "|";
3686                         token = this.getToken(true, true);
3687                         if (token.isIdent()) {
3688                             s += token.value;
3689                         } else {
3690                             return null;
3691                         }
3692                     } else {
3693                         this.ungetToken();
3694                     }
3695                 } else if (token.isSymbol("|")) {
3696                     s += "|";
3697                     token = this.getToken(true, true);
3698                     if (token.isIdent()) {
3699                         s += token.value;
3700                     } else {
3701                         return null;
3702                     }
3703                 } else {
3704                     return null;
3705                 }
3706 
3707                 // nothing, =, *=, $=, ^=, |=
3708                 token = this.getToken(true, true);
3709                 if (token.isIncludes() || token.isDashmatch() || token.isBeginsmatch() || token.isEndsmatch() || token.isContainsmatch() || token.isSymbol("=")) {
3710                     s += token.value;
3711                     token = this.getToken(true, true);
3712                     if (token.isString() || token.isIdent()) {
3713                         s += token.value;
3714                         token = this.getToken(true, true);
3715                     } else {
3716                         return null;
3717                     }
3718 
3719                     if (token.isSymbol("]")) {
3720                         s += token.value;
3721                         specificity.c++;
3722                     } else {
3723                         return null;
3724                     }
3725                 } else if (token.isSymbol("]")) {
3726                     s += token.value;
3727                     specificity.c++;
3728                 } else {
3729                     return null;
3730                 }
3731 
3732             } else if (token.isWhiteSpace()) {
3733                 var t = this.lookAhead(true, true);
3734                 if (t.isSymbol('{')) {
3735                     return ""
3736                 }
3737             }
3738             if (s) {
3739                 return {selector:s, specificity:specificity };
3740             }
3741             return null;
3742         },
3743 
3744         preserveState:function () {
3745             this.mPreservedTokens.push(this.currentToken());
3746             this.mScanner.preserveState();
3747         },
3748 
3749         restoreState:function () {
3750             if (this.mPreservedTokens.length) {
3751                 this.mScanner.restoreState();
3752                 this.mToken = this.mPreservedTokens.pop();
3753             }
3754         },
3755 
3756         forgetState:function () {
3757             if (this.mPreservedTokens.length) {
3758                 this.mScanner.forgetState();
3759                 this.mPreservedTokens.pop();
3760             }
3761         },
3762 
3763         parse:function (aString, aTryToPreserveWhitespaces, aTryToPreserveComments) {
3764             if (!aString) {
3765                 return null;
3766             } // early way out if we can
3767 
3768             this.mPreserveWS = aTryToPreserveWhitespaces;
3769             this.mPreserveComments = aTryToPreserveComments;
3770             this.mPreservedTokens = [];
3771             this.mScanner.init(aString);
3772             var sheet = new jscsspStylesheet();
3773 
3774             // @charset can only appear at first char of the stylesheet
3775             var token = this.getToken(false, false);
3776             if (!token.isNotNull()) {
3777                 return;
3778             }
3779             if (token.isAtRule("@charset")) {
3780                 this.parseCharsetRule(token, sheet);
3781                 token = this.getToken(false, false);
3782             }
3783 
3784             var foundStyleRules = false;
3785             var foundImportRules = false;
3786             var foundNameSpaceRules = false;
3787             while (true) {
3788                 if (!token.isNotNull()) {
3789                     break;
3790                 }
3791                 if (token.isWhiteSpace()) {
3792                     if (aTryToPreserveWhitespaces) {
3793                         this.addWhitespace(sheet, token.value);
3794                     }
3795                 }
3796 
3797                 else if (token.isComment()) {
3798                     if (this.mPreserveComments) {
3799                         this.addComment(sheet, token.value);
3800                     }
3801                 }
3802 
3803                 else if (token.isAtRule()) {
3804                     if (token.isAtRule("@variables")) {
3805                         if (!foundImportRules && !foundStyleRules) {
3806                             this.parseVariablesRule(token, sheet);
3807                         } else {
3808                             this.reportError(kVARIABLES_RULE_POSITION);
3809                             this.addUnknownAtRule(sheet, token.value);
3810                         }
3811                     } else if (token.isAtRule("@import")) {
3812                         // @import rules MUST occur before all style and namespace
3813                         // rules
3814                         if (!foundStyleRules && !foundNameSpaceRules) {
3815                             foundImportRules = this.parseImportRule(token, sheet);
3816                         } else {
3817                             this.reportError(kIMPORT_RULE_POSITION);
3818                             this.addUnknownAtRule(sheet, token.value);
3819                         }
3820                     } else if (token.isAtRule("@namespace")) {
3821                         // @namespace rules MUST occur before all style rule and
3822                         // after all @import rules
3823                         if (!foundStyleRules) {
3824                             foundNameSpaceRules = this.parseNamespaceRule(token, sheet);
3825                         } else {
3826                             this.reportError(kNAMESPACE_RULE_POSITION);
3827                             this.addUnknownAtRule(sheet, token.value);
3828                         }
3829                     } else if (token.isAtRule("@font-face")) {
3830                         if (this.parseFontFaceRule(token, sheet)) {
3831                             foundStyleRules = true;
3832                         } else {
3833                             this.addUnknownAtRule(sheet, token.value);
3834                         }
3835                     } else if (token.isAtRule("@page")) {
3836                         if (this.parsePageRule(token, sheet)) {
3837                             foundStyleRules = true;
3838                         } else {
3839                             this.addUnknownAtRule(sheet, token.value);
3840                         }
3841                     } else if (token.isAtRule("@media")) {
3842                         if (this.parseMediaRule(token, sheet)) {
3843                             foundStyleRules = true;
3844                         } else {
3845                             this.addUnknownAtRule(sheet, token.value);
3846                         }
3847                     } else if (token.isAtRule("@charset")) {
3848                         this.reportError(kCHARSET_RULE_CHARSET_SOF);
3849                         this.addUnknownAtRule(sheet, token.value);
3850                     } else {
3851                         this.reportError(kUNKNOWN_AT_RULE);
3852                         this.addUnknownAtRule(sheet, token.value);
3853                     }
3854                 }
3855 
3856                 else // plain style rules
3857                 {
3858                     var ruleText = this.parseStyleRule(token, sheet, false);
3859                     if (ruleText) {
3860                         foundStyleRules = true;
3861                     }
3862                 }
3863                 token = this.getToken(false);
3864             }
3865 
3866             return sheet;
3867         }
3868 
3869     };
3870 
3871 
3872     function jscsspToken(aType, aValue, aUnit) {
3873         this.type = aType;
3874         this.value = aValue;
3875         this.unit = aUnit;
3876     }
3877 
3878     jscsspToken.NULL_TYPE = 0;
3879 
3880     jscsspToken.WHITESPACE_TYPE = 1;
3881     jscsspToken.STRING_TYPE = 2;
3882     jscsspToken.COMMENT_TYPE = 3;
3883     jscsspToken.NUMBER_TYPE = 4;
3884     jscsspToken.IDENT_TYPE = 5;
3885     jscsspToken.FUNCTION_TYPE = 6;
3886     jscsspToken.ATRULE_TYPE = 7;
3887     jscsspToken.INCLUDES_TYPE = 8;
3888     jscsspToken.DASHMATCH_TYPE = 9;
3889     jscsspToken.BEGINSMATCH_TYPE = 10;
3890     jscsspToken.ENDSMATCH_TYPE = 11;
3891     jscsspToken.CONTAINSMATCH_TYPE = 12;
3892     jscsspToken.SYMBOL_TYPE = 13;
3893     jscsspToken.DIMENSION_TYPE = 14;
3894     jscsspToken.PERCENTAGE_TYPE = 15;
3895     jscsspToken.HEX_TYPE = 16;
3896 
3897     jscsspToken.prototype = {
3898 
3899         isNotNull:function () {
3900             return this.type;
3901         },
3902 
3903         _isOfType:function (aType, aValue) {
3904             return (this.type == aType && (!aValue || this.value.toLowerCase() == aValue));
3905         },
3906 
3907         isWhiteSpace:function (w) {
3908             return this._isOfType(jscsspToken.WHITESPACE_TYPE, w);
3909         },
3910 
3911         isString:function () {
3912             return this._isOfType(jscsspToken.STRING_TYPE);
3913         },
3914 
3915         isComment:function () {
3916             return this._isOfType(jscsspToken.COMMENT_TYPE);
3917         },
3918 
3919         isNumber:function (n) {
3920             return this._isOfType(jscsspToken.NUMBER_TYPE, n);
3921         },
3922 
3923         isSymbol:function (c) {
3924             return this._isOfType(jscsspToken.SYMBOL_TYPE, c);
3925         },
3926 
3927         isIdent:function (i) {
3928             return this._isOfType(jscsspToken.IDENT_TYPE, i);
3929         },
3930 
3931         isFunction:function (f) {
3932             return this._isOfType(jscsspToken.FUNCTION_TYPE, f);
3933         },
3934 
3935         isAtRule:function (a) {
3936             return this._isOfType(jscsspToken.ATRULE_TYPE, a);
3937         },
3938 
3939         isIncludes:function () {
3940             return this._isOfType(jscsspToken.INCLUDES_TYPE);
3941         },
3942 
3943         isDashmatch:function () {
3944             return this._isOfType(jscsspToken.DASHMATCH_TYPE);
3945         },
3946 
3947         isBeginsmatch:function () {
3948             return this._isOfType(jscsspToken.BEGINSMATCH_TYPE);
3949         },
3950 
3951         isEndsmatch:function () {
3952             return this._isOfType(jscsspToken.ENDSMATCH_TYPE);
3953         },
3954 
3955         isContainsmatch:function () {
3956             return this._isOfType(jscsspToken.CONTAINSMATCH_TYPE);
3957         },
3958 
3959         isSymbol:function (c) {
3960             return this._isOfType(jscsspToken.SYMBOL_TYPE, c);
3961         },
3962 
3963         isDimension:function () {
3964             return this._isOfType(jscsspToken.DIMENSION_TYPE);
3965         },
3966 
3967         isPercentage:function () {
3968             return this._isOfType(jscsspToken.PERCENTAGE_TYPE);
3969         },
3970 
3971         isHex:function () {
3972             return this._isOfType(jscsspToken.HEX_TYPE);
3973         },
3974 
3975         isDimensionOfUnit:function (aUnit) {
3976             return (this.isDimension() && this.unit == aUnit);
3977         },
3978 
3979         isLength:function () {
3980             return (this.isPercentage() || this.isDimensionOfUnit("cm") || this.isDimensionOfUnit("mm") || this.isDimensionOfUnit("in") || this.isDimensionOfUnit("pc") || this.isDimensionOfUnit("px") || this.isDimensionOfUnit("em") || this.isDimensionOfUnit("ex") || this.isDimensionOfUnit("pt"));
3981         },
3982 
3983         isAngle:function () {
3984             return (this.isDimensionOfUnit("deg") || this.isDimensionOfUnit("rad") || this.isDimensionOfUnit("grad"));
3985         }
3986     }
3987 
3988     var kJscsspUNKNOWN_RULE = 0;
3989     var kJscsspSTYLE_RULE = 1
3990     var kJscsspCHARSET_RULE = 2;
3991     var kJscsspIMPORT_RULE = 3;
3992     var kJscsspMEDIA_RULE = 4;
3993     var kJscsspFONT_FACE_RULE = 5;
3994     var kJscsspPAGE_RULE = 6;
3995     var kJscsspVARIABLES_RULE = 7;
3996 
3997     var kJscsspNAMESPACE_RULE = 100;
3998     var kJscsspCOMMENT = 101;
3999     var kJscsspWHITE_SPACE = 102;
4000 
4001     var kJscsspSTYLE_DECLARATION = 1000;
4002 
4003     var gTABS = "";
4004 
4005     function jscsspStylesheet() {
4006         this.cssRules = [];
4007         this.variables = {};
4008     }
4009 
4010     jscsspStylesheet.prototype = {
4011         insertRule:function (aRule, aIndex) {
4012             try {
4013                 this.cssRules.splice(aIndex, 1, aRule);
4014             } catch (e) {
4015             }
4016         },
4017 
4018         deleteRule:function (aIndex) {
4019             try {
4020                 this.cssRules.splice(aIndex);
4021             } catch (e) {
4022             }
4023         },
4024 
4025         cssText:function () {
4026             var rv = "";
4027             for (var i = 0; i < this.cssRules.length; i++) {
4028                 rv += this.cssRules[i].cssText() + "\n";
4029             }
4030             return rv;
4031         },
4032 
4033         resolveVariables:function (aMedium) {
4034 
4035             function ItemFoundInArray(aArray, aItem) {
4036                 for (var i = 0; i < aArray.length; i++) {
4037                     if (aItem == aArray[i]) {
4038                         return true;
4039                     }
4040                 }
4041                 return false;
4042             }
4043 
4044             for (var i = 0; i < this.cssRules.length; i++) {
4045                 var rule = this.cssRules[i];
4046                 if (rule.type == kJscsspSTYLE_RULE || rule.type == kJscsspIMPORT_RULE) {
4047                     break;
4048                 } else if (rule.type == kJscsspVARIABLES_RULE && (!rule.media.length || ItemFoundInArray(rule.media, aMedium))) {
4049 
4050                     for (var j = 0; j < rule.declarations.length; j++) {
4051                         var valueText = "";
4052                         for (var k = 0; k < rule.declarations[j].values.length; k++) {
4053                             valueText += (k ? " " : "") + rule.declarations[j].values[k].value;
4054                         }
4055                         this.variables[rule.declarations[j].property] = valueText;
4056                     }
4057                 }
4058             }
4059         }
4060     };
4061 
4062     /* kJscsspCHARSET_RULE */
4063 
4064     function jscsspCharsetRule() {
4065         this.type = kJscsspCHARSET_RULE;
4066         this.encoding = null;
4067         this.parsedCssText = null;
4068         this.parentStyleSheet = null;
4069         this.parentRule = null;
4070     }
4071 
4072     jscsspCharsetRule.prototype = {
4073 
4074         cssText:function () {
4075             return "@charset " + this.encoding + ";";
4076         },
4077 
4078         setCssText:function (val) {
4079             var sheet = {cssRules:[]};
4080             var parser = new CSSParser(val);
4081             var token = parser.getToken(false, false);
4082             if (token.isAtRule("@charset")) {
4083                 if (parser.parseCharsetRule(token, sheet)) {
4084                     var newRule = sheet.cssRules[0];
4085                     this.encoding = newRule.encoding;
4086                     this.parsedCssText = newRule.parsedCssText;
4087                     return;
4088                 }
4089             }
4090             throw DOMException.SYNTAX_ERR;
4091         }
4092     };
4093 
4094     /* kJscsspUNKNOWN_RULE */
4095 
4096     function jscsspErrorRule(aErrorMsg) {
4097         this.error = aErrorMsg ? aErrorMsg : "INVALID";
4098         this.type = kJscsspUNKNOWN_RULE;
4099         this.parsedCssText = null;
4100         this.parentStyleSheet = null;
4101         this.parentRule = null;
4102     }
4103 
4104     jscsspErrorRule.prototype = {
4105         cssText:function () {
4106             return this.parsedCssText;
4107         }
4108     };
4109 
4110     /* kJscsspCOMMENT */
4111 
4112     function jscsspComment() {
4113         this.type = kJscsspCOMMENT;
4114         this.parsedCssText = null;
4115         this.parentStyleSheet = null;
4116         this.parentRule = null;
4117     }
4118 
4119     jscsspComment.prototype = {
4120         cssText:function () {
4121             return this.parsedCssText;
4122         },
4123 
4124         setCssText:function (val) {
4125             var parser = new CSSParser(val);
4126             var token = parser.getToken(true, false);
4127             if (token.isComment()) {
4128                 this.parsedCssText = token.value;
4129             } else {
4130                 throw DOMException.SYNTAX_ERR;
4131             }
4132         }
4133     };
4134 
4135     /* kJscsspWHITE_SPACE */
4136 
4137     function jscsspWhitespace() {
4138         this.type = kJscsspWHITE_SPACE;
4139         this.parsedCssText = null;
4140         this.parentStyleSheet = null;
4141         this.parentRule = null;
4142     }
4143 
4144     jscsspWhitespace.prototype = {
4145         cssText:function () {
4146             return this.parsedCssText;
4147         }
4148     };
4149 
4150     /* kJscsspIMPORT_RULE */
4151 
4152     function jscsspImportRule() {
4153         this.type = kJscsspIMPORT_RULE;
4154         this.parsedCssText = null;
4155         this.href = null;
4156         this.media = [];
4157         this.parentStyleSheet = null;
4158         this.parentRule = null;
4159     }
4160 
4161     jscsspImportRule.prototype = {
4162         cssText:function () {
4163             var mediaString = this.media.join(", ");
4164             return "@import " + this.href + ((mediaString && mediaString != "all") ? mediaString + " " : "") + ";";
4165         },
4166 
4167         setCssText:function (val) {
4168             var sheet = {cssRules:[]};
4169             var parser = new CSSParser(val);
4170             var token = parser.getToken(true, true);
4171             if (token.isAtRule("@import")) {
4172                 if (parser.parseImportRule(token, sheet)) {
4173                     var newRule = sheet.cssRules[0];
4174                     this.href = newRule.href;
4175                     this.media = newRule.media;
4176                     this.parsedCssText = newRule.parsedCssText;
4177                     return;
4178                 }
4179             }
4180             throw DOMException.SYNTAX_ERR;
4181         }
4182     };
4183 
4184     /* kJscsspNAMESPACE_RULE */
4185 
4186     function jscsspNamespaceRule() {
4187         this.type = kJscsspNAMESPACE_RULE;
4188         this.parsedCssText = null;
4189         this.prefix = null;
4190         this.url = null;
4191         this.parentStyleSheet = null;
4192         this.parentRule = null;
4193     }
4194 
4195     jscsspNamespaceRule.prototype = {
4196         cssText:function () {
4197             return "@namespace " + (this.prefix ? this.prefix + " " : "") + this.url + ";";
4198         },
4199 
4200         setCssText:function (val) {
4201             var sheet = {cssRules:[]};
4202             var parser = new CSSParser(val);
4203             var token = parser.getToken(true, true);
4204             if (token.isAtRule("@namespace")) {
4205                 if (parser.parseNamespaceRule(token, sheet)) {
4206                     var newRule = sheet.cssRules[0];
4207                     this.url = newRule.url;
4208                     this.prefix = newRule.prefix;
4209                     this.parsedCssText = newRule.parsedCssText;
4210                     return;
4211                 }
4212             }
4213             throw DOMException.SYNTAX_ERR;
4214         }
4215     };
4216 
4217     /* kJscsspSTYLE_DECLARATION */
4218 
4219     function jscsspDeclaration() {
4220         this.type = kJscsspSTYLE_DECLARATION;
4221         this.property = null;
4222         this.values = [];
4223         this.valueText = null;
4224         this.priority = null;
4225         this.parsedCssText = null;
4226         this.parentStyleSheet = null;
4227         this.parentRule = null;
4228     }
4229 
4230     jscsspDeclaration.prototype = {
4231         kCOMMA_SEPARATED:{
4232             "cursor":true,
4233             "font-family":true,
4234             "voice-family":true,
4235             "background-image":true
4236         },
4237 
4238         kUNMODIFIED_COMMA_SEPARATED_PROPERTIES:{
4239             "text-shadow":true,
4240             "box-shadow":true,
4241             "-moz-transition":true,
4242             "-moz-transition-property":true,
4243             "-moz-transition-duration":true,
4244             "-moz-transition-timing-function":true,
4245             "-moz-transition-delay":true
4246         },
4247 
4248         cssText:function () {
4249             var prefixes = CssInspector.prefixesForProperty(this.property);
4250 
4251             if (this.property in this.kUNMODIFIED_COMMA_SEPARATED_PROPERTIES) {
4252                 if (prefixes) {
4253                     var rv = "";
4254                     for (var propertyIndex = 0; propertyIndex < prefixes.length; propertyIndex++) {
4255                         var property = prefixes[propertyIndex];
4256                         rv += (propertyIndex ? gTABS : "") + property + ": ";
4257                         rv += this.valueText + (this.priority ? " !important" : "") + ";";
4258                         rv += ((prefixes.length > 1 && propertyIndex != prefixes.length - 1) ? "\n" : "");
4259                     }
4260                     return rv;
4261                 }
4262                 return this.property + ": " + this.valueText + (this.priority ? " !important" : "") + ";"
4263             }
4264 
4265             if (prefixes) {
4266                 var rv = "";
4267                 for (var propertyIndex = 0; propertyIndex < prefixes.length; propertyIndex++) {
4268                     var property = prefixes[propertyIndex];
4269                     rv += (propertyIndex ? gTABS : "") + property + ": ";
4270                     var separator = (property in this.kCOMMA_SEPARATED) ? ", " : " ";
4271                     for (var i = 0; i < this.values.length; i++) {
4272                         if (this.values[i].cssText() != null) {
4273                             rv += (i ? separator : "") + this.values[i].cssText();
4274                         } else {
4275                             return null;
4276                         }
4277                     }
4278                     rv += (this.priority ? " !important" : "") + ";" + ((prefixes.length > 1 && propertyIndex != prefixes.length - 1) ? "\n" : "");
4279                 }
4280                 return rv;
4281             }
4282 
4283             var rv = this.property + ": ";
4284             var separator = (this.property in this.kCOMMA_SEPARATED) ? ", " : " ";
4285             var extras = {"webkit":false, "presto":false, "trident":false, "generic":false }
4286             for (var i = 0; i < this.values.length; i++) {
4287                 var v = this.values[i].cssText();
4288                 if (v != null) {
4289                     var paren = v.indexOf("(");
4290                     var kwd = v;
4291                     if (paren != -1) {
4292                         kwd = v.substr(0, paren);
4293                     }
4294                     if (kwd in kCSS_VENDOR_VALUES) {
4295                         for (var j in kCSS_VENDOR_VALUES[kwd]) {
4296                             extras[j] = extras[j] || (kCSS_VENDOR_VALUES[kwd][j] != "");
4297                         }
4298                     }
4299                     rv += (i ? separator : "") + v;
4300                 } else {
4301                     return null;
4302                 }
4303             }
4304             rv += (this.priority ? " !important" : "") + ";";
4305 
4306             for (var j in extras) {
4307                 if (extras[j]) {
4308                     var str = "\n" + gTABS + this.property + ": ";
4309                     for (var i = 0; i < this.values.length; i++) {
4310                         var v = this.values[i].cssText();
4311                         if (v != null) {
4312                             var paren = v.indexOf("(");
4313                             var kwd = v;
4314                             if (paren != -1) {
4315                                 kwd = v.substr(0, paren);
4316                             }
4317                             if (kwd in kCSS_VENDOR_VALUES) {
4318                                 functor = kCSS_VENDOR_VALUES[kwd][j];
4319                                 if (functor) {
4320                                     v = (typeof functor == "string") ? functor : functor(v, j);
4321                                     if (!v) {
4322                                         str = null;
4323                                         break;
4324                                     }
4325                                 }
4326                             }
4327                             str += (i ? separator : "") + v;
4328                         } else {
4329                             return null;
4330                         }
4331                     }
4332                     if (str) {
4333                         rv += str + ";"
4334                     } else {
4335                         rv += "\n" + gTABS + "/* Impossible to translate property " + this.property + " for " + j + " */";
4336                     }
4337                 }
4338             }
4339             return rv;
4340         },
4341 
4342         setCssText:function (val) {
4343             var declarations = [];
4344             var parser = new CSSParser(val);
4345             var token = parser.getToken(true, true);
4346             if (parser.parseDeclaration(token, declarations, true, true, null) && declarations.length && declarations[0].type == kJscsspSTYLE_DECLARATION) {
4347                 var newDecl = declarations.cssRules[0];
4348                 this.property = newDecl.property;
4349                 this.value = newDecl.value;
4350                 this.priority = newDecl.priority;
4351                 this.parsedCssText = newRule.parsedCssText;
4352                 return;
4353             }
4354             throw DOMException.SYNTAX_ERR;
4355         }
4356     };
4357 
4358     /* kJscsspFONT_FACE_RULE */
4359 
4360     function jscsspFontFaceRule() {
4361         this.type = kJscsspFONT_FACE_RULE;
4362         this.parsedCssText = null;
4363         this.descriptors = [];
4364         this.parentStyleSheet = null;
4365         this.parentRule = null;
4366     }
4367 
4368     jscsspFontFaceRule.prototype = {
4369         cssText:function () {
4370             var rv = gTABS + "@font-face {\n";
4371             var preservedGTABS = gTABS;
4372             gTABS += "  ";
4373             for (var i = 0; i < this.descriptors.length; i++) {
4374                 rv += gTABS + this.descriptors[i].cssText() + "\n";
4375             }
4376             gTABS = preservedGTABS;
4377             return rv + gTABS + "}";
4378         },
4379 
4380         setCssText:function (val) {
4381             var sheet = {cssRules:[]};
4382             var parser = new CSSParser(val);
4383             var token = parser.getToken(true, true);
4384             if (token.isAtRule("@font-face")) {
4385                 if (parser.parseFontFaceRule(token, sheet)) {
4386                     var newRule = sheet.cssRules[0];
4387                     this.descriptors = newRule.descriptors;
4388                     this.parsedCssText = newRule.parsedCssText;
4389                     return;
4390                 }
4391             }
4392             throw DOMException.SYNTAX_ERR;
4393         }
4394     };
4395 
4396     /* kJscsspMEDIA_RULE */
4397 
4398     function jscsspMediaRule() {
4399         this.type = kJscsspMEDIA_RULE;
4400         this.parsedCssText = null;
4401         this.cssRules = [];
4402         this.media = [];
4403         this.parentStyleSheet = null;
4404         this.parentRule = null;
4405     }
4406 
4407     jscsspMediaRule.prototype = {
4408         cssText:function () {
4409             var rv = gTABS + "@media " + this.media.join(", ") + " {\n";
4410             var preservedGTABS = gTABS;
4411             gTABS += "  ";
4412             for (var i = 0; i < this.cssRules.length; i++) {
4413                 rv += gTABS + this.cssRules[i].cssText() + "\n";
4414             }
4415             gTABS = preservedGTABS;
4416             return rv + gTABS + "}";
4417         },
4418 
4419         setCssText:function (val) {
4420             var sheet = {cssRules:[]};
4421             var parser = new CSSParser(val);
4422             var token = parser.getToken(true, true);
4423             if (token.isAtRule("@media")) {
4424                 if (parser.parseMediaRule(token, sheet)) {
4425                     var newRule = sheet.cssRules[0];
4426                     this.cssRules = newRule.cssRules;
4427                     this.media = newRule.media;
4428                     this.parsedCssText = newRule.parsedCssText;
4429                     return;
4430                 }
4431             }
4432             throw DOMException.SYNTAX_ERR;
4433         }
4434     };
4435 
4436     /* kJscsspSTYLE_RULE */
4437 
4438     function jscsspStyleRule() {
4439         this.type = kJscsspSTYLE_RULE;
4440         this.parsedCssText = null;
4441         this.declarations = []
4442         this.mSelectorText = null;
4443         this.parentStyleSheet = null;
4444         this.parentRule = null;
4445     }
4446 
4447     jscsspStyleRule.prototype = {
4448         cssText:function () {
4449             var rv = this.mSelectorText + " {\n";
4450             var preservedGTABS = gTABS;
4451             gTABS += "  ";
4452             for (var i = 0; i < this.declarations.length; i++) {
4453                 var declText = this.declarations[i].cssText();
4454                 if (declText) {
4455                     rv += gTABS + this.declarations[i].cssText() + "\n";
4456                 }
4457             }
4458             gTABS = preservedGTABS;
4459             return rv + gTABS + "}";
4460         },
4461 
4462         setCssText:function (val) {
4463             var sheet = {cssRules:[]};
4464             var parser = new CSSParser(val);
4465             var token = parser.getToken(true, true);
4466             if (!token.isNotNull()) {
4467                 if (parser.parseStyleRule(token, sheet, false)) {
4468                     var newRule = sheet.cssRules[0];
4469                     this.mSelectorText = newRule.mSelectorText;
4470                     this.declarations = newRule.declarations;
4471                     this.parsedCssText = newRule.parsedCssText;
4472                     return;
4473                 }
4474             }
4475             throw DOMException.SYNTAX_ERR;
4476         },
4477 
4478         selectorText:function () {
4479             return this.mSelectorText;
4480         },
4481 
4482         setSelectorText:function (val) {
4483             var parser = new CSSParser(val);
4484             var token = parser.getToken(true, true);
4485             if (!token.isNotNull()) {
4486                 var s = parser.parseSelector(token, true);
4487                 if (s) {
4488                     this.mSelectorText = s.selector;
4489                     return;
4490                 }
4491             }
4492             throw DOMException.SYNTAX_ERR;
4493         }
4494     };
4495 
4496     /* kJscsspPAGE_RULE */
4497 
4498     function jscsspPageRule() {
4499         this.type = kJscsspPAGE_RULE;
4500         this.parsedCssText = null;
4501         this.pageSelector = null;
4502         this.declarations = [];
4503         this.parentStyleSheet = null;
4504         this.parentRule = null;
4505     }
4506 
4507     jscsspPageRule.prototype = {
4508         cssText:function () {
4509             var rv = gTABS + "@page " + (this.pageSelector ? this.pageSelector + " " : "") + "{\n";
4510             var preservedGTABS = gTABS;
4511             gTABS += "  ";
4512             for (var i = 0; i < this.declarations.length; i++) {
4513                 rv += gTABS + this.declarations[i].cssText() + "\n";
4514             }
4515             gTABS = preservedGTABS;
4516             return rv + gTABS + "}";
4517         },
4518 
4519         setCssText:function (val) {
4520             var sheet = {cssRules:[]};
4521             var parser = new CSSParser(val);
4522             var token = parser.getToken(true, true);
4523             if (token.isAtRule("@page")) {
4524                 if (parser.parsePageRule(token, sheet)) {
4525                     var newRule = sheet.cssRules[0];
4526                     this.pageSelector = newRule.pageSelector;
4527                     this.declarations = newRule.declarations;
4528                     this.parsedCssText = newRule.parsedCssText;
4529                     return;
4530                 }
4531             }
4532             throw DOMException.SYNTAX_ERR;
4533         }
4534     };
4535 
4536     /* kJscsspVARIABLES_RULE */
4537 
4538     function jscsspVariablesRule() {
4539         this.type = kJscsspVARIABLES_RULE;
4540         this.parsedCssText = null;
4541         this.declarations = [];
4542         this.parentStyleSheet = null;
4543         this.parentRule = null;
4544         this.media = null;
4545     }
4546 
4547     jscsspVariablesRule.prototype = {
4548         cssText:function () {
4549             var rv = gTABS + "@variables " + (this.media.length ? this.media.join(", ") + " " : "") + "{\n";
4550             var preservedGTABS = gTABS;
4551             gTABS += "  ";
4552             for (var i = 0; i < this.declarations.length; i++) {
4553                 rv += gTABS + this.declarations[i].cssText() + "\n";
4554             }
4555             gTABS = preservedGTABS;
4556             return rv + gTABS + "}";
4557         },
4558 
4559         setCssText:function (val) {
4560             var sheet = {cssRules:[]};
4561             var parser = new CSSParser(val);
4562             var token = parser.getToken(true, true);
4563             if (token.isAtRule("@variables")) {
4564                 if (parser.parseVariablesRule(token, sheet)) {
4565                     var newRule = sheet.cssRules[0];
4566                     this.declarations = newRule.declarations;
4567                     this.parsedCssText = newRule.parsedCssText;
4568                     return;
4569                 }
4570             }
4571             throw DOMException.SYNTAX_ERR;
4572         }
4573     };
4574 
4575     var kJscsspINHERIT_VALUE = 0;
4576     var kJscsspPRIMITIVE_VALUE = 1;
4577     var kJscsspVARIABLE_VALUE = 4;
4578 
4579     function jscsspVariable(aType, aSheet) {
4580         this.value = "";
4581         this.type = aType;
4582         this.name = null;
4583         this.parentRule = null;
4584         this.parentStyleSheet = aSheet;
4585     }
4586 
4587     jscsspVariable.prototype = {
4588         cssText:function () {
4589             if (this.type == kJscsspVARIABLE_VALUE) {
4590                 return this.resolveVariable(this.name, this.parentRule, this.parentStyleSheet);
4591             } else {
4592                 return this.value;
4593             }
4594         },
4595 
4596         setCssText:function (val) {
4597             if (this.type == kJscsspVARIABLE_VALUE) {
4598                 throw DOMException.SYNTAX_ERR;
4599             } else {
4600                 this.value = val;
4601             }
4602         },
4603 
4604         resolveVariable:function (aName, aRule, aSheet) {
4605             if (aName.toLowerCase() in aSheet.variables) {
4606                 return aSheet.variables[aName.toLowerCase()];
4607             }
4608             return null;
4609         }
4610     };
4611 
4612     function ParseURL(buffer) {
4613         var result = { };
4614         result.protocol = "";
4615         result.user = "";
4616         result.password = "";
4617         result.host = "";
4618         result.port = "";
4619         result.path = "";
4620         result.query = "";
4621 
4622         var section = "PROTOCOL";
4623         var start = 0;
4624         var wasSlash = false;
4625 
4626         while (start < buffer.length) {
4627             if (section == "PROTOCOL") {
4628                 if (buffer.charAt(start) == ':') {
4629                     section = "AFTER_PROTOCOL";
4630                     start++;
4631                 } else if (buffer.charAt(start) == '/' && result.protocol.length() == 0) {
4632                     section = PATH;
4633                 } else {
4634                     result.protocol += buffer.charAt(start++);
4635                 }
4636             } else if (section == "AFTER_PROTOCOL") {
4637                 if (buffer.charAt(start) == '/') {
4638                     if (!wasSlash) {
4639                         wasSlash = true;
4640                     } else {
4641                         wasSlash = false;
4642                         section = "USER";
4643                     }
4644                     start++;
4645                 } else {
4646                     throw new ParseException("Protocol shell be separated with 2 slashes");
4647                 }
4648             } else if (section == "USER") {
4649                 if (buffer.charAt(start) == '/') {
4650                     result.host = result.user;
4651                     result.user = "";
4652                     section = "PATH";
4653                 } else if (buffer.charAt(start) == '?') {
4654                     result.host = result.user;
4655                     result.user = "";
4656                     section = "QUERY";
4657                     start++;
4658                 } else if (buffer.charAt(start) == ':') {
4659                     section = "PASSWORD";
4660                     start++;
4661                 } else if (buffer.charAt(start) == '@') {
4662                     section = "HOST";
4663                     start++;
4664                 } else {
4665                     result.user += buffer.charAt(start++);
4666                 }
4667             } else if (section == "PASSWORD") {
4668                 if (buffer.charAt(start) == '/') {
4669                     result.host = result.user;
4670                     result.port = result.password;
4671                     result.user = "";
4672                     result.password = "";
4673                     section = "PATH";
4674                 } else if (buffer.charAt(start) == '?') {
4675                     result.host = result.user;
4676                     result.port = result.password;
4677                     result.user = "";
4678                     result.password = "";
4679                     section = "QUERY";
4680                     start++;
4681                 } else if (buffer.charAt(start) == '@') {
4682                     section = "HOST";
4683                     start++;
4684                 } else {
4685                     result.password += buffer.charAt(start++);
4686                 }
4687             } else if (section == "HOST") {
4688                 if (buffer.charAt(start) == '/') {
4689                     section = "PATH";
4690                 } else if (buffer.charAt(start) == ':') {
4691                     section = "PORT";
4692                     start++;
4693                 } else if (buffer.charAt(start) == '?') {
4694                     section = "QUERY";
4695                     start++;
4696                 } else {
4697                     result.host += buffer.charAt(start++);
4698                 }
4699             } else if (section == "PORT") {
4700                 if (buffer.charAt(start) = '/') {
4701                     section = "PATH";
4702                 } else if (buffer.charAt(start) == '?') {
4703                     section = "QUERY";
4704                     start++;
4705                 } else {
4706                     result.port += buffer.charAt(start++);
4707                 }
4708             } else if (section == "PATH") {
4709                 if (buffer.charAt(start) == '?') {
4710                     section = "QUERY";
4711                     start++;
4712                 } else {
4713                     result.path += buffer.charAt(start++);
4714                 }
4715             } else if (section == "QUERY") {
4716                 result.query += buffer.charAt(start++);
4717             }
4718         }
4719 
4720         if (section == "PROTOCOL") {
4721             result.host = result.protocol;
4722             result.protocol = "http";
4723         } else if (section == "AFTER_PROTOCOL") {
4724             throw new ParseException("Invalid url");
4725         } else if (section == "USER") {
4726             result.host = result.user;
4727             result.user = "";
4728         } else if (section == "PASSWORD") {
4729             result.host = result.user;
4730             result.port = result.password;
4731             result.user = "";
4732             result.password = "";
4733         }
4734 
4735         return result;
4736     }
4737 
4738     function ParseException(description) {
4739         this.description = description;
4740     }
4741 
4742     function CountLF(s) {
4743         var nCR = s.match(/\n/g);
4744         return nCR ? nCR.length + 1 : 1;
4745     }
4746 
4747     function FilterLinearGradientForOutput(aValue, aEngine) {
4748         if (aEngine == "generic") {
4749             return aValue.substr(5);
4750         }
4751 
4752         if (aEngine == "webkit") {
4753             return aValue.replace(/\-moz\-/g, "-webkit-")
4754         }
4755 
4756         if (aEngine != "webkit20110101") {
4757             return "";
4758         }
4759 
4760         var g = CssInspector.parseBackgroundImages(aValue)[0];
4761 
4762         var cancelled = false;
4763         var str = "-webkit-gradient(linear, ";
4764         var position = ("position" in g.value) ? g.value.position.toLowerCase() : "";
4765         var angle = ("angle" in g.value) ? g.value.angle.toLowerCase() : "";
4766         // normalize angle
4767         if (angle) {
4768             var match = angle.match(/^([0-9\-\.\\+]+)([a-z]*)/);
4769             var angle = parseFloat(match[1]);
4770             var unit = match[2];
4771             switch (unit) {
4772                 case "grad":
4773                     angle = angle * 90 / 100;
4774                     break;
4775                 case "rad":
4776                     angle = angle * 180 / Math.PI;
4777                     break;
4778                 default:
4779                     break;
4780             }
4781             while (angle < 0) {
4782                 angle += 360;
4783             }
4784             while (angle >= 360) {
4785                 angle -= 360;
4786             }
4787         }
4788         // get startpoint w/o keywords
4789         var startpoint = [];
4790         var endpoint = [];
4791         if (position != "") {
4792             if (position == "center") {
4793                 position = "center center";
4794             }
4795             startpoint = position.split(" ");
4796             if (angle == "" && angle != 0) {
4797                 // no angle, then we just turn the point 180 degrees around center
4798                 switch (startpoint[0]) {
4799                     case "left":
4800                         endpoint.push("right");
4801                         break;
4802                     case "center":
4803                         endpoint.push("center");
4804                         break;
4805                     case "right":
4806                         endpoint.push("left");
4807                         break;
4808                     default:
4809                     {
4810                         var match = startpoint[0].match(/^([0-9\-\.\\+]+)([a-z]*)/);
4811                         var v = parseFloat(match[0]);
4812                         var unit = match[1];
4813                         if (unit == "%") {
4814                             endpoint.push((100 - v) + "%");
4815                         } else {
4816                             cancelled = true;
4817                         }
4818                     }
4819                         break;
4820                 }
4821                 if (!cancelled) {
4822                     switch (startpoint[1]) {
4823                         case "top":
4824                             endpoint.push("bottom");
4825                             break;
4826                         case "center":
4827                             endpoint.push("center");
4828                             break;
4829                         case "bottom":
4830                             endpoint.push("top");
4831                             break;
4832                         default:
4833                         {
4834                             var match = startpoint[1].match(/^([0-9\-\.\\+]+)([a-z]*)/);
4835                             var v = parseFloat(match[0]);
4836                             var unit = match[1];
4837                             if (unit == "%") {
4838                                 endpoint.push((100 - v) + "%");
4839                             } else {
4840                                 cancelled = true;
4841                             }
4842                         }
4843                             break;
4844                     }
4845                 }
4846             } else {
4847                 switch (angle) {
4848                     case 0:
4849                         endpoint.push("right");
4850                         endpoint.push(startpoint[1]);
4851                         break;
4852                     case 90:
4853                         endpoint.push(startpoint[0]);
4854                         endpoint.push("top");
4855                         break;
4856                     case 180:
4857                         endpoint.push("left");
4858                         endpoint.push(startpoint[1]);
4859                         break;
4860                     case 270:
4861                         endpoint.push(startpoint[0]);
4862                         endpoint.push("bottom");
4863                         break;
4864                     default:
4865                         cancelled = true;
4866                         break;
4867                 }
4868             }
4869         } else {
4870             // no position defined, we accept only vertical and horizontal
4871             if (angle == "") {
4872                 angle = 270;
4873             }
4874             switch (angle) {
4875                 case 0:
4876                     startpoint = ["left", "center"];
4877                     endpoint = ["right", "center"];
4878                     break;
4879                 case 90:
4880                     startpoint = ["center", "bottom"];
4881                     endpoint = ["center", "top"];
4882                     break;
4883                 case 180:
4884                     startpoint = ["right", "center"];
4885                     endpoint = ["left", "center"];
4886                     break;
4887                 case 270:
4888                     startpoint = ["center", "top"];
4889                     endpoint = ["center", "bottom"];
4890                     break;
4891                 default:
4892                     cancelled = true;
4893                     break;
4894             }
4895         }
4896 
4897         if (cancelled) {
4898             return "";
4899         }
4900 
4901         str += startpoint.join(" ") + ", " + endpoint.join(" ");
4902         if (!g.value.stops[0].position) {
4903             g.value.stops[0].position = "0%";
4904         }
4905         if (!g.value.stops[g.value.stops.length - 1].position) {
4906             g.value.stops[g.value.stops.length - 1].position = "100%";
4907         }
4908         var current = 0;
4909         for (var i = 0; i < g.value.stops.length && !cancelled; i++) {
4910             var s = g.value.stops[i];
4911             if (s.position) {
4912                 if (s.position.indexOf("%") == -1) {
4913                     cancelled = true;
4914                     break;
4915                 }
4916             } else {
4917                 var j = i + 1;
4918                 while (j < g.value.stops.length && !g.value.stops[j].position) {
4919                     j++;
4920                 }
4921                 var inc = parseFloat(g.value.stops[j].position) - current;
4922                 for (var k = i; k < j; k++) {
4923                     g.value.stops[k].position = (current + inc * (k - i + 1) / (j - i + 1)) + "%";
4924                 }
4925             }
4926             current = parseFloat(s.position);
4927             str += ", color-stop(" + (parseFloat(current) / 100) + ", " + s.color + ")";
4928         }
4929 
4930         if (cancelled) {
4931             return "";
4932         }
4933         return str + ")";
4934     }
4935 
4936     function FilterRadialGradientForOutput(aValue, aEngine) {
4937         if (aEngine == "generic") {
4938             return aValue.substr(5);
4939         }
4940 
4941         else if (aEngine == "webkit") {
4942             return aValue.replace(/\-moz\-/g, "-webkit-")
4943         }
4944 
4945         else if (aEngine != "webkit20110101") {
4946             return "";
4947         }
4948 
4949         var g = CssInspector.parseBackgroundImages(aValue)[0];
4950 
4951         var shape = ("shape" in g.value) ? g.value.shape : "";
4952         var size = ("size"  in g.value) ? g.value.size : "";
4953         if (shape != "circle" || (size != "farthest-corner" && size != "cover")) {
4954             return "";
4955         }
4956 
4957         if (g.value.stops.length < 2 || !("position" in g.value.stops[0]) || !g.value.stops[g.value.stops.length - 1].position || !("position" in g.value.stops[0]) || !g.value.stops[g.value.stops.length - 1].position) {
4958             return "";
4959         }
4960 
4961         for (var i = 0; i < g.value.stops.length; i++) {
4962             var s = g.value.stops[i];
4963             if (("position" in s) && s.position && s.position.indexOf("px") == -1) {
4964                 return "";
4965             }
4966         }
4967 
4968         var str = "-webkit-gradient(radial, ";
4969         var position = ("position"  in g.value) ? g.value.position : "center center";
4970         str += position + ", " + parseFloat(g.value.stops[0].position) + ", ";
4971         str += position + ", " + parseFloat(g.value.stops[g.value.stops.length - 1].position);
4972 
4973         // at this point we're sure to deal with pixels
4974         var current = parseFloat(g.value.stops[0].position);
4975         for (var i = 0; i < g.value.stops.length; i++) {
4976             var s = g.value.stops[i];
4977             if (!("position" in s) || !s.position) {
4978                 var j = i + 1;
4979                 while (j < g.value.stops.length && !g.value.stops[j].position) {
4980                     j++;
4981                 }
4982                 var inc = parseFloat(g.value.stops[j].position) - current;
4983                 for (var k = i; k < j; k++) {
4984                     g.value.stops[k].position = (current + inc * (k - i + 1) / (j - i + 1)) + "px";
4985                 }
4986             }
4987             current = parseFloat(s.position);
4988             var c = (current - parseFloat(g.value.stops[0].position)) / (parseFloat(g.value.stops[g.value.stops.length - 1].position) - parseFloat(g.value.stops[0].position));
4989             str += ", color-stop(" + c + ", " + s.color + ")";
4990         }
4991         str += ")"
4992         return str;
4993     }
4994 
4995     function FilterRepeatingGradientForOutput(aValue, aEngine) {
4996         if (aEngine == "generic") {
4997             return aValue.substr(5);
4998         }
4999 
5000         else if (aEngine == "webkit") {
5001             return aValue.replace(/\-moz\-/g, "-webkit-")
5002         }
5003 
5004         return "";
5005     }
5006 
5007     /**#nocode-*/
5008 
5009     /**
5010      * Creates a style declaration
5011      *
5012      * @param {string} property property name
5013      * @param {string} valueText property value
5014      *
5015      * @class CSS Property declaration
5016      * @name StyleDeclaration
5017      */
5018     function StyleDeclaration(property, valueText) {
5019         this.property = property;
5020         this.valueText = valueText;
5021     }
5022 
5023     StyleDeclaration.prototype = {
5024         /**
5025          * Property name
5026          * @type string
5027          */
5028         property:"",
5029         /**
5030          * Property value
5031          * @type string
5032          */
5033         valueText:"",
5034     };
5035 
5036     var cssParser, logger, CssParserInstance;
5037 
5038     logger = wef.logger("wef.cssParser");
5039 
5040     /**
5041      * Creates a CSS parser
5042      *
5043      * @class CSS parser
5044      */
5045     cssParser = function () {
5046         return new cssParser.prototype.init();
5047     };
5048 
5049     cssParser.prototype = {
5050         /**
5051          * Version number*/
5052         version:"0.2.0",
5053         /**
5054          * Stores callback functions. Looks like register events
5055          */
5056         callbacks:{
5057             /**
5058              * todo
5059              */
5060             parserStar:undefined,
5061             parserStop:undefined,
5062             cssRuleFound:undefined,
5063             propertyFound:undefined,
5064             error:undefined
5065         },
5066         constructor:cssParser,
5067         /**
5068          * @ignore
5069          */
5070         init:function () {
5071             this.callbacks = {
5072                 parserStar:undefined,
5073                 parserStop:undefined,
5074                 cssRuleFound:undefined,
5075                 propertyFound:undefined,
5076                 error:undefined
5077             };
5078             return this;
5079         }
5080     };
5081 
5082     /**
5083      * Extension point
5084      */
5085     cssParser.fn = cssParser.prototype;
5086 
5087     cssParser.prototype.init.prototype = cssParser.prototype;
5088 
5089     wef.extend(cssParser.prototype.init.prototype,
5090         /**
5091          * @lends cssParser#
5092          */
5093                {
5094                    /**
5095                     * Reference to jscssp parser
5096                     */
5097                    backend:undefined,
5098 
5099                    /**
5100                     * Calls parserStar callback
5101                     * @param {Function}callback "Parser starts" callback
5102                     */
5103                    whenStart:function (callback) {
5104                        if (wef.isFunction(callback)) {
5105                            logger.debug("set parserStart callback");
5106                            this.callbacks.parserStar = callback;
5107                        }
5108                        return this;
5109                    },
5110                    /**
5111                     * Calls parserStop callback
5112                     * @param {Function}callback "Parser stops" callback
5113                     */
5114                    whenStop:function (callback) {
5115                        if (wef.isFunction(callback)) {
5116                            logger.debug("set parserStop callback");
5117                            this.callbacks.parserStop = callback;
5118                        }
5119                        return this;
5120                    },
5121                    /**
5122                     * Calls cssRuleFound callback
5123                     * @param {Function}callback "Css rule found" callback
5124                     */
5125                    whenCssRule:function (callback) {
5126                        if (wef.isFunction(callback)) {
5127                            logger.debug("set CssRuleFound callback");
5128                            this.callbacks.cssRuleFound = callback;
5129                        }
5130                        return this;
5131                    },
5132                    /**
5133                     * Calls propertyFound callback
5134                     * @param {Function}callback "Property found" callback
5135                     */
5136                    whenProperty:function (callback) {
5137                        if (wef.isFunction(callback)) {
5138                            logger.debug("set propertyFound callback");
5139                            this.callbacks.propertyFound = callback;
5140                        }
5141                        return this;
5142                    },
5143                    /**
5144                     * Calls error callback
5145                     * @param {Function}callback "Error" callback
5146                     */
5147                    whenError:function (callback) {
5148                        if (wef.isFunction(callback)) {
5149                            logger.debug("set error callback");
5150                            this.callbacks.error = callback;
5151                        }
5152                        return this;
5153                    },
5154                    /**
5155                     * Parses given text calling callbacks when registered actions happens
5156                     * @param {string}data CSS code
5157                     */
5158                    parse:function (data) {
5159                        var sheet, property, context = this;
5160                        try {
5161                            if (!data || !wef.isString(data) || data === "") {
5162                                var message = "InvalidArgumentException - data must be a non empty string";
5163                                logger.error(message);
5164                                throw new Error(message);
5165                            }
5166                            if (context.callbacks.parserStar) {
5167                                logger.debug("call parserStart callback");
5168                                context.callbacks.parserStar.call(context,
5169                                    /**
5170                                     * @namespace Parser start data format
5171                                     * @name StartCallbackData
5172                                     */
5173                                    /**
5174                                     * @lends StartCallbackData#
5175                                     */
5176                                                                  {
5177                                                                      /**
5178                                                                       * Start time in millisecons
5179                                                                       * @type Number
5180                                                                       */
5181                                                                      time:new Date().getTime()});
5182                            }
5183                            sheet = new CSSParser().parse(data, false, false);
5184                            //start
5185                            sheet.cssRules.forEach(function (cssRule) {
5186                                logger.debug("cssRule:", cssRule);
5187                                if (context.callbacks.cssRuleFound) {
5188                                    logger.debug("call cssRuleFound callback");
5189                                    context.callbacks.cssRuleFound.call(context, cssRule);
5190                                }
5191                                //ErrorRule
5192                                if (cssRule.type === 0) {
5193                                    var message = "ParserException - Error in line " + cssRule.currentLine + ": " + cssRule.parsedCssText;
5194                                    logger.error(message);
5195                                    throw new Error(message);
5196                                }
5197                                cssRule.declarations.forEach(function (declaration) {
5198                                    /**
5199                                     * @namespace CSS property found data format
5200                                     * @name CSSParserProperty
5201                                     */
5202                                    property =
5203                                    /**
5204                                     * @lends CSSParserProperty#
5205                                     */
5206                                    {
5207                                        /**
5208                                         * Property selector
5209                                         * @type string
5210                                         */
5211                                        selectorText:cssRule.selectorText(),
5212                                        /**
5213                                         * Property declaration
5214                                         * @type StyleDeclaration
5215                                         */
5216                                        declaration:new StyleDeclaration(declaration.property, declaration.valueText)
5217                                    };
5218                                    logger.debug("property:", property);
5219                                    if (context.callbacks.propertyFound) {
5220                                        logger.debug("call propertyFound callback");
5221                                        context.callbacks.propertyFound.call(context, property);
5222                                    }
5223                                });
5224                            });
5225                            //done
5226                            if (context.callbacks.parserStop) {
5227                                logger.debug("call parserStop callback");
5228                                context.callbacks.parserStop.call(context,
5229                                    /**
5230                                     * @namespace Parser stop data format
5231                                     * @name StopCallbackData
5232                                     */
5233                                    /**
5234                                     * @lends StopCallbackData#
5235                                     */
5236                                                                  {
5237                                                                      /**
5238                                                                       * Stop time in millisecons
5239                                                                       * @type Number
5240                                                                       */
5241                                                                      time:new Date().getTime()});
5242                            }
5243                        } catch (e) {
5244                            if (context.callbacks.error) {
5245                                logger.error("call error callback:", e);
5246                                context.callbacks.error.call(context, e.message);
5247                                return this;
5248                            } else {
5249                                logger.error("unhandled error call wef.error:", e);
5250                                wef.error(e.message);
5251                                return null;
5252                            }
5253                        }
5254                        return this;
5255                    }
5256                });
5257 
5258     wef.cssParser = cssParser;
5259 
5260     logger.info("cssParser plugged to wef.cssParser");
5261 
5262 })(window.wef);