1932 lines
44 KiB
JavaScript
1932 lines
44 KiB
JavaScript
var CSSOM = {};
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
|
|
*/
|
|
CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration(){
|
|
this.length = 0;
|
|
this.parentRule = null;
|
|
|
|
// NON-STANDARD
|
|
this._importants = {};
|
|
};
|
|
|
|
|
|
CSSOM.CSSStyleDeclaration.prototype = {
|
|
|
|
constructor: CSSOM.CSSStyleDeclaration,
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
|
|
* @return {string} the value of the property if it has been explicitly set for this declaration block.
|
|
* Returns the empty string if the property has not been set.
|
|
*/
|
|
getPropertyValue: function(name) {
|
|
return this[name] || "";
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @param {string} value
|
|
* @param {string} [priority=null] "important" or null
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
|
|
*/
|
|
setProperty: function(name, value, priority) {
|
|
if (this[name]) {
|
|
// Property already exist. Overwrite it.
|
|
var index = Array.prototype.indexOf.call(this, name);
|
|
if (index < 0) {
|
|
this[this.length] = name;
|
|
this.length++;
|
|
}
|
|
} else {
|
|
// New property.
|
|
this[this.length] = name;
|
|
this.length++;
|
|
}
|
|
this[name] = value + "";
|
|
this._importants[name] = priority;
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
|
|
* @return {string} the value of the property if it has been explicitly set for this declaration block.
|
|
* Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
|
|
*/
|
|
removeProperty: function(name) {
|
|
if (!(name in this)) {
|
|
return "";
|
|
}
|
|
var index = Array.prototype.indexOf.call(this, name);
|
|
if (index < 0) {
|
|
return "";
|
|
}
|
|
var prevValue = this[name];
|
|
this[name] = "";
|
|
|
|
// That's what WebKit and Opera do
|
|
Array.prototype.splice.call(this, index, 1);
|
|
|
|
// That's what Firefox does
|
|
//this[index] = ""
|
|
|
|
return prevValue;
|
|
},
|
|
|
|
getPropertyCSSValue: function() {
|
|
//FIXME
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {String} name
|
|
*/
|
|
getPropertyPriority: function(name) {
|
|
return this._importants[name] || "";
|
|
},
|
|
|
|
|
|
/**
|
|
* element.style.overflow = "auto"
|
|
* element.style.getPropertyShorthand("overflow-x")
|
|
* -> "overflow"
|
|
*/
|
|
getPropertyShorthand: function() {
|
|
//FIXME
|
|
},
|
|
|
|
isPropertyImplicit: function() {
|
|
//FIXME
|
|
},
|
|
|
|
// Doesn't work in IE < 9
|
|
get cssText(){
|
|
var properties = [];
|
|
for (var i=0, length=this.length; i < length; ++i) {
|
|
var name = this[i];
|
|
var value = this.getPropertyValue(name);
|
|
var priority = this.getPropertyPriority(name);
|
|
if (priority) {
|
|
priority = " !" + priority;
|
|
}
|
|
properties[i] = name + ": " + value + priority + ";";
|
|
}
|
|
return properties.join(" ");
|
|
},
|
|
|
|
set cssText(text){
|
|
var i, name;
|
|
for (i = this.length; i--;) {
|
|
name = this[i];
|
|
this[name] = "";
|
|
}
|
|
Array.prototype.splice.call(this, 0, this.length);
|
|
this._importants = {};
|
|
|
|
var dummyRule = CSSOM.parse('#bogus{' + text + '}').cssRules[0].style;
|
|
var length = dummyRule.length;
|
|
for (i = 0; i < length; ++i) {
|
|
name = dummyRule[i];
|
|
this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name));
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule
|
|
*/
|
|
CSSOM.CSSRule = function CSSRule() {
|
|
this.parentRule = null;
|
|
this.parentStyleSheet = null;
|
|
};
|
|
|
|
CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete
|
|
CSSOM.CSSRule.STYLE_RULE = 1;
|
|
CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete
|
|
CSSOM.CSSRule.IMPORT_RULE = 3;
|
|
CSSOM.CSSRule.MEDIA_RULE = 4;
|
|
CSSOM.CSSRule.FONT_FACE_RULE = 5;
|
|
CSSOM.CSSRule.PAGE_RULE = 6;
|
|
CSSOM.CSSRule.KEYFRAMES_RULE = 7;
|
|
CSSOM.CSSRule.KEYFRAME_RULE = 8;
|
|
CSSOM.CSSRule.MARGIN_RULE = 9;
|
|
CSSOM.CSSRule.NAMESPACE_RULE = 10;
|
|
CSSOM.CSSRule.COUNTER_STYLE_RULE = 11;
|
|
CSSOM.CSSRule.SUPPORTS_RULE = 12;
|
|
CSSOM.CSSRule.DOCUMENT_RULE = 13;
|
|
CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14;
|
|
CSSOM.CSSRule.VIEWPORT_RULE = 15;
|
|
CSSOM.CSSRule.REGION_STYLE_RULE = 16;
|
|
CSSOM.CSSRule.CONTAINER_RULE = 17;
|
|
CSSOM.CSSRule.STARTING_STYLE_RULE = 1002;
|
|
|
|
|
|
CSSOM.CSSRule.prototype = {
|
|
constructor: CSSOM.CSSRule
|
|
//FIXME
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface
|
|
*/
|
|
CSSOM.CSSGroupingRule = function CSSGroupingRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
|
|
|
|
|
|
/**
|
|
* Used to insert a new CSS rule to a list of CSS rules.
|
|
*
|
|
* @example
|
|
* cssGroupingRule.cssText
|
|
* -> "body{margin:0;}"
|
|
* cssGroupingRule.insertRule("img{border:none;}", 1)
|
|
* -> 1
|
|
* cssGroupingRule.cssText
|
|
* -> "body{margin:0;}img{border:none;}"
|
|
*
|
|
* @param {string} rule
|
|
* @param {number} [index]
|
|
* @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-insertrule
|
|
* @return {number} The index within the grouping rule's collection of the newly inserted rule.
|
|
*/
|
|
CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) {
|
|
if (index < 0 || index > this.cssRules.length) {
|
|
throw new RangeError("INDEX_SIZE_ERR");
|
|
}
|
|
var cssRule = CSSOM.parse(rule).cssRules[0];
|
|
cssRule.parentRule = this;
|
|
this.cssRules.splice(index, 0, cssRule);
|
|
return index;
|
|
};
|
|
|
|
/**
|
|
* Used to delete a rule from the grouping rule.
|
|
*
|
|
* cssGroupingRule.cssText
|
|
* -> "img{border:none;}body{margin:0;}"
|
|
* cssGroupingRule.deleteRule(0)
|
|
* cssGroupingRule.cssText
|
|
* -> "body{margin:0;}"
|
|
*
|
|
* @param {number} index within the grouping rule's rule list of the rule to remove.
|
|
* @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-deleterule
|
|
*/
|
|
CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) {
|
|
if (index < 0 || index >= this.cssRules.length) {
|
|
throw new RangeError("INDEX_SIZE_ERR");
|
|
}
|
|
this.cssRules.splice(index, 1)[0].parentRule = null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface
|
|
*/
|
|
CSSOM.CSSConditionRule = function CSSConditionRule() {
|
|
CSSOM.CSSGroupingRule.call(this);
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule();
|
|
CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule;
|
|
CSSOM.CSSConditionRule.prototype.conditionText = ''
|
|
CSSOM.CSSConditionRule.prototype.cssText = ''
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#cssstylerule
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule
|
|
*/
|
|
CSSOM.CSSStyleRule = function CSSStyleRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.selectorText = "";
|
|
this.style = new CSSOM.CSSStyleDeclaration();
|
|
this.style.parentRule = this;
|
|
};
|
|
|
|
CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule;
|
|
CSSOM.CSSStyleRule.prototype.type = 1;
|
|
|
|
Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
|
|
get: function() {
|
|
var text;
|
|
if (this.selectorText) {
|
|
text = this.selectorText + " {" + this.style.cssText + "}";
|
|
} else {
|
|
text = "";
|
|
}
|
|
return text;
|
|
},
|
|
set: function(cssText) {
|
|
var rule = CSSOM.CSSStyleRule.parse(cssText);
|
|
this.style = rule.style;
|
|
this.selectorText = rule.selectorText;
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* NON-STANDARD
|
|
* lightweight version of parse.js.
|
|
* @param {string} ruleText
|
|
* @return CSSStyleRule
|
|
*/
|
|
CSSOM.CSSStyleRule.parse = function(ruleText) {
|
|
var i = 0;
|
|
var state = "selector";
|
|
var index;
|
|
var j = i;
|
|
var buffer = "";
|
|
|
|
var SIGNIFICANT_WHITESPACE = {
|
|
"selector": true,
|
|
"value": true
|
|
};
|
|
|
|
var styleRule = new CSSOM.CSSStyleRule();
|
|
var name, priority="";
|
|
|
|
for (var character; (character = ruleText.charAt(i)); i++) {
|
|
|
|
switch (character) {
|
|
|
|
case " ":
|
|
case "\t":
|
|
case "\r":
|
|
case "\n":
|
|
case "\f":
|
|
if (SIGNIFICANT_WHITESPACE[state]) {
|
|
// Squash 2 or more white-spaces in the row into 1
|
|
switch (ruleText.charAt(i - 1)) {
|
|
case " ":
|
|
case "\t":
|
|
case "\r":
|
|
case "\n":
|
|
case "\f":
|
|
break;
|
|
default:
|
|
buffer += " ";
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// String
|
|
case '"':
|
|
j = i + 1;
|
|
index = ruleText.indexOf('"', j) + 1;
|
|
if (!index) {
|
|
throw '" is missing';
|
|
}
|
|
buffer += ruleText.slice(i, index);
|
|
i = index - 1;
|
|
break;
|
|
|
|
case "'":
|
|
j = i + 1;
|
|
index = ruleText.indexOf("'", j) + 1;
|
|
if (!index) {
|
|
throw "' is missing";
|
|
}
|
|
buffer += ruleText.slice(i, index);
|
|
i = index - 1;
|
|
break;
|
|
|
|
// Comment
|
|
case "/":
|
|
if (ruleText.charAt(i + 1) === "*") {
|
|
i += 2;
|
|
index = ruleText.indexOf("*/", i);
|
|
if (index === -1) {
|
|
throw new SyntaxError("Missing */");
|
|
} else {
|
|
i = index + 1;
|
|
}
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case "{":
|
|
if (state === "selector") {
|
|
styleRule.selectorText = buffer.trim();
|
|
buffer = "";
|
|
state = "name";
|
|
}
|
|
break;
|
|
|
|
case ":":
|
|
if (state === "name") {
|
|
name = buffer.trim();
|
|
buffer = "";
|
|
state = "value";
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case "!":
|
|
if (state === "value" && ruleText.indexOf("!important", i) === i) {
|
|
priority = "important";
|
|
i += "important".length;
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case ";":
|
|
if (state === "value") {
|
|
styleRule.style.setProperty(name, buffer.trim(), priority);
|
|
priority = "";
|
|
buffer = "";
|
|
state = "name";
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case "}":
|
|
if (state === "value") {
|
|
styleRule.style.setProperty(name, buffer.trim(), priority);
|
|
priority = "";
|
|
buffer = "";
|
|
} else if (state === "name") {
|
|
break;
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
state = "selector";
|
|
break;
|
|
|
|
default:
|
|
buffer += character;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
return styleRule;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#the-medialist-interface
|
|
*/
|
|
CSSOM.MediaList = function MediaList(){
|
|
this.length = 0;
|
|
};
|
|
|
|
CSSOM.MediaList.prototype = {
|
|
|
|
constructor: CSSOM.MediaList,
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
get mediaText() {
|
|
return Array.prototype.join.call(this, ", ");
|
|
},
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
set mediaText(value) {
|
|
var values = value.split(",");
|
|
var length = this.length = values.length;
|
|
for (var i=0; i<length; i++) {
|
|
this[i] = values[i].trim();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {string} medium
|
|
*/
|
|
appendMedium: function(medium) {
|
|
if (Array.prototype.indexOf.call(this, medium) === -1) {
|
|
this[this.length] = medium;
|
|
this.length++;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {string} medium
|
|
*/
|
|
deleteMedium: function(medium) {
|
|
var index = Array.prototype.indexOf.call(this, medium);
|
|
if (index !== -1) {
|
|
Array.prototype.splice.call(this, index, 1);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#cssmediarule
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule
|
|
*/
|
|
CSSOM.CSSMediaRule = function CSSMediaRule() {
|
|
CSSOM.CSSConditionRule.call(this);
|
|
this.media = new CSSOM.MediaList();
|
|
};
|
|
|
|
CSSOM.CSSMediaRule.prototype = new CSSOM.CSSConditionRule();
|
|
CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule;
|
|
CSSOM.CSSMediaRule.prototype.type = 4;
|
|
|
|
// https://opensource.apple.com/source/WebCore/WebCore-7611.1.21.161.3/css/CSSMediaRule.cpp
|
|
Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
|
|
"conditionText": {
|
|
get: function() {
|
|
return this.media.mediaText;
|
|
},
|
|
set: function(value) {
|
|
this.media.mediaText = value;
|
|
},
|
|
configurable: true,
|
|
enumerable: true
|
|
},
|
|
"cssText": {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}";
|
|
},
|
|
configurable: true,
|
|
enumerable: true
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://drafts.csswg.org/css-contain-3/
|
|
* @see https://www.w3.org/TR/css-contain-3/
|
|
*/
|
|
CSSOM.CSSContainerRule = function CSSContainerRule() {
|
|
CSSOM.CSSConditionRule.call(this);
|
|
};
|
|
|
|
CSSOM.CSSContainerRule.prototype = new CSSOM.CSSConditionRule();
|
|
CSSOM.CSSContainerRule.prototype.constructor = CSSOM.CSSContainerRule;
|
|
CSSOM.CSSContainerRule.prototype.type = 17;
|
|
|
|
Object.defineProperties(CSSOM.CSSContainerRule.prototype, {
|
|
"conditionText": {
|
|
get: function() {
|
|
return this.containerText;
|
|
},
|
|
set: function(value) {
|
|
this.containerText = value;
|
|
},
|
|
configurable: true,
|
|
enumerable: true
|
|
},
|
|
"cssText": {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
return "@container " + this.containerText + " {" + cssTexts.join("") + "}";
|
|
},
|
|
configurable: true,
|
|
enumerable: true
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
|
|
*/
|
|
CSSOM.CSSSupportsRule = function CSSSupportsRule() {
|
|
CSSOM.CSSConditionRule.call(this);
|
|
};
|
|
|
|
CSSOM.CSSSupportsRule.prototype = new CSSOM.CSSConditionRule();
|
|
CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule;
|
|
CSSOM.CSSSupportsRule.prototype.type = 12;
|
|
|
|
Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
|
|
for (var i = 0, length = this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
|
|
return "@supports " + this.conditionText + " {" + cssTexts.join("") + "}";
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#cssimportrule
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule
|
|
*/
|
|
CSSOM.CSSImportRule = function CSSImportRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.href = "";
|
|
this.media = new CSSOM.MediaList();
|
|
this.styleSheet = new CSSOM.CSSStyleSheet();
|
|
};
|
|
|
|
CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule;
|
|
CSSOM.CSSImportRule.prototype.type = 3;
|
|
|
|
Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
get: function() {
|
|
var mediaText = this.media.mediaText;
|
|
return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";";
|
|
},
|
|
set: function(cssText) {
|
|
var i = 0;
|
|
|
|
/**
|
|
* @import url(partial.css) screen, handheld;
|
|
* || |
|
|
* after-import media
|
|
* |
|
|
* url
|
|
*/
|
|
var state = '';
|
|
|
|
var buffer = '';
|
|
var index;
|
|
for (var character; (character = cssText.charAt(i)); i++) {
|
|
|
|
switch (character) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
case '\n':
|
|
case '\f':
|
|
if (state === 'after-import') {
|
|
state = 'url';
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case '@':
|
|
if (!state && cssText.indexOf('@import', i) === i) {
|
|
state = 'after-import';
|
|
i += 'import'.length;
|
|
buffer = '';
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
if (state === 'url' && cssText.indexOf('url(', i) === i) {
|
|
index = cssText.indexOf(')', i + 1);
|
|
if (index === -1) {
|
|
throw i + ': ")" not found';
|
|
}
|
|
i += 'url('.length;
|
|
var url = cssText.slice(i, index);
|
|
if (url[0] === url[url.length - 1]) {
|
|
if (url[0] === '"' || url[0] === "'") {
|
|
url = url.slice(1, -1);
|
|
}
|
|
}
|
|
this.href = url;
|
|
i = index;
|
|
state = 'media';
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
if (state === 'url') {
|
|
index = cssText.indexOf('"', i + 1);
|
|
if (!index) {
|
|
throw i + ": '\"' not found";
|
|
}
|
|
this.href = cssText.slice(i + 1, index);
|
|
i = index;
|
|
state = 'media';
|
|
}
|
|
break;
|
|
|
|
case "'":
|
|
if (state === 'url') {
|
|
index = cssText.indexOf("'", i + 1);
|
|
if (!index) {
|
|
throw i + ': "\'" not found';
|
|
}
|
|
this.href = cssText.slice(i + 1, index);
|
|
i = index;
|
|
state = 'media';
|
|
}
|
|
break;
|
|
|
|
case ';':
|
|
if (state === 'media') {
|
|
if (buffer) {
|
|
this.media.mediaText = buffer.trim();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (state === 'media') {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#css-font-face-rule
|
|
*/
|
|
CSSOM.CSSFontFaceRule = function CSSFontFaceRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.style = new CSSOM.CSSStyleDeclaration();
|
|
this.style.parentRule = this;
|
|
};
|
|
|
|
CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule;
|
|
CSSOM.CSSFontFaceRule.prototype.type = 5;
|
|
//FIXME
|
|
//CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp
|
|
Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
|
|
get: function() {
|
|
return "@font-face {" + this.style.cssText + "}";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/shadow-dom/#host-at-rule
|
|
*/
|
|
CSSOM.CSSHostRule = function CSSHostRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule;
|
|
CSSOM.CSSHostRule.prototype.type = 1001;
|
|
//FIXME
|
|
//CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
return "@host {" + cssTexts.join("") + "}";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/shadow-dom/#host-at-rule
|
|
*/
|
|
CSSOM.CSSStartingStyleRule = function CSSStartingStyleRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSStartingStyleRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSStartingStyleRule.prototype.constructor = CSSOM.CSSStartingStyleRule;
|
|
CSSOM.CSSStartingStyleRule.prototype.type = 1002;
|
|
//FIXME
|
|
//CSSOM.CSSStartingStyleRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSStartingStyleRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "cssText", {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
return "@starting-style {" + cssTexts.join("") + "}";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
|
|
*/
|
|
CSSOM.StyleSheet = function StyleSheet() {
|
|
this.parentStyleSheet = null;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet
|
|
*/
|
|
CSSOM.CSSStyleSheet = function CSSStyleSheet() {
|
|
CSSOM.StyleSheet.call(this);
|
|
this.cssRules = [];
|
|
};
|
|
|
|
|
|
CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
|
|
CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
|
|
|
|
|
|
/**
|
|
* Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
|
|
*
|
|
* sheet = new Sheet("body {margin: 0}")
|
|
* sheet.toString()
|
|
* -> "body{margin:0;}"
|
|
* sheet.insertRule("img {border: none}", 0)
|
|
* -> 0
|
|
* sheet.toString()
|
|
* -> "img{border:none;}body{margin:0;}"
|
|
*
|
|
* @param {string} rule
|
|
* @param {number} index
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule
|
|
* @return {number} The index within the style sheet's rule collection of the newly inserted rule.
|
|
*/
|
|
CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
if (index < 0 || index > this.cssRules.length) {
|
|
throw new RangeError("INDEX_SIZE_ERR");
|
|
}
|
|
var cssRule = CSSOM.parse(rule).cssRules[0];
|
|
cssRule.parentStyleSheet = this;
|
|
this.cssRules.splice(index, 0, cssRule);
|
|
return index;
|
|
};
|
|
|
|
|
|
/**
|
|
* Used to delete a rule from the style sheet.
|
|
*
|
|
* sheet = new Sheet("img{border:none} body{margin:0}")
|
|
* sheet.toString()
|
|
* -> "img{border:none;}body{margin:0;}"
|
|
* sheet.deleteRule(0)
|
|
* sheet.toString()
|
|
* -> "body{margin:0;}"
|
|
*
|
|
* @param {number} index within the style sheet's rule list of the rule to remove.
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule
|
|
*/
|
|
CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
|
|
if (index < 0 || index >= this.cssRules.length) {
|
|
throw new RangeError("INDEX_SIZE_ERR");
|
|
}
|
|
this.cssRules.splice(index, 1);
|
|
};
|
|
|
|
|
|
/**
|
|
* NON-STANDARD
|
|
* @return {string} serialize stylesheet
|
|
*/
|
|
CSSOM.CSSStyleSheet.prototype.toString = function() {
|
|
var result = "";
|
|
var rules = this.cssRules;
|
|
for (var i=0; i<rules.length; i++) {
|
|
result += rules[i].cssText + "\n";
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule
|
|
*/
|
|
CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.name = '';
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule;
|
|
CSSOM.CSSKeyframesRule.prototype.type = 7;
|
|
//FIXME
|
|
//CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp
|
|
Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(" " + this.cssRules[i].cssText);
|
|
}
|
|
return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule
|
|
*/
|
|
CSSOM.CSSKeyframeRule = function CSSKeyframeRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.keyText = '';
|
|
this.style = new CSSOM.CSSStyleDeclaration();
|
|
this.style.parentRule = this;
|
|
};
|
|
|
|
CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule;
|
|
CSSOM.CSSKeyframeRule.prototype.type = 8;
|
|
//FIXME
|
|
//CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp
|
|
Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", {
|
|
get: function() {
|
|
return this.keyText + " {" + this.style.cssText + "} ";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://developer.mozilla.org/en/CSS/@-moz-document
|
|
*/
|
|
CSSOM.MatcherList = function MatcherList(){
|
|
this.length = 0;
|
|
};
|
|
|
|
CSSOM.MatcherList.prototype = {
|
|
|
|
constructor: CSSOM.MatcherList,
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
get matcherText() {
|
|
return Array.prototype.join.call(this, ", ");
|
|
},
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
set matcherText(value) {
|
|
// just a temporary solution, actually it may be wrong by just split the value with ',', because a url can include ','.
|
|
var values = value.split(",");
|
|
var length = this.length = values.length;
|
|
for (var i=0; i<length; i++) {
|
|
this[i] = values[i].trim();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {string} matcher
|
|
*/
|
|
appendMatcher: function(matcher) {
|
|
if (Array.prototype.indexOf.call(this, matcher) === -1) {
|
|
this[this.length] = matcher;
|
|
this.length++;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {string} matcher
|
|
*/
|
|
deleteMatcher: function(matcher) {
|
|
var index = Array.prototype.indexOf.call(this, matcher);
|
|
if (index !== -1) {
|
|
Array.prototype.splice.call(this, index, 1);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see https://developer.mozilla.org/en/CSS/@-moz-document
|
|
*/
|
|
CSSOM.CSSDocumentRule = function CSSDocumentRule() {
|
|
CSSOM.CSSRule.call(this);
|
|
this.matcher = new CSSOM.MatcherList();
|
|
this.cssRules = [];
|
|
};
|
|
|
|
CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule();
|
|
CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule;
|
|
CSSOM.CSSDocumentRule.prototype.type = 10;
|
|
//FIXME
|
|
//CSSOM.CSSDocumentRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
|
|
//CSSOM.CSSDocumentRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
|
|
Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", {
|
|
get: function() {
|
|
var cssTexts = [];
|
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
cssTexts.push(this.cssRules[i].cssText);
|
|
}
|
|
return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
|
|
*
|
|
* TODO: add if needed
|
|
*/
|
|
CSSOM.CSSValue = function CSSValue() {
|
|
};
|
|
|
|
CSSOM.CSSValue.prototype = {
|
|
constructor: CSSOM.CSSValue,
|
|
|
|
// @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
|
|
set cssText(text) {
|
|
var name = this._getConstructorName();
|
|
|
|
throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!');
|
|
},
|
|
|
|
get cssText() {
|
|
var name = this._getConstructorName();
|
|
|
|
throw new Error('getter "cssText" of "' + name + '" is not implemented!');
|
|
},
|
|
|
|
_getConstructorName: function() {
|
|
var s = this.constructor.toString(),
|
|
c = s.match(/function\s([^\(]+)/),
|
|
name = c[1];
|
|
|
|
return name;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
|
|
*
|
|
*/
|
|
CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {
|
|
this._token = token;
|
|
this._idx = idx;
|
|
};
|
|
|
|
CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue();
|
|
CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression;
|
|
|
|
/**
|
|
* parse css expression() value
|
|
*
|
|
* @return {Object}
|
|
* - error:
|
|
* or
|
|
* - idx:
|
|
* - expression:
|
|
*
|
|
* Example:
|
|
*
|
|
* .selector {
|
|
* zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
|
|
* }
|
|
*/
|
|
CSSOM.CSSValueExpression.prototype.parse = function() {
|
|
var token = this._token,
|
|
idx = this._idx;
|
|
|
|
var character = '',
|
|
expression = '',
|
|
error = '',
|
|
info,
|
|
paren = [];
|
|
|
|
|
|
for (; ; ++idx) {
|
|
character = token.charAt(idx);
|
|
|
|
// end of token
|
|
if (character === '') {
|
|
error = 'css expression error: unfinished expression!';
|
|
break;
|
|
}
|
|
|
|
switch(character) {
|
|
case '(':
|
|
paren.push(character);
|
|
expression += character;
|
|
break;
|
|
|
|
case ')':
|
|
paren.pop(character);
|
|
expression += character;
|
|
break;
|
|
|
|
case '/':
|
|
if ((info = this._parseJSComment(token, idx))) { // comment?
|
|
if (info.error) {
|
|
error = 'css expression error: unfinished comment in expression!';
|
|
} else {
|
|
idx = info.idx;
|
|
// ignore the comment
|
|
}
|
|
} else if ((info = this._parseJSRexExp(token, idx))) { // regexp
|
|
idx = info.idx;
|
|
expression += info.text;
|
|
} else { // other
|
|
expression += character;
|
|
}
|
|
break;
|
|
|
|
case "'":
|
|
case '"':
|
|
info = this._parseJSString(token, idx, character);
|
|
if (info) { // string
|
|
idx = info.idx;
|
|
expression += info.text;
|
|
} else {
|
|
expression += character;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
expression += character;
|
|
break;
|
|
}
|
|
|
|
if (error) {
|
|
break;
|
|
}
|
|
|
|
// end of expression
|
|
if (paren.length === 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
var ret;
|
|
if (error) {
|
|
ret = {
|
|
error: error
|
|
};
|
|
} else {
|
|
ret = {
|
|
idx: idx,
|
|
expression: expression
|
|
};
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* @return {Object|false}
|
|
* - idx:
|
|
* - text:
|
|
* or
|
|
* - error:
|
|
* or
|
|
* false
|
|
*
|
|
*/
|
|
CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) {
|
|
var nextChar = token.charAt(idx + 1),
|
|
text;
|
|
|
|
if (nextChar === '/' || nextChar === '*') {
|
|
var startIdx = idx,
|
|
endIdx,
|
|
commentEndChar;
|
|
|
|
if (nextChar === '/') { // line comment
|
|
commentEndChar = '\n';
|
|
} else if (nextChar === '*') { // block comment
|
|
commentEndChar = '*/';
|
|
}
|
|
|
|
endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1);
|
|
if (endIdx !== -1) {
|
|
endIdx = endIdx + commentEndChar.length - 1;
|
|
text = token.substring(idx, endIdx + 1);
|
|
return {
|
|
idx: endIdx,
|
|
text: text
|
|
};
|
|
} else {
|
|
var error = 'css expression error: unfinished comment in expression!';
|
|
return {
|
|
error: error
|
|
};
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* @return {Object|false}
|
|
* - idx:
|
|
* - text:
|
|
* or
|
|
* false
|
|
*
|
|
*/
|
|
CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) {
|
|
var endIdx = this._findMatchedIdx(token, idx, sep),
|
|
text;
|
|
|
|
if (endIdx === -1) {
|
|
return false;
|
|
} else {
|
|
text = token.substring(idx, endIdx + sep.length);
|
|
|
|
return {
|
|
idx: endIdx,
|
|
text: text
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* parse regexp in css expression
|
|
*
|
|
* @return {Object|false}
|
|
* - idx:
|
|
* - regExp:
|
|
* or
|
|
* false
|
|
*/
|
|
|
|
/*
|
|
|
|
all legal RegExp
|
|
|
|
/a/
|
|
(/a/)
|
|
[/a/]
|
|
[12, /a/]
|
|
|
|
!/a/
|
|
|
|
+/a/
|
|
-/a/
|
|
* /a/
|
|
/ /a/
|
|
%/a/
|
|
|
|
===/a/
|
|
!==/a/
|
|
==/a/
|
|
!=/a/
|
|
>/a/
|
|
>=/a/
|
|
</a/
|
|
<=/a/
|
|
|
|
&/a/
|
|
|/a/
|
|
^/a/
|
|
~/a/
|
|
<</a/
|
|
>>/a/
|
|
>>>/a/
|
|
|
|
&&/a/
|
|
||/a/
|
|
?/a/
|
|
=/a/
|
|
,/a/
|
|
|
|
delete /a/
|
|
in /a/
|
|
instanceof /a/
|
|
new /a/
|
|
typeof /a/
|
|
void /a/
|
|
|
|
*/
|
|
CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {
|
|
var before = token.substring(0, idx).replace(/\s+$/, ""),
|
|
legalRegx = [
|
|
/^$/,
|
|
/\($/,
|
|
/\[$/,
|
|
/\!$/,
|
|
/\+$/,
|
|
/\-$/,
|
|
/\*$/,
|
|
/\/\s+/,
|
|
/\%$/,
|
|
/\=$/,
|
|
/\>$/,
|
|
/<$/,
|
|
/\&$/,
|
|
/\|$/,
|
|
/\^$/,
|
|
/\~$/,
|
|
/\?$/,
|
|
/\,$/,
|
|
/delete$/,
|
|
/in$/,
|
|
/instanceof$/,
|
|
/new$/,
|
|
/typeof$/,
|
|
/void$/
|
|
];
|
|
|
|
var isLegal = legalRegx.some(function(reg) {
|
|
return reg.test(before);
|
|
});
|
|
|
|
if (!isLegal) {
|
|
return false;
|
|
} else {
|
|
var sep = '/';
|
|
|
|
// same logic as string
|
|
return this._parseJSString(token, idx, sep);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* find next sep(same line) index in `token`
|
|
*
|
|
* @return {Number}
|
|
*
|
|
*/
|
|
CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
|
|
var startIdx = idx,
|
|
endIdx;
|
|
|
|
var NOT_FOUND = -1;
|
|
|
|
while(true) {
|
|
endIdx = token.indexOf(sep, startIdx + 1);
|
|
|
|
if (endIdx === -1) { // not found
|
|
endIdx = NOT_FOUND;
|
|
break;
|
|
} else {
|
|
var text = token.substring(idx + 1, endIdx),
|
|
matched = text.match(/\\+$/);
|
|
if (!matched || matched[0] % 2 === 0) { // not escaped
|
|
break;
|
|
} else {
|
|
startIdx = endIdx;
|
|
}
|
|
}
|
|
}
|
|
|
|
// boundary must be in the same line(js sting or regexp)
|
|
var nextNewLineIdx = token.indexOf('\n', idx + 1);
|
|
if (nextNewLineIdx < endIdx) {
|
|
endIdx = NOT_FOUND;
|
|
}
|
|
|
|
|
|
return endIdx;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @param {string} token
|
|
*/
|
|
CSSOM.parse = function parse(token) {
|
|
|
|
var i = 0;
|
|
|
|
/**
|
|
"before-selector" or
|
|
"selector" or
|
|
"atRule" or
|
|
"atBlock" or
|
|
"conditionBlock" or
|
|
"before-name" or
|
|
"name" or
|
|
"before-value" or
|
|
"value"
|
|
*/
|
|
var state = "before-selector";
|
|
|
|
var index;
|
|
var buffer = "";
|
|
var valueParenthesisDepth = 0;
|
|
|
|
var SIGNIFICANT_WHITESPACE = {
|
|
"selector": true,
|
|
"value": true,
|
|
"value-parenthesis": true,
|
|
"atRule": true,
|
|
"importRule-begin": true,
|
|
"importRule": true,
|
|
"atBlock": true,
|
|
"containerBlock": true,
|
|
"conditionBlock": true,
|
|
'documentRule-begin': true
|
|
};
|
|
|
|
var styleSheet = new CSSOM.CSSStyleSheet();
|
|
|
|
// @type CSSStyleSheet|CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
|
|
var currentScope = styleSheet;
|
|
|
|
// @type CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
|
|
var parentRule;
|
|
|
|
var ancestorRules = [];
|
|
var hasAncestors = false;
|
|
var prevScope;
|
|
|
|
var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule;
|
|
|
|
var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;
|
|
|
|
var parseError = function(message) {
|
|
var lines = token.substring(0, i).split('\n');
|
|
var lineCount = lines.length;
|
|
var charCount = lines.pop().length + 1;
|
|
var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
|
|
error.line = lineCount;
|
|
/* jshint sub : true */
|
|
error['char'] = charCount;
|
|
error.styleSheet = styleSheet;
|
|
throw error;
|
|
};
|
|
|
|
for (var character; (character = token.charAt(i)); i++) {
|
|
|
|
switch (character) {
|
|
|
|
case " ":
|
|
case "\t":
|
|
case "\r":
|
|
case "\n":
|
|
case "\f":
|
|
if (SIGNIFICANT_WHITESPACE[state]) {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
// String
|
|
case '"':
|
|
index = i + 1;
|
|
do {
|
|
index = token.indexOf('"', index) + 1;
|
|
if (!index) {
|
|
parseError('Unmatched "');
|
|
}
|
|
} while (token[index - 2] === '\\');
|
|
buffer += token.slice(i, index);
|
|
i = index - 1;
|
|
switch (state) {
|
|
case 'before-value':
|
|
state = 'value';
|
|
break;
|
|
case 'importRule-begin':
|
|
state = 'importRule';
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case "'":
|
|
index = i + 1;
|
|
do {
|
|
index = token.indexOf("'", index) + 1;
|
|
if (!index) {
|
|
parseError("Unmatched '");
|
|
}
|
|
} while (token[index - 2] === '\\');
|
|
buffer += token.slice(i, index);
|
|
i = index - 1;
|
|
switch (state) {
|
|
case 'before-value':
|
|
state = 'value';
|
|
break;
|
|
case 'importRule-begin':
|
|
state = 'importRule';
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// Comment
|
|
case "/":
|
|
if (token.charAt(i + 1) === "*") {
|
|
i += 2;
|
|
index = token.indexOf("*/", i);
|
|
if (index === -1) {
|
|
parseError("Missing */");
|
|
} else {
|
|
i = index + 1;
|
|
}
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
if (state === "importRule-begin") {
|
|
buffer += " ";
|
|
state = "importRule";
|
|
}
|
|
break;
|
|
|
|
// At-rule
|
|
case "@":
|
|
if (token.indexOf("@-moz-document", i) === i) {
|
|
state = "documentRule-begin";
|
|
documentRule = new CSSOM.CSSDocumentRule();
|
|
documentRule.__starts = i;
|
|
i += "-moz-document".length;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@media", i) === i) {
|
|
state = "atBlock";
|
|
mediaRule = new CSSOM.CSSMediaRule();
|
|
mediaRule.__starts = i;
|
|
i += "media".length;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@container", i) === i) {
|
|
state = "containerBlock";
|
|
containerRule = new CSSOM.CSSContainerRule();
|
|
containerRule.__starts = i;
|
|
i += "container".length;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@supports", i) === i) {
|
|
state = "conditionBlock";
|
|
supportsRule = new CSSOM.CSSSupportsRule();
|
|
supportsRule.__starts = i;
|
|
i += "supports".length;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@host", i) === i) {
|
|
state = "hostRule-begin";
|
|
i += "host".length;
|
|
hostRule = new CSSOM.CSSHostRule();
|
|
hostRule.__starts = i;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@starting-style", i) === i) {
|
|
state = "startingStyleRule-begin";
|
|
i += "starting-style".length;
|
|
startingStyleRule = new CSSOM.CSSStartingStyleRule();
|
|
startingStyleRule.__starts = i;
|
|
buffer = "";
|
|
break;
|
|
} else if (token.indexOf("@import", i) === i) {
|
|
state = "importRule-begin";
|
|
i += "import".length;
|
|
buffer += "@import";
|
|
break;
|
|
} else if (token.indexOf("@font-face", i) === i) {
|
|
state = "fontFaceRule-begin";
|
|
i += "font-face".length;
|
|
fontFaceRule = new CSSOM.CSSFontFaceRule();
|
|
fontFaceRule.__starts = i;
|
|
buffer = "";
|
|
break;
|
|
} else {
|
|
atKeyframesRegExp.lastIndex = i;
|
|
var matchKeyframes = atKeyframesRegExp.exec(token);
|
|
if (matchKeyframes && matchKeyframes.index === i) {
|
|
state = "keyframesRule-begin";
|
|
keyframesRule = new CSSOM.CSSKeyframesRule();
|
|
keyframesRule.__starts = i;
|
|
keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
|
|
i += matchKeyframes[0].length - 1;
|
|
buffer = "";
|
|
break;
|
|
} else if (state === "selector") {
|
|
state = "atRule";
|
|
}
|
|
}
|
|
buffer += character;
|
|
break;
|
|
|
|
case "{":
|
|
if (state === "selector" || state === "atRule") {
|
|
styleRule.selectorText = buffer.trim();
|
|
styleRule.style.__starts = i;
|
|
buffer = "";
|
|
state = "before-name";
|
|
} else if (state === "atBlock") {
|
|
mediaRule.media.mediaText = buffer.trim();
|
|
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
}
|
|
|
|
currentScope = parentRule = mediaRule;
|
|
mediaRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
} else if (state === "containerBlock") {
|
|
containerRule.containerText = buffer.trim();
|
|
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
}
|
|
currentScope = parentRule = containerRule;
|
|
containerRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
} else if (state === "conditionBlock") {
|
|
supportsRule.conditionText = buffer.trim();
|
|
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
}
|
|
|
|
currentScope = parentRule = supportsRule;
|
|
supportsRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
} else if (state === "hostRule-begin") {
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
}
|
|
|
|
currentScope = parentRule = hostRule;
|
|
hostRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
} else if (state === "startingStyleRule-begin") {
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
}
|
|
|
|
currentScope = parentRule = startingStyleRule;
|
|
startingStyleRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
|
|
} else if (state === "fontFaceRule-begin") {
|
|
if (parentRule) {
|
|
fontFaceRule.parentRule = parentRule;
|
|
}
|
|
fontFaceRule.parentStyleSheet = styleSheet;
|
|
styleRule = fontFaceRule;
|
|
buffer = "";
|
|
state = "before-name";
|
|
} else if (state === "keyframesRule-begin") {
|
|
keyframesRule.name = buffer.trim();
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
keyframesRule.parentRule = parentRule;
|
|
}
|
|
keyframesRule.parentStyleSheet = styleSheet;
|
|
currentScope = parentRule = keyframesRule;
|
|
buffer = "";
|
|
state = "keyframeRule-begin";
|
|
} else if (state === "keyframeRule-begin") {
|
|
styleRule = new CSSOM.CSSKeyframeRule();
|
|
styleRule.keyText = buffer.trim();
|
|
styleRule.__starts = i;
|
|
buffer = "";
|
|
state = "before-name";
|
|
} else if (state === "documentRule-begin") {
|
|
// FIXME: what if this '{' is in the url text of the match function?
|
|
documentRule.matcher.matcherText = buffer.trim();
|
|
if (parentRule) {
|
|
ancestorRules.push(parentRule);
|
|
documentRule.parentRule = parentRule;
|
|
}
|
|
currentScope = parentRule = documentRule;
|
|
documentRule.parentStyleSheet = styleSheet;
|
|
buffer = "";
|
|
state = "before-selector";
|
|
}
|
|
break;
|
|
|
|
case ":":
|
|
if (state === "name") {
|
|
name = buffer.trim();
|
|
buffer = "";
|
|
state = "before-value";
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case "(":
|
|
if (state === 'value') {
|
|
// ie css expression mode
|
|
if (buffer.trim() === 'expression') {
|
|
var info = (new CSSOM.CSSValueExpression(token, i)).parse();
|
|
|
|
if (info.error) {
|
|
parseError(info.error);
|
|
} else {
|
|
buffer += info.expression;
|
|
i = info.idx;
|
|
}
|
|
} else {
|
|
state = 'value-parenthesis';
|
|
//always ensure this is reset to 1 on transition
|
|
//from value to value-parenthesis
|
|
valueParenthesisDepth = 1;
|
|
buffer += character;
|
|
}
|
|
} else if (state === 'value-parenthesis') {
|
|
valueParenthesisDepth++;
|
|
buffer += character;
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case ")":
|
|
if (state === 'value-parenthesis') {
|
|
valueParenthesisDepth--;
|
|
if (valueParenthesisDepth === 0) state = 'value';
|
|
}
|
|
buffer += character;
|
|
break;
|
|
|
|
case "!":
|
|
if (state === "value" && token.indexOf("!important", i) === i) {
|
|
priority = "important";
|
|
i += "important".length;
|
|
} else {
|
|
buffer += character;
|
|
}
|
|
break;
|
|
|
|
case ";":
|
|
switch (state) {
|
|
case "value":
|
|
styleRule.style.setProperty(name, buffer.trim(), priority);
|
|
priority = "";
|
|
buffer = "";
|
|
state = "before-name";
|
|
break;
|
|
case "atRule":
|
|
buffer = "";
|
|
state = "before-selector";
|
|
break;
|
|
case "importRule":
|
|
importRule = new CSSOM.CSSImportRule();
|
|
importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
|
|
importRule.cssText = buffer + character;
|
|
styleSheet.cssRules.push(importRule);
|
|
buffer = "";
|
|
state = "before-selector";
|
|
break;
|
|
default:
|
|
buffer += character;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case "}":
|
|
switch (state) {
|
|
case "value":
|
|
styleRule.style.setProperty(name, buffer.trim(), priority);
|
|
priority = "";
|
|
/* falls through */
|
|
case "before-name":
|
|
case "name":
|
|
styleRule.__ends = i + 1;
|
|
if (parentRule) {
|
|
styleRule.parentRule = parentRule;
|
|
}
|
|
styleRule.parentStyleSheet = styleSheet;
|
|
currentScope.cssRules.push(styleRule);
|
|
buffer = "";
|
|
if (currentScope.constructor === CSSOM.CSSKeyframesRule) {
|
|
state = "keyframeRule-begin";
|
|
} else {
|
|
state = "before-selector";
|
|
}
|
|
break;
|
|
case "keyframeRule-begin":
|
|
case "before-selector":
|
|
case "selector":
|
|
// End of media/supports/document rule.
|
|
if (!parentRule) {
|
|
parseError("Unexpected }");
|
|
}
|
|
|
|
// Handle rules nested in @media or @supports
|
|
hasAncestors = ancestorRules.length > 0;
|
|
|
|
while (ancestorRules.length > 0) {
|
|
parentRule = ancestorRules.pop();
|
|
|
|
if (
|
|
parentRule.constructor.name === "CSSMediaRule"
|
|
|| parentRule.constructor.name === "CSSSupportsRule"
|
|
|| parentRule.constructor.name === "CSSContainerRule"
|
|
|| parentRule.constructor.name === "CSSStartingStyleRule"
|
|
) {
|
|
prevScope = currentScope;
|
|
currentScope = parentRule;
|
|
currentScope.cssRules.push(prevScope);
|
|
break;
|
|
}
|
|
|
|
if (ancestorRules.length === 0) {
|
|
hasAncestors = false;
|
|
}
|
|
}
|
|
|
|
if (!hasAncestors) {
|
|
currentScope.__ends = i + 1;
|
|
styleSheet.cssRules.push(currentScope);
|
|
currentScope = styleSheet;
|
|
parentRule = null;
|
|
}
|
|
|
|
buffer = "";
|
|
state = "before-selector";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
switch (state) {
|
|
case "before-selector":
|
|
state = "selector";
|
|
styleRule = new CSSOM.CSSStyleRule();
|
|
styleRule.__starts = i;
|
|
break;
|
|
case "before-name":
|
|
state = "name";
|
|
break;
|
|
case "before-value":
|
|
state = "value";
|
|
break;
|
|
case "importRule-begin":
|
|
state = "importRule";
|
|
break;
|
|
}
|
|
buffer += character;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return styleSheet;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Produces a deep copy of stylesheet — the instance variables of stylesheet are copied recursively.
|
|
* @param {CSSStyleSheet|CSSOM.CSSStyleSheet} stylesheet
|
|
* @nosideeffects
|
|
* @return {CSSOM.CSSStyleSheet}
|
|
*/
|
|
CSSOM.clone = function clone(stylesheet) {
|
|
|
|
var cloned = new CSSOM.CSSStyleSheet();
|
|
|
|
var rules = stylesheet.cssRules;
|
|
if (!rules) {
|
|
return cloned;
|
|
}
|
|
|
|
for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) {
|
|
var rule = rules[i];
|
|
var ruleClone = cloned.cssRules[i] = new rule.constructor();
|
|
|
|
var style = rule.style;
|
|
if (style) {
|
|
var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration();
|
|
for (var j = 0, styleLength = style.length; j < styleLength; j++) {
|
|
var name = styleClone[j] = style[j];
|
|
styleClone[name] = style[name];
|
|
styleClone._importants[name] = style.getPropertyPriority(name);
|
|
}
|
|
styleClone.length = style.length;
|
|
}
|
|
|
|
if (rule.hasOwnProperty('keyText')) {
|
|
ruleClone.keyText = rule.keyText;
|
|
}
|
|
|
|
if (rule.hasOwnProperty('selectorText')) {
|
|
ruleClone.selectorText = rule.selectorText;
|
|
}
|
|
|
|
if (rule.hasOwnProperty('mediaText')) {
|
|
ruleClone.mediaText = rule.mediaText;
|
|
}
|
|
|
|
if (rule.hasOwnProperty('conditionText')) {
|
|
ruleClone.conditionText = rule.conditionText;
|
|
}
|
|
|
|
if (rule.hasOwnProperty('cssRules')) {
|
|
ruleClone.cssRules = clone(rule).cssRules;
|
|
}
|
|
}
|
|
|
|
return cloned;
|
|
|
|
};
|
|
|
|
|