Commit edf5bf06 by Aaron Leung

Implemented media queries.

parent fcae17e8
......@@ -145,7 +145,7 @@ namespace Sass {
Node parse_simple_selector();
Node parse_pseudo();
Node parse_attribute_selector();
Node parse_block(Node surrounding_rulesetbool, Node::Type inside_of = Node::none);
Node parse_block(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_rule();
Node parse_values();
Node parse_list();
......@@ -165,6 +165,8 @@ namespace Sass {
Node parse_for_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_each_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_while_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_media_query(Node::Type inside_of = Node::none);
Node parse_media_expression();
Selector_Lookahead lookahead_for_selector(const char* start = 0);
......
......@@ -51,6 +51,9 @@ namespace Sass {
else if (peek< while_directive >()) {
root << parse_while_directive(Node(), Node::none);
}
else if (peek< media >()) {
root << parse_media_query(Node::none);
}
else {
lex< spaces_and_comments >();
throw_syntax_error("invalid top-level expression");
......@@ -535,6 +538,9 @@ namespace Sass {
context.has_extensions = true;
semicolon = true;
}
else if (peek< media >()) {
block << parse_media_query(inside_of);
}
else if (!peek< exactly<';'> >()) {
Node rule(parse_rule());
// check for lbrace; if it's there, we have a namespace property with a value
......@@ -991,7 +997,62 @@ namespace Sass {
loop << predicate << body;
return loop;
}
Node Document::parse_media_query(Node::Type inside_of)
{
lex< media >();
Node media_query(context.new_Node(Node::media_query, path, line, 2));
Node media_expr(parse_media_expression());
if (peek< exactly<'{'> >()) {
media_query << media_expr;
}
else if (peek< exactly<','> >()) {
Node media_expr_group(context.new_Node(Node::media_expression_group, path, line, 2));
media_expr_group << media_expr;
while (lex< exactly<','> >()) {
media_expr_group << parse_media_expression();
}
media_query << media_expr_group;
}
else {
throw_syntax_error("expected '{' in media query");
}
media_query << parse_block(Node(), inside_of);
return media_query;
}
// extern const char not_kwd[] = "not";
extern const char only_kwd[] = "only";
Node Document::parse_media_expression()
{
Node media_expr(context.new_Node(Node::media_expression, path, line, 1));
// if the query begins with 'not' or 'only', then a media type is required
if (lex< not_kwd >() || lex< exactly<only_kwd> >()) {
media_expr << context.new_Node(Node::identifier, path, line, lexed);
if (!lex< identifier >()) throw_syntax_error("media type expected in media query");
media_expr << context.new_Node(Node::identifier, path, line, lexed);
}
// otherwise, the media type is optional
else if (lex< identifier >()) {
media_expr << context.new_Node(Node::identifier, path, line, lexed);
}
// if no media type was present, then require a parenthesized property
if (media_expr.empty()) {
if (!lex< exactly<'('> >()) throw_syntax_error("invalid media query");
media_expr << parse_rule();
if (!lex< exactly<')'> >()) throw_syntax_error("unclosed parenthesis");
}
// parse the rest of the properties for this disjunct
while (!peek< exactly<','> >() && !peek< exactly<'{'> >()) {
if (!lex< and_kwd >()) throw_syntax_error("invalid media query");
media_expr << context.new_Node(Node::identifier, path, line, lexed);
if (!lex< exactly<'('> >()) throw_syntax_error("invalid media query");
media_expr << parse_rule();
if (!lex< exactly<')'> >()) throw_syntax_error("unclosed parenthesis");
}
return media_expr;
}
Selector_Lookahead Document::lookahead_for_selector(const char* start)
{
const char* p = start ? start : position;
......
......@@ -51,7 +51,6 @@ namespace Sass {
} break;
case Node::ruleset: {
// if the selector contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) {
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
......@@ -59,7 +58,6 @@ namespace Sass {
// expand the selector with the prefix and save it in expr[2]
expr << expand_selector(expr[0], prefix, new_Node);
// gather selector extensions into a pending queue
if (ctx.has_extensions) {
Node sel(selector_base(expr.back()));
......@@ -77,6 +75,14 @@ namespace Sass {
return expr;
} break;
case Node::media_query: {
Node block(expr[1]);
Node new_ruleset(new_Node(Node::ruleset, expr.path(), expr.line(), 3));
new_ruleset << prefix << block << prefix;
expr[1] = eval(new_ruleset, new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
return expr;
} break;
case Node::selector_schema: {
string expansion;
for (size_t i = 0, S = expr.size(); i < S; ++i) {
......
......@@ -74,6 +74,7 @@ namespace Sass {
root,
ruleset,
propset,
media_query,
selector_group,
selector,
......@@ -90,6 +91,9 @@ namespace Sass {
attribute_selector,
selector_schema,
media_expression_group,
media_expression,
block,
rule,
property,
......@@ -212,7 +216,7 @@ namespace Sass {
bool operator>=(Node rhs) const;
string to_string() const;
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false);
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false);
void emit_propset(stringstream& buf, size_t depth, const string& prefix);
void echo(stringstream& buf, size_t depth = 0);
void emit_expanded_css(stringstream& buf, const string& prefix);
......@@ -286,6 +290,7 @@ namespace Sass {
case Node::rule:
case Node::propset: has_statements = true; break;
case Node::media_query:
case Node::ruleset: has_blocks = true; break;
case Node::if_directive:
......@@ -312,9 +317,19 @@ namespace Sass {
case Node::css_import:
case Node::rule:
case Node::propset: has_statements = true; break;
case Node::media_query:
case Node::ruleset: has_blocks = true; break;
case Node::if_directive:
case Node::for_through_directive:
case Node::for_to_directive:
case Node::each_directive:
case Node::while_directive:
case Node::expansion: has_expansions = true; break;
case Node::backref: has_backref = true; break;
default: break;
}
if (n.has_backref()) has_backref = true;
......
......@@ -19,7 +19,8 @@ namespace Sass {
{
switch (type())
{
case selector_group: { // really only needed for arg to :not
case selector_group:
case media_expression_group: { // really only needed for arg to :not
string result(at(0).to_string());
for (size_t i = 1, S = size(); i < S; ++i) {
result += ", ";
......@@ -27,6 +28,30 @@ namespace Sass {
}
return result;
} break;
case media_expression: {
string result;
if (at(0).type() == rule) {
result += "(";
result += at(0).to_string();
result += ")";
}
else {
result += at(0).to_string();
}
for (size_t i = 1, S = size(); i < S; ++i) {
result += " ";
if (at(i).type() == rule) {
result += "(";
result += at(i).to_string();
result += ")";
}
else {
result += at(i).to_string();
}
}
return result;
} break;
case selector: {
string result;
......@@ -84,6 +109,13 @@ namespace Sass {
return result;
} break;
case rule: {
string result(at(0).to_string());
result += ": ";
result += at(1).to_string();
return result;
} break;
case comma_list: {
string result(at(0).to_string());
for (size_t i = 1, S = size(); i < S; ++i) {
......@@ -295,16 +327,16 @@ namespace Sass {
}
}
void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel)
void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel, bool in_media_query)
{
switch (type())
{
case root:
case root: {
if (has_expansions()) flatten();
for (size_t i = 0, S = size(); i < S; ++i) {
at(i).emit_nested_css(buf, depth, true);
}
break;
} break;
case ruleset: {
Node sel_group(at(2));
......@@ -321,18 +353,26 @@ namespace Sass {
block[i].emit_nested_css(buf, depth+1);
}
}
buf << " }" << endl;
buf << " }";
if (!in_media_query || (in_media_query && block.has_blocks())) buf << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets
}
if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) {
if (block[i].type() == ruleset) {
block[i].emit_nested_css(buf, depth);
if (block[i].type() == ruleset || block[i].type() == media_query) {
block[i].emit_nested_css(buf, depth, false, in_media_query);
}
}
}
if (block.has_statements()) --depth; // see previous comment
if ((depth == 0) && at_toplevel) buf << endl;
if ((depth == 0) && at_toplevel && !in_media_query) buf << endl;
} break;
case media_query: {
buf << string(2*depth, ' ');
buf << "@media " << at(0).to_string() << " {" << endl;
at(1).emit_nested_css(buf, depth+1, false, true);
buf << " }" << endl;
} break;
case propset: {
......
......@@ -103,6 +103,10 @@ namespace Sass {
const char* import(const char* src) {
return exactly<import_kwd>(src);
}
extern const char media_kwd[] = "@media";
const char* media(const char* src) {
return exactly<media_kwd>(src);
}
extern const char mixin_kwd[] = "@mixin";
const char* mixin(const char* src) {
return exactly<mixin_kwd>(src);
......
......@@ -308,6 +308,7 @@ namespace Sass {
// Match CSS '@' keywords.
const char* at_keyword(const char* src);
const char* import(const char* src);
const char* media(const char* src);
const char* mixin(const char* src);
const char* function(const char* src);
const char* return_directive(const char* src);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment