JavaScriptでCSSパーサーを書くための情報を収集中(2日目)

はい、JavaScriptクロスブラウザCSSパーサー の2日目です。
今日は、<link rel="stylesheet" type="text/css" href="a.css" /> の読み込みや @import url(...) への対応にトライしてみます。

これが HTML です。
ロードが終わると、CSSの情報をかき集めて表示します。

<!DOCTYPE html><html><head><title></title>
<link rel="stylesheet" type="text/css" href="a.css" />
<style type="text/css"><!--
  @import url('e.css');
  :digit {
    color: pink;
  }
/* hoge
 * hoge
 * hoge */
--></style>
</head>
<body>
<script src="uuMeta.js"></script>      // ブラウザ判別など
<script src="uuCSSParser.js"></script> // 主役
<script src="uuURL.js"></script>       // URLハンドラ
<script src="uuAjax.js"></script>      // uuAjax.sync() 同期読込機能を利用
<script>
window.onload = function() {
  alert(uuCSSParser.parse()); // かき集めて表示
};
</script>
</body></html>

折角なので、CSS ファイルの中でさらに、CSSファイルを読み込むようにしてあります。
最終的には a.css, b.css, c.css, d.css, e.css の5つのファイルが読み込まれます。

a.css

@import "b.css";
@import "d.css";

/* a.css */
a {
  background-color: #aaa;
}

b.css

@import url("c.css");

/* b.css */
b {
  text-align: center;
}
b {
  background-color: #bbb;
}

c.css

/* c.css */
canvas {
  background-color: #ccc;
}

d.css

/* d.css */
div {
  background-color: #ddd;
}

e.css

/* e.css */
pre {
  background-color: #eee;
}

uuCSSParser.js です。
今は、CSSファイルをかき集めたり、コメントを除去するぐらいのことしかやっていません。

(function() {
var _cssparser, // inner namespace
    _ie = document.uniqueID,
    MEMENTO = "uuCSSParserMemento";

_cssparser = {
  parse: function() {
    // IEなら、style 要素のテキスト情報(生のCSS)が失われないように、
    // element.uCSSParserMemento に退避(固着)しておく。
    // なぜこの処理が必要かは、昨日の日記参照
    _ie && _cssparser._memento();

    var rv = _cssparser._collect();
    // strip comment
    return rv.replace(/<!\-\-|\-\->/g, ""). // <!-- ... -->
              replace(/\/\*[^*]*\*+([^\/][^*]*\*+)*\//g, ""). // /* ... */
              replace(/\s*[\r\n]+/g, "\n"); // ___[CRLF]
  },

  _memento: function() {
    var node = document.getElementsByTagName("style"),
        i = 0, iz = node.length;

    for (; i < iz; ++i) {
      node[i][MEMENTO] = node[i].innerHTML;
    }
  },

  _collect: function() {
    var rv = [], node = document.styleSheets, i = 0, iz = node.length,
        key = _ie ? "owningElement" : "ownerNode",
        key2 = _ie ? MEMENTO : "textContent",
        rex = /(?:^|[\r\n]+|\s+)@import\s*(?:url)?[\("']+\s*([\w\.\/\+\-]+)\s*["'\)]+\s*([\w]+)?\s*;/g;

    // @import url(...) を見つけたら再帰的に読み込む
    // CSSの仕様(というよりブラウザの実装かな?)では、@規則はルールの前に記述することになっているが、
    // 現在は CSS の途中に @import を見つけても include してしまう。
    function load(css) {
      return css.replace(rex, function(m, url, media) {
        return load(uuAjax.sync(url));
      });
    }

    for (; i < iz; ++i) {
      if (!node[i].disabled) { // style.disabled = true なら無視する
        if (node[i].href &&    // link には URLが設定されている
            !/\.html?$/.test(node[i].href)) { // Firefox2.0 は style要素にページのURLが設定されてしまうので(バグ)
                                              // 拡張子が .htm または .html なら対象外としている
                                              // 拡張子を持たない HTML ページで問題になりそうなので見直しが必要かも
          // <link href="..."> を読み込む
          rv.push(load(uuAjax.sync(node[i].href)));
        } else {
          // <style>...</style> を読み込む。
          // IE なら退避しておいた element.uCSSParserMemento から読み込む
          rv.push(load(node[i][key][key2]));  // 
        }
      }
    }
    return rv.join(""); // 読み込んだCSSファイルの内容を結合して返す
  }
};
window.uuCSSParser = _cssparser; // export
})(); // uuCSSParser scope

実行するとこうなります

canvas {
  background-color: #ccc;
}
b {
  text-align: center;
}
b {
  background-color: #bbb;
}
div {
  background-color: #ddd;
}
a {
  background-color: #aaa;
}
pre {
  background-color: #eee;
}
  :digit {
    color: pink;
  }

!important と specificity(重み付け) については次回