@acemir/cssom 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/parse.js ADDED
@@ -0,0 +1,783 @@
1
+ //.CommonJS
2
+ var CSSOM = {};
3
+ ///CommonJS
4
+
5
+
6
+ /**
7
+ * @param {string} token
8
+ */
9
+ CSSOM.parse = function parse(token) {
10
+
11
+ var i = 0;
12
+
13
+ /**
14
+ "before-selector" or
15
+ "selector" or
16
+ "atRule" or
17
+ "atBlock" or
18
+ "conditionBlock" or
19
+ "before-name" or
20
+ "name" or
21
+ "before-value" or
22
+ "value"
23
+ */
24
+ var state = "before-selector";
25
+
26
+ var index;
27
+ var buffer = "";
28
+ var valueParenthesisDepth = 0;
29
+
30
+ var SIGNIFICANT_WHITESPACE = {
31
+ "selector": true,
32
+ "value": true,
33
+ "value-parenthesis": true,
34
+ "atRule": true,
35
+ "importRule-begin": true,
36
+ "importRule": true,
37
+ "atBlock": true,
38
+ "containerBlock": true,
39
+ "conditionBlock": true,
40
+ 'documentRule-begin': true,
41
+ "layerBlock": true
42
+ };
43
+
44
+ var styleSheet = new CSSOM.CSSStyleSheet();
45
+
46
+ // @type CSSStyleSheet|CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
47
+ var currentScope = styleSheet;
48
+
49
+ // @type CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
50
+ var parentRule;
51
+
52
+ var ancestorRules = [];
53
+ var hasAncestors = false;
54
+ var prevScope;
55
+
56
+ var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, layerBlockRule, nestedSelectorRule;
57
+
58
+ var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; // Match @keyframes and vendor-prefixed @keyframes
59
+ var atRulesStatemenRegExp = /(?<!{.*)[;}]\s*/; // Match a statement by verifying it finds a semicolon or closing brace not followed by another semicolon or closing brace
60
+ var beforeRulePortionRegExp = /{(?!.*{)|}(?!.*})|;(?!.*;)|\*\/(?!.*\*\/)/g; // Match the closest allowed character (a opening or closing brace, a semicolon or a comment ending) before the rule
61
+ var beforeRuleValidationRegExp = /^[\s{};]*(\*\/\s*)?$/; // Match that the portion before the rule is empty or contains only whitespace, semicolons, opening/closing braces, and optionally a comment ending (*/) followed by whitespace
62
+ var forwardRuleValidationRegExp = /(?:\(|\s|\/\*)/; // Match that the rule is followed by any whitespace, a opening comment or a condition opening parenthesis
63
+ var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block
64
+
65
+ /**
66
+ * Finds the first balanced block (including nested braces) in the string, starting from fromIndex.
67
+ * Returns an object similar to RegExp.prototype.match output.
68
+ * @param {string} str - The string to search.
69
+ * @param {number} [fromIndex=0] - The index to start searching from.
70
+ * @returns {object|null} - { 0: matchedString, index: startIndex, input: str } or null if not found.
71
+ */
72
+ function matchBalancedBlock(str, fromIndex = 0) {
73
+ const openIndex = str.indexOf('{', fromIndex);
74
+ if (openIndex === -1) return null;
75
+ let depth = 0;
76
+ for (let i = openIndex; i < str.length; i++) {
77
+ if (str[i] === '{') {
78
+ depth++;
79
+ } else if (str[i] === '}') {
80
+ depth--;
81
+ if (depth === 0) {
82
+ const matchedString = str.slice(openIndex, i + 1);
83
+ return {
84
+ 0: matchedString,
85
+ index: openIndex,
86
+ input: str
87
+ };
88
+ }
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+
94
+ var parseError = function(message) {
95
+ var lines = token.substring(0, i).split('\n');
96
+ var lineCount = lines.length;
97
+ var charCount = lines.pop().length + 1;
98
+ var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
99
+ error.line = lineCount;
100
+ /* jshint sub : true */
101
+ error['char'] = charCount;
102
+ error.styleSheet = styleSheet;
103
+ throw error;
104
+ };
105
+
106
+ var validateAtRule = function(atRuleKey, validCallback, cannotBeNested) {
107
+ var isValid = false;
108
+ var ruleRegExp = new RegExp(atRuleKey + forwardRuleValidationRegExp.source, forwardRuleValidationRegExp.flags);
109
+ var ruleSlice = token.slice(i);
110
+ // Not all rules can be nested, if the rule cannot be nested and is in the root scope, do not perform the check
111
+ var shouldPerformCheck = cannotBeNested && currentScope !== styleSheet ? false : true;
112
+ // First, check if there is no invalid characters just after the at-rule
113
+ if (shouldPerformCheck && ruleSlice.search(ruleRegExp) === 0) {
114
+ // Find the closest allowed character before the at-rule (a opening or closing brace, a semicolon or a comment ending)
115
+ var beforeSlice = token.slice(0, i);
116
+ var regexBefore = new RegExp(beforeRulePortionRegExp.source, beforeRulePortionRegExp.flags);
117
+ var matches = beforeSlice.match(regexBefore);
118
+ var lastI = matches ? beforeSlice.lastIndexOf(matches[matches.length - 1]) : 0;
119
+ var toCheckSlice = token.slice(lastI, i);
120
+ // Check if we don't have any invalid in the portion before the `at-rule` and the closest allowed character
121
+ var checkedSlice = toCheckSlice.search(beforeRuleValidationRegExp);
122
+ if (checkedSlice === 0) {
123
+ isValid = true;
124
+ }
125
+ }
126
+ if (!isValid) {
127
+ // If it's invalid the browser will simply ignore the entire invalid block
128
+ // Use regex to find the closing brace of the invalid rule
129
+
130
+ var ruleStatementMatch = ruleSlice.match(atRulesStatemenRegExp);
131
+
132
+ // If it's a statement inside a nested rule, ignore only the statement
133
+ if (ruleStatementMatch && currentScope !== styleSheet) {
134
+ var ignoreEnd = ruleStatementMatch[0].indexOf(";");
135
+ i += ruleStatementMatch.index + ignoreEnd;
136
+ return;
137
+ }
138
+
139
+ // Ignore the entire rule block (if it's a statement it should ignore the statement plus the next block)
140
+ var ruleClosingMatch = matchBalancedBlock(ruleSlice);
141
+ if (ruleClosingMatch) {
142
+ const ignoreRange = ruleClosingMatch.index + ruleClosingMatch[0].length;
143
+ i+= ignoreRange;
144
+ if (token.charAt(i) === '}') {
145
+ i -= 1;
146
+ }
147
+ } else {
148
+ i += ruleSlice.length;
149
+ }
150
+ state = "before-selector";
151
+ } else {
152
+ validCallback.call(this);
153
+ }
154
+ }
155
+
156
+ for (var character; (character = token.charAt(i)); i++) {
157
+
158
+ switch (character) {
159
+
160
+ case " ":
161
+ case "\t":
162
+ case "\r":
163
+ case "\n":
164
+ case "\f":
165
+ if (SIGNIFICANT_WHITESPACE[state]) {
166
+ buffer += character;
167
+ }
168
+ break;
169
+
170
+ // String
171
+ case '"':
172
+ index = i + 1;
173
+ do {
174
+ index = token.indexOf('"', index) + 1;
175
+ if (!index) {
176
+ parseError('Unmatched "');
177
+ }
178
+ } while (token[index - 2] === '\\');
179
+ buffer += token.slice(i, index);
180
+ i = index - 1;
181
+ switch (state) {
182
+ case 'before-value':
183
+ state = 'value';
184
+ break;
185
+ case 'importRule-begin':
186
+ state = 'importRule';
187
+ break;
188
+ }
189
+ break;
190
+
191
+ case "'":
192
+ index = i + 1;
193
+ do {
194
+ index = token.indexOf("'", index) + 1;
195
+ if (!index) {
196
+ parseError("Unmatched '");
197
+ }
198
+ } while (token[index - 2] === '\\');
199
+ buffer += token.slice(i, index);
200
+ i = index - 1;
201
+ switch (state) {
202
+ case 'before-value':
203
+ state = 'value';
204
+ break;
205
+ case 'importRule-begin':
206
+ state = 'importRule';
207
+ break;
208
+ }
209
+ break;
210
+
211
+ // Comment
212
+ case "/":
213
+ if (token.charAt(i + 1) === "*") {
214
+ i += 2;
215
+ index = token.indexOf("*/", i);
216
+ if (index === -1) {
217
+ parseError("Missing */");
218
+ } else {
219
+ i = index + 1;
220
+ }
221
+ } else {
222
+ buffer += character;
223
+ }
224
+ if (state === "importRule-begin") {
225
+ buffer += " ";
226
+ state = "importRule";
227
+ }
228
+ break;
229
+
230
+ // At-rule
231
+ case "@":
232
+ if (token.indexOf("@-moz-document", i) === i) {
233
+ validateAtRule("@-moz-document", function(){
234
+ state = "documentRule-begin";
235
+ documentRule = new CSSOM.CSSDocumentRule();
236
+ documentRule.__starts = i;
237
+ i += "-moz-document".length;
238
+ });
239
+ buffer = "";
240
+ break;
241
+ } else if (token.indexOf("@media", i) === i) {
242
+ validateAtRule("@media", function(){
243
+ state = "atBlock";
244
+ mediaRule = new CSSOM.CSSMediaRule();
245
+ mediaRule.__starts = i;
246
+ i += "media".length;
247
+ });
248
+ buffer = "";
249
+ break;
250
+ } else if (token.indexOf("@container", i) === i) {
251
+ validateAtRule("@container", function(){
252
+ state = "containerBlock";
253
+ containerRule = new CSSOM.CSSContainerRule();
254
+ containerRule.__starts = i;
255
+ i += "container".length;
256
+ });
257
+ buffer = "";
258
+ break;
259
+ } else if (token.indexOf("@layer", i) === i) {
260
+ validateAtRule("@layer", function(){
261
+ state = "layerBlock"
262
+ layerBlockRule = new CSSOM.CSSLayerBlockRule();
263
+ layerBlockRule.__starts = i;
264
+ i += "layer".length;
265
+ });
266
+ buffer = "";
267
+ break;
268
+ } else if (token.indexOf("@supports", i) === i) {
269
+ validateAtRule("@supports", function(){
270
+ state = "conditionBlock";
271
+ supportsRule = new CSSOM.CSSSupportsRule();
272
+ supportsRule.__starts = i;
273
+ i += "supports".length;
274
+ });
275
+ buffer = "";
276
+ break;
277
+ } else if (token.indexOf("@host", i) === i) {
278
+ validateAtRule("@host", function(){
279
+ state = "hostRule-begin";
280
+ i += "host".length;
281
+ hostRule = new CSSOM.CSSHostRule();
282
+ hostRule.__starts = i;
283
+ });
284
+ buffer = "";
285
+ break;
286
+ } else if (token.indexOf("@starting-style", i) === i) {
287
+ validateAtRule("@starting-style", function(){
288
+ state = "startingStyleRule-begin";
289
+ i += "starting-style".length;
290
+ startingStyleRule = new CSSOM.CSSStartingStyleRule();
291
+ startingStyleRule.__starts = i;
292
+ });
293
+ buffer = "";
294
+ break;
295
+ } else if (token.indexOf("@import", i) === i) {
296
+ buffer = "";
297
+ validateAtRule("@import", function(){
298
+ state = "importRule-begin";
299
+ i += "import".length;
300
+ buffer += "@import";
301
+ }, true);
302
+ break;
303
+ } else if (token.indexOf("@font-face", i) === i) {
304
+ buffer = "";
305
+ validateAtRule("@font-face", function(){
306
+ state = "fontFaceRule-begin";
307
+ i += "font-face".length;
308
+ fontFaceRule = new CSSOM.CSSFontFaceRule();
309
+ fontFaceRule.__starts = i;
310
+ }, parentRule && parentRule.constructor.name === "CSSStyleRule" );
311
+ break;
312
+ } else {
313
+ atKeyframesRegExp.lastIndex = i;
314
+ var matchKeyframes = atKeyframesRegExp.exec(token);
315
+ if (matchKeyframes && matchKeyframes.index === i) {
316
+ state = "keyframesRule-begin";
317
+ keyframesRule = new CSSOM.CSSKeyframesRule();
318
+ keyframesRule.__starts = i;
319
+ keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
320
+ i += matchKeyframes[0].length - 1;
321
+ buffer = "";
322
+ break;
323
+ } else if (state === "selector") {
324
+ state = "atRule";
325
+ }
326
+ }
327
+ buffer += character;
328
+ break;
329
+
330
+ case "{":
331
+ if (currentScope === styleSheet) {
332
+ nestedSelectorRule = null;
333
+ }
334
+ if (state === "selector" || state === "atRule") {
335
+ if (!nestedSelectorRule && buffer.includes(";")) {
336
+ var ruleClosingMatch = token.slice(i).match(forwardRuleClosingBraceRegExp);
337
+ if (ruleClosingMatch) {
338
+ styleRule = null;
339
+ buffer = "";
340
+ state = "before-selector";
341
+ i += ruleClosingMatch.index + ruleClosingMatch[0].length;
342
+ break;
343
+ }
344
+ }
345
+
346
+ if (parentRule) {
347
+ styleRule.parentRule = parentRule;
348
+ ancestorRules.push(parentRule);
349
+ }
350
+
351
+ currentScope = parentRule = styleRule;
352
+ styleRule.selectorText = buffer.trim();
353
+ styleRule.style.__starts = i;
354
+ styleRule.parentStyleSheet = styleSheet;
355
+ buffer = "";
356
+ state = "before-name";
357
+ } else if (state === "atBlock") {
358
+ mediaRule.media.mediaText = buffer.trim();
359
+
360
+ if (parentRule) {
361
+ mediaRule.parentRule = parentRule;
362
+ ancestorRules.push(parentRule);
363
+ }
364
+
365
+ currentScope = parentRule = mediaRule;
366
+ mediaRule.parentStyleSheet = styleSheet;
367
+ buffer = "";
368
+ state = "before-selector";
369
+ } else if (state === "containerBlock") {
370
+ containerRule.containerText = buffer.trim();
371
+
372
+ if (parentRule) {
373
+ containerRule.parentRule = parentRule;
374
+ ancestorRules.push(parentRule);
375
+ }
376
+ currentScope = parentRule = containerRule;
377
+ containerRule.parentStyleSheet = styleSheet;
378
+ buffer = "";
379
+ state = "before-selector";
380
+ } else if (state === "conditionBlock") {
381
+ supportsRule.conditionText = buffer.trim();
382
+
383
+ if (parentRule) {
384
+ supportsRule.parentRule = parentRule;
385
+ ancestorRules.push(parentRule);
386
+ }
387
+
388
+ currentScope = parentRule = supportsRule;
389
+ supportsRule.parentStyleSheet = styleSheet;
390
+ buffer = "";
391
+ state = "before-selector";
392
+ } else if (state === "layerBlock") {
393
+ layerBlockRule.layerNameText = buffer.trim();
394
+
395
+ if (parentRule) {
396
+ layerBlockRule.parentRule = parentRule;
397
+ ancestorRules.push(parentRule);
398
+ }
399
+
400
+ currentScope = parentRule = layerBlockRule;
401
+ layerBlockRule.parentStyleSheet = styleSheet;
402
+ buffer = "";
403
+ state = "before-selector";
404
+ } else if (state === "hostRule-begin") {
405
+ if (parentRule) {
406
+ ancestorRules.push(parentRule);
407
+ }
408
+
409
+ currentScope = parentRule = hostRule;
410
+ hostRule.parentStyleSheet = styleSheet;
411
+ buffer = "";
412
+ state = "before-selector";
413
+ } else if (state === "startingStyleRule-begin") {
414
+ if (parentRule) {
415
+ startingStyleRule.parentRule = parentRule;
416
+ ancestorRules.push(parentRule);
417
+ }
418
+
419
+ currentScope = parentRule = startingStyleRule;
420
+ startingStyleRule.parentStyleSheet = styleSheet;
421
+ buffer = "";
422
+ state = "before-selector";
423
+
424
+ } else if (state === "fontFaceRule-begin") {
425
+ if (parentRule) {
426
+ fontFaceRule.parentRule = parentRule;
427
+ }
428
+ fontFaceRule.parentStyleSheet = styleSheet;
429
+ styleRule = fontFaceRule;
430
+ buffer = "";
431
+ state = "before-name";
432
+ } else if (state === "keyframesRule-begin") {
433
+ keyframesRule.name = buffer.trim();
434
+ if (parentRule) {
435
+ ancestorRules.push(parentRule);
436
+ keyframesRule.parentRule = parentRule;
437
+ }
438
+ keyframesRule.parentStyleSheet = styleSheet;
439
+ currentScope = parentRule = keyframesRule;
440
+ buffer = "";
441
+ state = "keyframeRule-begin";
442
+ } else if (state === "keyframeRule-begin") {
443
+ styleRule = new CSSOM.CSSKeyframeRule();
444
+ styleRule.keyText = buffer.trim();
445
+ styleRule.__starts = i;
446
+ buffer = "";
447
+ state = "before-name";
448
+ } else if (state === "documentRule-begin") {
449
+ // FIXME: what if this '{' is in the url text of the match function?
450
+ documentRule.matcher.matcherText = buffer.trim();
451
+ if (parentRule) {
452
+ ancestorRules.push(parentRule);
453
+ documentRule.parentRule = parentRule;
454
+ }
455
+ currentScope = parentRule = documentRule;
456
+ documentRule.parentStyleSheet = styleSheet;
457
+ buffer = "";
458
+ state = "before-selector";
459
+ } else if (state === "name") {
460
+ if (styleRule.constructor.name === "CSSNestedDeclarations") {
461
+ if (styleRule.style.length) {
462
+ parentRule.cssRules.push(styleRule);
463
+ styleRule.parentRule = parentRule;
464
+ styleRule.parentStyleSheet = styleSheet;
465
+ ancestorRules.push(parentRule);
466
+ } else {
467
+ // If the styleRule is empty, we can assume that it's a nested selector
468
+ ancestorRules.push(parentRule);
469
+ }
470
+ } else {
471
+ currentScope = parentRule = styleRule;
472
+ ancestorRules.push(parentRule);
473
+ styleRule.parentStyleSheet = styleSheet;
474
+ }
475
+
476
+
477
+ styleRule = new CSSOM.CSSStyleRule();
478
+ styleRule.selectorText = buffer.trim();
479
+ styleRule.style.__starts = i - buffer.length;
480
+ styleRule.parentRule = parentRule;
481
+ nestedSelectorRule = styleRule;
482
+
483
+ buffer = "";
484
+ state = "before-name";
485
+ }
486
+ break;
487
+
488
+ case ":":
489
+ if (state === "name") {
490
+ // It can be a nested selector, let's check
491
+ var openBraceBeforeMatch = token.slice(i).match(/[{;}]/);
492
+ var hasOpenBraceBefore = openBraceBeforeMatch && openBraceBeforeMatch[0] === '{';
493
+ if (hasOpenBraceBefore) {
494
+ // Is a selector
495
+ buffer += character;
496
+ } else {
497
+ // Is a declaration
498
+ name = buffer.trim();
499
+ buffer = "";
500
+ state = "before-value";
501
+ }
502
+ } else {
503
+ buffer += character;
504
+ }
505
+ break;
506
+
507
+ case "(":
508
+ if (state === 'value') {
509
+ // ie css expression mode
510
+ if (buffer.trim() === 'expression') {
511
+ var info = (new CSSOM.CSSValueExpression(token, i)).parse();
512
+
513
+ if (info.error) {
514
+ parseError(info.error);
515
+ } else {
516
+ buffer += info.expression;
517
+ i = info.idx;
518
+ }
519
+ } else {
520
+ state = 'value-parenthesis';
521
+ //always ensure this is reset to 1 on transition
522
+ //from value to value-parenthesis
523
+ valueParenthesisDepth = 1;
524
+ buffer += character;
525
+ }
526
+ } else if (state === 'value-parenthesis') {
527
+ valueParenthesisDepth++;
528
+ buffer += character;
529
+ } else {
530
+ buffer += character;
531
+ }
532
+ break;
533
+
534
+ case ")":
535
+ if (state === 'value-parenthesis') {
536
+ valueParenthesisDepth--;
537
+ if (valueParenthesisDepth === 0) state = 'value';
538
+ }
539
+ buffer += character;
540
+ break;
541
+
542
+ case "!":
543
+ if (state === "value" && token.indexOf("!important", i) === i) {
544
+ priority = "important";
545
+ i += "important".length;
546
+ } else {
547
+ buffer += character;
548
+ }
549
+ break;
550
+
551
+ case ";":
552
+ switch (state) {
553
+ case "value":
554
+ styleRule.style.setProperty(name, buffer.trim(), priority);
555
+ priority = "";
556
+ buffer = "";
557
+ state = "before-name";
558
+ break;
559
+ case "atRule":
560
+ buffer = "";
561
+ state = "before-selector";
562
+ break;
563
+ case "importRule":
564
+ importRule = new CSSOM.CSSImportRule();
565
+ importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
566
+ importRule.cssText = buffer + character;
567
+ styleSheet.cssRules.push(importRule);
568
+ buffer = "";
569
+ state = "before-selector";
570
+ break;
571
+ default:
572
+ buffer += character;
573
+ break;
574
+ }
575
+ break;
576
+
577
+ case "}":
578
+ switch (state) {
579
+ case "value":
580
+ styleRule.style.setProperty(name, buffer.trim(), priority);
581
+ priority = "";
582
+ /* falls through */
583
+ case "before-name":
584
+ case "name":
585
+ styleRule.__ends = i + 1;
586
+
587
+ if (parentRule === styleRule) {
588
+ parentRule = ancestorRules.pop()
589
+ }
590
+
591
+ if (parentRule) {
592
+ styleRule.parentRule = parentRule;
593
+ }
594
+ styleRule.parentStyleSheet = styleSheet;
595
+
596
+ if (currentScope === styleRule) {
597
+ currentScope = parentRule || styleSheet;
598
+ }
599
+
600
+ currentScope.cssRules.push(styleRule);
601
+ buffer = "";
602
+ if (currentScope.constructor === CSSOM.CSSKeyframesRule) {
603
+ state = "keyframeRule-begin";
604
+ } else {
605
+ state = "before-selector";
606
+ }
607
+
608
+ if (styleRule.constructor.name === "CSSNestedDeclarations") {
609
+ if (currentScope !== styleSheet) {
610
+ nestedSelectorRule = currentScope;
611
+ }
612
+ styleRule = null;
613
+ } else {
614
+ styleRule = null;
615
+ break;
616
+ }
617
+ case "keyframeRule-begin":
618
+ case "before-selector":
619
+ case "selector":
620
+ // End of media/supports/document rule.
621
+ if (!parentRule) {
622
+ break;
623
+ //parseError("Unexpected }");
624
+ }
625
+
626
+ // Handle rules nested in @media or @supports
627
+ hasAncestors = ancestorRules.length > 0;
628
+
629
+ while (ancestorRules.length > 0) {
630
+ parentRule = ancestorRules.pop();
631
+
632
+ if (
633
+ parentRule.constructor.name === "CSSStyleRule"
634
+ || parentRule.constructor.name === "CSSMediaRule"
635
+ || parentRule.constructor.name === "CSSSupportsRule"
636
+ || parentRule.constructor.name === "CSSContainerRule"
637
+ || parentRule.constructor.name === "CSSLayerBlockRule"
638
+ || parentRule.constructor.name === "CSSStartingStyleRule"
639
+ ) {
640
+ if (nestedSelectorRule) {
641
+ if (nestedSelectorRule.parentRule) {
642
+ prevScope = nestedSelectorRule;
643
+ currentScope = nestedSelectorRule.parentRule;
644
+ if (currentScope.cssRules.findIndex(function (rule) {
645
+ return rule === prevScope
646
+ }) === -1) {
647
+ currentScope.cssRules.push(prevScope);
648
+ }
649
+ nestedSelectorRule = currentScope;
650
+ }
651
+ } else {
652
+ prevScope = currentScope;
653
+ currentScope = parentRule;
654
+ currentScope.cssRules.push(prevScope);
655
+ break;
656
+ }
657
+ }
658
+
659
+ if (ancestorRules.length === 0) {
660
+ hasAncestors = false;
661
+ }
662
+ }
663
+
664
+ if (currentScope.parentRule == null) {
665
+ currentScope.__ends = i + 1;
666
+ if (currentScope !== styleSheet && styleSheet.cssRules.findIndex(function (rule) {
667
+ return rule === currentScope
668
+ }) === -1) {
669
+ styleSheet.cssRules.push(currentScope);
670
+ }
671
+ currentScope = styleSheet;
672
+ if (nestedSelectorRule === parentRule) {
673
+ // Check if this selector is really starting inside another selector
674
+ var nestedSelectorTokenToCurrentSelectorToken = token.slice(nestedSelectorRule.__starts, i + 1);
675
+
676
+ if (nestedSelectorTokenToCurrentSelectorToken.match(/{/g)?.length === nestedSelectorTokenToCurrentSelectorToken.match(/}/g)?.length) {
677
+ // If the number of opening and closing braces are equal, we can assume that the new selector is starting outside the nestedSelectorRule
678
+ nestedSelectorRule.__ends = i + 1;
679
+ nestedSelectorRule = null;
680
+ parentRule = null;
681
+ }
682
+ } else {
683
+ parentRule = null;
684
+
685
+ }
686
+ }
687
+
688
+ buffer = "";
689
+ state = "before-selector";
690
+ break;
691
+ }
692
+ break;
693
+
694
+ default:
695
+ switch (state) {
696
+ case "before-selector":
697
+ state = "selector";
698
+ if (styleRule && parentRule) {
699
+ // Assuming it's a declaration inside Nested Selector OR a Nested Declaration
700
+ // If Declaration inside Nested Selector let's keep the same styleRule
701
+ if (
702
+ parentRule.constructor.name === "CSSStyleRule"
703
+ || parentRule.constructor.name === "CSSMediaRule"
704
+ || parentRule.constructor.name === "CSSSupportsRule"
705
+ || parentRule.constructor.name === "CSSContainerRule"
706
+ || parentRule.constructor.name === "CSSLayerBlockRule"
707
+ || parentRule.constructor.name === "CSSStartingStyleRule"
708
+ ) {
709
+ // parentRule.parentRule = styleRule;
710
+ state = "before-name";
711
+ if (styleRule !== parentRule) {
712
+ styleRule = new CSSOM.CSSNestedDeclarations();
713
+ styleRule.__starts = i;
714
+ }
715
+ }
716
+
717
+ } else if (nestedSelectorRule && parentRule && (
718
+ parentRule.constructor.name === "CSSStyleRule"
719
+ || parentRule.constructor.name === "CSSMediaRule"
720
+ || parentRule.constructor.name === "CSSSupportsRule"
721
+ || parentRule.constructor.name === "CSSContainerRule"
722
+ || parentRule.constructor.name === "CSSLayerBlockRule"
723
+ || parentRule.constructor.name === "CSSStartingStyleRule"
724
+ )) {
725
+ state = "before-name";
726
+ if (parentRule.cssRules.length) {
727
+ currentScope = nestedSelectorRule = parentRule;
728
+ styleRule = new CSSOM.CSSNestedDeclarations();
729
+ styleRule.__starts = i;
730
+ } else {
731
+ if (parentRule.constructor.name === "CSSStyleRule") {
732
+ styleRule = parentRule;
733
+ } else {
734
+ styleRule = new CSSOM.CSSStyleRule();
735
+ styleRule.__starts = i;
736
+ }
737
+ }
738
+ } else {
739
+ styleRule = new CSSOM.CSSStyleRule();
740
+ styleRule.__starts = i;
741
+ }
742
+ break;
743
+ case "before-name":
744
+ state = "name";
745
+ break;
746
+ case "before-value":
747
+ state = "value";
748
+ break;
749
+ case "importRule-begin":
750
+ state = "importRule";
751
+ break;
752
+ }
753
+ buffer += character;
754
+ break;
755
+ }
756
+ }
757
+
758
+ return styleSheet;
759
+ };
760
+
761
+
762
+ //.CommonJS
763
+ exports.parse = CSSOM.parse;
764
+ // The following modules cannot be included sooner due to the mutual dependency with parse.js
765
+ CSSOM.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet;
766
+ CSSOM.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule;
767
+ CSSOM.CSSNestedDeclarations = require("./CSSNestedDeclarations").CSSNestedDeclarations;
768
+ CSSOM.CSSImportRule = require("./CSSImportRule").CSSImportRule;
769
+ CSSOM.CSSGroupingRule = require("./CSSGroupingRule").CSSGroupingRule;
770
+ CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
771
+ CSSOM.CSSContainerRule = require("./CSSContainerRule").CSSContainerRule;
772
+ CSSOM.CSSConditionRule = require("./CSSConditionRule").CSSConditionRule;
773
+ CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule;
774
+ CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule;
775
+ CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule;
776
+ CSSOM.CSSStartingStyleRule = require("./CSSStartingStyleRule").CSSStartingStyleRule;
777
+ CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration;
778
+ CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule;
779
+ CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule;
780
+ CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression;
781
+ CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule;
782
+ CSSOM.CSSLayerBlockRule = require("./CSSLayerBlockRule").CSSLayerBlockRule;
783
+ ///CommonJS