[svn r5] getters and setters for cssText and selectorText (excepting selectorText on PAGE_RULE for the time being) trunk
authordglazman
Wed, 10 Mar 2010 03:39:03 -0600
branchtrunk
changeset 2 f6f854eba2b1
parent 1 11141edf0f12
child 3 82342fdd2e83
[svn r5] getters and setters for cssText and selectorText (excepting selectorText on PAGE_RULE for the time being)
cssParser.js
--- a/cssParser.js	Tue Mar 09 07:34:45 2010 -0600
+++ b/cssParser.js	Wed Mar 10 03:39:03 2010 -0600
@@ -333,6 +333,12 @@
   mToken : null,
   mLookAhead : null,
 
+  init: function(aString) {
+    this.mToken = null;
+    this.mLookAhead = null;
+    CSSScanner.init(aString);
+  },
+
   getToken: function(aSkipWS, aSkipComment) {
     if (this.mLookAhead) {
       this.mToken = this.mLookAhead;
@@ -396,25 +402,24 @@
 
   addUnknownRule: function(aSheet, aString) {
     var rule = new jscsspErrorRule();
-    rule.cssText = aString;
+    rule.parsedCssText = aString;
     aSheet.cssRules.push(rule);
   },
 
   addWhitespace: function(aSheet, aString) {
     var rule = new jscsspWhitespace();
-    rule.cssText = aString;
+    rule.parsedCssText = aString;
     aSheet.cssRules.push(rule);
   },
 
   addComment: function(aSheet, aString) {
     var rule = new jscsspComment();
-    rule.cssText = aString;
+    rule.parsedCssText = aString;
     aSheet.cssRules.push(rule);
   },
 
   parseCharsetRule: function(aToken, aSheet) {
     var s = aToken.value;
-    var valid = false;
     var token = this.getToken(false, false);
     if (token.isNotNull()
         && token.isWhiteSpace(" ")) {
@@ -426,17 +431,17 @@
         token = this.getToken(false, false);
         if (token.isSymbol(";")) {
           s += token.value;
-          valid = true;
           var rule = new jscsspCharsetRule();
           rule.encoding = encoding;
-          rule.cssText = s;
+          rule.parsedCssText = s;
           aSheet.cssRules.push(rule);
+          return true;
         }
       }
     }
 
-    if (!valid)
-      this.addUnknownAtRule(aSheet, s);
+    this.addUnknownAtRule(aSheet, s);
+    return false;
   },
 
   parseImportRule: function(aToken, aSheet) {
@@ -468,7 +473,7 @@
         s += ";"
         CSSScanner.forgetState();
         var rule = new jscsspImportRule();
-        rule.cssText = s;
+        rule.parsedCssText = s;
         rule.href = href;
         rule.media = media;
         aSheet.cssRules.push(rule);
@@ -543,7 +548,7 @@
           s += ";";
           CSSScanner.forgetState();
           var rule = new jscsspNamespaceRule();
-          rule.cssText = s;
+          rule.parsedCssText = s;
           rule.prefix = prefix;
           rule.url = url;
           aSheet.cssRules.push(rule);
@@ -584,7 +589,7 @@
     if (valid) {
       CSSScanner.forgetState();
       var rule = new jscsspFontFaceRule();
-      rule.cssText = s;
+      rule.parsedCssText = s;
       rule.descriptors = descriptors;
       aSheet.cssRules.push(rule)
       return true;
@@ -629,7 +634,7 @@
     if (valid) {
       CSSScanner.forgetState();
       var rule = new jscsspPageRule();
-      rule.cssText = s;
+      rule.parsedCssText = s;
       rule.pageSelector = pageSelector;
       rule.declarations = declarations;
       aSheet.cssRules.push(rule)
@@ -705,17 +710,17 @@
           var decl = new jscsspDeclaration();
           decl.property = descriptor;
           decl.value = value;
-          decl.cssText = descriptor + ": " + value + ";";
+          decl.parsedCssText = descriptor + ": " + value + ";";
           aDecl.push(decl);
-          return decl.cssText;
+          return decl.parsedCssText;
         }
       }
     } else if (aToken.isComment()) {
       CSSScanner.forgetState();
       var comment = new jscsspComment();
-      comment.cssText = aToken.value;
+      comment.parsedCssText = aToken.value;
       aDecl.push(comment);
-      return comment.cssText;
+      return comment.parsedCssText;
     }
 
     // we have an error here, let's skip it
@@ -792,7 +797,7 @@
         if (token.isComment()) {
           s += " " + token.value;
           var comment = new jscsspComment();
-          comment.cssText = token.value;
+          comment.parsedCssText = token.value;
           mediaRule.cssRules.push(comment);
         } else if (token.isSymbol("}")) {
           valid = true;
@@ -807,7 +812,7 @@
     }
     if (valid) {
       CSSScanner.forgetState();
-      mediaRule.cssText = s;
+      mediaRule.parsedCssText = s;
       aSheet.cssRules.push(mediaRule);
       return true;
     }
@@ -817,7 +822,7 @@
 
   parseStyleRule: function(aToken, aCssRules) {
     // first let's see if we have a selector here...
-    var selector = this.parseSelector(aToken);
+    var selector = this.parseSelector(aToken, false);
     var valid = false;
     var declarations = [];
     if (selector) {
@@ -841,24 +846,29 @@
     }
     if (valid) {
       var rule = new jscsspStyleRule();
-      rule.cssText = s;
+      rule.parsedCssText = s;
       rule.declarations = declarations;
-      rule.selectorText = selector;
+      rule.mSelectorText = selector;
       aCssRules.cssRules.push(rule);
       return s;
     }
     return "";
   },
 
-  parseSelector: function(aToken) {
-    CSSScanner.preserveState();
+  parseSelector: function(aToken, aParseSelectorOnly) {
     var s = "";
     var isFirstInChain = true;
     var token = aToken;
     var valid = false;
     var combinatorFound = false;
-    while (token.isNotNull()) {
-      if (token.isSymbol("{")) {
+    while (true) {
+      if (!token.isNotNull()) {
+        if (aParseSelectorOnly)
+          return s;
+        return "";
+      }
+
+      if (!aParseSelectorOnly && token.isSymbol("{")) {
         // end of selector
         this.ungetToken();
         valid = true;
@@ -902,10 +912,8 @@
     }
 
     if (valid) {
-      CSSScanner.forgetState();
       return s;
     }
-    CSSScanner.forgetState();
     return "";
   },
 
@@ -1047,8 +1055,8 @@
     if (!aString)
       return null; // early way out if we can
 
+    this.init(aString);
     var sheet = new jscsspStylesheet();
-    CSSScanner.init(aString);
 
     // @charset can only appear at first char of the stylesheet
     var token = this.getToken(false, false);
@@ -1154,7 +1162,7 @@
 
   _isOfType: function (aType, aValue)
   {
-    return (this.type == aType && (!aValue || this.value == aValue));
+    return (this.type == aType && (!aValue || this.value.toLowerCase() == aValue));
   },
 
   isWhitespace: function(w)
@@ -1242,10 +1250,11 @@
 
 const kJscsspSTYLE_DECLARATION = 1000;
 
+var gTABS = "";
+
 function jscsspStylesheet()
 {
   this.cssRules = [];
-  this.ownerRule = null;
 }
 
 jscsspStylesheet.prototype = {
@@ -1265,54 +1274,162 @@
     catch(e) {
       dump("DOMException: jscsspStylesheet.insertRule\n")
     }
+  },
+
+  get cssText() {
+    var rv = "";
+    for (var i = 0; i < this.cssRules.length; i++)
+      rv += this.cssRules[i].cssText + "\n";
+    return rv;
   }
 };
 
+/* kJscsspCHARSET_RULE */
+
 function jscsspCharsetRule()
 {
   this.type = kJscsspCHARSET_RULE;
   this.encoding = null;
-  this.cssText = null;
+  this.parsedCssText = null;
 }
 
+jscsspCharsetRule.prototype = {
+
+  get cssText() {
+    return "@charset " + this.encoding + ";";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(false, false);
+    if (token.isAtRule("@charset")) {
+      if (CSSParser.parseCharsetRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.encoding = newRule.encoding;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspUNKNOWN_RULE */
 
 function jscsspErrorRule()
 {
   this.type = kJscsspUNKNOWN_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
 }
 
+jscsspErrorRule.prototype = {
+  get cssText() {
+    return this.parsedCssText;
+  }
+};
+
+/* kJscsspCOMMENT */
+
 function jscsspComment()
 {
   this.type = kJscsspCOMMENT;
-  this.cssText = null;
+  this.parsedCssText = null;
 }
 
+jscsspComment.prototype = {
+  get cssText() {
+    return this.parsedCssText;
+  },
+
+  set cssText(val) {
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, false);
+    if (token.isComment())
+      this.parsedCssText = token.value;
+    else
+      throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspWHITE_SPACE */
+
 function jscsspWhitespace()
 {
   this.type = kJscsspWHITE_SPACE;
-  this.cssText = null;
+  this.parsedCssText = null;
 }
 
+/* kJscsspIMPORT_RULE */
+
 function jscsspImportRule()
 {
   this.type = kJscsspIMPORT_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
+  this.href = null;
   this.media = []; 
 }
 
+jscsspImportRule.prototype = {
+  get cssText() {
+    var mediaString = this.media.join(", ");
+    return "@import " + (mediaString ? mediaString + " " : "")
+                      + this.href
+                      + ";";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (token.isAtRule("@import")) {
+      if (CSSParser.parseImportRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.href = newRule.href;
+        this.media = newRule.media;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspNAMESPACE_RULE */
+
 function jscsspNamespaceRule()
 {
   this.type = kJscsspNAMESPACE_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
   this.prefix = null;
   this.url = null;
 }
 
-function jscsspDeclarations()
-{
-  this.declarations = [];
-}
+jscsspNamespaceRule.prototype = {
+  get cssText() {
+    return "@namespace" + (this.prefix ? this.prefix + " ": "")
+                        + this.url
+                        + ";";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (token.isAtRule("@namespace")) {
+      if (CSSParser.parseNamespaceRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.url = newRule.url;
+        this.prefix = newRule.prefix;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspSTYLE_DECLARATION */
 
 function jscsspDeclaration()
 {
@@ -1320,28 +1437,199 @@
   this.property = null;
   this.value = null;
   this.priority = null;
-  this.cssText = null;
+  this.parsedCssText = null;
 }
 
+jscsspDeclaration.prototype = {
+  get cssText() {
+    return this.property + ": "
+                    + this.value
+                    + (this.priority ? this.priority : "")
+                    + ";";
+  },
+
+  set cssText(val) {
+    var declarations = [];
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (CSSParser.parseDeclaration(token, declarations, true)
+        && declarations.length
+        && declarations[0].type == kJscsspSTYLE_DECLARATION) {
+      var newDecl = declarations.cssRules[0];
+      this.property = newDecl.property;
+      this.value = newDecl.value;
+      this.priority = newDecl.priority;
+      this.parsedCssText = newRule.parsedCssText;
+      return;
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspFONT_FACE_RULE */
+
 function jscsspFontFaceRule()
 {
   this.type = kJscsspFONT_FACE_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
   this.descriptors = [];
 }
 
+jscsspFontFaceRule.prototype = {
+  get cssText() {
+    var rv = gTABS + "@font-face {\n";
+    var preservedGTABS = gTABS;
+    gTABS += "  ";
+    for (var i = 0; i < this.descriptors.length; i++)
+      rv += gTABS + this.descriptors[i].cssText + "\n";
+    gTABS = preservedGTABS;
+    return rv + gTABS + "}";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (token.isAtRule("@font-face")) {
+      if (CSSParser.parseFontFaceRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.descriptors = newRule.descriptors;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspMEDIA_RULE */
+
 function jscsspMediaRule()
 {
   this.type = kJscsspMEDIA_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
   this.cssRules = [];
   this.media = [];
 }
 
+jscsspMediaRule.prototype = {
+  get cssText() {
+    var rv = gTABS + "@media " + this.media.join(", ") + " {\n";
+    var preservedGTABS = gTABS;
+    gTABS += "  ";
+    for (var i = 0; i < this.cssRules.length; i++)
+      rv += this.cssRules[i].cssText + "\n";
+    gTABS = preservedGTABS;
+    return rv + gTABS + "}";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (token.isAtRule("@media")) {
+      if (CSSParser.parseMediaRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.cssRules = newRule.cssRules;
+        this.media = newRule.media;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspSTYLE_RULE */
+
 function jscsspStyleRule()
 {
   this.type = kJscsspSTYLE_RULE;
-  this.cssText = null;
+  this.parsedCssText = null;
   this.declarations = []
-  this.selectorText = null;;
+  this.mSelectorText = null;;
 }
+
+jscsspStyleRule.prototype = {
+  get cssText() {
+    var rv = gTABS + this.mSelectorText + " {\n";
+    var preservedGTABS = gTABS;
+    gTABS += "  ";
+    for (var i = 0; i < this.declarations.length; i++)
+      rv += gTABS + this.declarations[i].cssText + "\n";
+    gTABS = preservedGTABS;
+    return rv + gTABS + "}";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (!token.isNotNull()) {
+      if (CSSParser.parseStyleRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.mSelectorText = newRule.mSelectorText;
+        this.declarations = newRule.declarations;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  },
+
+  get selectorText() {
+    return this.mSelectorText;
+  },
+
+  set selectorText(val) {
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (!token.isNotNull()) {
+      var s = CSSParser.parseSelector(token, true);
+      if (s) {
+        this.mSelectorText = s;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};
+
+/* kJscsspPAGE_RULE */
+
+function jscsspPageRule()
+{
+  this.type = kJscsspPAGE_RULE;
+  this.parsedCssText = null;
+  this.pageSelector = null;
+  this.declarations = [];
+}
+
+jscsspPageRule.prototype = {
+  get cssText() {
+    var rv = gTABS + "@page " + (this.mediaSelector ? this.mediaSelector : "")
+                   + " {\n";
+    var preservedGTABS = gTABS;
+    gTABS += "  ";
+    for (var i = 0; i < this.declarations.length; i++)
+      rv += gTABS + this.declarations[i].cssText + "\n";
+    gTABS = preservedGTABS;
+    return rv + gTABS + "}";
+  },
+
+  set cssText(val) {
+    var sheet = {cssRules: []};
+    CSSParser.init(val);
+    var token = CSSParser.getToken(true, true);
+    if (token.isAtRule("@page")) {
+      if (CSSParser.parsePageRule(token, sheet)) {
+        var newRule = sheet.cssRules[0];
+        this.pageSelector = newRule.pageSelector;
+        this.declarations = newRule.declarations;
+        this.parsedCssText = newRule.parsedCssText;
+        return;
+      }
+    }
+    throw DOMException.SYNTAX_ERR;
+  }
+};