UNPKG

24.5 kBJavaScriptView Raw
1import { off } from "process";
2import { modifiers } from "../src/compiler/theme.imba";
3import { TAG_NAMES } from '../src/compiler/constants.imba1';
4
5/*
6Copyright (c) 2013 Dulin Marat
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in
16all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24THE SOFTWARE.
25*/
26function CssSelectorParser() {
27 this.pseudos = {};
28 this.attrEqualityMods = {};
29 this.ruleNestingOperators = {};
30 this.substitutesEnabled = false;
31}
32
33function RULE(obj,base=[]){
34 return Object.assign(base,obj);
35}
36
37CssSelectorParser.prototype.registerSelectorPseudos = function(name) {
38 for (var j = 0, len = arguments.length; j < len; j++) {
39 name = arguments[j];
40 this.pseudos[name] = 'selector';
41 }
42 return this;
43};
44
45CssSelectorParser.prototype.unregisterSelectorPseudos = function(name) {
46 for (var j = 0, len = arguments.length; j < len; j++) {
47 name = arguments[j];
48 delete this.pseudos[name];
49 }
50 return this;
51};
52
53CssSelectorParser.prototype.registerNumericPseudos = function(name) {
54 for (var j = 0, len = arguments.length; j < len; j++) {
55 name = arguments[j];
56 this.pseudos[name] = 'numeric';
57 }
58 return this;
59};
60
61CssSelectorParser.prototype.unregisterNumericPseudos = function(name) {
62 for (var j = 0, len = arguments.length; j < len; j++) {
63 name = arguments[j];
64 delete this.pseudos[name];
65 }
66 return this;
67};
68
69CssSelectorParser.prototype.registerNestingOperators = function(operator) {
70 for (var j = 0, len = arguments.length; j < len; j++) {
71 operator = arguments[j];
72 this.ruleNestingOperators[operator] = true;
73 }
74 return this;
75};
76
77CssSelectorParser.prototype.unregisterNestingOperators = function(operator) {
78 for (var j = 0, len = arguments.length; j < len; j++) {
79 operator = arguments[j];
80 delete this.ruleNestingOperators[operator];
81 }
82 return this;
83};
84
85CssSelectorParser.prototype.registerAttrEqualityMods = function(mod) {
86 for (var j = 0, len = arguments.length; j < len; j++) {
87 mod = arguments[j];
88 this.attrEqualityMods[mod] = true;
89 }
90 return this;
91};
92
93CssSelectorParser.prototype.unregisterAttrEqualityMods = function(mod) {
94 for (var j = 0, len = arguments.length; j < len; j++) {
95 mod = arguments[j];
96 delete this.attrEqualityMods[mod];
97 }
98 return this;
99};
100
101CssSelectorParser.prototype.enableSubstitutes = function() {
102 this.substitutesEnabled = true;
103 return this;
104};
105
106CssSelectorParser.prototype.disableSubstitutes = function() {
107 this.substitutesEnabled = false;
108 return this;
109};
110
111function isIdentStart(c) {
112 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c === '-') || (c === '_');
113}
114
115function isIdent(c) {
116 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '-' || c === '_';
117}
118
119function isHex(c) {
120 return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9');
121}
122
123function isDecimal(c) {
124 return c >= '0' && c <= '9';
125}
126
127function isAttrMatchOperator(chr) {
128 return chr === '=' || chr === '^' || chr === '$' || chr === '*' || chr === '~';
129}
130
131var identSpecialChars = {
132 '!': true,
133 '"': true,
134 '#': true,
135 '$': true,
136 '%': true,
137 '&': true,
138 '\'': true,
139 '(': true,
140 ')': true,
141 '*': true,
142 '+': true,
143 ',': true,
144 '.': true,
145 '/': true,
146 ';': true,
147 '<': true,
148 '=': true,
149 '>': true,
150 '?': true,
151 '@': true,
152 '[': true,
153 '\\': true,
154 ']': true,
155 '^': true,
156 '`': true,
157 '{': true,
158 '|': true,
159 '}': true,
160 '~': true
161};
162
163var strReplacementsRev = {
164 '\n': '\\n',
165 '\r': '\\r',
166 '\t': '\\t',
167 '\f': '\\f',
168 '\v': '\\v'
169};
170
171var singleQuoteEscapeChars = {
172 n: '\n',
173 r: '\r',
174 t: '\t',
175 f: '\f',
176 '\\': '\\',
177 '\'': '\''
178};
179
180var doubleQuotesEscapeChars = {
181 n: '\n',
182 r: '\r',
183 t: '\t',
184 f: '\f',
185 '\\': '\\',
186 '"': '"'
187};
188
189function ParseContext(str, pos, pseudos, attrEqualityMods, ruleNestingOperators, substitutesEnabled) {
190 var chr, getIdent, getStr, l, skipWhitespace;
191 l = str.length;
192 chr = null;
193 getStr = function(quote, escapeTable) {
194 var esc, hex, result;
195 result = '';
196 pos++;
197 chr = str.charAt(pos);
198 while (pos < l) {
199 if (chr === quote) {
200 pos++;
201 return result;
202 } else if (chr === '\\') {
203 pos++;
204 chr = str.charAt(pos);
205 if (chr === quote) {
206 result += quote;
207 } else if (esc = escapeTable[chr]) {
208 result += esc;
209 } else if (isHex(chr)) {
210 hex = chr;
211 pos++;
212 chr = str.charAt(pos);
213 while (isHex(chr)) {
214 hex += chr;
215 pos++;
216 chr = str.charAt(pos);
217 }
218 if (chr === ' ') {
219 pos++;
220 chr = str.charAt(pos);
221 }
222 result += String.fromCharCode(parseInt(hex, 16));
223 continue;
224 } else {
225 result += chr;
226 }
227 } else {
228 result += chr;
229 }
230 pos++;
231 chr = str.charAt(pos);
232 }
233 return result;
234 };
235 getIdent = function(specials) {
236 var result = '';
237 chr = str.charAt(pos);
238 while (pos < l) {
239 if (isIdent(chr) || (specials && specials[chr])) {
240 result += chr;
241 } else if (chr === '\\') {
242 pos++;
243 if (pos >= l) {
244 throw Error('Expected symbol but end of file reached.');
245 }
246 chr = str.charAt(pos);
247 if (identSpecialChars[chr]) {
248 result += chr;
249 } else if (isHex(chr)) {
250 var hex = chr;
251 pos++;
252 chr = str.charAt(pos);
253 while (isHex(chr)) {
254 hex += chr;
255 pos++;
256 chr = str.charAt(pos);
257 }
258 if (chr === ' ') {
259 pos++;
260 chr = str.charAt(pos);
261 }
262 result += String.fromCharCode(parseInt(hex, 16));
263 continue;
264 } else {
265 result += chr;
266 }
267 } else {
268 return result;
269 }
270 pos++;
271 chr = str.charAt(pos);
272 }
273 return result;
274 };
275 skipWhitespace = function() {
276 chr = str.charAt(pos);
277 var result = false;
278 while (chr === ' ' || chr === "\t" || chr === "\n" || chr === "\r" || chr === "\f") {
279 result = true;
280 pos++;
281 chr = str.charAt(pos);
282 }
283 return result;
284 };
285 this.parse = function() {
286 var res = this.parseSelector();
287 if (pos < l) {
288 throw Error('Rule expected but "' + str.charAt(pos) + '" found.');
289 }
290 return res;
291 };
292 this.parseSelector = function() {
293 var res;
294 var selector = res = this.parseSingleSelector();
295 chr = str.charAt(pos);
296 while (chr === ',') {
297 pos++;
298 skipWhitespace();
299 if (res.type !== 'selectors') {
300 res = {
301 type: 'selectors',
302 selectors: [selector]
303 };
304 }
305 selector = this.parseSingleSelector();
306 if (!selector) {
307 throw Error('Rule expected after ",".');
308 }
309 res.selectors.push(selector);
310 }
311 return res;
312 };
313
314 this.parseSingleSelector = function() {
315 skipWhitespace();
316
317 let opm = str.slice(pos,pos + 4).match(/^(\>{1,3}|\+|~)/);
318
319 var selector = {
320 type: 'ruleSet'
321 };
322
323 var rule = opm ? Object.assign([],{type: 'rule', isScope: true}) : this.parseRule();
324
325 if (!rule) {
326 return null;
327 }
328 var currentRule = selector;
329 while (rule) {
330 rule.type = 'rule';
331 if(currentRule == rule){
332 // console.log('still at the same rule!');
333 } else {
334 currentRule.rule = rule;
335 currentRule = rule;
336 }
337
338 skipWhitespace();
339 chr = str.charAt(pos);
340 if (pos >= l || chr === ',' || chr === ')') {
341 break;
342 }
343 if (ruleNestingOperators[chr]) {
344 var op = chr;
345 if(op == '>' && str.charAt(pos + 1) == '>' && str.charAt(pos + 2) == '>'){
346 op = '>>>';
347 pos = pos + 3;
348 } else if(op == '>' && str.charAt(pos + 1) == '>'){
349 op = '>>';
350 pos = pos + 2;
351 } else {
352 pos++;
353 }
354 skipWhitespace();
355 rule = this.parseRule(null);
356
357 if (!rule) {
358 if(op == '>' || op == '>>>' || op == '>>'){
359 rule = RULE({tagName: '*'});
360 } else {
361 throw Error('Rule expected after "' + op + '".');
362 }
363 }
364 rule.nestingOperator = op;
365 } else {
366 rule = this.parseRule(currentRule);
367 }
368 }
369 return selector;
370 };
371
372 this.parseSubRule = function(type = 'is',simple = false,nest = false){
373 let pseudo = {name: type, valueType: 'selector', up: true};
374 // Should only go forward to the next whitehspace
375 // console.log(this.parseSelector())
376
377 if(simple){
378 let value = this.parseRule();
379 value.type = 'rule';
380 pseudo.value = {type: 'ruleSet',rule: value};
381 if(nest){
382 pseudo.after = value.rule = RULE({tagName: '*', nestingOperator: null, type: 'rule'});
383 }
384 } else {
385 let value = this.parseSelector();
386 pseudo.value = value;
387 }
388
389 return pseudo;
390 }
391
392 this.parseRule = function(currentRule) {
393 var rule = null;
394 var unimportant = false;
395 var nextIsPseudo = false;
396 var negate = false;
397 var closest = false;
398 // var up = false
399 var part = {}
400
401 // console.log('parseRule!',str.slice(pos),l);
402
403 // simplest solution is to just remember
404 var up = 0;
405
406 while (pos < l) {
407 chr = str.charAt(pos);
408 // console.log('chr is now!!',chr);
409 part = {}
410
411 if (chr == '!') {
412 negate = true;
413 chr = str.charAt(++pos);
414 rule = rule || currentRule;
415 part.not = true;
416 }
417
418 // Legacy support for the @.flag stuff
419 if(chr == '@' && str.charAt(pos + 1) == '.'){
420 rule = rule || currentRule;
421 part.implicitScope = true;
422 pos++; chr = '.';
423 } else if(chr == '@' && str.charAt(pos + 1) == '@'){
424 part.closest = true;
425 rule = rule || currentRule;
426 pos++;
427 } else if(chr == '.' && str.charAt(pos + 1) == '.'){
428 closest = part;
429 rule = rule || currentRule;
430 pos++;
431 // Now add a closestRule
432 // rule.closest ||= []
433
434 let next = str.charAt(pos + 1);
435 if(next == '%' || next == '$' || next == '@'){
436 chr = next;
437 pos++;
438 }
439 // console.warn('.. !!!',chr,str.charAt(pos + 1));
440 }
441
442 while (chr == '^') {
443 chr = str.charAt(++pos);
444 rule = rule || currentRule;
445 up++;
446 // part.up = (part.up || 0) + 1;
447 }
448
449 part.up = up;
450 // if(closest && part != closest){
451 // }
452 part.closest = closest;
453 // part.closest = closest;
454
455 if (chr === '&') {
456 pos++;
457 (rule = rule || []).isScope = true;
458
459 } else if (chr === '^') {
460 pos++;
461 let pseudo = this.parseSubRule('is',true,true);
462 (rule = rule || currentRule || []).push(pseudo);
463 } else if (chr === '*') {
464 pos++;
465 // This has to be a new rule right?
466 (rule = rule || []).tagName = '*';
467
468 } else if (isIdentStart(chr) || chr === '\\') {
469 (rule = rule || []).tagName = getIdent();
470 } else if (chr === '$') {
471 pos++;
472 part.flag = '$' + getIdent();
473 part.ref = true;
474 (rule = rule || []).push(part);
475 } else if (chr === '%') {
476 pos++;
477 part.flag = chr + getIdent();
478 (rule = rule || []).push(part);
479 } else if (chr === '.') {
480 pos++;
481 let flag = str.charAt(pos++);
482 if(flag == '!'){
483 part.not = true
484 flag = ''
485 }
486 flag += getIdent({});
487 part.flag = flag;
488 (rule = rule || []).push(part);
489 } else if (chr === '#') {
490 pos++;
491 (rule = rule || []).id = getIdent();
492 } else if (chr === '[') {
493 pos++;
494 skipWhitespace();
495 var attr = part.attr = {
496 name: getIdent()
497 };
498 skipWhitespace();
499 if (chr === ']') {
500 pos++;
501 } else {
502 var operator = '';
503 if (attrEqualityMods[chr]) {
504 operator = chr;
505 pos++;
506 chr = str.charAt(pos);
507 }
508 if (pos >= l) {
509 throw Error('Expected "=" but end of file reached.');
510 }
511 if (chr !== '=') {
512 throw Error('Expected "=" but "' + chr + '" found.');
513 }
514 attr.operator = operator + '=';
515 pos++;
516 skipWhitespace();
517 var attrValue = '';
518 attr.valueType = 'string';
519 if (chr === '"') {
520 attrValue = getStr('"', doubleQuotesEscapeChars);
521 } else if (chr === '\'') {
522 attrValue = getStr('\'', singleQuoteEscapeChars);
523 } else if (substitutesEnabled && chr === '$') {
524 pos++;
525 attrValue = getIdent();
526 attr.valueType = 'substitute';
527 } else {
528 while (pos < l) {
529 if (chr === ']') {
530 break;
531 }
532 attrValue += chr;
533 pos++;
534 chr = str.charAt(pos);
535 }
536 attrValue = attrValue.trim();
537 }
538 skipWhitespace();
539 if (pos >= l) {
540 throw Error('Expected "]" but end of file reached.');
541 }
542 if (chr !== ']') {
543 throw Error('Expected "]" but "' + chr + '" found.');
544 }
545 pos++;
546 attr.value = attrValue;
547 }
548 (rule = rule || []).push(part);
549 // (rule.attrs = rule.attrs || []).push(attr);
550 } else if (chr === ':' || chr === '@') {
551 // This is the pseudo element
552 if(chr == ':' && str.charAt(pos + 1) == ':'){
553 (rule = rule || currentRule || []).pseudoElement = getIdent({':':true})
554 continue;
555 }
556
557 pos++;
558 part.name = chr;
559 var pseudo = part;
560
561 let pseudoName = str.charAt(pos++);
562
563 if(pseudoName == '!'){
564 part.not = true;
565 pseudoName = '';
566 }
567
568 pseudoName += getIdent({'~':true,'+':true,'.':false,'>':true,'<':true});
569
570 if(pseudoName == 'unimportant'){
571 unimportant = true;
572 part.type = 'unimportant';
573
574 (rule = rule || currentRule || []).push(part);
575 //let pseudo = this.parseSubRule('where');
576 // (rule.pseudos = rule.pseudos || []).push(pseudo);
577 continue;
578 }
579
580 part.name += pseudoName;
581 part.pseudo = pseudoName;
582
583 if (chr === '(') {
584 pos++;
585 var value = '';
586 skipWhitespace();
587 if (pseudos[pseudoName] === 'selector') {
588 pseudo.valueType = 'selector';
589 value = this.parseSelector();
590 } else {
591 pseudo.valueType = pseudos[pseudoName] || 'string';
592 if (chr === '"') {
593 value = getStr('"', doubleQuotesEscapeChars);
594 } else if (chr === '\'') {
595 value = getStr('\'', singleQuoteEscapeChars);
596 } else if (substitutesEnabled && chr === '$') {
597 pos++;
598 value = getIdent();
599 pseudo.valueType = 'substitute';
600 } else {
601 while (pos < l) {
602 if (chr === ')') {
603 break;
604 }
605 value += chr;
606 pos++;
607 chr = str.charAt(pos);
608 }
609 value = value.trim();
610 }
611 skipWhitespace();
612 }
613 if (pos >= l) {
614 throw Error('Expected ")" but end of file reached.');
615 }
616 if (chr !== ')') {
617 throw Error('Expected ")" but "' + chr + '" found.');
618 }
619 pos++;
620 pseudo.value = value;
621
622 }
623 (rule = rule || currentRule || []).push(part);
624 } else {
625 break;
626 }
627 }
628 return rule;
629 };
630 return this;
631}
632
633CssSelectorParser.prototype.parse = function(str) {
634 var context = new ParseContext(
635 str,
636 0,
637 this.pseudos,
638 this.attrEqualityMods,
639 this.ruleNestingOperators,
640 this.substitutesEnabled
641 );
642 return context.parse();
643};
644
645CssSelectorParser.prototype.escapeIdentifier = function(s) {
646 var result = '';
647 var i = 0;
648 var len = s.length;
649 while (i < len) {
650 var chr = s.charAt(i);
651 if (identSpecialChars[chr]) {
652 result += '\\' + chr;
653 } else {
654 if (
655 !(
656 chr === '_' || chr === '-' ||
657 (chr >= 'A' && chr <= 'Z') ||
658 (chr >= 'a' && chr <= 'z') ||
659 (i !== 0 && chr >= '0' && chr <= '9')
660 )
661 ) {
662 var charCode = chr.charCodeAt(0);
663 if ((charCode & 0xF800) === 0xD800) {
664 var extraCharCode = s.charCodeAt(i++);
665 if ((charCode & 0xFC00) !== 0xD800 || (extraCharCode & 0xFC00) !== 0xDC00) {
666 throw Error('UCS-2(decode): illegal sequence');
667 }
668 charCode = ((charCode & 0x3FF) << 10) + (extraCharCode & 0x3FF) + 0x10000;
669 }
670 result += '\\' + charCode.toString(16) + ' ';
671 } else {
672 result += chr;
673 }
674 }
675 i++;
676 }
677 return result;
678};
679
680CssSelectorParser.prototype.escapeStr = function(s) {
681 var result = '';
682 var i = 0;
683 var len = s.length;
684 var chr, replacement;
685 while (i < len) {
686 chr = s.charAt(i);
687 if (chr === '"') {
688 chr = '\\"';
689 } else if (chr === '\\') {
690 chr = '\\\\';
691 } else if (replacement = strReplacementsRev[chr]) {
692 chr = replacement;
693 }
694 result += chr;
695 i++;
696 }
697 return "\"" + result + "\"";
698};
699
700CssSelectorParser.prototype.render = function(path) {
701 return this._renderEntity(path).trim();
702};
703
704var rootSelector = null;
705CssSelectorParser.prototype._renderEntity = function(entity,parent) {
706 var currentEntity, parts, res;
707 res = '';
708 switch (entity.type) {
709 case 'ruleSet':
710 currentEntity = entity.rule;
711 rootSelector = entity;
712 parts = [];
713 while (currentEntity) {
714 if (currentEntity.nestingOperator) {
715 parts.push(currentEntity.nestingOperator);
716 }
717 parts.push(this._renderEntity(currentEntity));
718 currentEntity = currentEntity.rule;
719 }
720 let media = entity.media && entity.media.length ? ` @media ${entity.media.join(' and ')}` : ''
721 res = parts.join(' ') + media;
722 break;
723 case 'selectors':
724 res = entity.selectors.map(this._renderEntity, this).join(', ');
725 break;
726 case 'rule':
727 let s0 = entity.s1;
728 let s1 = entity.s2;
729 let tagName = entity.tagName;
730
731 if (tagName) {
732 if (tagName === '*') {
733 res = '*';
734 } else {
735 let native = TAG_NAMES[tagName] || tagName == 'svg' || tagName.indexOf('-') > 0;
736 let escaped = this.escapeIdentifier(tagName);
737 // || TAG_NAMES[`svg_${tagName}`]
738 if(native){
739 res = escaped;
740 } else {
741 res = `:is(${escaped},${escaped}-tag)`
742 }
743 }
744 }
745 if (entity.id) {
746 res += "#" + this.escapeIdentifier(entity.id);
747 }
748
749 let idx = 0;
750 let len = entity.length;
751
752 while(idx < len){
753 let shortest = null;
754 let part = entity[idx++];
755 let attr = part.attr;
756 let flag = part.flag;
757 let out = "";
758 let neg = part.not;
759 let pseudo = part.pseudo ? part : null;
760 let desc = modifiers[part.pseudo];
761
762 // Identify numeric media here?
763
764 if(part.media || part.skip){
765 // console.log('media',rootSelector);
766 continue;
767 }
768
769 if(desc && desc.flag){
770 flag = desc.flag;
771 pseudo = null;
772 }
773
774 if(desc && desc.type == 'el'){
775 pseudo = null;
776 entity.pseudoElement ||= '::' + part.pseudo;
777 }
778
779 if(flag){
780 out = '.' + this.escapeIdentifier(flag);
781 }
782
783 if(attr) {
784 if (attr.operator) {
785 if (attr.valueType === 'substitute') {
786 out = "[" + this.escapeIdentifier(attr.name) + attr.operator + "$" + attr.value + "]";
787 } else {
788 out = "[" + this.escapeIdentifier(attr.name) + attr.operator + this.escapeStr(attr.value) + "]";
789 }
790 } else {
791 out = "[" + this.escapeIdentifier(attr.name) + "]";
792 }
793 }
794
795 if(pseudo){
796 // check if it is a native one
797 let name = (desc && desc.name) ?? pseudo.pseudo;
798 let escaped = this.escapeIdentifier(name);
799
800 if(desc && desc.valueType) {
801 pseudo = desc;
802 }
803
804 // Check if it is a well known type
805 let post = "";
806 let value = pseudo.value || pseudo.name;
807 // let neg = pseudo.not;
808 let pre = ":" + escaped;
809 // Hack doesnt work with @[] as selectors
810
811 if (pseudo.valueType) {
812 if (pseudo.valueType === 'selector') {
813 out = pre + "(" + this._renderEntity(pseudo.value,parent) + ")" + post;
814 } else if (pseudo.valueType === 'substitute') {
815 out = pre + "($" + pseudo.value + ")" + post;
816 } else if (pseudo.valueType === 'numeric') {
817 out = pre + "(" + pseudo.value + ")" + post;
818 } else if (pseudo.valueType === 'raw' || pseudo.valueType === 'string' ) {
819 out = pre + "(" + pseudo.value + ")" + post;
820 } else {
821 out = pre + "(" + this.escapeIdentifier(pseudo.value) + ")" + post;
822 }
823 } else if(pseudo.type == 'el') {
824 out = ':' + pre;
825 } else if(!desc) {
826 out = `.\\@${escaped}`
827 } else if(desc.shim) {
828 let pre = neg ? ':not' : ':is';
829 out = `.\\@${escaped}`
830 out = `${pre}(:${typeof desc.native == 'string' ? desc.native : escaped},${out})`;
831 neg = false;
832 } else if(desc.flag) {
833 out = `.\\@${escaped}`
834 } else {
835 out = pre + post;
836 }
837 }
838
839 if(part.closest) {
840 // fetch all the other
841 // out = `:${neg ? 'not' : 'is'}(${out},${out} *)`
842 let parts = entity.filter(v=> v.closest == part);
843 // console.log('found parts',parts.length);
844 parts.map( v=> v.closest = null )
845 part.not = false;
846 let all = this._renderEntity(RULE({type: 'rule'},parts))
847 parts.map( v=> v.skip = true )
848 // console.log("rendered",all);
849 // find better way?
850 out = `:${neg ? 'not' : 'is'}(${all} *)`
851 neg = false;
852
853 } else if (part.up) {
854 let rest = part.up > 5 ? ' *' : ' > *'.repeat(part.up);
855 out = `:${neg ? 'not' : 'is'}(${out}${rest})`
856 neg = false;
857 }
858
859 if(neg){
860 out = `:not(${out})`
861 }
862
863 res += out;
864 }
865
866 if(s0 > 0){
867 while (--s0 >= 0) res += ":not(#_)";
868 }
869 if(s1 > 0){
870 while (--s1 >= 0) res += ":not(._0)";
871 }
872
873 if(entity.pseudoElement){
874 res += entity.pseudoElement;
875 }
876 break;
877 default:
878 throw Error('Unknown entity type: "' + entity.type(+'".'));
879 }
880 return res;
881};
882
883var parser = new CssSelectorParser();
884parser.registerSelectorPseudos('has','not','is','matches','any','where')
885parser.registerNumericPseudos('nth-child')
886parser.registerNestingOperators('>>>','>>','>', '+', '~')
887parser.registerAttrEqualityMods('^', '$', '*', '~')
888// parser.enableSubstitutes()
889
890export const parse = function(v){
891 // console.log('parsing',v);
892 return parser.parse(v) }
893export const render = function(v){ return parser.render(v) }
894// exports.default = parser;
\No newline at end of file