You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1932 lines
44 KiB
1932 lines
44 KiB
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; |
|
|
|
}; |
|
|
|
|
|
|