Archived
lastest
This commit is contained in:
1 parent
620fc5bda1
commit
bd3ef9f279
779 files changed
+202868
No files matched your search
+3
@@ -0,0 +1,3 @@
|
||||
*.swp
|
||||
.DS_Store
|
||||
node_modules
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# scss-js
|
||||
## A JavaScript implementation of SCSS (Sassy CSS)
|
||||
|
||||
## API
|
||||
|
||||
### Connect Compiler
|
||||
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
compiler = require('../src/scssCompiler');
|
||||
|
||||
fs.readFile(__dirname + '/blog.scss', function(err, scssFile) {
|
||||
compiler.compile(scssFile.toString(), function(err, css) {
|
||||
if(err) {
|
||||
sys.puts(sys.inspect(err));
|
||||
} else {
|
||||
sys.puts(css);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
The compile function takes a string and returns another string with the file's css.
|
||||
|
||||
## Where Does the Project Stand?
|
||||
|
||||
### What's Usable?
|
||||
* CSS Selectors
|
||||
* Nested Rules
|
||||
* Variables
|
||||
* Fixed Mixins
|
||||
|
||||
### What Needs Work?
|
||||
* CSS Selectors
|
||||
* Variable and Mixin names
|
||||
|
||||
### What's Unimplemented?
|
||||
* Parameterized Mixins
|
||||
* Functions
|
||||
|
||||
## Next Release (0.2.0)
|
||||
|
||||
Full CSS Selector implementation
|
||||
|
||||
|
||||
## Special Thanks
|
||||
|
||||
I would like to give special thanks to [Hampton Catlin](http://hamptoncatlin.com),
|
||||
[Nathan Weizenbaum](http://nex-3.com), and [Chris Eppstein](http://acts-as-architect.blogspot.com)
|
||||
for starting and continuing the haml and sass project. I'd also like to thank the rest of the people
|
||||
that have contributed to [Sass](http://sass-lang.com/) and I hope I can catch up to your
|
||||
awesomeness.
|
||||
|
||||
## Modified MIT License
|
||||
|
||||
Copyright (c) 2010 Brian Mavity
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
Except as contained in this notice, the name(s) of the above copyright
|
||||
holders shall not be used in advertising or otherwise to promote the sale,
|
||||
use or other dealings in this Software without prior written authorization.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
$mainColor: green;
|
||||
|
||||
@mixin a_mixin {
|
||||
color: $mainColor;
|
||||
div {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin child {
|
||||
@include a_mixin;
|
||||
background-color: $mainColor;
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
#main {
|
||||
margin: 0 5.625em;
|
||||
h5, h6 {
|
||||
font-size: 1em;
|
||||
line-height: 1.25em;
|
||||
}
|
||||
.post {
|
||||
text-align: left;
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
list-style-type: circle;
|
||||
margin: 0.625em;
|
||||
}
|
||||
}
|
||||
.post-dash {
|
||||
@include child;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
compiler = require('../src/scssCompiler');
|
||||
|
||||
fs.readFile(__dirname + '/blog.scss', function(err, scssFile) {
|
||||
compiler.compile(scssFile.toString(), function(err, css) {
|
||||
if(err) {
|
||||
sys.puts(sys.inspect(err));
|
||||
} else {
|
||||
sys.puts(css);
|
||||
}
|
||||
});
|
||||
});
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
ometa = require('ometa'),
|
||||
createdParser;
|
||||
|
||||
var getParser = function(callback) {
|
||||
if(!createdParser) {
|
||||
fs.readFile(__dirname + '/../src/cssSelector.ometa', function(err, contents) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
ometa.createParser(contents.toString(), function(err, parser) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
createdParser = parser;
|
||||
callback(null, createdParser);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, createdParser);
|
||||
}
|
||||
};
|
||||
|
||||
var parse = function(scssFile, callback) {
|
||||
getParser(function(err, parser) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
parser.parse(scssFile, 'selectors_group', function(err, css) {
|
||||
sys.puts(css.toString());
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports.parse = parse;
|
||||
parse(':not(h1, h2, h3)', function(err) {
|
||||
sys.puts(sys.inspect(err, true, null));
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
$b : #234567; div { height:#d3e115; .error { background: $b; color:green; }} a:hover{color: $b; }
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
var sys = require('sys')
|
||||
, fs = require('fs')
|
||||
, path = require('path')
|
||||
, scss = require('../')
|
||||
;
|
||||
|
||||
fs.readFile(path.join(__dirname, 'blog.scss'), function(err, scssFile) {
|
||||
scss.parse(scssFile.toString(), function(err, css) {
|
||||
if(err) {
|
||||
sys.puts(sys.inspect(err));
|
||||
} else {
|
||||
sys.puts(css);
|
||||
}
|
||||
});
|
||||
});
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "scss",
|
||||
"description": "JavaScript Implementation of SCSS (Sassy CSS)",
|
||||
"version": "0.2.4",
|
||||
"contributors": [
|
||||
{ "name": "Brian Mavity", "email": "[email protected]" }
|
||||
],
|
||||
"main": "./src/index.js",
|
||||
"engines": { "node": ">= 0.2.0" },
|
||||
"dependencies": {
|
||||
"ometa": "0.2.2"
|
||||
}
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
var vows = require('vows'),
|
||||
assert = require('assert'),
|
||||
selectorGrammar = require(__dirname + '/grammarInvoker');
|
||||
|
||||
var shouldParse = function() {
|
||||
var selector,
|
||||
context = {
|
||||
topic: function() {
|
||||
selector = this.context.name;
|
||||
selectorGrammar.parse(selector, this.callback);
|
||||
}
|
||||
};
|
||||
|
||||
context['should parse selector'] = function(err, parsedSelector) {
|
||||
if(err) {
|
||||
assert.fail(err.inner.toString());
|
||||
} else {
|
||||
assert.equal(parsedSelector, selector);
|
||||
}
|
||||
};
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
var shouldParseTo = function(expectedSelector) {
|
||||
var context = {
|
||||
topic: function() {
|
||||
var selector = this.context.name;
|
||||
selectorGrammar.parse(selector, this.callback);
|
||||
}
|
||||
};
|
||||
|
||||
context['should parse selector'] = function(err, parsedSelector) {
|
||||
if(err) {
|
||||
assert.fail(err.inner.toString());
|
||||
} else {
|
||||
assert.equal(parsedSelector, expectedSelector);
|
||||
}
|
||||
};
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
vows.describe('Summarized Selectors').addBatch({
|
||||
// Taken from http://www.w3.org/TR/css3-selectors/#selectors
|
||||
|
||||
'*': shouldParse(),
|
||||
'E': shouldParse(),
|
||||
'E[foo]': shouldParse(),
|
||||
'E[foo="bar"]': shouldParse(),
|
||||
'E[foo~="bar"]': shouldParse(),
|
||||
'E[foo^="bar"]': shouldParse(),
|
||||
'E[foo$="bar"]': shouldParse(),
|
||||
'E[foo*="bar"]': shouldParse(),
|
||||
'E[foo|="en"]': shouldParse(),
|
||||
'E:root': shouldParse(),
|
||||
'E:nth-child(n)': shouldParse(),
|
||||
'E:nth-last-child(n)': shouldParse(),
|
||||
'E:nth-of-type(n)': shouldParse(),
|
||||
'E:nth-last-of-type(n)': shouldParse(),
|
||||
'E:first-child': shouldParse(),
|
||||
'E:last-child': shouldParse(),
|
||||
'E:first-of-type': shouldParse(),
|
||||
'E:last-of-type': shouldParse(),
|
||||
'E:only-child': shouldParse(),
|
||||
'E:only-of-type': shouldParse(),
|
||||
'E:empty': shouldParse(),
|
||||
'E:link': shouldParse(),
|
||||
'E:visited': shouldParse(),
|
||||
'E:active': shouldParse(),
|
||||
'E:hover': shouldParse(),
|
||||
'E:focus': shouldParse(),
|
||||
'E:target': shouldParse(),
|
||||
'E:lang(fr)': shouldParse(),
|
||||
'E:enabled': shouldParse(),
|
||||
'E:disabled': shouldParse(),
|
||||
'E:checked': shouldParse(),
|
||||
'E::first-line': shouldParse(),
|
||||
'E::first-letter': shouldParse(),
|
||||
'E::before': shouldParse(),
|
||||
'E::after': shouldParse(),
|
||||
'E.warning': shouldParse(),
|
||||
'E#myid': shouldParse(),
|
||||
'E:not(s)': shouldParse(),
|
||||
'E F': shouldParse(),
|
||||
'E > F': shouldParse(),
|
||||
'E + F': shouldParse(),
|
||||
'E ~ F': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Lonely Selectors').addBatch({
|
||||
// Taken from http://www.w3.org/TR/css3-selectors/#selectors,
|
||||
// but without the element names
|
||||
|
||||
'[foo]': shouldParse(),
|
||||
'[foo="bar"]': shouldParse(),
|
||||
'[foo~="bar"]': shouldParse(),
|
||||
'[foo^="bar"]': shouldParse(),
|
||||
'[foo$="bar"]': shouldParse(),
|
||||
'[foo*="bar"]': shouldParse(),
|
||||
'[foo|="en"]': shouldParse(),
|
||||
':root': shouldParse(),
|
||||
':nth-child(n)': shouldParse(),
|
||||
':nth-last-child(n)': shouldParse(),
|
||||
':nth-of-type(n)': shouldParse(),
|
||||
':nth-last-of-type(n)': shouldParse(),
|
||||
':first-child': shouldParse(),
|
||||
':last-child': shouldParse(),
|
||||
':first-of-type': shouldParse(),
|
||||
':last-of-type': shouldParse(),
|
||||
':only-child': shouldParse(),
|
||||
':only-of-type': shouldParse(),
|
||||
':empty': shouldParse(),
|
||||
':link': shouldParse(),
|
||||
':visited': shouldParse(),
|
||||
':active': shouldParse(),
|
||||
':hover': shouldParse(),
|
||||
':focus': shouldParse(),
|
||||
':target': shouldParse(),
|
||||
':lang(fr)': shouldParse(),
|
||||
':enabled': shouldParse(),
|
||||
':disabled': shouldParse(),
|
||||
':checked': shouldParse(),
|
||||
'::first-line': shouldParse(),
|
||||
'::first-letter': shouldParse(),
|
||||
'::before': shouldParse(),
|
||||
'::after': shouldParse(),
|
||||
'.warning': shouldParse(),
|
||||
'#myid': shouldParse(),
|
||||
':not(s)': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Attribute Selectors with Identifiers').addBatch({
|
||||
'[foo~=bar]': shouldParse(),
|
||||
'[foo^=bar]': shouldParse(),
|
||||
'[foo$=bar]': shouldParse(),
|
||||
'[foo*=bar]': shouldParse(),
|
||||
'[foo|=en]': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Nth Selectors').addBatch({
|
||||
':nth-child(-n)': shouldParse(),
|
||||
':nth-child(+n)': shouldParse(),
|
||||
|
||||
':nth-child(even)': shouldParse(),
|
||||
':nth-child(odd)': shouldParse(),
|
||||
|
||||
':nth-child(50)': shouldParse(),
|
||||
':nth-child(-50)': shouldParse(),
|
||||
':nth-child(+50)': shouldParse(),
|
||||
|
||||
':nth-child(2n+3)': shouldParse(),
|
||||
':nth-child(2n-3)': shouldParse(),
|
||||
':nth-child(+2n-3)': shouldParse(),
|
||||
':nth-child(-2n+3)': shouldParse(),
|
||||
':nth-child(-2n+ 3)': shouldParse(),
|
||||
':nth-child(-2n+ 3)': shouldParse(),
|
||||
':nth-child(-2n+ 3)': shouldParse(),
|
||||
':nth-child( 2n + 3 )': shouldParseTo(':nth-child(2n + 3)')
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Negation Selectors').addBatch({
|
||||
':not(foo|bar)': shouldParse(),
|
||||
':not(*|bar)': shouldParse(),
|
||||
|
||||
':not(foo|*)': shouldParse(),
|
||||
':not(*|*)': shouldParse(),
|
||||
|
||||
':not(#blah)': shouldParse(),
|
||||
':not(.blah)': shouldParse(),
|
||||
|
||||
':not([foo])': shouldParse(),
|
||||
':not([foo^="bar"])': shouldParse(),
|
||||
':not([baz|foo~="bar"])': shouldParse(),
|
||||
|
||||
':not(:hover)': shouldParse(),
|
||||
':not(:nth-child(2n + 3))': shouldParse(),
|
||||
|
||||
// Not technically allowed, but what the heck
|
||||
':not(:not(#foo))': shouldParse(),
|
||||
':not(a#foo.bar)': shouldParse(),
|
||||
':not(#foo .bar > baz)': shouldParse(),
|
||||
':not(h1, h2, h3)': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('moz-any Selector').addBatch({
|
||||
':-moz-any(h1, h2, h3)': shouldParse(),
|
||||
':-moz-any(.foo)': shouldParse(),
|
||||
':-moz-any(foo bar, .baz > .bang)': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Namespaced Selectors').addBatch({
|
||||
'foo|E': shouldParse(),
|
||||
'*|E': shouldParse(),
|
||||
'foo|*': shouldParse(),
|
||||
'*|*': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
|
||||
vows.describe('Namespaced Attribute Selectors').addBatch({
|
||||
'[foo|bar=baz]': shouldParse(),
|
||||
'[*|bar=baz]': shouldParse(),
|
||||
'[foo|bar|=baz]': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Comma Selectors').addBatch({
|
||||
'E, F': shouldParse(),
|
||||
'E F, G H': shouldParse(),
|
||||
'E > F, G > H': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Selectors with Newlines').addBatch({
|
||||
"E,\nF": shouldParse(),
|
||||
"E\nF": shouldParse(),
|
||||
"E, F\nG, H": shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Expression Fallback Selectors').addBatch({
|
||||
'0%': shouldParse(),
|
||||
'60%': shouldParse(),
|
||||
'100%': shouldParse(),
|
||||
'12px': shouldParse(),
|
||||
'"foo"': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Functional Pseudo Selectors').addBatch({
|
||||
':foo("bar")': shouldParse(),
|
||||
':foo(bar)': shouldParse(),
|
||||
':foo(12px)': shouldParse(),
|
||||
':foo(+)': shouldParse(),
|
||||
':foo(-)': shouldParse(),
|
||||
':foo(+"bar")': shouldParse(),
|
||||
':foo(-++--baz-"bar"12px)': shouldParse()
|
||||
}).run();
|
||||
|
||||
|
||||
vows.describe('Selector Hacks').addBatch({
|
||||
'> E': shouldParse(),
|
||||
'+ E': shouldParse(),
|
||||
'~ E': shouldParse(),
|
||||
'> > E': shouldParse(),
|
||||
'>> E': shouldParseTo('> > E'),
|
||||
'E*': shouldParse(),
|
||||
'E*.foo': shouldParse(),
|
||||
'E*:hover': shouldParse()
|
||||
}).run();
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
ometa = require('ometa'),
|
||||
creatingParser = false;
|
||||
|
||||
var fixReadFile = function(rawFile) {
|
||||
return rawFile.slice(1).replace(/\\n/g, '\n');
|
||||
};
|
||||
|
||||
var createParser = function(parserInitializer) {
|
||||
if(!creatingParser) {
|
||||
creatingParser = true;
|
||||
//fs.readFile(__dirname + '/../src/cssSelector.ometa', 'utf8', function(err, contents) {
|
||||
fs.readFile(__dirname + '/../../ometa-js/newLineGrammar.ometa', 'utf8', function(err, contents) {
|
||||
if(err) {
|
||||
parserInitializer.setParser(err);
|
||||
} else {
|
||||
ometa.createParser(fixReadFile(contents), parserInitializer.setParser);
|
||||
}
|
||||
creatingParser = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var parserInitializer = (function() {
|
||||
var createdParser,
|
||||
parserErr,
|
||||
queuedCallbacks = [],
|
||||
that = {};
|
||||
|
||||
that.setParser = function(err, parser) {
|
||||
parserErr = err;
|
||||
createdParser = parser;
|
||||
queuedCallbacks.forEach(function(callback) {
|
||||
callback(parserErr, createdParser);
|
||||
});
|
||||
queuedCallbacks = [];
|
||||
};
|
||||
|
||||
that.addCallback = function(callback) {
|
||||
if(parserErr || createdParser) {
|
||||
callback(parserErr, createdParser);
|
||||
} else {
|
||||
queuedCallbacks.push(callback);
|
||||
createParser(that);
|
||||
}
|
||||
};
|
||||
|
||||
that.reset = function() {
|
||||
createdParser = null;
|
||||
};
|
||||
|
||||
return that;
|
||||
})();
|
||||
|
||||
var parse = function(selector, callback) {
|
||||
parserInitializer.addCallback(function(err, parser) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
parser.parse(selector, 'selectors_group', function(err, css) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
//callback(null, css.toString());
|
||||
callback(null, css.join(''));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports.parse = parse;
|
||||
module.exports.reset = parserInitializer.reset;
|
||||
var ssss = 'a,\nb';
|
||||
console.log(ssss);
|
||||
parse(ssss, function(err, shiznit) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
}
|
||||
console.log(shiznit);
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
var scss = require('./index');
|
||||
|
||||
var compiler = (function() {
|
||||
return {
|
||||
match: /\.css$/,
|
||||
ext: '.scss',
|
||||
compile: function(str, fn) {
|
||||
scss.parse(str, function(err, css) {
|
||||
if(err) {
|
||||
fn(err);
|
||||
} else {
|
||||
fn(null, css);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
module.exports = compiler;
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
ometa CssSelector <: Parser {
|
||||
crChar = '\r',
|
||||
ffChar = '\f',
|
||||
nlChar = '\n',
|
||||
tabChar = '\t',
|
||||
lineEnding = crChar | ffChar | nlChar,
|
||||
tabOrLineEnding = tabChar | lineEnding,
|
||||
|
||||
|
||||
ident = '-' nmstart:s nmchar*:cs -> { '-' + s + cs.join('') }
|
||||
| nmstart:s nmchar*:cs -> { s + cs.join('') },
|
||||
name = nmchar+:n -> { n.join('') },
|
||||
nmstart = ('_' | letter | nonascii | escape):n -> { n },
|
||||
nonascii = '',
|
||||
unicode = '',
|
||||
escape = unicode | '',
|
||||
nmchar = '_' | '-' | letterOrDigit | nonascii | escape,
|
||||
num = digit+: d -> d.join('')
|
||||
| digit* '.' digit+,
|
||||
string = (string1 | string2):s -> { s },
|
||||
//string1 = '\"' (~(lineEnding | '\"') | '\\' nl | nonascii | escape)*:s '\"' -> { '\"' + s.join('') + '\"' },
|
||||
string1 = '"' letter*:s '"' -> { '"' + s.join('') + '"' },
|
||||
string2 = '\'' (~(lineEnding | '\'') | '\\' nl | nonascii | escape)*:s '\'' -> { '\'' + s.join('') + '\'' },
|
||||
//invalid = invalid1 | invalid2,
|
||||
//invalid1 = '\"' (~(lineEnding | '\"') | '\\' nl | nonascii | escape)*,
|
||||
//invalid2 = '\'' (~(lineEnding | '\'') | '\\' nl | nonascii | escape)*,
|
||||
nl = crChar nlChar
|
||||
| lineEnding,
|
||||
D = 'd' | 'D',
|
||||
E = 'e' | 'E',
|
||||
N = 'n' | 'N',
|
||||
O = 'o' | 'O',
|
||||
T = 't' | 'T',
|
||||
V = 'v' | 'V',
|
||||
|
||||
|
||||
S = ' ' -> { ' ' }
|
||||
| '\n' -> { '\n' },
|
||||
INCLUDES = '~' '=' -> { '~=' },
|
||||
DASHMATCH = '|' '=' -> { '|=' },
|
||||
PREFIXMATCH = '^' '=' -> { '^=' },
|
||||
SUFFIXMATCH = '$' '=' -> { '$=' },
|
||||
SUBSTRINGMATCH = '*' '=' -> { '*=' },
|
||||
IDENT = ident,
|
||||
STRING = string:s -> { s },
|
||||
FUNCTION = ident:i '(' -> { i + '(' },
|
||||
NUMBER = num:n -> { n },
|
||||
HASH = '#' name:n -> { '#' + n },
|
||||
PLUS = S+ '+' -> { ' +' }
|
||||
| '+' -> { '+' },
|
||||
// First line of next selector is a Css Hack
|
||||
GREATER = '>' '>' -> { '> >' }
|
||||
| S+ '>' -> { ' >' }
|
||||
| '>' -> { '>' },
|
||||
COMMA = S+ ',' -> { ' ,' }
|
||||
| ',' -> { ',' },
|
||||
TILDE = S+ '~' -> { ' ~' }
|
||||
| '~' -> { '~' },
|
||||
NOT = ':' N O T '(' -> { ':not(' },
|
||||
ATKEYWORD = '@' ident,
|
||||
INVALID = invalid,
|
||||
PERCENTAGE = num:n '%' -> { n + '%' },
|
||||
DIMENSION = num:n ident:i -> { n + i },
|
||||
CDO = '<' '!' '-' '-',
|
||||
CDC = '-' '-' '>',
|
||||
|
||||
|
||||
selectors_group = selector:pre comma_separated_selector* -> { self.addSelector(pre); self },
|
||||
comma_separated_selector = COMMA:com S*:spacing selector:sel -> { self.addCommaSeparatedSelector(com + spacing.join(''), sel) },
|
||||
selector = simple_selector_sequence:sim (combined_sequence)*:additional -> { sim + additional.join('') }
|
||||
// Css Hack
|
||||
| combined_sequence*:comb -> { comb.join('') },
|
||||
combinator = PLUS:p S+ -> { p + ' ' }
|
||||
| PLUS:p -> { p }
|
||||
| GREATER:g S+ -> { g + ' ' }
|
||||
| GREATER:g -> { g }
|
||||
| TILDE:t S+ -> { t + ' ' }
|
||||
| TILDE:t -> { t }
|
||||
| S+:spacing -> { spacing.join('') },
|
||||
combined_sequence = combinator+:comb simple_selector_sequence:sel -> { comb.join('') + sel },
|
||||
non_namespaced_selector = (HASH | class | attrib | negation | pseudo):sel -> { sel },
|
||||
simple_selector_sequence = namespace_prefix:pre '*' non_namespaced_selector*:post -> { pre + '*' + post.join('') }
|
||||
| namespace_prefix:pre element_name:ele non_namespaced_selector*:post -> { pre + ele + post.join('') }
|
||||
| '*' non_namespaced_selector*:post -> { '*' + post.join('') }
|
||||
| element_name:ele non_namespaced_selector*:post -> { ele + post.join('') }
|
||||
| non_namespaced_selector+:sels -> { sels.join('') }
|
||||
// Css Hack
|
||||
| expression:ex -> { ex },
|
||||
namespace_prefix = ('*' | IDENT):pre '|' -> { pre + '|' }
|
||||
| '|' -> { '|' },
|
||||
// First line of the next selector is a Css Hack
|
||||
element_name = IDENT:i '*' -> { i + '*' }
|
||||
| IDENT:i -> { i },
|
||||
class = '.' IDENT:i -> { '.' + i },
|
||||
|
||||
attrib = '[' S* possible_namespaced_attrib:att ']' -> { '[' + att + ']' },
|
||||
possible_namespaced_attrib = namespace_prefix:pre ident_with_possible_postfix:post -> { pre + post }
|
||||
| ident_with_possible_postfix:post -> { post },
|
||||
ident_with_possible_postfix = IDENT:left S* attrib_match:match S* (IDENT | STRING):right S* -> { left + match + right }
|
||||
| IDENT:i S* -> { i },
|
||||
attrib_match = (PREFIXMATCH | SUFFIXMATCH | SUBSTRINGMATCH | equals_match | INCLUDES | DASHMATCH):m -> { m },
|
||||
equals_match = '=' -> { '=' },
|
||||
pseudo = ':' ':' (functional_pseudo | IDENT):i -> { '::' + i }
|
||||
| ':' (functional_pseudo | IDENT):i -> { ':' + i },
|
||||
functional_pseudo = FUNCTION:f S* full_expression:e ')' -> { f + e + ')' }
|
||||
// Css Hack for :-moz-any(...)
|
||||
| FUNCTION:f S* selectors_group:sel ')' -> { f + sel.toString() + ')' },
|
||||
|
||||
expression_content = (PLUS | '-' | PERCENTAGE | DIMENSION | NUMBER | STRING | IDENT):e -> { e },
|
||||
expression = expression_content:ec S+ expression:e -> { ec + ' ' + e }
|
||||
| expression_content:ec expression:e -> { ec + e }
|
||||
| expression_content:ec S* -> { ec },
|
||||
full_expression = (expression)+:ea -> { ea.join('') },
|
||||
negation = NOT:n S* negation_arg:na S* ')' -> { n + na + ')' },
|
||||
|
||||
//negation_arg = (type_selector | universal | HASH | class | attrib | pseudo):na -> { na }
|
||||
//Technically not allowed, but here for scss compatibility
|
||||
negation_arg = selectors_group:sg -> { sg.toString() }
|
||||
}
|
||||
|
||||
CssSelector.initialize = function() {
|
||||
var hasBeenStringified = false,
|
||||
self = this;
|
||||
|
||||
this.selector;
|
||||
this.selectors = [];
|
||||
this.commaSeparatedSelectors = [];
|
||||
|
||||
var resetFields = function() {
|
||||
self.selector = null;
|
||||
self.selectors = [];
|
||||
self.commaSeparatedSelectors = [];
|
||||
hasBeenStrigified = false;
|
||||
};
|
||||
|
||||
this.addSelector = function(sel) {
|
||||
if(hasBeenStringified) {
|
||||
resetFields();
|
||||
}
|
||||
this.selector = sel;
|
||||
};
|
||||
|
||||
this.addCommaSeparatedSelector = function(commaAndSpacing, selector) {
|
||||
if(hasBeenStringified) {
|
||||
resetFields();
|
||||
}
|
||||
this.selectors.push(selector);
|
||||
this.commaSeparatedSelectors.push({
|
||||
commaAndSpacing: commaAndSpacing,
|
||||
selector: selector
|
||||
});
|
||||
};
|
||||
|
||||
this.toString = function() {
|
||||
hasBeenStringified = true;
|
||||
return this.selector + this.commaSeparatedSelectors.map(function(csSel) {
|
||||
return csSel.commaAndSpacing + csSel.selector;
|
||||
}).join('');
|
||||
};
|
||||
};
|
||||
|
||||
CssSelector
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
var sys = require('sys')
|
||||
, fs = require('fs')
|
||||
, path = require('path')
|
||||
, ometa = require('ometa')
|
||||
, rule = 'scssFile'
|
||||
, createdParser
|
||||
;
|
||||
|
||||
var fixReadFile = function(rawFile) {
|
||||
return rawFile.slice(1).replace(/\\n/g, '\n');
|
||||
};
|
||||
|
||||
var getParser = function(callback) {
|
||||
if(!createdParser) {
|
||||
fs.readFile(path.join(__dirname, '/CssSelector.ometa'), 'utf8', function(err, cssSelectorContents) {
|
||||
var selectorFile = fixReadFile(cssSelectorContents);
|
||||
ometa.createParser(selectorFile, function(err, cssSelectorParser) {
|
||||
fs.readFile(path.join(__dirname, '/scss.ometa'), 'utf8', function(err, contents) {
|
||||
var contentFile = fixReadFile(contents);
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
ometa.createParser(contentFile, function(err, parser) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
createdParser = parser;
|
||||
callback(null, createdParser);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
callback(null, createdParser);
|
||||
}
|
||||
};
|
||||
|
||||
var parse = function(scssFile, callback) {
|
||||
getParser(function(err, parser) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
} else {
|
||||
parser.parse(scssFile, rule, function(err, css) {
|
||||
require(__dirname + '/prettyPrint').render(css, function(inner, printed) {
|
||||
callback(err, printed);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports.parse = parse;
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
function renderProperty(fileObj, propertyObj) {
|
||||
var getPropertyValue = function(val) {
|
||||
if(val.indexOf('$') == 0) {
|
||||
return fileObj.getValue(val.replace('$', ''));
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
return propertyObj.name + ': ' + getPropertyValue(propertyObj.val) + ';';
|
||||
};
|
||||
|
||||
function renderBlock(fileObj, blockObj, scope) {
|
||||
var selector = blockObj.selector,
|
||||
scopeIndent = (scope && scope.indent) || '',
|
||||
currentIndent = scopeIndent + ' ',
|
||||
paddedScopeSelector = (scope && scope.selector) ? scope.selector + ' ' : '',
|
||||
currentSelector = paddedScopeSelector + selector,
|
||||
selectors = blockObj.selectors,
|
||||
properties = blockObj.properties,
|
||||
blocks = blockObj.blocks,
|
||||
includes = blockObj.includes,
|
||||
css = scopeIndent + currentSelector;
|
||||
|
||||
var expandIncludes = function(includeNames) {
|
||||
includeNames.forEach(function(includeName) {
|
||||
var mixin = fileObj.getMixin(includeName);
|
||||
//Why doesn't .concat() work?
|
||||
//properties.concat(mixin.properties);
|
||||
//blocks.concat(mixin.blocks);
|
||||
mixin.properties.forEach(function(property) {
|
||||
properties.push(property);
|
||||
});
|
||||
mixin.blocks.forEach(function(block) {
|
||||
blocks.push(block);
|
||||
});
|
||||
expandIncludes(mixin.includes);
|
||||
});
|
||||
};
|
||||
expandIncludes(includes);
|
||||
|
||||
selectors.forEach(function(sel) {
|
||||
css += ',\n' + scopeIndent + paddedScopeSelector + sel;
|
||||
});
|
||||
|
||||
css += ' {\n';
|
||||
|
||||
properties.forEach(function(property) {
|
||||
css += currentIndent + renderProperty(fileObj, property) + '\n';
|
||||
});
|
||||
|
||||
css += scopeIndent + '}\n';
|
||||
|
||||
blocks.forEach(function(block) {
|
||||
css += renderBlock(fileObj, block, {
|
||||
indent: currentIndent,
|
||||
selector: currentSelector
|
||||
});
|
||||
});
|
||||
|
||||
return css;
|
||||
};
|
||||
|
||||
function renderFile(fileObj) {
|
||||
var blocks = fileObj.blocks,
|
||||
css = '';
|
||||
|
||||
blocks.forEach(function(block) {
|
||||
css += renderBlock(fileObj, block);
|
||||
css += '\n';
|
||||
});
|
||||
|
||||
return css;
|
||||
};
|
||||
|
||||
function render(cssObj, callback) {
|
||||
var css;
|
||||
try {
|
||||
css = renderFile(cssObj);
|
||||
callback(null, css)
|
||||
}
|
||||
catch(ex) {
|
||||
callback(ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports.render = render;
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
ometa Scss <: Parser {
|
||||
selector = foreign(CssSelector, 'selectors_group'):sel -> { self.addSelector(sel) },
|
||||
|
||||
propertyValue = '$' letterOrDigit+:val -> ('$' + val.join(''))
|
||||
| ('#' | '.' | ',' | '-' | space | letterOrDigit)+:val -> val.join(''),
|
||||
property = spaces ('-' | letter)+:prop ":" spaces propertyValue:val ";" -> { self.addProperty({ name: prop.join(''), val: val }); },
|
||||
|
||||
blockBody = "{" (mixinInclude | property | scssBlock)* "}" -> { self.endBlock(); },
|
||||
scssBlock = spaces selector blockBody,
|
||||
|
||||
scssVariable = "$" letter+:name ":" spaces propertyValue:val ";" -> { self.addVariable({ name: name.join(''), val: val }); },
|
||||
|
||||
mixinName = spaces ('-' | '_' | letterOrDigit)+:name -> name.join(''),
|
||||
mixinInclude = "@include" mixinName:name ";" -> { self.addInclude(name); },
|
||||
mixinDeclaration = "@mixin" mixinName:name -> { self.addMixin(name); },
|
||||
scssMixin = mixinDeclaration blockBody,
|
||||
|
||||
scssFile = (scssVariable | scssMixin | scssBlock)* -> { self.getFile(); }
|
||||
}
|
||||
|
||||
Scss.initialize = function() {
|
||||
var blocks = [],
|
||||
mixins = {},
|
||||
variables = {},
|
||||
file,
|
||||
currentBlock;
|
||||
|
||||
var createBlock = function() {
|
||||
var parentBlock = currentBlock;
|
||||
return {
|
||||
blocks: [],
|
||||
includes: [],
|
||||
properties: [],
|
||||
end: function() {
|
||||
currentBlock = parentBlock;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.addMixin = function(mixinName) {
|
||||
var block = createBlock();
|
||||
mixins[mixinName] = block;
|
||||
currentBlock = block;
|
||||
};
|
||||
|
||||
this.addProperty = function(prop) {
|
||||
currentBlock.properties.push(prop);
|
||||
};
|
||||
|
||||
this.addInclude = function(include) {
|
||||
currentBlock.includes.push(include);
|
||||
};
|
||||
|
||||
this.addSelector = function(sel) {
|
||||
var block;
|
||||
if(sel.selector) {
|
||||
block = createBlock();
|
||||
block.selector = sel.selector;
|
||||
block.selectors = sel.selectors;
|
||||
currentBlock.blocks.push(block);
|
||||
currentBlock = block;
|
||||
}
|
||||
};
|
||||
|
||||
this.endBlock = function() {
|
||||
currentBlock.end();
|
||||
};
|
||||
|
||||
this.addVariable = function(variable) {
|
||||
variables[variable.name] = variable.val;
|
||||
};
|
||||
|
||||
this.getFile = function() {
|
||||
return file;
|
||||
};
|
||||
|
||||
file = {
|
||||
blocks: [],
|
||||
end: function() {
|
||||
currentBlock = file;
|
||||
},
|
||||
getMixin: function(mixinName) {
|
||||
return mixins[mixinName];
|
||||
},
|
||||
getValue: function(variableName) {
|
||||
return variables[variableName];
|
||||
}
|
||||
};
|
||||
currentBlock = file;
|
||||
};
|
||||
|
||||
Scss
|
||||
Reference in new issue
Block a user