JavaScriptでCSSパーサーを書くための情報を収集中(4日目)
4日目です。今日は、
- レガシースタイル(<<font size="+1" color="red"> や <strike> など)を収集する。 ⇒ _collectLegacyStyle()
- インラインスタイルを収集する。 ⇒ _collectInlineStyle()
- body要素からターゲット要素までの絶対パスを生成する。⇒ _createPath()
といった機能を実装しています。
デモ
http://uupaa-js-spinoff.googlecode.com/svn/trunk/uuCSSParser.js/demo/fontcolor.htm
http://uupaa-js-spinoff.googlecode.com/svn/trunk/uuCSSParser.js/demo/inlinestyle.htm
レガシースタイル
レガシーなスタイルを収集し、重み付けゼロ(spec=0) で Hash に追加します。
<font color="" face="" size=""> <b>, <i>, <em>, <strong>, <big>, <small>, <s>, <strike>, <u> <body text="" link="" vlink="" alink="" bgcolor="" background="" background="">
インラインスタイル
インラインスタイル(<div style="...">) を収集し、重み付け1000(spec=1000) で Hash に追加します。
要素の絶対パスを生成し、ルールを合成します。
例: <body> <div> <p style="color: red">hoge</p> </div> </body> p要素のインラインスタイルを収集し、以下のルールを合成します。パスのルートは body になります。 body>div:nth-child(0)>p:nth-child(0) { color: red }
実装の抜粋( http://uupaa-js-spinoff.googlecode.com/svn/trunk/uuCSSParser.js/uuCSSParser.js )
今回から、uuMeta.js や uuQuery.js に依存するようになりました。uuMeta.createXHR() が new XMLHttpRequest() 、uuQuery() が querySelectorAll(), uuQuery.tag() が getElementsByTagName() 相当です。
(function() { var _cssp, // inner namespace _mm = uuMeta, _doc = document, _ie = _mm.ie, _int = parseInt, /* 略 */ // for Legacy Style SIZE = { 1: 0.64, 2: 0.8, 3: 1, 4: 1.2, 5: 1.44, 6: 1.73, 7: 2.07 }, TAGS = { b: "font-weight:bold", i: "font-style:italic", s: "text-decoration:line-through", u: "text-decoration:underline", em: "font-style:italic", // fuzzy big: "font-size:larger", small: "font-size:smaller", strike: "text-decoration:line-through", strong: "font-weight:bold" }; // fuzzy _cssp = { parse: function(css) { // @param CSSString(= ""): // @return Hash: { specs: [spec-num1, spec-num2, ...], // data[spec-num1]: { rule, sele, decl }, // data[spec-num2]: ... } var specs = [], data = {}; _ie && _cssp._memento(); if (!css) { _cssp._collectLegacyStyle(specs, data); // spec = 0 _cssp._collectInlineStyle(specs, data); // spec = 1000 css = _cssp._importStyleSheets(); } _cssp._collectStyleSheets(specs, data, css); specs.sort(function(a, b) { return a - b; // sort of number order }); return { specs: specs, data: data }; }, _memento: function() { /* 略 */ }, _collectStyleSheets: function(specs, data, css) { /* 略 */ }, _importStyleSheets: function() { /* 略 */ }, // collect legacy styles _collectLegacyStyle: function(specs, data) { function add(expr, decl) { data[0].push({ rule: expr + "{" + decl + "}", sele: expr, decl: [decl] }); } specs.push(0), data[0] = []; // add var node, v, w, n, nz, path; // <font color="" face="" size=""> node = uuQuery("font", _doc.body); for (n = 0, nz = node.length; n < nz; ++n) { v = node[n]; path = _cssp._createPath(v); v.color && add(path, "color:" + v.color); v.face && add(path, 'font-family:"' + v.face.split(",").join('","') + '"'); if (v.size) { w = 1; v.size.replace(/\-\d+/, function(m) { w = Math.pow(0.8, _int(m)); }). replace(/\+\d+/, function(m) { w = Math.pow(1.2, _int(m)); }). replace(/\d+/, function(m) { w = SIZE[_int(m)]; }); add(path, "font-size:" + (w * 100).toFixed(0) + "%"); } } // <b>, <i>, <em>, <strong>, <big>, <small>, <s>, <strike>, <u> node = uuQuery("b,i,em,strong,big,small,s,strike,u", _doc.body); for (n = 0, nz = node.length; n < nz; ++n) { v = node[n]; add(_cssp._createPath(v), TAGS[v.tagName.toLowerCase()]); } // <body text="" link="" vlink="" alink="" // bgcolor="" background=""> v = _doc.body; w = "color:"; v.text && add("body", w + v.text); v.link && add("a:link", w + v.link); v.vlink && add("a:visited", w + v.vlink); v.alink && add("a:active", w + v.alink); v.bgcolor && add("body", "background-" + w + v.bgcolor); v.background && add("body", "background-image:url(" + v.background + ")"); !data[0].length && (specs.pop(), delete data[0]); // undo }, // collect inline styles _collectInlineStyle: function(specs, data) { function add(expr, decl) { data[1000].push({ rule: expr + "{" + decl + "}", sele: expr, decl: [decl] }); } specs.push(1000), data[1000] = []; // add var node, n, nz; node = uuQuery("html[style]"); for (n = 0, nz = node.length; n < nz; ++n) { add("html", node[n].style.cssText); } node = uuQuery("body[style]"); for (n = 0, nz = node.length; n < nz; ++n) { add("body", node[n].style.cssText); } node = uuQuery("[style]", _doc.body); for (n = 0, nz = node.length; n < nz; ++n) { add(_cssp._createPath(node[n]), node[n].style.cssText); } !data[1000].length && (specs.pop(), delete data[1000]); // undo }, _createPath: function(elm) { var rv = [], e = elm, body = _doc.body, v, i; for (; e !== body; e = e.parentNode) { for (v = e.parentNode.firstChild, i = 0; v !== e; v = v.nextSibling) { (v.nodeType === 1) && ++i; } rv.push(e.tagName + ":nth-child(" + i + ")"); } rv.reverse(); return "body>" + rv.join(">"); } }; window.uuCSSParser = _cssp; // export })(); // uuCSSParser scope
align と center要素について
align(<align="left">) や <center> に関してはノータッチです。
CSS には幅が定義されていない要素のセンタリングを的確に行う方法が存在せず、align は古いブラウザでもそれなりにサポートされているため、JavaScriptレベルではフォローしません。
次回は、
- 収集したルールをアレコレして、要素にスタイルを適用する
です。