cssom
Version:
CSS Object Model implementation and CSS parser
519 lines (475 loc) • 11 kB
HTML
<html>
<head>
<meta charset="utf-8">
<title>CSSOM.js parse method</title>
<script>
var exports = {};
function require(){
return exports;
}
</script>
<script src="../lib/CSSStyleDeclaration.js"></script>
<script src="../lib/CSSRule.js"></script>
<script src="../lib/CSSStyleRule.js"></script>
<script src="../lib/CSSImportRule.js"></script>
<script src="../lib/MediaList.js"></script>
<script src="../lib/CSSMediaRule.js"></script>
<script src="../lib/StyleSheet.js"></script>
<script src="../lib/CSSStyleSheet.js"></script>
<script src="../lib/parse.js"></script>
<script>
window.CSSOM = exports;
</script>
<style>
.corner{
background: none;
border-radius:8px;
padding: 0 3px 3px;
border:none
}
</style>
<style>
/*@import url(foo.css);*/
/*@media all {*/
html {
background-position: 0;
border: red;
}
/*}*/
</style>
<style type="text/css">
html, body {
background: #333;
color: #EEE;
font: 12px sans-serif;
margin: 0;
height: 100%;
}
body {
padding-bottom: 1.7em;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
table {
width: 100%;
table-layout: fixed;
margin: 0 auto;
}
td {
vertical-align: top;
}
h1 {
font: normal 1em sans-serif;
display: inline;
}
#labels {
color: #FFE992;
width: 66%;
}
#labels td {
width: 50%;
text-align: center;
}
#labels td::before {
content: '↱ ';
color: #998e62;
position: relative;
top: .4em;
}
#labels td::after {
content: ' ↴';
color: #998e62;
position: relative;
top: .4em;
}
#content {
width: 100%;
height: 100%;
}
#content td {
width: 33%;
}
#content td + td {
padding-left: 1%;
}
.style-cell textarea {
width: 99%;
height: 100%;
font: 12px monospace;
white-space: pre-wrap;
}
.serialized-cell {
border-left: 1px solid #363636;
}
#message {
visibility: hidden;
}
.error #message {
visibility: visible;
position: absolute;
top: 0;
left: 34%;
padding: 1em;
background: black;
color: #e34343;
font-size: 24px;
}
</style>
</head>
<body>
<table id="labels">
<tr><td><h1>CSSOM.parse</h1></td><td>.toString</td></tr>
</table>
<table id="content">
<tr>
<td class="style-cell">
<textarea id="style" spellcheck="false" rows="40">img {
border: none
}</textarea></td>
<td class="output-cell"><pre id="output"></pre></td>
<td class="serialized-cell"><pre id="serialized"></pre></td>
</tr>
</table>
<div id="message"></div>
<script defer>
//console.log(document.styleSheets[0].cssRules[0].style["table-layout"]);
var CSS = {
background: [
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"background-origin",
"background-clip",
"background-color"
],
"background-repeat": [
"background-repeat-x",
"background-repeat-y"
],
"background-position": [
"background-position-x",
"background-position-y"
],
border: [
"border-top",
"border-right",
"border-bottom",
"border-left",
"border-width",
"border-style",
"border-color"
],
"border-top": [
"border-top-width",
"border-top-style",
"border-top-color"
],
"border-right": [
"border-right-width",
"border-right-style",
"border-right-color"
],
"border-bottom": [
"border-bottom-width",
"border-bottom-style",
"border-bottom-color"
],
"border-left": [
"border-left-width",
"border-left-style",
"border-left-color"
],
"border-style": [
"border-top-style",
"border-right-style",
"border-bottom-style",
"border-left-style"
],
"border-width": [
"border-top-width",
"border-right-width",
"border-bottom-width",
"border-left-width"
],
"border-color": [
"border-top-color",
"border-right-color",
"border-bottom-color",
"border-left-color"
],
"border-radius": [
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius"
],
font: [
"font-style",
"font-variant",
"font-weight",
"font-size",
"font-size-adjust",
"font-stretch",
"line-height",
"font-family"
],
"list-style": [
"list-style-type",
"list-style-position",
"list-style-image"
],
margin: [
"margin-top",
"margin-right",
"margin-bottom",
"margin-left"
],
padding: [
"padding-top",
"padding-right",
"padding-bottom",
"padding-left"
]
};
function camelize(string) {
return string.replace(/-+(.)?/g, function(match, chr) {
return chr ? chr.toUpperCase() : '';
});
}
CSS["background-repeat"].shorthand = function(style) {
if (style["background-repeat-x"] == style["background-repeat-y"]) {
return style["background-repeat-x"];
} else {
return style["background-repeat-x"] + " " + style["background-repeat-y"];
}
};
CSS["background-position"].shorthand = function(style) {
if (style["background-position-x"] == style["background-position-y"]) {
return style["background-position-x"];
} else {
return style["background-position-x"] + " " + style["background-position-y"];
}
};
CSS["border-radius"].shorthand = function(style) {
var radius = style["border-top-left-radius"];
if (style["border-top-right-radius"] == radius && style["border-bottom-right-radius"] == radius && style["border-bottom-left-radius"] == radius) {
var couple = radius.split(' ');
if (couple[0] == couple[1]) {
return couple[0];
} else {
return radius;
}
}
};
/*
CSS.background.shorthand = function(style) {
var values = [];
//FIXME
// Support CSS3 background shorthand
// http://www.w3.org/TR/css3-background/#the-background
var backgroundColor = style["background-color"];
if (backgroundColor != "initial" && backgroundColor != "transparent") {
values.push(backgroundColor);
}
var backgroundImage = style["background-image"];
if (backgroundImage != "initial" && backgroundImage != "none") {
values.push(backgroundImage);
}
var backgroundRepeat = style["background-repeat"];
if (backgroundRepeat != "initial" && backgroundRepeat != "repeat") {
values.push(backgroundRepeat);
}
var backgroundAttachment = style["background-attachment"];
if (backgroundAttachment != "initial" && backgroundAttachment != "scroll") {
values.push(backgroundAttachment);
}
var backgroundPosition = style["background-position"];
if (backgroundPosition != "initial" && parseInt(backgroundPosition) !== 0) {
values.push(backgroundPosition);
}
return values.join(" ") || "";
};
*/
(function(){
for (var key in CSS) {
var longhand = CSS[key];
if (!longhand.length) {
continue;
}
for (var i=0, ii=longhand.length; i<ii; i++) {
var siblings = longhand.slice(0);
siblings.splice(i, 1);
if (CSS[longhand[i]]) {
//console.log("array", CSS[longhand[i]]);
} else {
CSS[longhand[i]] = {}
}
CSS[longhand[i]].parent = key;
CSS[longhand[i]].siblings = siblings;
}
}
})();
function cloneMinimized(stylesheet) {
var cloned = new CSSOM.CSSStyleSheet;
var rules = stylesheet.cssRules;
if (!rules) {
return cloned;
}
var RULE_TYPES = {
1: CSSOM.CSSStyleRule,
4: CSSOM.CSSMediaRule
//FIXME
//3: CSSOM.CSSImportRule,
//5: CSSOM.CSSFontFaceRule,
//6: CSSOM.CSSPageRule,
};
for (var i=0, rulesLength=rules.length; i < rulesLength; i++) {
var rule = rules[i];
var ruleClone = cloned.cssRules[i] = new RULE_TYPES[rule.type];
var style = rule.style;
if (style) {
var styleClone = ruleClone.style = minimizeStyle(style);
}
if ("selectorText" in rule) {
ruleClone.selectorText = rule.selectorText;
}
if ("mediaText" in rule) {
ruleClone.mediaText = rule.mediaText;
}
if ("cssRules" in rule) {
rule.cssRules = clone(rule).cssRules;
}
}
return cloned;
}
function minimizeStyle(style) {
var result = new CSSOM.CSSStyleDeclaration;
for (var i=0; i<style.length; i++) {
var name = closestShorthand(style, style[i]);
if (!(name in result)) {
var value;
if (CSS[name].shorthand) {
value = CSS[name].shorthand(style);
} else {
value = style[camelize(name)];
}
if (value == 'initial') {
//console.warn(name +':'+ value);
} else {
result[name] = value;
result[result.length++] = name;
}
}
}
return result;
}
CSSStyleSheet.prototype.stringify = function() {
var result = '';
var rules = this.cssRules;
for (var i = 0; i < rules.length; i++) {
var rule = rules[i];
switch (rule.type) {
case CSSRule.STYLE_RULE:
case CSSRule.MEDIA_RULE:
result += rule.stringify();
break;
case CSSRule.IMPORT_RULE:
result += rule.cssText;
break;
}
}
return result;
};
onload = function(){
console.log(document.styleSheets[0].stringify());
};
CSSStyleRule.prototype.stringify = function() {
return this.selectorText + ' {' + minimizeStyle(this.style).cssText +'}\n';
};
function closestShorthand(style, name) {
if (!(name in CSS)) {
return name;
}
var siblings = CSS[name].siblings;
if (!siblings || !siblings.length) {
return name;
}
var properties = {};
var allPresent = true;
for (var j=0; j<siblings.length; j++) {
var sibling = siblings[j];
var aSibling = camelize(sibling);
if (style[aSibling] === "") {
// console.warn(sibling);
if (CSS[sibling].length) {
for (var i=0; i<CSS[sibling].length; i++) {
if (style[camelize(CSS[sibling][i])] === "") {
allPresent = false;
break;
}
}
} else {
allPresent = false;
break;
}
}
}
if (allPresent) {
return closestShorthand(style, CSS[name].parent);
} else {
return name;
}
}
//console.log(cloneMinimized(document.styleSheets[0]).toString());
//console.log(CSSOM.CSSStyleSheet.prototype.toString.call(minimize(document.styleSheets[0].cssRules[0].style)));
//console.log(document.styleSheets[0].cssRules[0].style["background-image"]);
//console.log(CSS);
var errors = [];
//if (!Object.defineProperty) {
// errors.push("Object.defineProperty isn’t supported");
//}
if (errors.length) {
document.getElementById("message").innerHTML = errors.join("<br>");
document.body.className = "error";
throw errors.join("\n\n");
}
var style = document.getElementById("style");
var output = document.getElementById("output");
var serialized = document.getElementById("serialized");
function outputUpdated() {
var value = style.value;
if (value != style.prevValue) {
var css = CSSOM.parse(value);
output.innerHTML = JSON.stringify(css, null, 2);
serialized.innerHTML = cloneMinimized(css).toString();
style.prevValue = value;
}
}
function hashChanged() {
var hash = location.hash;
var splitted = hash.split("=");
if (splitted.length < 2) {
return;
}
var name = splitted[0];
var value = splitted[1];
if (name == "#css") {
style.value = decodeURIComponent(value);
outputUpdated();
}
}
hashChanged();
outputUpdated();
window.onhashchange = hashChanged;
style.onkeyup = style.onpaste = function changed(){
outputUpdated();
};
style.onchange = function updateLocation(){
location.hash = "css=" + encodeURIComponent(style.value);
};
</script>
</body>
</html>