JavaScript で数字を 3 桁ずつカンマで区切る (commify)

var commify = function(s) {
    return s.toString()
            .split('').reverse().join('')
            .replace(/(\d\d\d)(?=\d)(?!\d*\.)/g, '$1,')
            .split('').reverse().join('');
};


- refs.: Perl クックブック (レシピ 2.16)
  4873110378

APC と jQuery を利用してファイルアップロードの進行状況を表示する

  ネタ的には特別新しいものではないですが、気になっていたので試してみました。
  PHP 5.2.0 以降、APC 3.0.13 が必要です。

1. APC の設定

  普通に APC をインストールして、以下のように apc.rfc1867 を有効にするだけです。

apc.rfc1867 = On

2. アップロードフォームの準備

  普通のアップロードフォームです。
  ただし、"APC_UPLOAD_PROGRESS" という name 属性を持った hidden フィールドを用意します。値は、何でもいいのですが、ここでは "progress_key" という風にしています (本当はランダムにするほうがいいと思います)。

<form id="upload" action="upload.php" method="post" enctype="multipart/form-data">
<p>
<input type="hidden" name="APC_UPLOAD_PROGRESS" value="progress_key" />
<input type="file" name="file" />
<input id="submit_button" type="submit" value="アップロード" />
</p>
</form>

3. アップロードファイルの処理

  上記アップロードフォームの upload.php ですが、これは通常のファイルアップロード時の処理を書いてください。
  今回はテストなので何もしませ

<?php
// 通常のファイルアップロードの処理
?>

4. アップロードの進行状況を返す処理

  次のファイルを progress.php とします。

<?php
// progress.php
header('Content-type: application/json; charset=UTF-8');
$status = apc_fetch('upload_progress_key');
echo json_encode($status);
exit;
?>


  2 で指定した "APC_UPLOAD_PROGRESS" の値の先頭に upload_ をつけたものを apc_fetch() の引数に指定します。upload_ は設定で変更することが出来ます (apc.rfc1867_prefix)。
  apc_fetch() で取得できる値は次のようになります。

total          アップロードされるファイルのサイズ                              
current        現時点までに受信したファイルのサイズ                            
rate           アップロード速度 (byte/second)           アップロード完了時のみ
filename       ファイル名                                                      
name           <input type="file" /> の name 属性                              
temp_filename 一時ファイル名                           アップロード完了時のみ
cancel_upload アップロードがキャンセルされたかどうか   アップロード完了時のみ
done           アップロードが完了したかどうか                                  
start_time     アップロード開始日時の UNIX TIME                                
  • cancel_upload: 0 = キャンセルされていない、1 = キャンセルされた
  • done: 0 = 未完了、1 = 完了

  これらの値を JSON で出力しています。

5. jQuery を使って進行状況を取得する

  jQuery Form Plugin を使って、フォームを POST し、1 秒毎に getJSON で進行状況を取得します。
  また、getJSON でリクエストに現在日時を渡しているのは、ブラウザのキャッシュを利用しないようにするためです。

var timer = null;

var progress = function() {
    // progress.php を呼び出して進行状況を取得する
    $.getJSON('progress.php', { 'd': new Date().getTime() }, function(json) {
        // 進行状況を % で表示する
        $('#status').html(parseInt(json.current / json.total * 100) + '%');
    });
};

$(function() {
    $('#upload').submit(function() {
        timer = setInterval('progress()', 1000);
        // フォームを POST する
        $(this).ajaxSubmit(function() {
            clearInterval(timer);
            progress();
        });
        return false;
    });
});

6. デモ

  本当は、デモを用意したかったのですが、ここのレンタルサーバでは APC が使えないようでしたのでアップロード状況をキャプッチャしました。
  基本的には、上記の処理を行なっているだけですが、進行状況の表示の部分は Progress Bar Plugin を利用して、プログレスバーで表示しています。



  このデモで使用したファイル一式を以下に置いておきます。

参考


- PHP V5.2 の新機能、第 5 回: ファイル・アップロードの進行状況を追跡する方法
  http://www.ibm.com/developerworks/jp/opensource/library/os-php-v525/

- upload meter for PHP with APC and Json
  http://progphp.com/progress.phps

- PHP: APC 関数 - Manual
  http://php.net/apc

- jQuery Form Plugin
  http://malsup.com/jquery/form/

- Progress Bar Plugin
  http://digitalbush.com/projects/progress-bar-plugin

IE でどうにかして color:inherit を有効にする

  IE (6 も 7) では、CSS の color: inherit に対応していないので、以下のような場合に他のブラウザと異なる表示になります。

* {
  color: #000;
}
 
p {
  color: #f00;
}
 
span {
  color: inherit;
}

<p><span>ここは color: inherit を指定しています。</span></p>


- Internet Explorer 6.0
  color: inherit of IE

- Firefox 2.0.0.12
  color: inherit of Firefox

  見て分かるとおりに、IE の場合は、ユニバーサルセレクタで指定した #000 が有効になっています。
  要は、CSS 2 に準拠していないということなのですが、これをどうにかして他のブラウザのように #f00 で表示してやろうというのが趣旨です。

  結論から言うと JavaScript での処理になるのですが、はじめに以下のようなスクリプトを書いてみました。

window.onload = function() {
  if (document.all) {
    var e = document.getElementsByTagName('span');
    for (var i = 0, l = e.length; i < l; ++i) {
      e[i].style.color = e[i].parentNode.currentStyle.color;
    }
  }
};


  要は、親要素の color を適用していくという感じです。もちろん、これでうまくいきます。

  jQuery を利用している場合は、以下のような感じで、

$(function() {
  if ($.browser.msie) {
    $('span').each(function() {
      $(this).css('color', $(this).parent().css('color'));
    });
  }
});


  prototype.js を利用している場合は、以下のような感じです。

Event.observe(window, 'load', function() {
  if (Prototype.Browser.IE) {
    $$('span').each(function(e) {
      e.style.color = e.parentNode.currentStyle.color;
    });
  }
});


  さて、上記のスクリプトを見て分かるとおりに IE でしかこの処理は行ないません。
  となると、IE の CSS 拡張 expression が使えそうです。

span {
  color: inherit; /* NOT IE */
  color: expression(this.parentNode.currentStyle.color); /* IE */
}


  これだとシンプルに書くことが出来ます。
  ただし、expression も問題があって、何かしらのイベントが発生するたびに評価されてしまいます。
  例えば、mouseover だったり riseze だったり。
  あともちろん、validator には通りません。

  結論としては、上記の方法を時と場合によって使い分けるのが良いと思います。

# IE が color: inherit をサポートしてくれるのが一番いいんですけどね……
# IE 8 ではどうなっているんでしょう?

- hxxk.jp - IE 7 と color: inherit
  http://hxxk.jp/2006/11/19/2347

長い URL を折り返して表示する jQuery プラグインを作りました

  主に Firefox での使用を意識しているのですが、Firefox では長い URL は折り返さない仕様なのでページのレイアウトが崩れてしまうことがあります (Firefox 3 では折り返す仕様になるみたいです)。

  このため MR Tech Link Wrapper というアドオンや、url_breaker+ という Greasemonky スクリプトがあります。

  それらを参考にして、長い URL を折り返して表示する jQuery プラグイン Link Wrapper を作ってみました。

- jquery.linkwrapper.js のデモ
  http://pocari.org/demo/jquery.linkwrapper/

  jquery.linkwrapper.js

  このプラグインは、MR Tech Link Wrapper や url_breaker+ と同じように、<wbr> という非標準のタグを挿入しています。また、Opera は <wbr> に対応していないようなので、&#8203; (ZERO WIDTH SPACE: ゼロ幅スペース) を挿入しています。

# ですので、Opera では URL の文字をコピーすると h t t p : / / w w w のようにスペースが入ってしまいます。

  このプラグインでは、<wbr> の挿入位置ですが、デフォルトでは 1 文字毎にしています。
  1 文字毎はやりすぎな感じもするのですが、まあ綺麗に折り返すために、取り合えずこのようにしてみました。

  オプション (pattern) に <wbr> を挿入する正規表現を指定することで、折り返しの位置を調整することができます。

- 使い方

<script type="text/javascript" src="jquery-1.2.2.pack.js"></script>
<script type="text/javascript" src="jquery.linkwrapper-1.0.3.js"></script>
<script type="text/javascript">
// <![CDATA[
$(function() {
  // オプションを指定しない場合
  $('a.link1').linkwrapper();
  // オプションを指定する場合
  $('a.link2').linkwrapper({ pattern: '(&|\\?)' });
});
// ]]>
</script>
</head>
<body>
<p><a href="http://..." class="link1">http://...</a></p>
<p><a href="http://..." class="link2">http://...</a></p>


  対応しているブラウザですが、IE 6.0、IE 7.0、Firefox 2.0.0.11、Safari 3.0.4、Netscape 7.1、Opera 9.25 で確認したところ問題ないようです。

  以下にソースを載せておきます。
  ライセンスは、jQuery と同様に MIT と GPL のデュアルライセンスにしました。

(function() {
  jQuery.fn.linkwrapper = function(config) {
    config = jQuery.extend({
      pattern: '(.)'
    }, config);
    var pattern = new RegExp(config.pattern, 'g');
    var tag = jQuery.browser.opera ? '&#8203;' : '<wbr />';
    return this.each(function() {
      jQuery(this).html(jQuery(this).text().replace(pattern, '$1' + tag));
    });
  };
})(jQuery);


- ダウンロード
  jquery.linkwrapper-1.0.3.js

  なお、jQuery のプラグインの作り方は、以下のエントリが大変参考になりました。

- ref.: jQuery のプラグインを作成する : ブログの新着記事を表示:Goodpic
  http://www.goodpic.com/mt/archives2/2007/11/jquery.html

- ref.: The wbr tag
  http://www.quirksmode.org/oddsandends/wbr.html

- 追記 (2008-02-05)
  メソッドチェーンができないことに気づいたので、jquery.linkwrapper-1.0.2 をリリースしました。
  これで、以下のようにできるようになります。

$('a.link1').linkwrapper().css('color', '#f00');


- 追記 (2008-03-21)
  jQuery.noConflict(); の時に動作しないのを修正して、jquery.linkwrapper-1.0.3 をリリースしました。

PEAR Log を使って Firebug にログを出力する

  PEAR Log 1.9.11 から Firebug にログを出力することができるようになったみたいなので試してみました。

- PEAR :: Package :: Log
  http://pear.php.net/package/Log

- Firebug - Web Development Evolved
  http://www.getfirebug.com/jp.html

require_once 'Log.php';

$log = &Log::singleton('firebug', '', 'PHP', array('buffering' => true), PEAR_LOG_DEBUG);

$log->log('デバッグレベルのログ', PEAR_LOG_DEBUG);
$log->log('情報レベルのログ', PEAR_LOG_INFO); // この場合は、PEAR_LOG_INFO は省略可
$log->log('警告レベルのログ', PEAR_LOG_WARNING);
$log->log('エラーレベルのログ', PEAR_LOG_ERR);


  コードはこれだけで、log() メソッドでログを出力します。

PEAR Log Firebug

  生成される HTML は以下のようになります。

<script type="text/javascript">
console.debug("PHP [debug] デバッグレベルのログ");
console.info("PHP [info] 情報レベルのログ");
console.warn("PHP [warning] 警告レベルのログ");
console.error("PHP [error] エラーレベルのログ");
</script>


  singleton の第 4 引数はオプションですが、上記の例ではバッファリングを有効にしています。
  バッファリング有効にすると JavaScript が HTML の一番最後に追記されるようになります。
  逆に、バッファリングを有効にしない場合 (デフォルト) は、log() メソッドの場所に JavaScript が生成されるようになります。

  header() で何か出力しているときや、セッションを使う場合はバッファリングが無効だと問題になる場合があるので、そのようなときには、バッファリングを有効にすることで、問題なくログを出力することができる場合があります。

prototype.js の Event.observe を使った onbeforeunload について

  JavaScript のイベントに、ウィンドウが閉じられる直前に起こる onbeforeunload があります。
  これを使うと、何がしかの内容を入力中にページを移動する際に警告のメッセージを出すことができます。
  WordPress とかで利用されています。

  使い方は簡単で、以下のようにダイアログに表示する任意のメッセージを返します。

window.onbeforeunload = function(e) {
    // なにも返さなければ、なにも起きない。
    return '<任意のメッセージ>';
};


  ダイアログの表示は以下のようになります。

- Internet Explorer 6.0
  Internet Explorer 6.0

- Internet Explorer 7.0
  Internet Explorer 7.0

- Firefox 2.0.0.5
  Firefox 2.0.0.5

- Safari 3.0.2 (beta)
  Safari 3.0.2

  本題はここからで、これを prototype.js の Event.observe を使った場合はどうなるかということです。
  まず、素直にやってみます。

Event.observe(window, 'beforeunload', function(e) {
    return '<任意のメッセージ>';
});


  これでうまくいくと思いきや、どころがどっこい Safari 3.0.2 (beta) ではうまくいきますが、IE 6.0/7.0、Firefox 2.0.0.5 ではダイアログが表示されません。

  次に、以下のように returnValue に値を入れてみます。

Event.observe(window, 'beforeunload', function(e) {
    e.returnValue = '<任意のメッセージ>';
});


  これだと、IE 6.0/7.0、Firefox 2.0.0.5 ではうまくいきますが、Safari 3.0.2 (beta) ではだめでした。

  上記の結果、以下のようにすれば IE 6.0/7.0、Firefox 2.0.0.5、Safari 3.0.2 (beta) でもうまくいくことがわかりました。

Event.observe(window, 'beforeunload', function(e) {
    // イベントをキャンセルする場合は、なにも返さない。
    return e.returnValue = '<任意のメッセージ>';
});


  まあ、window.onbeforeunload で問題ないわけなので、Event.observe を使う必要はないかもしれませんが、10 分ほどはまってしまったので、メモとして残しておきます。

  ちなみに onbeforeunload は、手元にある限りでは IE 6.0/7.0、Firefox 2.0.0.5 で対応していて、Opera 9.22 とか Netscape 7.1 では対応していませんでした。

prototype.js で element にメソッドを追加する方法

  prototype.js 1.5 から $ メソッドが改良されて

$('foo').show();


  のように書けるようになりました.

  prototype.js のソースを読んでいくと Element.Methods に好きなメソッドを追加できそうなのでやってみました.

Element.Methods.warning = function(element) {
    $(element).style.color = '#f00';
    return element;
};

Element.addMethods(); // addMethods しないと追加されない!


  上記は,指定された element を赤色にして警告するものです (いい例が思いつかない……).
  以下のようにして,呼び出せます.

$('bar').warning();


  あまりいい例ではないですが,何か面白いことに使えるかもしれません.

prototype.js を svn で取得してビルドする方法

  prototype.js の最新機能を確認したい時に,Subversion のリポジトリから取得してビルドする方法です.
  いつも忘れるのでメモ.

$ svn co http://svn.rubyonrails.org/rails/spinoffs/prototype/trunk prototype
$ cd prototype
$ rake

  dist/prototype.js に最新版がビルドされます.

- ref.: Prototype JavaScript framework: Download Prototype
  http://www.prototypejs.org/download

- ref.: Prototype JavaScript framework: Contribute
  http://www.prototypejs.org/contribute

PHP による JavaScript を書かない Ajax

  4861671752
  amazon.co.jp 詳細ページへ
  佐久嶋 ひろみ (著)
  九天社
  ISBN: 4861671752
  2007/05
  3,150 円

  HTML_AJAX/xajax,CakePHP/symfony の情報など

- サポートページ
  http://sakushima.net/BOOK/AjaxFramework/

<input type="hidden" /> の値を見やすく表示するデバッグ用の JavaScript ライブラリ

  フォームの <input type="hidden" /> の値をテーブルで見やすく表示するライブラリ HiddenView というのを作ってみました.
  いちいちソースを確認して,値が入っているか確認するのが面倒な人用.他の JavaScript ライブラリは必要としません.

- 使い方
  HiddenView.js を読み込んで,onload イベントで new HiddenView(); をしてください.これだけ.

<script type="text/javascript" src="HiddenView.js"></script>
<script type="text/javascript">
window.onload = function() {
  new HiddenView();
};
</script>


- 表示サンプル
  HiddenView

  ユーザ登録画面で,項目を入力してもらって,その確認画面というイメージ.上部の灰色のテーブル部分が実際の確認画面.
  下部の青色のテーブルが <input type="hidden" /> の値を示しています.
  
  この例では,テーブルにスタイルを指定しているので,この様な表示になっています.
  テーブルに hv-table というクラス名をつけていますので,自由にスタイルを決めることができます.

- ソース
  最下部でダウンロードできるようにしてありますが,一応ソース自体を載せておきます.

var HiddenView = function() {
    this.initialize();
};
HiddenView.prototype = {
    initialize: function() {
        var input = document.getElementsByTagName('input');
        var table = document.createElement('table');
        var tbody = document.createElement('tbody');
        
        var row = this.createRow([ 'Name', 'Value' ], true);
        tbody.appendChild(row);
        
        for (var i = 0, len = input.length; i < len; ++i) {
            if (input[i].type == 'hidden') {
                var row = this.createRow([ input[i].name, input[i].value ]);
                tbody.appendChild(row);
            }
        }
        table.appendChild(tbody);
        table.className = 'hv-table';
        table.setAttribute('class', 'hv-table');
        document.getElementsByTagName('body').item(0).appendChild(table);
    },
    createRow: function(values, header) {
        var row = document.createElement('tr');
        for (var i = 0, len = values.length; i < len; ++i) {
            var col = document.createElement(header ? 'th' : 'td');
            col.appendChild(document.createTextNode(values[i]));
            row.appendChild(col);
        }
        return row;
    }
};


  やっていることはシンプルで,input 要素の type 属性の値が hidden のものを探してきて,テーブルを作っています.

- ダウンロード
  http://pocari.org/tools/HiddenView/HiddenView.zip

  上記サンプルで表示してあるスタイルシートも同梱しています.
  一応,Firefox 2.0,IE 6.0, Opera 9.02 (いずれも OS は Windows XP) で動作確認しています.

- ライセンス
  MIT ライセンスにします.同梱しているスタイルシートについては著作権は主張しません.