November 1, 2010

VBA よりクリップボードにアクセスする

Excel VBA よりクリップボードに文字列を設定したくて、Microsoft Forms 2.0 Object Library のDataObject を使ってたんですけど、なぜか全くクリップボードが更新されず。先日の WSH 用の Tips を VBA に移植してみました。Excel 2003 SP3 + IE8 では動作してます。

Option Explicit

'' コマンドID
Private Enum OLECMDID
    OLECMDID_COPY = 12&
    OLECMDID_PASTE = 13&
    OLECMDID_SELECTALL = 17&
End Enum

'' IE のインスタンス
Private internetExplorer_ As Object

'' テキストエリア
Private textArea_ As Object

'' クリップボードに文字列をセットする
Public Sub SetText(text As String)
On Error GoTo ErrHandler:
    '' TEXTAREA に文字列をセット
    textArea_.innerText = text
    '' すべてを選択
    Call internetExplorer_.ExecWB(OLECMDID_SELECTALL, 0)
    '' コピー
    Call internetExplorer_.ExecWB(OLECMDID_COPY, 0)

    Exit Sub
ErrHandler:
    Call Err.Raise(Err.Number, "Clipboard.SetText()" & " ← " & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext)

End Sub

'' クリップボードより文字列を取得する
Public Function GetText() As String
On Error GoTo ErrHandler:
    '' TEXTAREA の初期化
    textArea_.innerText = ""

    '' ペーストコマンド
    Call textArea_.ExecWB(OLECMDID_PASTE, 0)

    '' TEXTAREA の文字列を取得する
    GetText = textArea_.innerText

    Exit Function
ErrHandler:
    Call Err.Raise(Err.Number, "Clipboard.GetText()" & " ← " & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext)

End Function

'' クラス初期化処理
Private Sub Class_Initialize()
On Error GoTo ErrHandler:

    '' IE を起動する
    Set internetExplorer_ = CreateObject("InternetExplorer.Application")
    Call internetExplorer_.Navigate("about:blank")

    '' 安定するまで待つ
    Do While internetExplorer_.Busy
    Loop

    '' TEXTAREA 要素を作成する
    Set textArea_ = internetExplorer_.document.createElement("textarea")
    Call internetExplorer_.document.body.appendChild(textArea_)

    '' フォーカスを与えておく
    Call textArea_.Focus

ExitHandler:
    Exit Sub

ErrHandler:
    Call MsgBox(Err.Number & ":" & Err.Source & vbLf & Err.Description, vbCritical, "エラー")
    Resume ExitHandler

End Sub

'' クラス破棄時処理
Private Sub Class_Terminate()

On Error Resume Next

    ' IE が起動していれば終了させる
    If (Not internetExplorer_ Is Nothing) Then
        internetExplorer_.Quit
    End If

End Sub

October 22, 2010

DELETE 文の複合テーブル構文

必要があったので、MySQL の DELETE 文で、複合テーブル構文ってのを調べた。例えばこんなん。

DELETE dept, emp
FROM dept LEFT JOIN emp
ON dept.deptno = emp.deptno
WHERE dept.deptno = 20;

例は Oracle のサンプルデータベース SCOTT のものだが、何らかの理由で SCOTT さんの会社から RESERCH 部がなくなって、その部署の従業員も一斉解雇になったらしい。SCOTT さんもクビで、愛猫 TIGER 君とともに路頭に迷うことに。それをこの DELETE 文一発でデータベースに反映することができる。

あるいは

DELETE sub
FROM emp boss LEFT JOIN emp sub
ON boss.empno = sub.mgr
WHERE boss.empno IN (7698, 7902)

何があったのかはわからないが、BLAKE 氏と FORD 氏の部下を全員削除する。この例は DELETE 句では FROM 句で指定した別名が使用でき、FROM 句で指定した表のうち一部の表のみを削除できることを示している。条件が複雑な場合に便利。他の RDBMS にもこういう構文あったっけな ?

Oracle の SCOTT データベースが分かんない人のために、EMP 表と DEPT 表のみ MySQL 用の DDL をのっけておきます。

続きを読む...

October 8, 2010

WSH よりクリップボード、次の手

このサイトで最も人気のある記事は WSH から IE を起動してクリップボードにアクセスする Tips を扱ったものなんだけど、IE のバージョンが上がってスクリプトからクリップボードにアクセスしようとすると警告が表示されるようになったため、現在では使えないテクニックになっちゃってた。で、年月を経て再び WSH からクリップボードを操作する方法を調べてたんだけど、id:ardarim さんがIE の execWB メソッドでコマンドを発行するテクニックを公開してくださってるのを発見した。

これは素晴らしい!というわけで、クリップボードからデータを取得する部分も考えてみた。

var jp;
if (!jp) jp = {};
if (!jp.raindrop) jp.raindrop = {};
if (!jp.raindrop.frog) jp.raindrop.frog = {};
jp.raindrop.frog.clipboard || (function ()
    {
        // コマンドのID
        var OLECMDID_COPY = 12;
        var OLECMDID_PASTE = 13;
        var OLECMDID_SELECTALL = 17;

        // IE の初期化
        var _internetExplorer = new ActiveXObject ('InternetExplorer.Application');
        _internetExplorer.navigate ("about:blank");
        while (_internetExplorer.Busy)
            WScript.Sleep (10);

        // textarea 要素を作成する
        var _textarea = _internetExplorer.document.createElement ("textarea");
        _internetExplorer.document.body.appendChild (_textarea);
        _textarea.focus ();

        jp.raindrop.frog.clipboard = {
            // クリップボードに文字列をコピー
            setText: function (text)
            {
                _textarea.innerText = text;
                _internetExplorer.execWB (OLECMDID_SELECTALL, 0);
                _internetExplorer.execWB (OLECMDID_COPY, 0);
            },

            // クリップボードより文字列を取得
            getText: function ()
            {
                _textarea.innerText = "";
                _internetExplorer.execWB (OLECMDID_PASTE, 0);
                return _textarea.innerText;
            },

            // IE を解放
            release: function ()
            {
                _internetExplorer.Quit ();
            }
        };
    }());

ちょっとテストしたところ動いてます。素晴らしい。

// クリップボードから取得したテキストを表示
WScript.echo (jp.raindrop.frog.clipboard.getText ());
// クリップボードに文字列を設定
jp.raindrop.frog.clipboard.setText ("JScript から設定したテキスト\nタブ「\t」も対応");

September 30, 2010

隣接兄弟セレクタ

CSS2 から導入された隣接兄弟セレクタだけど、IE でも 7 以降はサポートされてるみたいです。例えばこんな。

ul#menu > li { display: inline; margin: 0; padding: .5em 1em; }
ul#menu > li + li { border-left: 1px solid black; }

id="menu" の <ul> の子要素である <li> に隣接している <li> にのみ左ボーダーを指定しています。これを以下のような HTML に適用した結果を示します。

<ul id="menu">
    <li>トップページ</li>
    <li>事業内容</li>
    <li>会社概要</li>
    <li>お問い合わせ</li>
    <li>サイトマップ</li>
</ul>

結果 (Firefox 3.6.10 で表示したもの)

adjacent_sibling_selectors.png

1 番目の <li> は <li> に隣接していないので、左ボーダーがありません。2 番目以降にのみ左ボーダーが適用されています。

サンプル

September 29, 2010

Google AJAX Feed API を使ってみる

Google AJAX Feed API ってライブラリがあるみたいです。これを使えば公開されているフィードに Javascript でアクセスすることができ、他ドメインのフィードも取得できちゃいます。

ためしにはてなアンテナのフィードを取得して表示してみました。

<script type="text/javascript" src="http://frog.raindrop.jp/scripts/xmlight.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://www.google.com/jsapi?key=YOUR_AJAX_API_KEY"></script>
<script type="text/javascript">
//<![CDATA[
// Google AJAX FEED API Version 1 を読み込む
google.load ("feeds", "1");
// AJAX Feed API のサンプルでは google.setOnLoadCallback でコールバックをバインドしてるけど
// jQuery と一緒に使用する場合は document.ready で実行したらよいと思う
$(function ()
{
    // RSS フィードの URI を指定して google.feeds.Feed を生成
    var feed = new google.feeds.Feed ("http://a.hatena.ne.jp/ba-raindrop/rss");
    // 読み込み件数を指定
    feed.setNumEntries (10);
    // フィード取得時に呼び出すコールバックを指定して load を呼び出す
    feed.load (function (result)
        {
            // 処理結果を判断する
            if (result.error)
                return;
            // 表示用の HTML の構築には自作の簡易 XML ビルダ/パーサを使用しています
            var list = jp.raindrop.frog.xmlight.create ("dl");
            // result.feed.entries を列挙
            for (var i = 0; i < result.feed.entries.length; i ++)
            {
                var entry = result.feed.entries[i];
                list.addChild ('dt').addText (entry.title);
                var memberList = list.addChild ('dd').addChild ('dl');
                // entries の各要素のメンバを列挙
                for (var member in entry)
                {
                    if (member === 'categories')
                        continue;    // categories は配列なので後で列挙
                    memberList.addChild ('dt').addText (member);
                    memberList.addChild ('dd').addText (entry[member]);
                }
                // categories を列挙
                memberList.addChild ('dt').addText ('categories');
                var categoryList = memberList.addChild ('dd').addChild ('ul');
                for (var j = 0; j < entry.categories.length; j ++)
                    categoryList.addChild ('li').addText (entry.categories [j]);
            }
            $(document.body).append (list.toString ());
        });
});
//]]>
</script>

サンプルを見る

September 27, 2010

連想配列の列挙

PHP
<?php
// foreach は便利
$array = array ("year" => 2010, "month" => 9, "day" => 27);
foreach ($array as $key => $value)
    echo "$key: $value\n";

// こうすると perl っぽい
while (list ($key, $value) = each ($array))
    echo "$key: $value\n";
?>
Perl
# たとえば、keys が返すキーの配列を foreach で列挙
%hash = ("year" => 2010, "month" => 9, "day" => 27);
foreach $key (keys %hash1)
    print "$key: $hash{$key}\n";

# 順番を確定したければキーをソートするといい
foreach $key (sort keys %hash1)
    print "$key: $hash{$key}\n";

# たとえばキーと値を each で列挙
while (($key, $value) = each (%hash1))
    print "$key: $value\n";

for と foreach

PHP の for
for ($i = 0; $i < 10; $i ++)
    echo "$i\n";
Perl の for
for ($i = 0; $i < 10; $i ++)
    print "$i\n";
Javascript の for
for (var i = 0; i < 10; i ++)
    document.writeln (i);
PHP の foreach
$array1 = array ("foo", "bar", "baz");
foreach ($array1 as $value)
    echo "$value\n";

$array2 = array ("year" => 2010, "month" => 9, "day" => 27);
foreach ($array1 as $key => $value)
    echo "$key: $value\n";
Perl の foreach
@array1 = ("foo", "bar", "baz");
foreach $value (@array1)
    print "$value\n";

%hash1 = ("year" => 2010, "month" => 9, "day" => 27);
foreach $key (keys %hash1)
    print "$key: $hash1{$key}\n";

# 実は for と foreach は同様に使える。インタプリタが文脈で判断するので。
@array2 = qw (apple peach banana);
for $value (@array2)
    print "$value\n";
Javascript の for ( in )
// for ( in ) を配列で使用した場合は添字を列挙
var array = ["foo", "bar", "baz"];
for (var index in array)
    document.writeln (array [index]);

// 連想配列で使用した場合はキーを列挙
var hash = {year: 2010, month: 9, day: 27};
for (var key in hash)
    document.writeln (key + ': ' + hash [key]);

September 22, 2010

最後の要素の後のカンマの件

識別子、文字列または数がありません。 で、ECMA でも新しい仕様では最後のカンマを許容することになってるらしいって結論になったけど、じゃあ JSON はどうよ ? と思って調べてみた。

object
{}
{ members }
members
pair
pair , members
pair
string : value
array
[]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null

JSON では、最後の要素の後ろにカンマがあってはダメみたいです。配列でも同じ。
Standard ECMA-262 3rd Edition のサブセットってなってるから当然かな。

続きを読む...

September 21, 2010

識別子、文字列または数がありません。

Firefox や Chrome では正常に動作する Javascript が、IE では「識別子、文字列または数がありません。」というエラーになることが。さっぱり意味が分かんなくて調べてみたんですけど、結構あちこちのブログで取り上げられています。IE ではオブジェクトリテラルの最後のメンバの後ろにカンマがある場合に出るみたいです。

var person = {
    lastName: '鈴木',
    firstName: '一郎',
    age: 25,  // ←ここにカンマがあるとエラー
    };

C 言語などで、配列を中括弧で初期化する場合に最後の要素の後ろにもカンマを付けることが認められているので、ついついこのケースでもカンマを入れたくなってしまうんですが、意識して付けないようにした方がよさそうです。

ちなみに、WSH で実行するときも同じエラーになります。当然か。

ECMA の仕様ではどうなってるのかな…。

続きを読む...

September 16, 2010

関数内の this が参照するもの

あー、これはちょっと、今まで誤解してたかも。 this は実行時に評価されるんですね。
function Foo ()
{
    var _getx = function () { return this.x; };

    this.x = 'apple';
    this.getX = _getx;
    this.getBar = function () { return { x: 'banana', getX: _getx}; };
}

var foo = new Foo ();
alert (foo.getX ());    // 'apple'

var bar = foo.getBar ();
alert (bar.getX ());    // 'banana'
続きを読む...