Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(518)

Side by Side Diff: appengine/config_service/ui/bower_components/shadycss/src/css-parse.js

Issue 2923973003: Added base template for config ui. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /**
2 @license
3 Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 */
10
11 /*
12 Extremely simple css parser. Intended to be not more than what we need
13 and definitely not necessarily correct =).
14 */
15
16 'use strict';
17
18 /** @unrestricted */
19 class StyleNode {
20 constructor() {
21 /** @type {number} */
22 this['start'] = 0;
23 /** @type {number} */
24 this['end'] = 0;
25 /** @type {StyleNode} */
26 this['previous'] = null;
27 /** @type {StyleNode} */
28 this['parent'] = null;
29 /** @type {Array<StyleNode>} */
30 this['rules'] = null;
31 /** @type {string} */
32 this['parsedCssText'] = '';
33 /** @type {string} */
34 this['cssText'] = '';
35 /** @type {boolean} */
36 this['atRule'] = false;
37 /** @type {number} */
38 this['type'] = 0;
39 /** @type {string} */
40 this['keyframesName'] = '';
41 /** @type {string} */
42 this['selector'] = '';
43 /** @type {string} */
44 this['parsedSelector'] = '';
45 }
46 }
47
48 export {StyleNode}
49
50 // given a string of css, return a simple rule tree
51 /**
52 * @param {string} text
53 * @return {StyleNode}
54 */
55 export function parse(text) {
56 text = clean(text);
57 return parseCss(lex(text), text);
58 }
59
60 // remove stuff we don't care about that may hinder parsing
61 /**
62 * @param {string} cssText
63 * @return {string}
64 */
65 function clean(cssText) {
66 return cssText.replace(RX.comments, '').replace(RX.port, '');
67 }
68
69 // super simple {...} lexer that returns a node tree
70 /**
71 * @param {string} text
72 * @return {StyleNode}
73 */
74 function lex(text) {
75 let root = new StyleNode();
76 root['start'] = 0;
77 root['end'] = text.length
78 let n = root;
79 for (let i = 0, l = text.length; i < l; i++) {
80 if (text[i] === OPEN_BRACE) {
81 if (!n['rules']) {
82 n['rules'] = [];
83 }
84 let p = n;
85 let previous = p['rules'][p['rules'].length - 1] || null;
86 n = new StyleNode();
87 n['start'] = i + 1;
88 n['parent'] = p;
89 n['previous'] = previous;
90 p['rules'].push(n);
91 } else if (text[i] === CLOSE_BRACE) {
92 n['end'] = i + 1;
93 n = n['parent'] || root;
94 }
95 }
96 return root;
97 }
98
99 // add selectors/cssText to node tree
100 /**
101 * @param {StyleNode} node
102 * @param {string} text
103 * @return {StyleNode}
104 */
105 function parseCss(node, text) {
106 let t = text.substring(node['start'], node['end'] - 1);
107 node['parsedCssText'] = node['cssText'] = t.trim();
108 if (node['parent']) {
109 let ss = node['previous'] ? node['previous']['end'] : node['parent']['start' ];
110 t = text.substring(ss, node['start'] - 1);
111 t = _expandUnicodeEscapes(t);
112 t = t.replace(RX.multipleSpaces, ' ');
113 // TODO(sorvell): ad hoc; make selector include only after last ;
114 // helps with mixin syntax
115 t = t.substring(t.lastIndexOf(';') + 1);
116 let s = node['parsedSelector'] = node['selector'] = t.trim();
117 node['atRule'] = (s.indexOf(AT_START) === 0);
118 // note, support a subset of rule types...
119 if (node['atRule']) {
120 if (s.indexOf(MEDIA_START) === 0) {
121 node['type'] = types.MEDIA_RULE;
122 } else if (s.match(RX.keyframesRule)) {
123 node['type'] = types.KEYFRAMES_RULE;
124 node['keyframesName'] =
125 node['selector'].split(RX.multipleSpaces).pop();
126 }
127 } else {
128 if (s.indexOf(VAR_START) === 0) {
129 node['type'] = types.MIXIN_RULE;
130 } else {
131 node['type'] = types.STYLE_RULE;
132 }
133 }
134 }
135 let r$ = node['rules'];
136 if (r$) {
137 for (let i = 0, l = r$.length, r;
138 (i < l) && (r = r$[i]); i++) {
139 parseCss(r, text);
140 }
141 }
142 return node;
143 }
144
145 /**
146 * conversion of sort unicode escapes with spaces like `\33 ` (and longer) into
147 * expanded form that doesn't require trailing space `\000033`
148 * @param {string} s
149 * @return {string}
150 */
151 function _expandUnicodeEscapes(s) {
152 return s.replace(/\\([0-9a-f]{1,6})\s/gi, function() {
153 let code = arguments[1],
154 repeat = 6 - code.length;
155 while (repeat--) {
156 code = '0' + code;
157 }
158 return '\\' + code;
159 });
160 }
161
162 /**
163 * stringify parsed css.
164 * @param {StyleNode} node
165 * @param {boolean=} preserveProperties
166 * @param {string=} text
167 * @return {string}
168 */
169 export function stringify(node, preserveProperties, text = '') {
170 // calc rule cssText
171 let cssText = '';
172 if (node['cssText'] || node['rules']) {
173 let r$ = node['rules'];
174 if (r$ && !_hasMixinRules(r$)) {
175 for (let i = 0, l = r$.length, r;
176 (i < l) && (r = r$[i]); i++) {
177 cssText = stringify(r, preserveProperties, cssText);
178 }
179 } else {
180 cssText = preserveProperties ? node['cssText'] :
181 removeCustomProps(node['cssText']);
182 cssText = cssText.trim();
183 if (cssText) {
184 cssText = ' ' + cssText + '\n';
185 }
186 }
187 }
188 // emit rule if there is cssText
189 if (cssText) {
190 if (node['selector']) {
191 text += node['selector'] + ' ' + OPEN_BRACE + '\n';
192 }
193 text += cssText;
194 if (node['selector']) {
195 text += CLOSE_BRACE + '\n\n';
196 }
197 }
198 return text;
199 }
200
201 /**
202 * @param {Array<StyleNode>} rules
203 * @return {boolean}
204 */
205 function _hasMixinRules(rules) {
206 let r = rules[0];
207 return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START ) === 0;
208 }
209
210 /**
211 * @param {string} cssText
212 * @return {string}
213 */
214 function removeCustomProps(cssText) {
215 cssText = removeCustomPropAssignment(cssText);
216 return removeCustomPropApply(cssText);
217 }
218
219 /**
220 * @param {string} cssText
221 * @return {string}
222 */
223 export function removeCustomPropAssignment(cssText) {
224 return cssText
225 .replace(RX.customProp, '')
226 .replace(RX.mixinProp, '');
227 }
228
229 /**
230 * @param {string} cssText
231 * @return {string}
232 */
233 function removeCustomPropApply(cssText) {
234 return cssText
235 .replace(RX.mixinApply, '')
236 .replace(RX.varApply, '');
237 }
238
239 /** @enum {number} */
240 export const types = {
241 STYLE_RULE: 1,
242 KEYFRAMES_RULE: 7,
243 MEDIA_RULE: 4,
244 MIXIN_RULE: 1000
245 }
246
247 const OPEN_BRACE = '{';
248 const CLOSE_BRACE = '}';
249
250 // helper regexp's
251 const RX = {
252 comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
253 port: /@import[^;]*;/gim,
254 customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,
255 mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,
256 mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,
257 varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
258 keyframesRule: /^@[^\s]*keyframes/,
259 multipleSpaces: /\s+/g
260 }
261
262 const VAR_START = '--';
263 const MEDIA_START = '@media';
264 const AT_START = '@';
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698