Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

support for '@import'

  • Loading branch information...
commit 7281e406b9a5b57af9a61ffdf40e718ebbe1b0f3 1 parent 8df034d
@andyedinborough authored
View
201 bin/parseCSS.js
@@ -0,0 +1,201 @@
+var parseCSS = (function(){
+ var Reader = function(){
+ 'use strict';
+
+ var reader = function(text){
+ this.text = (text || '') + '';
+ this.position = -1;
+ this.length = this.text.length;
+ }
+
+ reader.prototype.read = function(len){
+ var value = this.peek(len);
+ this.position = Math.min(this.length, this.position + (len || 1));
+ return value;
+ };
+
+ reader.prototype.readAll = function(){
+ if(this.position>=this.length) return undefined;
+ var value = this.text.substr(this.position + 1);
+ this.position = this.length;
+ return value;
+ };
+
+ reader.prototype.peek = function(len){
+ if((this.position+1)>=this.length) return undefined;
+ return this.text.substr(this.position + 1, len || 1);
+ }
+
+ reader.prototype.seek = function(offset, pos){
+ this.position = Math.max(0,
+ Math.min(this.length,
+ (pos === 0 ? 0 : pos === 2 ? this.length : this.position) +
+ (offset || 1)
+ )
+ );
+ return this.position === this.length;
+ }
+
+
+ reader.prototype.readUntil = function(chars){
+ if(typeof chars === 'string') chars = [chars];
+ var l, rdr = this, cache = [], len = chars.length, result = { value: '', next: '' };
+
+ function predicate(chr) {
+ l = chr.length;
+ result.next = cache[l] || (cache[l] = rdr.peek(l));
+ return result.next === chr;
+ }
+
+ while(true) {
+ cache.length = 0;
+ if(chars.some(predicate)){
+ this.seek(l);
+ return result;
+ }
+
+ result.next = this.read();
+ if(result.next) {
+ result.value += result.next;
+ } else break;
+ }
+
+ return result;
+ };
+
+ return reader;
+}();
+ var parseCSS = (function(){
+ 'use strict';
+
+ Reader.prototype.readQuotedUntil = function(chars){
+ var result = '', block;
+ if(typeof chars == 'string') chars = [chars];
+ chars.push('"', "'", '/*');
+
+ while(block = this.readUntil(chars)){
+ result += block.value;
+ if(block.next === '"' || block.next === "'"){
+ result += block.next;
+ block = this.readUntil(block.next);
+ result += block.value + block.next;
+ } else if(block.next === '/*') {
+ this.readUntil('*/');
+ } else break;
+ }
+
+ return { value: result, next: block.next };
+ };
+
+ function Sheet(){
+ this.media = {};
+ }
+ Sheet.prototype.toString = function(){
+ var media = this.media;
+ return Object.keys(media).map(function(name){
+ return media[name];
+ }).join('\n\n');
+ };
+ Sheet.prototype.getMedia = function(name){
+ return this.media[name] || (this.media[name] = new Media(this, name));
+ };
+
+ function Media(sheet, name){
+ this.sheet = sheet;
+ this.name = name;
+ this.rules = [];
+ }
+ Media.prototype.toString = function(){
+ var isall = this.name === 'all';
+ return (isall ? '' : ('@media ' + this.name + ' {\n')) +
+ this.rules
+ .filter(function(rule){ return !rule.disabled; })
+ .join('\n') +
+ (isall ? '' : '\n}');
+ }
+
+ function Import(media, url){
+ this.url = url;
+ this.media = media;
+ }
+ Import.prototype.toString = function(){
+ var tab = this.media.name === 'all' ? '' : ' ';
+ return tab + '@import url("' + this.url + '");'
+ }
+
+ function Rule (media, selector, properties){
+ this.media = media;
+ this.selector = selector;
+ this.disabled = false;
+ this.properties = properties;
+ }
+ Rule.prototype.toString = function(){
+ var tab = this.media.name === 'all' ? '' : ' ';
+ return tab + this.selector + ' {' + this.properties.map(function(prop){
+ return '\n ' + tab + prop.name + ': ' + prop.value + ';';
+ }).join('') + '\n' + tab + '}';
+ }
+
+ function parseProperties(text){
+ var props = [], rdr = new Reader(text), block;
+
+ while((block = rdr.readQuotedUntil(':')).next) {
+ props.push({
+ name: block.value.trim(),
+ value: rdr.readQuotedUntil(';').value.trim()
+ });
+ }
+ return props;
+ }
+
+ function parseImport(text){
+ var rdr = new Reader(text), block = rdr.readUntil('('), peek = rdr.peek();
+ if(peek === '"' || peek === "'"){
+ block = rdr.readUntil(peek);
+ } else {
+ block = rdr.readUntil(')');
+ }
+
+ return { url: block.value, media: (rdr.readAll() || 'all').trim() }
+ }
+
+ return function parse(css){
+ var rdr = new Reader(css), sheet = new Sheet(), media, block, temp;
+
+ while(true){
+ block = rdr.readQuotedUntil(['{', '}', ';']);
+ block.value = block.value.trim();
+ temp = block.value.toLowerCase();
+
+ if(block.next === '{'){
+ if(temp.substr(0,6) === '@media'){
+ media = sheet.getMedia(block.value.substr(6).trim().toLowerCase());
+ } else {
+ if(!media) {
+ media = sheet.getMedia('all');
+ }
+
+ media.rules.push(new Rule(
+ media, block.value,
+ parseProperties(rdr.readQuotedUntil('}').value)
+ ));
+ }
+
+ } else if(block.next === ';') {
+ if(temp.substr(0,7) === '@import') {
+ var importd = parseImport(block.value), tempmedia = sheet.getMedia(importd.media);
+ tempmedia.rules.push(new Import(tempmedia, importd.url));
+ }
+ } else {
+ media = undefined;
+ if(!block.next) {
+ break;
+ }
+ }
+ }
+
+ return sheet;
+ };
+})();
+ return parseCSS;
+})();
View
6 bin/parseCSS.min.js
@@ -0,0 +1,6 @@
+var parseCSS=function(){var g=function(){var e=function(d){this.text=(d||"")+"";this.position=-1;this.length=this.text.length};e.prototype.read=function(d){var e=this.peek(d);this.position=Math.min(this.length,this.position+(d||1));return e};e.prototype.readAll=function(){if(!(this.position>=this.length)){var d=this.text.substr(this.position+1);this.position=this.length;return d}};e.prototype.peek=function(d){return this.position+1>=this.length?void 0:this.text.substr(this.position+1,d||1)};e.prototype.seek=
+function(d,e){this.position=Math.max(0,Math.min(this.length,(e===0?0:e===2?this.length:this.position)+(d||1)));return this.position===this.length};e.prototype.readUntil=function(d){function e(c){f=c.length;a.next=b[f]||(b[f]=g.peek(f));return a.next===c}typeof d==="string"&&(d=[d]);for(var f,g=this,b=[],a={value:"",next:""};;){b.length=0;if(d.some(e)){this.seek(f);break}a.next=this.read();if(a.next)a.value+=a.next;else break}return a};return e}();return function(){function e(){this.media={}}function d(b,
+a){this.sheet=b;this.name=a;this.rules=[]}function i(b,a){this.url=a;this.media=b}function f(b,a,c){this.media=b;this.selector=a;this.disabled=false;this.properties=c}function j(b){for(var a=[],b=new g(b),c;(c=b.readQuotedUntil(":")).next;)a.push({name:c.value.trim(),value:b.readQuotedUntil(";").value.trim()});return a}g.prototype.readQuotedUntil=function(b){var a="",c;typeof b=="string"&&(b=[b]);for(b.push('"',"'","/*");c=this.readUntil(b);)if(a+=c.value,c.next==='"'||c.next==="'")a+=c.next,c=this.readUntil(c.next),
+a+=c.value+c.next;else if(c.next==="/*")this.readUntil("*/");else break;return{value:a,next:c.next}};e.prototype.toString=function(){var b=this.media;return Object.keys(b).map(function(a){return b[a]}).join("\n\n")};e.prototype.getMedia=function(b){return this.media[b]||(this.media[b]=new d(this,b))};d.prototype.toString=function(){var b=this.name==="all";return(b?"":"@media "+this.name+" {\n")+this.rules.filter(function(a){return!a.disabled}).join("\n")+(b?"":"\n}")};i.prototype.toString=function(){return(this.media.name===
+"all"?"":" ")+'@import url("'+this.url+'");'};f.prototype.toString=function(){var b=this.media.name==="all"?"":" ";return b+this.selector+" {"+this.properties.map(function(a){return"\n "+b+a.name+": "+a.value+";"}).join("")+"\n"+b+"}"};return function(b){for(var a,c,b=new g(b),d=new e,h;;)if(a=b.readQuotedUntil(["{","}",";"]),a.value=a.value.trim(),c=a.value.toLowerCase(),a.next==="{")c.substr(0,6)==="@media"?h=d.getMedia(a.value.substr(6).trim().toLowerCase()):(h||(h=d.getMedia("all")),h.rules.push(new f(h,
+a.value,j(b.readQuotedUntil("}").value))));else if(a.next===";"){if(c.substr(0,7)==="@import")c=new g(a.value),a=c.readUntil("("),a=c.peek(),a=a==='"'||a==="'"?c.readUntil(a):c.readUntil(")"),a=a.value,c=(c.readAll()||"all").trim(),c=d.getMedia(c),c.rules.push(new i(c,a))}else if(h=void 0,!a.next)break;return d}}()}();
View
25 make.ps1
@@ -8,23 +8,30 @@ function minify-js($js) {
}
function build-parser() {
- $parseCSS = [System.IO.File]::ReadAllText((absolute-ref("parseCSS.js")))
- $reader = [System.IO.File]::ReadAllText((absolute-ref("reader.js")))
+ $parseCSS = read-text parseCSS.js
+ $reader = read-text reader.js
$all = "var parseCSS = (function(){
$reader
$parseCSS
return parseCSS;
-})();";
+})();"
- $allmin = minify-js($all)
-
- [System.IO.File]::WriteAllText((absolute-ref("bin\\parseCSS.js")), $all)
- [System.IO.File]::WriteAllText((absolute-ref("bin\\parseCSS.min.js")), $allmin)
+ write-text bin\\parseCSS.js $all
+ $allmin = minify-js $all
+ write-text bin\\parseCSS.min.js $allmin
+}
+
+function read-text ($fn){
+ return [System.IO.File]::ReadAllText((absolute-ref $fn))
+}
+
+function write-text ($fn, $text){
+ [System.IO.File]::WriteAllText((absolute-ref $fn), $text)
}
-function absolute-ref ($file) {
- return [System.IO.Path]::Combine([System.Environment]::CurrentDirectory, $file)
+function absolute-ref ($fn) {
+ return [System.IO.Path]::Combine([System.Environment]::CurrentDirectory, $fn)
}
build-parser
View
6 makefile
@@ -1,6 +0,0 @@
-files = reader.js parseCSS.js
-
-all: bin/parseCSS.js
-
-bin/parseCSS.js: ${files}
- cat > $@ $^
View
88 parseCSS.js
@@ -20,8 +20,53 @@ var parseCSS = (function(){
return { value: result, next: block.next };
};
- function getOrAddMedia(sheet, name){
- return sheet[name] || (sheet[name] = { name: name, rules: [] });
+ function Sheet(){
+ this.media = {};
+ }
+ Sheet.prototype.toString = function(){
+ var media = this.media;
+ return Object.keys(media).map(function(name){
+ return media[name];
+ }).join('\n\n');
+ };
+ Sheet.prototype.getMedia = function(name){
+ return this.media[name] || (this.media[name] = new Media(this, name));
+ };
+
+ function Media(sheet, name){
+ this.sheet = sheet;
+ this.name = name;
+ this.rules = [];
+ }
+ Media.prototype.toString = function(){
+ var isall = this.name === 'all';
+ return (isall ? '' : ('@media ' + this.name + ' {\n')) +
+ this.rules
+ .filter(function(rule){ return !rule.disabled; })
+ .join('\n') +
+ (isall ? '' : '\n}');
+ }
+
+ function Import(media, url){
+ this.url = url;
+ this.media = media;
+ }
+ Import.prototype.toString = function(){
+ var tab = this.media.name === 'all' ? '' : ' ';
+ return tab + '@import url("' + this.url + '");'
+ }
+
+ function Rule (media, selector, properties){
+ this.media = media;
+ this.selector = selector;
+ this.disabled = false;
+ this.properties = properties;
+ }
+ Rule.prototype.toString = function(){
+ var tab = this.media.name === 'all' ? '' : ' ';
+ return tab + this.selector + ' {' + this.properties.map(function(prop){
+ return '\n ' + tab + prop.name + ': ' + prop.value + ';';
+ }).join('') + '\n' + tab + '}';
}
function parseProperties(text){
@@ -33,32 +78,47 @@ var parseCSS = (function(){
value: rdr.readQuotedUntil(';').value.trim()
});
}
-
return props;
}
+
+ function parseImport(text){
+ var rdr = new Reader(text), block = rdr.readUntil('('), peek = rdr.peek();
+ if(peek === '"' || peek === "'"){
+ block = rdr.readUntil(peek);
+ } else {
+ block = rdr.readUntil(')');
+ }
+
+ return { url: block.value, media: (rdr.readAll() || 'all').trim() }
+ }
return function parse(css){
- var rdr = new Reader(css), sheet = {}, media, block, temp;
+ var rdr = new Reader(css), sheet = new Sheet(), media, block, temp;
while(true){
- block = rdr.readQuotedUntil(['{', '}']);
+ block = rdr.readQuotedUntil(['{', '}', ';']);
block.value = block.value.trim();
+ temp = block.value.toLowerCase();
if(block.next === '{'){
- if(block.value.toLowerCase().substr(0,6) === '@media'){
- temp = block.value.substr(6).trim();
- media = getOrAddMedia(sheet, temp);
+ if(temp.substr(0,6) === '@media'){
+ media = sheet.getMedia(block.value.substr(6).trim().toLowerCase());
} else {
if(!media) {
- media = getOrAddMedia(sheet, 'all');
+ media = sheet.getMedia('all');
}
- media.rules.push({
- selector: block.value,
- properties: parseProperties(rdr.readQuotedUntil('}').value)
- });
+ media.rules.push(new Rule(
+ media, block.value,
+ parseProperties(rdr.readQuotedUntil('}').value)
+ ));
}
-
+
+ } else if(block.next === ';') {
+ if(temp.substr(0,7) === '@import') {
+ var importd = parseImport(block.value), tempmedia = sheet.getMedia(importd.media);
+ tempmedia.rules.push(new Import(tempmedia, importd.url));
+ }
} else {
media = undefined;
if(!block.next) {
View
7 reader.js
@@ -12,6 +12,13 @@ var Reader = function(){
this.position = Math.min(this.length, this.position + (len || 1));
return value;
};
+
+ reader.prototype.readAll = function(){
+ if(this.position>=this.length) return undefined;
+ var value = this.text.substr(this.position + 1);
+ this.position = this.length;
+ return value;
+ };
reader.prototype.peek = function(len){
if((this.position+1)>=this.length) return undefined;
View
12 test-parser.html
@@ -3,12 +3,18 @@
<head>
<script src="reader.js"></script>
<script src="parseCSS.js"></script>
-
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<head>
<body>
<script>
- var sheet = parseCSS(' test /*test*/ { color: red; }' );
- console.log(sheet);
+ var sheet;
+ $.get('test.css')
+ .success(function(txt){
+ sheet = parseCSS(txt);
+ console.log(sheet, sheet.media.print);
+ $('<pre/>').html(sheet.toString()).appendTo('body');
+ });
+
</script>
View
12 test.css
@@ -0,0 +1,12 @@
+@import url(http://www.example.org/styles/adv-style.css) print;
+
+@media screen {
+ h1 {border-bottom: 2px solid gray; margin-bottom: 0.25em;}
+ a:link, a:visited {background: #FF9; font-weight: bold;}
+ a:link {color: blue;}
+ a:visited {color: purple;}
+}
+
+@media print {
+ body { background: white; }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.