Skip to content

Commit

Permalink
VASTLY improved parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr0grog committed Apr 29, 2013
1 parent 706e252 commit 1dc4f1b
Show file tree
Hide file tree
Showing 5 changed files with 389 additions and 104 deletions.
8 changes: 8 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- Not/or/only for media queries
- Support alternative syntax (use the .query-property-value class name) so we don't need to (re)load via XHR
- Unit tests
- Packaging support
- Other query types
- Plugin hooks for queries
- Explanation of how/why it differs from MediaClass (especially w/r/t "available" space)
- Support html:media() by synthesizing actual @media(){} rules
10 changes: 6 additions & 4 deletions lib/element-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ var elementQuery = (function() {
// parse a single CSSStyleSheet object for element queries
loadStyleSheet: function(sheet, callback) {
if (sheet.ownerNode.nodeName === "STYLE") {
var newStyles = elementQuery.parser.parseStyleText(sheet.ownerNode.innerHTML);
sheet.ownerNode.innerHTML += newStyles;
var result = elementQuery.parser.parseStyleText(sheet.ownerNode.innerHTML);
sheet.ownerNode.innerHTML += result.newCss;
elementQuery.queries = elementQuery.queries.concat(result.queries);
callback && callback();
}
else if (sheet.href) {
Expand All @@ -98,9 +99,10 @@ var elementQuery = (function() {
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var newStyles = elementQuery.parser.parseStyleText(xhr.responseText);
var result = elementQuery.parser.parseStyleText(xhr.responseText);
elementQuery.queries = elementQuery.queries.concat(result.queries);
var style = document.createElement("style");
style.innerHTML = newStyles;
style.innerHTML = result.newCss;
document.body.appendChild(style);
}
else if (window.console) {
Expand Down
101 changes: 101 additions & 0 deletions lib/parser-old.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
(function(elementQuery) {
// Regexes for parsing
var COMMENT_PATTERN = /(\/\*)[\s\S]*?(\*\/)/g;
var CSS_RULE_PATTERN = /\s*([^{]+)\{([^}]*)\}\s*/g;
var QUERY_PATTERN = /:media\s*\(([^)]*)\)/g;
var QUERY_RULES_PATTERN = /\(?([^\s:]+):\s*(\d+(?:\.\d+)?)(px|em|rem|vw|vh|vmin|vmax)\)?/g;
var MEDIA_PATTERN = /(@media[^{]*)\{((?:\s*([^{]+)\{([^}]*)\}\s*)*)\}/g;

/**
* el-cheapo parser. It at least screws up nested @media rules
* and puts things that were in @media rules out-of-order
*/
elementQuery.parser = {
// parse the raw text of a style sheet for element queries
parseStyleText: function(sheet) {
// new stylesheet content to add (replacing rules with `:media()` in them)
var newRules = "";

// remove comments
sheet = sheet.replace(COMMENT_PATTERN, "");

// manage vanilla media queries
var mediaRules = "";
var parser = this;
sheet = sheet.replace(MEDIA_PATTERN, function(mediaString, query, content) {
var newMediaRules = parser.parseStyleText(content);
if (!/^\s*$/.test(newMediaRules)) {
mediaRules += query + "{\n" + newMediaRules + "\n}\n";
}
return "";
});

var ruleMatch;
while (ruleMatch = CSS_RULE_PATTERN.exec(sheet)) {
var results = this.queriesForSelector(ruleMatch[1]);
if (results.queries.length) {
newRules += results.selector + "{" + ruleMatch[2] + "}\n";
elementQuery.queries.push.apply(elementQuery.queries, results.queries);
}
}
return newRules + "\n" + mediaRules;
},

// find all the queries in a selector
queriesForSelector: function(selectorString) {
var selectors = selectorString.split(",");
var selectorResults = [];
var queryResults = [];
for (var i = 0, len = selectors.length; i < len; i++) {
var result = this.queriesForSingleSelector(selectors[i]);
selectorResults.push(result.selector);
queryResults = queryResults.concat(result.queries);
}
return {
selector: selectorResults.join(","),
queries: queryResults
};
},

// find all the queries in a *single* selector (i.e. no commas)
queriesForSingleSelector: function(selectorString) {
var queries = [];
var newSelector = "";
var lastIndex = 0;
var queryMatch;
while (queryMatch = QUERY_PATTERN.exec(selectorString)) {
var querySelector = selectorString.slice(0, queryMatch.index);
var queryRules = this.parseQuery(queryMatch[1]);
var className = elementQuery.classNameForRules(queryRules);
newSelector += selectorString.slice(lastIndex, queryMatch.index);
lastIndex = queryMatch.index + queryMatch[0].length;
queries.push({
selector: newSelector,
className: className,
rules: queryRules
});
newSelector += "." + className;
}
newSelector += selectorString.slice(lastIndex);
return {
selector: newSelector,
queries: queries
};
},

// find the actual queried properties in an element query
parseQuery: function(queryString) {
var rules = [];
var ruleMatch;
while (ruleMatch = QUERY_RULES_PATTERN.exec(queryString)) {
rules.push({
property: ruleMatch[1],
value: parseFloat(ruleMatch[2]),
units: ruleMatch[3]
});
}
return rules;
}
};

}(elementQuery));
Loading

0 comments on commit 1dc4f1b

Please sign in to comment.