@acemir/cssom 0.9.20 → 0.9.22

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.
@@ -1,5 +1,6 @@
1
1
  //.CommonJS
2
2
  var CSSOM = {
3
+ MediaList: require("./MediaList").MediaList,
3
4
  StyleSheet: require("./StyleSheet").StyleSheet,
4
5
  CSSRuleList: require("./CSSRuleList").CSSRuleList,
5
6
  CSSStyleRule: require("./CSSStyleRule").CSSStyleRule,
@@ -59,7 +60,10 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
59
60
  }
60
61
 
61
62
  var ruleToParse = String(rule);
62
- var parsedSheet = CSSOM.parse(ruleToParse);
63
+ var parseErrors = [];
64
+ var parsedSheet = CSSOM.parse(ruleToParse, undefined, function(err) {
65
+ parseErrors.push(err);
66
+ } );
63
67
  if (parsedSheet.cssRules.length !== 1) {
64
68
  errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
65
69
  }
@@ -127,12 +131,21 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
127
131
  'HierarchyRequestError');
128
132
  }
129
133
 
134
+ // Cannot insert if there are already non-special rules
135
+ if (firstNonImportNamespaceIndex < this.cssRules.length) {
136
+ errorUtils.throwError(this, 'DOMException',
137
+ "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
138
+ 'InvalidStateError');
139
+ }
140
+
130
141
  // Cannot insert after other types of rules
131
142
  if (index > firstNonImportNamespaceIndex) {
132
143
  errorUtils.throwError(this, 'DOMException',
133
144
  "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
134
145
  'HierarchyRequestError');
135
146
  }
147
+
148
+
136
149
  } else if (cssRule.constructor.name === 'CSSLayerStatementRule') {
137
150
  // @layer statement rules can be inserted anywhere before @import and @namespace
138
151
  // No additional restrictions beyond what's already handled
@@ -149,6 +162,10 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
149
162
  "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
150
163
  'HierarchyRequestError');
151
164
  }
165
+
166
+ if (parseErrors.filter(function(error) { return !error.isNested; }).length !== 0) {
167
+ errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
168
+ }
152
169
  }
153
170
 
154
171
  cssRule.__parentStyleSheet = this;
@@ -188,13 +205,20 @@ CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
188
205
  if (index >= this.cssRules.length) {
189
206
  errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length);
190
207
  }
191
- if (this.cssRules[index] && this.cssRules[index].constructor.name == "CSSNamespaceRule") {
192
- var shouldContinue = this.cssRules.every(function (rule) {
193
- return ['CSSImportRule','CSSLayerStatementRule','CSSNamespaceRule'].indexOf(rule.constructor.name) !== -1
194
- });
195
- if (!shouldContinue) {
196
- errorUtils.throwError(this, 'DOMException', "Failed to execute 'deleteRule' on '" + this.constructor.name + "': Failed to delete rule.", "InvalidStateError");
208
+ if (this.cssRules[index]) {
209
+ if (this.cssRules[index].constructor.name == "CSSNamespaceRule") {
210
+ var shouldContinue = this.cssRules.every(function (rule) {
211
+ return ['CSSImportRule','CSSLayerStatementRule','CSSNamespaceRule'].indexOf(rule.constructor.name) !== -1
212
+ });
213
+ if (!shouldContinue) {
214
+ errorUtils.throwError(this, 'DOMException', "Failed to execute 'deleteRule' on '" + this.constructor.name + "': Failed to delete rule.", "InvalidStateError");
215
+ }
197
216
  }
217
+ if (this.cssRules[index].constructor.name == "CSSImportRule") {
218
+ this.cssRules[index].styleSheet.__parentStyleSheet = null;
219
+ }
220
+
221
+ this.cssRules[index].__parentStyleSheet = null;
198
222
  }
199
223
  this.cssRules.splice(index, 1);
200
224
  };
package/lib/StyleSheet.js CHANGED
@@ -1,5 +1,7 @@
1
1
  //.CommonJS
2
- var CSSOM = {};
2
+ var CSSOM = {
3
+ MediaList: require("./MediaList").MediaList
4
+ };
3
5
  ///CommonJS
4
6
 
5
7
 
@@ -8,10 +10,23 @@ var CSSOM = {};
8
10
  * @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
9
11
  */
10
12
  CSSOM.StyleSheet = function StyleSheet() {
13
+ this.__media = new CSSOM.MediaList();
11
14
  this.__parentStyleSheet = null;
12
15
  };
13
16
 
14
17
  Object.defineProperties(CSSOM.StyleSheet.prototype, {
18
+ media: {
19
+ get: function() {
20
+ return this.__media;
21
+ },
22
+ set: function(value) {
23
+ if (typeof value === "string") {
24
+ this.__media.mediaText = value;
25
+ } else {
26
+ this.__media = value;
27
+ }
28
+ }
29
+ },
15
30
  parentStyleSheet: {
16
31
  get: function() {
17
32
  return this.__parentStyleSheet;
package/lib/index.js CHANGED
@@ -33,5 +33,6 @@ exports.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression;
33
33
  exports.CSSScopeRule = require('./CSSScopeRule').CSSScopeRule;
34
34
  exports.CSSLayerBlockRule = require('./CSSLayerBlockRule').CSSLayerBlockRule;
35
35
  exports.CSSLayerStatementRule = require('./CSSLayerStatementRule').CSSLayerStatementRule;
36
+ exports.CSSPageRule = require('./CSSPageRule').CSSPageRule;
36
37
  exports.parse = require('./parse').parse;
37
38
  exports.clone = require('./clone').clone;
package/lib/parse.js CHANGED
@@ -51,7 +51,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
51
51
  "counterStyleBlock": true,
52
52
  'documentRule-begin': true,
53
53
  "scopeBlock": true,
54
- "layerBlock": true
54
+ "layerBlock": true,
55
+ "pageBlock": true
55
56
  };
56
57
 
57
58
  var styleSheet = new CSSOM.CSSStyleSheet();
@@ -69,7 +70,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
69
70
  var ancestorRules = [];
70
71
  var prevScope;
71
72
 
72
- var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
73
+ var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
73
74
 
74
75
  // Track defined namespace prefixes for validation
75
76
  var definedNamespacePrefixes = {};
@@ -83,8 +84,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
83
84
  var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote
84
85
  var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block
85
86
  var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule
86
- var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a single @layer name
87
+ var cssCustomIdentifierRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a css custom identifier
87
88
  var startsWithCombinatorRegExp = /^\s*[>+~]/; // Checks if a selector starts with a CSS combinator (>, +, ~)
89
+ var atPageRuleSelectorRegExp = /^([^\s:]+)?((?::\w+)*)$/;
88
90
 
89
91
  /**
90
92
  * Searches for the first occurrence of a CSS at-rule statement terminator (`;` or `}`)
@@ -409,12 +411,12 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
409
411
  if (isValid && atRuleKey === "@scope") {
410
412
  var openBraceIndex = ruleSlice.indexOf('{');
411
413
  if (openBraceIndex !== -1) {
412
- // Extract the scope prelude (everything between @scope and {)
413
- var scopePrelude = ruleSlice.slice(0, openBraceIndex).trim();
414
-
415
- // Skip past '@scope' keyword and whitespace
416
- var preludeContent = scopePrelude.slice(6).trim();
417
-
414
+ // Extract the rule prelude (everything between the at-rule and {)
415
+ var rulePrelude = ruleSlice.slice(0, openBraceIndex).trim();
416
+
417
+ // Skip past at-rule keyword and whitespace
418
+ var preludeContent = rulePrelude.slice("@scope".length).trim();
419
+
418
420
  if (preludeContent.length > 0) {
419
421
  // Parse the scope prelude
420
422
  var parsedScopePrelude = parseScopePrelude(preludeContent);
@@ -453,6 +455,64 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
453
455
  // Empty prelude (@scope {}) is valid
454
456
  }
455
457
  }
458
+
459
+ if (isValid && atRuleKey === "@page") {
460
+ var openBraceIndex = ruleSlice.indexOf('{');
461
+ if (openBraceIndex !== -1) {
462
+ // Extract the rule prelude (everything between the at-rule and {)
463
+ var rulePrelude = ruleSlice.slice(0, openBraceIndex).trim();
464
+
465
+ // Skip past at-rule keyword and whitespace
466
+ var preludeContent = rulePrelude.slice("@page".length).trim();
467
+
468
+ if (preludeContent.length > 0) {
469
+ var trimmedValue = preludeContent.trim();
470
+
471
+ // Empty selector is valid for @page
472
+ if (trimmedValue !== '') {
473
+ // Parse @page selectorText for page name and pseudo-pages
474
+ // Valid formats:
475
+ // - (empty - no name, no pseudo-page)
476
+ // - :left, :right, :first, :blank (pseudo-page only)
477
+ // - named (named page only)
478
+ // - named:first (named page with single pseudo-page)
479
+ // - named:first:left (named page with multiple pseudo-pages)
480
+ var match = trimmedValue.match(atPageRuleSelectorRegExp);
481
+ if (match) {
482
+ var pageName = match[1] || '';
483
+ var pseudoPages = match[2] || '';
484
+
485
+ // Validate page name if present
486
+ if (pageName) {
487
+ if (!cssCustomIdentifierRegExp.test(pageName)) {
488
+ isValid = false;
489
+ }
490
+ }
491
+
492
+ // Validate pseudo-pages if present
493
+ if (pseudoPages) {
494
+ var pseudos = pseudoPages.split(':').filter(function(p) { return p; });
495
+ var validPseudos = ['left', 'right', 'first', 'blank'];
496
+ var allValid = true;
497
+ for (var j = 0; j < pseudos.length; j++) {
498
+ if (validPseudos.indexOf(pseudos[j].toLowerCase()) === -1) {
499
+ allValid = false;
500
+ break;
501
+ }
502
+ }
503
+
504
+ if (!allValid) {
505
+ isValid = false;
506
+ }
507
+ }
508
+ } else {
509
+ isValid = false;
510
+ }
511
+ }
512
+
513
+ }
514
+ }
515
+ }
456
516
 
457
517
  if (!isValid) {
458
518
  // If it's invalid the browser will simply ignore the entire invalid block
@@ -532,6 +592,27 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
532
592
  return false;
533
593
  }
534
594
 
595
+ // Check for invalid pseudo-class usage with quoted strings
596
+ // Pseudo-classes like :lang(), :dir(), :nth-*() should not accept quoted strings
597
+ var pseudoPattern = /::?([a-zA-Z][\w-]*)\(([^)]+)\)/g;
598
+ var pseudoMatch;
599
+ while ((pseudoMatch = pseudoPattern.exec(selector)) !== null) {
600
+ var pseudoName = pseudoMatch[1];
601
+ var pseudoContent = pseudoMatch[2];
602
+
603
+ // List of pseudo-classes that should not accept quoted strings
604
+ // :lang() - accepts language codes: en, fr-CA
605
+ // :dir() - accepts direction: ltr, rtl
606
+ // :nth-*() - accepts An+B notation: 2n+1, odd, even
607
+ var noQuotesPseudos = ['lang', 'dir', 'nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type'];
608
+
609
+ for (var i = 0; i < noQuotesPseudos.length; i++) {
610
+ if (pseudoName === noQuotesPseudos[i] && /['"]/.test(pseudoContent)) {
611
+ return false;
612
+ }
613
+ }
614
+ }
615
+
535
616
  // Fallback to a loose regexp for the overall selector structure (without deep paren matching)
536
617
  // This is similar to the original, but without nested paren limitations
537
618
  // Modified to support namespace selectors: *|element, prefix|element, |element
@@ -636,7 +717,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
636
717
  * @returns {boolean} Returns `true` if the selector is valid, otherwise `false`.
637
718
  */
638
719
 
639
- // Cache to store validated selectors (ES5-compliant object)
720
+ // Cache to store validated selectors (previously a ES6 Map, now an ES5-compliant object)
640
721
  var validatedSelectorsCache = {};
641
722
 
642
723
  // Only pseudo-classes that accept selector lists should recurse
@@ -746,6 +827,28 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
746
827
  return definedNamespacePrefixes.hasOwnProperty(namespacePrefix);
747
828
  }
748
829
 
830
+ /**
831
+ * Processes a CSS selector text
832
+ *
833
+ * @param {string} selectorText - The CSS selector text to process
834
+ * @returns {string} The processed selector text with normalized whitespace
835
+ */
836
+ function processSelectorText(selectorText) {
837
+ // TODO: Remove invalid selectors that appears inside pseudo classes
838
+ // TODO: The same processing here needs to be reused in CSSStyleRule.selectorText setter
839
+ // TODO: Move these validation logic to a shared function to be reused in CSSStyleRule.selectorText setter
840
+
841
+ /**
842
+ * Normalizes whitespace and preserving quoted strings.
843
+ * Replaces all newline characters (CRLF, CR, or LF) with spaces while keeping quoted
844
+ * strings (single or double quotes) intact, including any escaped characters within them.
845
+ */
846
+ return selectorText.replace(/(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g, function(match, _, newline) {
847
+ if (newline) return " ";
848
+ return match;
849
+ });
850
+ }
851
+
749
852
  /**
750
853
  * Checks if a given CSS selector text is valid by splitting it by commas
751
854
  * and validating each individual selector using the `validateSelector` function.
@@ -754,6 +857,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
754
857
  * @returns {boolean} Returns true if all selectors are valid, otherwise false.
755
858
  */
756
859
  function isValidSelectorText(selectorText) {
860
+ // TODO: The same validations here needs to be reused in CSSStyleRule.selectorText setter
861
+ // TODO: Move these validation logic to a shared function to be reused in CSSStyleRule.selectorText setter
862
+
757
863
  // Check for newlines inside single or double quotes using regex
758
864
  // This matches any quoted string (single or double) containing a newline
759
865
  var quotedNewlineRegExp = /(['"])(?:\\.|[^\\])*?\1/g;
@@ -774,7 +880,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
774
880
  return true;
775
881
  }
776
882
 
777
- function parseError(message) {
883
+ function parseError(message, isNested) {
778
884
  var lines = token.substring(0, i).split('\n');
779
885
  var lineCount = lines.length;
780
886
  var charCount = lines.pop().length + 1;
@@ -783,6 +889,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
783
889
  /* jshint sub : true */
784
890
  error['char'] = charCount;
785
891
  error.styleSheet = styleSheet;
892
+ error.isNested = !!isNested;
786
893
  // Print the error but continue parsing the sheet
787
894
  try {
788
895
  throw error;
@@ -880,7 +987,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
880
987
  i += 2;
881
988
  index = token.indexOf("*/", i);
882
989
  if (index === -1) {
883
- parseError("Missing */");
990
+ i = token.length - 1;
991
+ buffer = "";
884
992
  } else {
885
993
  i = index + 1;
886
994
  }
@@ -953,6 +1061,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
953
1061
  });
954
1062
  buffer = "";
955
1063
  break;
1064
+ } else if (token.indexOf("@page", i) === i) {
1065
+ validateAtRule("@page", function(){
1066
+ state = "pageBlock"
1067
+ pageRule = new CSSOM.CSSPageRule();
1068
+ pageRule.__starts = i;
1069
+ i += "page".length;
1070
+ });
1071
+ buffer = "";
1072
+ break;
956
1073
  } else if (token.indexOf("@supports", i) === i) {
957
1074
  validateAtRule("@supports", function(){
958
1075
  state = "conditionBlock";
@@ -1050,10 +1167,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1050
1167
  }
1051
1168
 
1052
1169
  currentScope = parentRule = styleRule;
1053
- styleRule.selectorText = buffer.trim().replace(/(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g, function(match, _, newline) {
1054
- if (newline) return " ";
1055
- return match;
1056
- });
1170
+ styleRule.selectorText = processSelectorText(buffer.trim());
1057
1171
  styleRule.style.__starts = i;
1058
1172
  styleRule.__parentStyleSheet = styleSheet;
1059
1173
  buffer = "";
@@ -1071,7 +1185,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1071
1185
  buffer = "";
1072
1186
  state = "before-selector";
1073
1187
  } else if (state === "containerBlock") {
1074
- containerRule.containerText = buffer.trim();
1188
+ containerRule.__conditionText = buffer.trim();
1075
1189
 
1076
1190
  if (parentRule) {
1077
1191
  containerRule.__parentRule = parentRule;
@@ -1088,7 +1202,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1088
1202
  counterStyleRule.__parentStyleSheet = styleSheet;
1089
1203
  buffer = "";
1090
1204
  } else if (state === "conditionBlock") {
1091
- supportsRule.conditionText = buffer.trim();
1205
+ supportsRule.__conditionText = buffer.trim();
1092
1206
 
1093
1207
  if (parentRule) {
1094
1208
  supportsRule.__parentRule = parentRule;
@@ -1123,7 +1237,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1123
1237
  } else if (state === "layerBlock") {
1124
1238
  layerBlockRule.name = buffer.trim();
1125
1239
 
1126
- var isValidName = layerBlockRule.name.length === 0 || layerBlockRule.name.match(layerRuleNameRegExp) !== null;
1240
+ var isValidName = layerBlockRule.name.length === 0 || layerBlockRule.name.match(cssCustomIdentifierRegExp) !== null;
1127
1241
 
1128
1242
  if (isValidName) {
1129
1243
  if (parentRule) {
@@ -1136,6 +1250,19 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1136
1250
  }
1137
1251
  buffer = "";
1138
1252
  state = "before-selector";
1253
+ } else if (state === "pageBlock") {
1254
+ pageRule.selectorText = buffer.trim();
1255
+
1256
+ if (parentRule) {
1257
+ pageRule.__parentRule = parentRule;
1258
+ ancestorRules.push(parentRule);
1259
+ }
1260
+
1261
+ currentScope = parentRule = pageRule;
1262
+ pageRule.__parentStyleSheet = styleSheet;
1263
+ styleRule = pageRule;
1264
+ buffer = "";
1265
+ state = "before-name";
1139
1266
  } else if (state === "hostRule-begin") {
1140
1267
  if (parentRule) {
1141
1268
  ancestorRules.push(parentRule);
@@ -1209,10 +1336,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1209
1336
  }
1210
1337
 
1211
1338
  styleRule = new CSSOM.CSSStyleRule();
1212
- var processedSelectorText = buffer.trim().replace(/(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g, function(match, _, newline) {
1213
- if (newline) return " ";
1214
- return match;
1215
- });
1339
+ var processedSelectorText = processSelectorText(buffer.trim());
1216
1340
  // In a nested selector, ensure each selector contains '&' at the beginning, except for selectors that already have '&' somewhere
1217
1341
  if (parentRule.constructor.name !== "CSSStyleRule" && parentRule.parentRule === null) {
1218
1342
  styleRule.selectorText = processedSelectorText;
@@ -1336,7 +1460,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1336
1460
  testNamespaceRule.cssText = buffer + character;
1337
1461
 
1338
1462
  namespaceRule = testNamespaceRule;
1339
- namespaceRule.__parentStyleSheet = namespaceRule.styleSheet.__parentStyleSheet = styleSheet;
1463
+ namespaceRule.__parentStyleSheet = styleSheet;
1340
1464
  styleSheet.cssRules.push(namespaceRule);
1341
1465
 
1342
1466
  // Track the namespace prefix for validation
@@ -1355,7 +1479,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1355
1479
  return name.trim();
1356
1480
  });
1357
1481
  var isInvalid = parentRule !== undefined || nameListStr.some(function (name) {
1358
- return name.trim().match(layerRuleNameRegExp) === null;
1482
+ return name.trim().match(cssCustomIdentifierRegExp) === null;
1359
1483
  });
1360
1484
 
1361
1485
  if (!isInvalid) {
@@ -1412,7 +1536,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1412
1536
  if (styleRule === nestedSelectorRule) {
1413
1537
  nestedSelectorRule = null;
1414
1538
  }
1415
- parseError('Invalid CSSStyleRule (selectorText = "' + styleRule.selectorText + '")');
1539
+ parseError('Invalid CSSStyleRule (selectorText = "' + styleRule.selectorText + '")', styleRule.parentRule !== null);
1416
1540
  } else {
1417
1541
  currentScope.cssRules.push(styleRule);
1418
1542
  }
@@ -1582,6 +1706,10 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
1582
1706
  }
1583
1707
  }
1584
1708
 
1709
+ if (buffer.trim() !== "") {
1710
+ parseError("Unexpected end of input");
1711
+ }
1712
+
1585
1713
  return styleSheet;
1586
1714
  };
1587
1715
 
@@ -1611,6 +1739,7 @@ CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule;
1611
1739
  CSSOM.CSSScopeRule = require('./CSSScopeRule').CSSScopeRule;
1612
1740
  CSSOM.CSSLayerBlockRule = require("./CSSLayerBlockRule").CSSLayerBlockRule;
1613
1741
  CSSOM.CSSLayerStatementRule = require("./CSSLayerStatementRule").CSSLayerStatementRule;
1742
+ CSSOM.CSSPageRule = require("./CSSPageRule").CSSPageRule;
1614
1743
  // Use cssstyle if available
1615
1744
  try {
1616
1745
  CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "parser",
8
8
  "styleSheet"
9
9
  ],
10
- "version": "0.9.20",
10
+ "version": "0.9.22",
11
11
  "author": "Nikita Vasilyev <me@elv1s.ru>",
12
12
  "contributors": [
13
13
  "Acemir Sousa Mendes <acemirsm@gmail.com>"