m2

僕たちプログラマーは、プログラミングに、Excelを使います!

なんとかカレンダー 21日目 id:miya2000 です。こんばんは!

低級テキストエディタでせっせとコーディングしているあなた!そろそろ手がが疲れてきていませんか?

そんなあなたは、この記事で紹介する Excel コーディングをぜひ覚えてください。
わずか数秒で世界が変わります!

元ネタ

めんどうな作業がわずか数秒に!新人デザイナーが 知らないと一生後悔するExcelを使ったHTML生成 | Webロケッツマガジン
http://webrocketsmagazine.com/entry/20111209/html-code-generation-using-excel.html

Excelを使った高級コーディング

とにかく Excel でコーディングを行った画面を見てみましょう。今回は Java のコーディングをやってみます。

どうでしょう? 画面を見るだけでどれだけ恩恵があるかお分かりになるでしょうか。
画像にも書いていますが、パッと思いつくだけで以下のメリットがあります。

  • インデントをセルの幅で行うことで 2tab 4tab 戦争が終結
  • 行頭以外を縦にそろえるのも列を揃えてあげれば簡単!
  • 「連続データ」機能で簡単入力!
  • コメントが別レイヤになり、ソースの邪魔にならずスッキリ!
  • マクロ機能で様々な機能を追加可能!

インデントや文字の位置揃え等、プログラムの本質以外の部分で時間を使う時代は終わりました!
そしてコメントは視覚的にもコメントらしくなり、「長文コメントのせいでソースが縦に長くなってスクロールめんどい」なんてこともありません!


ソースコードはテキストファイルである必要はなかったんや〜」
「な、なんだってー!」

でも、コンパイルも実行もできなんでしょう?

Excel でコーディングしてもコンパイルも実行もできない… そんなふうに考えていた時期が俺にもありました
とはいえシートからコードを抽出してファイルに出してコンパイルすればいいだけなので、その程度のことはマクロで書いちゃいましょう。

クラスパスは「.classpath」のシートに列挙してそこから読み出すようにしました。

実行引数も「.args」のシートに列挙。値の列挙も Excel が得意とする部分です。

あとは「開発」→「マクロ」からマクロを実行します。

結果はこんな感じです。ちゃんと実行できましたね!

Excel コーディングの目玉! 「連続データ」機能!

「連続データ」機能の威力をお見せしましょう!
現在 data10 まであるプロパティを datra15 まで増やしたいとします。
やり方は簡単で data10 のセルの右下の部分を右クリック→下へドラッグするだけ!

ドラッグ後に表示されるコンテキストメニューで「連続データ」を選択すると、ホラ! 「data10」の数字部分が 10 からの連番になってセルが埋められるんです!

まとめ

以上、id:miya2000 でした!

最近のブラウザで flash に半透明 div を被せたらどうなるか

flash に「wmode="opaque"」つけると HTML 要素を被せられることはflash 上でマウスジェスチャーできるようにする user.js (wmode="opaque") - m2O - チーム俺等で書きましたが、最近のブラウザでどうなっているのかというのを試してみました。

サンプルとして使用したのは以下の動画です。

FF6オペラを(opera)ボーカロイドで(vocaloid)ver高音質(high-quality)‬‏ - YouTube

このページを開いて以下のブックマークレットを実行しました。

javascript:void(document.body.appendChild(document.createElement('div')).style.cssText='position:absolute;zIndex:9999;top:50px;left:100px;width:300px;height:300px;background:rgba(255,255,0,0.5)')

Opera10.10


私が今でも愛用している Opera10.10 です。
古いブラウザですので、前述のように「wmode="opaque"」を付けないと z-index に関わらず上に被せることができません。

「wmode="opaque"」を付けると上に被せることができて、被せた部分が半透明になり、下の動画が見えるようになります。

Opera12 (alpha)


Opera の現在の開発バージョンである 12 です。
被せている部分の動画は見えず、白く(動画の下の要素が)見えています。これは現在の安定バージョン(11.50) と同じ動きです。
Opera さんはほんとに奇妙な実装をしますね。

画像は省略しますが、Opera10.10 と同じように「wmode="opaque"」を付けると上に被せることができて、被せた部分が半透明になり、下の動画が見えるようになります。

Firefox 5


Firefox 最新バージョンの 5 です。
Opera10.10 と同じです。

こちらも画像は省略しますが、「wmode="opaque"」を付けると上に被せることができて、被せた部分が半透明になり、下の動画が見えるようになります。

Google Chrome 12


「wmode="opaque"」をつけなくても、被せた部分がきちんと半透明になり、下の動画が見えています。期待通りの動きです。

こちらも Firefox5 と同じでした。

まとめ

Opera12 での挙動が奇妙だったので他のブラウザも調べてみたら、
Google Chrome が期待通りの動きをしてくれて驚きました。

が、他のブラウザを見ると
まだまだ HTML 要素を重ねて使用したい場合は wmode をつけた方が良いようです。

wmode については 日本語入力が行えなくなる などの不具合が言われていましたが、最近の flash プレイヤーではどんなんですかね? その辺は調べてません。

jQuery can't select elements by completely valid CSS selector "$('.\\')" but makes unwished DOM element


I posted a ticket but it was rejected as a sapn. So I wrote it my blog and linked it.

Trac Error

Submission rejected as potential spam (Akismet says content is spam)

http://bugs.jquery.com/newticket

http://bugs.jquery.com/ticket/9776

    • -

".\" is a completely valid CSS selector.

CSS Validator Result:
http://jigsaw.w3.org/css-validator/validator?text=.tab%5C%3Cimg%5C%3E+%7B%0D%0A++++background-color+%3A+%23ffc0cb%3B+%0D%0A%7D+%0D%0A.tab%5C%3CHello%5C%21%3Abefore+%7B+%0D%0A++++content+%3A+%22Hello%21+%22%3B+%0D%0A%7D%0D%0A.World%5C%21%5C%3E%3Aafter+%7B+%0D%0A++++content+%3A+%22+World%21%22%3B+%0D%0A%7D%0D%0A&profile=css21&usermedium=all&type=css&warning=2&vextwarning=


But jQuery can't find elements by the selector.

jQuery Result:
http://jsfiddle.net/nH26U/1/
http://jsfiddle.net/nH26U/2/

Solution:
Ticket #9521

Togetter を下までスクロールすると重くなっていたので Lazy Load Plugin for jQuery にパッチを書いた

https://github.com/miya2000/jquery_lazyload/commit/a739355d631209f0e482f7523dbc436aac05dba4

Togetter でとトギャられたページを下までスクロールすると重くなっているのを確認しました。たしか Chrome, Firefox だとそれほどでもなかった気がしますが、Opera, ie では顕著です。

何が起こっているのかといいますと、Togetter では Lazy Load Plugin for jQuery を使って画像の遅延ロードを行っているのですが、

この「原発」・「被災」等の注目のキーワードの下にけっこうな数の画像(はてブ数、Twitterアイコン)があるのですね。これが LazyLoad できずにスクロールイベント毎に、残りの画像すべて(100個くらい)をループで回していました。スクロールイベントはホイール1回で3回くらい発生しますので、これまたけっこうな数です。

	var counter = 0;
	elements.each(function() {
	    if ($.abovethetop(this, settings) ||
	        $.leftofbegin(this, settings)) {
	            /* Nothing. */
	    } else if (!$.belowthefold(this, settings) &&
	        !$.rightoffold(this, settings)) {
	            $(this).trigger("appear");
	    } else {
	        if (counter++ > settings.failurelimit) {
	            return false;
	        }
	    }
	});

非表示の画像は最初の if ブロックに入りますので、$.abovethetop がぼちぼち重たいということになります。
これを避けるため、、ループの先頭で非表示の画像は読み飛ばすコードを追加しました。

一番下までスクロールしないと再現しないのは、それまでは最後の else に入って、途中でループを終えていたからです*1

  • -

Lazy Load Plugin for jQuery は

Lazy Load is currently not usable. It does not work with the latest browsers as expected. I have currently no time updating the code myself.

http://www.appelsiini.net/projects/lazyload

とご本人がおっしゃっているように、今後積極的にアップデートされることは無いと思われます。
実装をみるとまだまだ改善できるところはあると思いますので、どなたか挑戦してみるとよいのではないでしょうか。

  • -

Togetter さんでは、上のパッチを適用するのもいいですが、そもそも該当部分の LazyLoad は不要だと思うので、外してもらうのがいいのではないかと思います。

  • -

Togetter の application.js は minify されていたのですが、id:os0x が紹介してくれていた Chrome の Developer Tools を使うことで難なくデバッグすることが出来ました。Opera でもできるといいですね。

  • -

Opera でページ側のスクリプトをハックする時は、こんな user.js を書いて、ページ側のスクリプトを上書きしています。

// ==UserScript==
// @include http://togetter.com/li/*
// ==/UserScript==
(function(){
    opera.addEventListener('AfterScript', function(e) {
        var script = e.element;
        if (script.src && /application\.js/.test(script.src)) {
            _ll(jQuery);
        }
    }, false);

    function _ll($) {
        ...

*1:画像を上から走査していき、表示領域より下に位置する画像が settings.failurelimit 個(2個)見つかったらループを終える、という実装です

境界マッチを含む置換をすると予想外の動きをするかも

javascript:(function(){
  var r = /(?:^|.|$)/g;
  var s = "abc";
  s.replace(r, function($0) {
    alert($0);
  });
})()
// => '', 'b', 'c', ''

後方参照の直後に他の数字が続くような置換パターン

2011/08/11 追記


このエントリで紹介した Opera11 の挙動は Opera12 の開発版で修正されたようです。

これも私が報告しました。発見したのは miya2000 さんだったと思います。

alert("123456789X".replace(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/, '$010'));

これで本来は10と出るはずがXと出てました。

http://orera.g.hatena.ne.jp/edvakf/20110809/1312910857

  • -
"123456789X".replace(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/, '$10');

みたいな置換をしたときに、「'X'」(10番目として解釈)になってほしいのか「'10'」(1番目+'0')になってほしいのかわからないよね、と言う話。
JavaScript で言えばどのブラウザでも「'X'」(10番目として解釈)になっていて、おそらく他の正規表現処理系でも同じなんじゃないかなと思います。
というわけで「1番目+'0'」と解釈させるにはどう書けばいいか、という視点で進めていきましょう。

JavaScript の場合

JavaScript の場合は replace の第2引数に function が書けますから、それを使うのが無難です*1

javascript:(function(){
  alert("123456789X".replace(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/, function($0,$1){
    return $1 + '0';
  }))
})() 


それから、第2引数が function じゃない場合の仕様をあたってみました。第2引数が function じゃない場合は文字列扱いし、その文字列の中の「$」の後に数字が続く時は以下のように置換されます。

Characters Replacement text
$n n を 1 桁の数字 1-9 、かつ $n に10進数が続かないものとして、 n 番目の捕捉。 n≤m かつ n 番目の捕捉が undefined ならば、代わりに空文字列を使用する。 n>m ならば、 結果は実装定義である。
$nn nn を 2 桁の数字 01-99 として、 nn 番目の捕捉。 nn≤m かつ nn 番目の補足が undefined ならば、代わりに空文字列を使用する。 nn>m ならば、 結果は実装定義である。

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/15-5_String_Objects.html#section-15.5.4.11

これを読むと1桁扱いさせるには「$01」と表記すればよさそう…なんですが、数字が3桁以上続く場合にどうするかが規定されていません。(「2桁まで」と規定されている、とも読めますが。)

そういうわけで「$010」の扱いが Opera11 と他のブラウザで解釈が異なります。

javascript:(function(){
  alert("x23456789X".replace(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/, '$010'));
})()
// Opera11 => 'X'  (Opera10.10 => '$010')
// Other   => 'x0'

function で置換すれば解釈の違いは発生しません。

(function(){
  var chars = ['あ'.charCodeAt(0)];
  var regStr = ['(.)'];
  for (var i = 1; i < 82; i++) {
    chars[i] = chars[0] + i;
    regStr[i] = regStr[0];
  }
  var str = String.fromCharCode.apply(null, chars);
  var replaced = str.replace(new RegExp(regStr.join('')), function() {
    var m = arguments;
    return m[78] + '0';
  });
  alert(replaced);
  alert(str);
})()

Java の場合

Java の場合は単純に「\」(文字列リテラルで表現すると「\\」)でエスケープできます。String#replaceAll は最終的に Matcher#appendReplacement を呼びますから、その仕様に準じます。

置換文字列内でバックスラッシュ (\) とドル記号 ($) を使用すると、それをリテラル置換文字列として処理した場合とは結果が異なる場合があります。ドル記号は、先に説明したとおり、前方参照された部分シーケンスへの参照として処理される場合があり、バックスラッシュは置換文字列内のリテラル文字をエスケープするのに使用されます。

http://java.sun.com/javase/ja/6/docs/ja/api/java/util/regex/Matcher.html#appendReplacement(java.lang.StringBuffer, java.lang.String)

ソースを読む限り、何桁になっても大丈夫です。
また、Java の場合「$0」はマッチした部分文字列全体を指す(JavaScript での「$&」)という違いがあります。

PHP の場合

PHP は詳しくないんですが、@m61k さんに教えてもらいました。ありがとうございます。

例えば、\\11 は、 後方参照 \\1 の後にリテラル 1 が続くのか、後方参照 \\11 で その後には何も続かないのかが不明のため、 preg_replace() を混乱させる可能性があります。 この場合、解決策は、\${1}1 を使用することです。 こうすることで、1 はリテラルとなり、後方参照 $1 を明確に作成できます。

http://jp2.php.net/manual/ja/function.preg-replace.php

jQuery 1.4.4 で使えなくなってるセレクタ

jQuery 1.4.4 の document.querySelectorAll を使う部分で、属性値をクォートする(ような)コードが入っています。
(L.4084)

// Make sure that attribute selectors are quoted
query = query.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");

うーん、なんで必ずクォートされないといかんのだろ。少なくとも Opera ではどっちでも動くみたい。
(Google トップページで)

javascript:alert(document.querySelectorAll('input[name=q]').length) // 1
javascript:alert(document.querySelectorAll('input[name="q"]').length) // 1

これによって Struts なんかの indexed な name の要素が取得できないようになっています。(たとえきちんとエスケープしていてもです!)

javascript:(function(){
  var query = 'input[name=hoge\\[0\\]\\.fuga]';
  alert(query.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']")); // input[name='hoge\[0\']\.fuga]
})()

で、チケットを検索したら数多く報告されているようです。

This is a known regression and has been reported numerous times. You wouldn't have run into this issue when using the syntax recommended on api.jquery.com. Which says "Quotes are mandatory." for attribute values in attribute selectors. Like this
$(':input[name$="[bar]"]')
It has been fixed, you can check that with the recently released 1.5rc1 version in this test case

http://bugs.jquery.com/ticket/8058

属性値はクォートしてね、とのこと。

jQuery('[attribute="value"]')
attribute
An attribute name.
value
An attribute value. Quotes are mandatory.

http://api.jquery.com/attribute-equals-selector/


余談ですが
実は 1.4.2 では属性値をエスケープしなくても $('input[name=hoge[0].fuga]') のような要素が取得できます。これはなぜかと言うと
jQuery 1.4.2

Sizzle = function(query, context, extra, seed){
    context = context || document;

    // Only use querySelectorAll on non-XML documents
    // (ID selectors don't work in non-HTML documents)
    if ( !seed && context.nodeType === 9 && !isXML(context) ) {
        try {
            return makeArray( context.querySelectorAll(query), extra );
        } catch(e){}
    }

    return oldSizzle(query, context, extra, seed);
};

例外が発生して oldSizzle が何とかしてくれるからです。
動くとはいえこれでは querySelectorAll が使われません(遅い)し、CSSセレクタとしても Valid ではありませんので修正すべきです。前述のとおりクォートすれば CSSjQueryセレクタとして問題ありません。

  • -


結論として、同じ 1.4 系でも 1.4.4 はバグセレクタの仕様が大きく変わっています。現状動いているならバージョンアップは控えるべきでしょう。

(2011/02/28 追記)
・冒頭で挙げているように、jQuery 1.4.4 はユーザーの入力したセレクタを改変してしまう不完全なクォート処理が含まれていますので、私はオススメしません。
jQuery で属性セレクタを書く時は常に属性値をクォートするようにしましょう。

  • -

(2011/02/24 追記)

間違ったセレクタを使っているコードを全部直してverupするのが正解では?

http://b.hatena.ne.jp/monjudoh/20110223#bookmark-31495385

ありがとうございます。問題なく動いていたので、『間違った』セレクタだとは思ってませんでした。

querySelector(All) で使用できる属性セレクタの仕様は CSS3 を見ればよさそうです。

Attribute values must be CSS identifiers or strings. [CSS21] The case-sensitivity of attribute names in selectors depends on the document language.

6.3.2. Substring matching attribute selectors - Selectors Level 3

CSS identifiers」か「strings」でなければならない、とのことです。
まず先に「string」というのは今回話題になっている「クォートされた文字列」のことです。

Strings can either be written with double quotes or with single quotes. Double quotes cannot occur inside double quotes, unless escaped (e.g., as '\"' or as '\22'). Analogously for single quotes (e.g., "\'" or "\27").
"this is a 'string'"
"this is a \"string\""
'this is a "string"'
'this is a \'string\''

4.3.7 Strings - 4 Syntax and basic data types - Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

この後に適切にエスケープすれば改行も含められると書いてあります。

CSS identifiers」については以下のような仕様になっています。

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_);

4.1.3 Characters and case - 4 Syntax and basic data types - Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

アルファベット・数字・ハイフン・アンダースコアと「U+00A0」以上のユニコード文字だけが使える、と。
んーですがこの後に

Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".

4.1.3 Characters and case - 4 Syntax and basic data types - Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

『「\」でエスケープできるよ』とあり、CSS 的には「hoge\[0\]\.fuga」は間違ったセレクタでは無いように思えます。

時間が無いのでここまで。

  • -

(2011/02/28 追記)
エスケープしていない Invalid なセレクタを使用するために jQuery 1.4.2 を推奨するような記述になっていたため、修正しました。