omi/packages/omii/css2json.html

293 lines
8.1 KiB
HTML

<script>
var CSSJSON = new function () {
var base = this;
base.init = function () {
// String functions
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
String.prototype.repeat = function (n) {
return new Array(1 + n).join(this);
};
};
base.init();
var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
var endX = /\}/g;
var lineX = /([^\;\{\}]*)\;/g;
var commentX = /\/\*[\s\S]*?\*\//g;
var lineAttrX = /([^\:]+):([^\;]*);/;
// This is used, a concatenation of all above. We use alternation to
// capture.
var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;
// Capture groups
var capComment = 1;
var capSelector = 2;
var capEnd = 3;
var capAttr = 4;
var isEmpty = function (x) {
return typeof x == 'undefined' || x.length == 0 || x == null;
};
var isCssJson = function (node) {
return !isEmpty(node) ? (node.attributes && node.children) : false;
}
/**
* Input is css string and current pos, returns JSON object
*
* @param cssString
* The CSS string.
* @param args
* An optional argument object. ordered: Whether order of
* comments and other nodes should be kept in the output. This
* will return an object where all the keys are numbers and the
* values are objects containing "name" and "value" keys for each
* node. comments: Whether to capture comments. split: Whether to
* split each comma separated list of selectors.
*/
base.toJSON = function (cssString, args) {
var node = {
children: {},
attributes: {}
};
var match = null;
var count = 0;
if (typeof args == 'undefined') {
var args = {
ordered: false,
comments: false,
stripComments: false,
split: false
};
}
if (args.stripComments) {
args.comments = false;
cssString = cssString.replace(commentX, '');
}
while ((match = altX.exec(cssString)) != null) {
if (!isEmpty(match[capComment]) && args.comments) {
// Comment
var add = match[capComment].trim();
node[count++] = add;
} else if (!isEmpty(match[capSelector])) {
// New node, we recurse
var name = match[capSelector].trim();
// This will return when we encounter a closing brace
var newNode = base.toJSON(cssString, args);
if (args.ordered) {
var obj = {};
obj['name'] = name;
obj['value'] = newNode;
// Since we must use key as index to keep order and not
// name, this will differentiate between a Rule Node and an
// Attribute, since both contain a name and value pair.
obj['type'] = 'rule';
node[count++] = obj;
} else {
if (args.split) {
var bits = name.split(',');
} else {
var bits = [name];
}
for (var i = 0; i < bits.length; i++) {
var sel = bits[i].trim();
if (sel in node.children) {
for (var att in newNode.attributes) {
node.children[sel].attributes[att] = newNode.attributes[att];
}
} else {
node.children[sel] = newNode;
}
}
}
} else if (!isEmpty(match[capEnd])) {
// Node has finished
return node;
} else if (!isEmpty(match[capAttr])) {
var line = match[capAttr].trim();
var attr = lineAttrX.exec(line);
if (attr) {
// Attribute
var name = attr[1].trim();
var value = attr[2].trim();
if (args.ordered) {
var obj = {};
obj['name'] = name;
obj['value'] = value;
obj['type'] = 'attr';
node[count++] = obj;
} else {
if (name in node.attributes) {
var currVal = node.attributes[name];
if (!(currVal instanceof Array)) {
node.attributes[name] = [currVal];
}
node.attributes[name].push(value);
} else {
node.attributes[name] = value;
}
}
} else {
// Semicolon terminated line
node[count++] = line;
}
}
}
return node;
};
/**
* @param node
* A JSON node.
* @param depth
* The depth of the current node; used for indentation and
* optional.
* @param breaks
* Whether to add line breaks in the output.
*/
base.toCSS = function (node, depth, breaks) {
var cssString = '';
if (typeof depth == 'undefined') {
depth = 0;
}
if (typeof breaks == 'undefined') {
breaks = false;
}
if (node.attributes) {
for (i in node.attributes) {
var att = node.attributes[i];
if (att instanceof Array) {
for (var j = 0; j < att.length; j++) {
cssString += strAttr(i, att[j], depth);
}
} else {
cssString += strAttr(i, att, depth);
}
}
}
if (node.children) {
var first = true;
for (i in node.children) {
if (breaks && !first) {
cssString += '\n';
} else {
first = false;
}
cssString += strNode(i, node.children[i], depth);
}
}
return cssString;
};
/**
* @param data
* You can pass css string or the CSSJS.toJSON return value.
* @param id (Optional)
* To identify and easy removable of the style element
* @param replace (Optional. defaults to TRUE)
* Whether to remove or simply do nothing
* @return HTMLLinkElement
*/
base.toHEAD = function (data, id, replace) {
var head = document.getElementsByTagName('head')[0];
var xnode = document.getElementById(id);
var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);
if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
if (_xnodeTest) {
if (replace === true || isEmpty(replace)) {
xnode.removeAttribute('id');
} else return;
}
if (isCssJson(data)) {
data = base.toCSS(data);
}
var node = document.createElement('style');
node.type = 'text/css';
if (!isEmpty(id)) {
node.id = id;
} else {
node.id = 'cssjson_' + timestamp();
}
if (node.styleSheet) {
node.styleSheet.cssText = data;
} else {
node.appendChild(document.createTextNode(data));
}
head.appendChild(node);
if (isValidStyleNode(node)) {
if (_xnodeTest) {
xnode.parentNode.removeChild(xnode);
}
} else {
node.parentNode.removeChild(node);
if (_xnodeTest) {
xnode.setAttribute('id', id);
node = xnode;
} else return;
}
return node;
};
// Alias
if (typeof window != 'undefined') {
window.createCSS = base.toHEAD;
}
// Helpers
var isValidStyleNode = function (node) {
return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
}
var timestamp = function () {
return Date.now() || +new Date();
};
var strAttr = function (name, value, depth) {
return '\t'.repeat(depth) + name + ': ' + value + ';\n';
};
var strNode = function (name, value, depth) {
var cssString = '\t'.repeat(depth) + name + ' {\n';
cssString += base.toCSS(value, depth + 1);
cssString += '\t'.repeat(depth) + '}\n';
return cssString;
};
};
if (typeof module === 'object') {
module.exports = CSSJSON;
}
</script>
<script>
var a = CSSJSON.toJSON(`
a{color:red;}
.a,.b{color:red;}
.a:hover,.b{color:red;}
`)
console.log(a)
</script>