var css = new _CSS("a {color: red;} pre {margin: 0;}", "<html><head></head><body><a>link</a></body></html>");
//var css = new _CSS(css[, html]);
//html: HTML-source; String. (default: document.body.parentElement.outerHTML)
console.log(css.json());
//{
// IMPORT: [],
// KEYFRAME: {},
// MEDIA: {},
// STYLE: {
// a: {color: "red"},
// pre: {margin: "0"}
// }
//}console.log(css.json({
filter: true
}));
//{
// IMPORT: [],
// KEYFRAME: {},
// MEDIA: {},
// STYLE: {
// a: {color: "red"}
// }
//}
console.log(css.string());
//a {
// color: red;
//}
//pre {
// margin: 0;
//}console.log(css.string({
filter: true,
specificity: true,
space: 8
}));
///* 0.0.1 */
//a {
// color: red;
//}console.log(css.string({
min: true
}));
//a {color: red;}pre {margin: 0;}
arguments for css.string() and css.json()
filter: If it's true, delete unnecessary selectors for the HTML source (the second argument of new _CSS()).
arguments only for css.string()
specificity: If it's true, display CSS-specificity (link).
space: The number of space characters to use as white space.
min: If it's true, the outputted style will be minified.
/* CSS Parser for client-side javascript created by 7happy7 http://www.wikidot.com/user:info/7happy7 CC BY-SA 3.0 */ !function(w) { var pse = ["active","any-link","blank","current","default","defined","dir","drop","empty","enabled","first","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","left","link","local-link","not","nth-col","nth-last-col","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","state","target","target-within","user-invalid","valid","visited","where","after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error","-webkit-.+?","-moz-.+?","-ms-.+?"]; var REG = { PSEUDO_S: new RegExp('(\\:{1,2}(?:' + pse.join('|') + ')(?:$| |\\(.*?\\)))', "g"), COMMENT_S: new RegExp('\\/\\*[\\s\\S]*?\\*\\/','g'), SELECTOR_S: new RegExp('([\\s\\S]*?){([\\s\\S]*?)}','g'), SELECTOR: new RegExp('([\\s\\S]*?){([\\s\\S]*?)}'), MEDIA_S: new RegExp('((@media [\\s\\S]*?){([\\s\\S]*?}\\s*?)})','g'), MEDIA: new RegExp('((@media [\\s\\S]*?){([\\s\\S]*?}\\s*?)})'), KEYFRAME_S: new RegExp('((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})','g'), KEYFRAME: new RegExp('((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})'), IMPORT_S: new RegExp('@import (?:url\\(.*?\\)|).*?;','g'), CHARSET_S: new RegExp('@charset .*?;','g') }; var styleFormator = function(style, target, html) { var selector = style.match(REG.SELECTOR_S); var res = target || {}; if (selector) { for (var s of selector) { var base = s.match(REG.SELECTOR); var sel = base[1].trim(); var raw = sel.replace(REG.PSEUDO_S, "").trim(); res[sel] = res[sel] || {}; res[sel]._status = { invalid: (function() { try { return html.querySelector(raw) ? false : true; }catch (e) { return null; } })() }; var variables = base[2].split(";"); for (var v of variables) { var key = v.split(":")[0].trim(); var value = v.split(":").slice(1).join(":").trim(); if (key !== "" && value !== "") { res[sel][key] = value; } } } } return res; } var CSSParserObject = function(css,html) { this.RAW = css || ""; this.HTML = html ? (function(){ var p = new DOMParser(); try { return p.parseFromString(html, "text/html") }catch (e) { throw new Error("invalid HTML"); } })() : w.document; var STYLES = { STYLE: {}, MEDIA: {}, KEYFRAME: {}, IMPORT: [], CHARSET: null }; /* parse */ var a = this.RAW.replace(REG.COMMENT_S, ""); var _charset = a.match(REG.CHARSET_S); if (_charset) { STYLES.CHARSET = _charset[_charset.length-1]; } a = a.replace(REG.CHARSET_S, ""); var _import = a.match(REG.IMPORT_S); if (_import) { STYLES.IMPORT = STYLES.IMPORT.concat(_import) } a = a.replace(REG.IMPORT_S, ""); var mediaStyle = a.match(REG.MEDIA_S); if (mediaStyle) { for (var s of mediaStyle) { var base = s.match(REG.MEDIA); var sel = base[2].trim(); STYLES.MEDIA[sel] = STYLES.MEDIA[sel] || {}; styleFormator(base[3], STYLES.MEDIA[sel], this.HTML); } } a = a.replace(REG.MEDIA_S, ""); var keyStyle = a.match(REG.KEYFRAME_S); if (keyStyle) { for (var s of keyStyle) { var base = s.match(REG.KEYFRAME); var sel = base[2].trim(); STYLES.KEYFRAME[sel] = STYLES.KEYFRAME[sel] || {}; styleFormator(base[3], STYLES.KEYFRAME[sel], this.HTML); } } a = a.replace(REG.KEYFRAME_S, ""); styleFormator(a, STYLES.STYLE, this.HTML); this.STYLE = STYLES; return this; } var _j = function(_o, filter) { var o = Object.assign({}, _o); for(var k of Object.keys(o).filter(function(r) {return r!=="CHARSET"&&r!=="IMPORT"})) { if(k=="STYLE") { for(var _k in o[k]) { if(filter&&o[k][_k]._status&&o[k][_k]._status.invalid) { delete o[k][_k]; }else { delete o[k][_k]._status; } } }else { for(var _k in o[k]) { for(var __k in o[k][_k]) { if(filter&&o[k][_k][__k]._status&&o[k][_k][__k]._status.invalid) { delete o[k][_k][__k]; if(!Object.keys(o[k][_k]).length) { delete o[k][_k]; break; } }else { delete o[k][_k][__k]._status; } } } } } return o; } CSSParserObject.prototype.json = function({filter=false}={}) { return _j(this.STYLE, filter); } CSSParserObject.prototype.string = function({space=2, filter=false, specificity=false, min=false}={}) { var o = _j(this.STYLE, filter); var INDENT = function(lv) { return min ? "" : " ".repeat(space).repeat(lv); } var key_format = function(sels, ind="") { return (sels.split(",").map(function(v) {return v.trim()}).join(",\n" + ind)); } var spec_calc = function(sels, esc, ind="") { if(!specificity || esc) { return ""; } var res = []; sels = sels.split(",").map(function(r) {return r.trim();}).filter(function(r) {return r;}); function sep(key, value) {return value.split(key).length - 1;} for(var sel of sels) { var count = {a: 0, b: 0, c: 0}; sel.split(/ [\+|\>] |\*(?!\=)| /ig).filter(function(r) { return r; }).forEach(function(r) { if(sep("#", r)) count.a += sep("#", r); if(sep(".", r)) count.b += sep(".", r); if(sep(/\[.*?\]/g, r)) count.b += sep(/\[.*?\]/g, r); if(sep(/^[a-zA-Z]/, r)) count.c += sep(/^[a-zA-Z]/, r); if(sep(/\:\:|\:/g, r)) count.c += sep(/\:\:|\:/g, r); }) res.push(count.a + "." + count.b + "." + count.c); } return "/* " + res.join(", ") + " */\n" + ind; } var _s = (o.CHARSET ? o.CHARSET + "\n" : "") + (o.IMPORT.length ? o.IMPORT.join("\n") + "\n" : ""); if (Object.keys(o.STYLE).length) { for (var key in o.STYLE) { _s += spec_calc(key) + key_format(key) + " {\n"; for (var _key of Object.keys(o.STYLE[key]).sort()) { _s += INDENT(1) + _key + ": " + o.STYLE[key][_key].split(/\n/).map(function(v) {return v.trim()}).join("").split(",").map(function(v) {return v.trim()}).join(",") + ";\n" } _s += "}\n"; } } for (var que of ["KEYFRAME", "MEDIA"]) { if (Object.keys(o[que]).length) { for (var key in o[que]) { _s += key + " {\n"; for (var _key in o[que][key]) { _s += INDENT(1) + spec_calc(_key, que=="KEYFRAME", INDENT(1)) + key_format(_key, INDENT(1)) + " {\n" for (var __key of Object.keys(o[que][key][_key]).sort()) { _s += INDENT(2) + __key + ": " + o[que][key][_key][__key].split(/\n/).map(function(v) {return v.trim()}).join("").split(",").map(function(v) {return v.trim()}).join(",") + ";\n" } _s += INDENT(1) + "}\n" } _s += "}\n"; } } } return (min ? _s.replace(/\n/g, "") : _s).trim(); } w._CSS = CSSParserObject; }(window);