yamachan Ajax/.NET/C# メモ

2007-05-03

[C#] synergy 補助ツール mac2ip v1.00

最近、Synergy というフリーの キーボード/マウス の共有ツールを愛用しています。 非常に便利です。 ツールの概要は Yet Another 仕事のツール あたりを参照してください。 日本語版の入手は このへん とか。

さて、ウチはマンション備え付けのインターネット接続を利用していて、ルーターの設定が変更できません。 サーバーとなるマシンの IP Address を固定できず、再起動のたびに調べ、クライアント側に設定する必要があります。 なかなか面倒です。

マシンの MACアドレスは固定ですから、そこから IPアドレス を得られれば自動化ができそうな気がします。 そこで Windows用の rarp 的なツールを探してみたのですが、うまく見つかりませんでした。

探すのが面倒になりまして、勉強中の C# で簡単なツールを作成してみました。 実行には .Net Framework 2.0 が必要です。

mac2ip v1.00: mac2ip_v100.zip

使い方は簡単で、マシンの MAC Address (例:00-11-22-33-44-55) を指定すると、対応する IP Address (例:192.168.20.18) を探して表示してくれます。

mac2ip v1.00 実行例
引数を2つ指定した場合には、環境変数の設定用の出力になります。 バッククォーテーション(`)で実行結果と置換できない Windows でのバッチ処理に組み込むための、簡易的な実装です。

IP Address の検索は、arp テーブルを参照することで実現しています。 見つからない場合はブロードキャスト(例の場合は 192.168.20.255) への ping を実行し、arp テーブルを再チェックします。

それでも見つからない場合には、自分に近いアドレスから順に ping して探していきます。 調査対象の IP Address が、調査元のものより離れている場合には、少し時間がかかります。

さて、僕は以下のようなバッチファイルを作成し、synergy ディレクトリに mac2ip.exe と一緒に置いています。

mac2ip.exe SVR_ADDR 00-11-22-33-44-55 > mac2ip-tmp.cmd
echo start /b synergyc %%SVR_ADDR%% >> mac2ip-tmp.cmd
mac2ip-tmp.cmd

サーバーの IP Address を自動的に検索し、synergy クライアントを起動するバッチです。 "00-11-22-33-44-55" の部分は、synergy サーバーの実際の MAC Address に書き換えてください。

このバッチをスタートアップに登録しておくのも良いでしょう。

【2007/05/06 追記】

synergy は多重起動するとエラーになるみたいですね。 クライアント再起動のために改良したバッチは以下になります。

echo taskkill /F /IM synergyc.exe > mac2ip-tmp.cmd
mac2ip.exe SVR_ADDR 00-11-22-33-44-55 >> mac2ip-tmp.cmd
echo start /b synergyc %%SVR_ADDR%% >> mac2ip-tmp.cmd
mac2ip-tmp.cmd

taskkill コマンドは Windows XP Home には無いようです。 この場合 pskill コマンドを導入して代用するとよいでしょう。

echo pskill -t synergyc.exe > mac2ip-tmp.cmd
mac2ip.exe SVR_ADDR 00-11-22-33-44-55 >> mac2ip-tmp.cmd
echo start /b synergyc %%SVR_ADDR%% >> mac2ip-tmp.cmd
mac2ip-tmp.cmd

2007-03-01

[JS] 画像に簡単に効果をつけるクラス

画像にちょっとした効果をつけたいという要望はけっこうあります。 良く使う効果を簡単に記述できるようなコード、ImgTool を作成してみました。

まずは、マウスを重ねると画像が変わるという効果です。 ボタンなんかで、よく使用しますね。


<img src="search_glass.gif"
onload="new jp.rinco.ImgTool(this,'search_orange.gif');"
/>

onload の部分が、今回作成した ImgTool を使用している部分です。 最初の引数は this、二番目の引数にはマウスを重ねている間に表示する画像を指定します。 簡単でしょ?

次は、マウスを重ねているあいだだけ、画像を拡大表示する効果です。


<img src="yamachan1.jpg" width="43" height="55"
onload="new jp.rinco.ImgTool(this, 5);"
/>

さきほどの画像のかわりに、5 という数値を指定しています。 これは拡大する倍数で、この例ではマウスを重ねると5倍の大きさになります。

次は同じような例ですが、倍数ではなく、拡大したときの横幅、縦幅をドットで指定します。


<img src="yamachan1.jpg" width="43" height="55"
onload="new jp.rinco.ImgTool(this, 215, 62);"
/>

どうでしょう、マウスを重ねると画像が横に伸びたでしょうか?

さて、これまでの例は onload にコードを記載していましたが、画像の id を指定して、後から効果を加えることもできます。


<img src="yamachan1.jpg" id="ImgTool-sample" />

<script>
var it = new jp.rinco.ImgTool('ImgTool-sample');
it.overSrc = "search_orange.gif";
it.overBorder = "solid 2px red";
it.downWidth = it.width * 3;
it.downHeight = it.height * 3;
</script>

今回はマウスを重ねると画像が変わり、赤い枠がつきます。 また画像をクリックしている間、三倍に拡大表示します。

現在の ImgTool では、以下のパラメーターを設定することができます。

* 画像の初期設定値 (参照のみ)

width, height, src, border, color, bgcolor

* マウスオーバー時の設定

overWidth, overHeight, overSrc
overBorder, overColor, overBackgroundColor

* マウスクリック時の設定

downWidth, downHeight, downSrc
downBorder, downColor, downBackgroundColor

実際のコードは以下になります。 長いので、効果の数は減らしてあります。 完全なコードに興味のある方は、yamachan_common.js ファイルを参照してください。

まずは最初の初期化部分です。 画像の最初の状態を保存しておき、また初期化の際の引数を処理しています。

jp.rinco.ImgTool = function(t, p1, p2) {
if (typeof(t) == 'string')
this.target = document.getElementById(t);
else
this.target = t;
this.src = this.target.src;
if (this.target.style.width == "")
this.width = this.target.width;
else
this.width = this.target.style.width;
if (this.target.style.height == "")
this.height = this.target.height;
else
this.height = this.target.style.height;
this.overWidth = "";
this.overHeight = "";
this.overSrc = "";

if (p1 != undefined) {
if (typeof(p1) == 'string')
this.overSrc = p1;
else {
if (p2 == undefined) {
this.overWidth = this.FxMulti(this.width, p1);
this.overHeight = this.FxMulti(this.height,p1);
} else {
this.overWidth = p1;
this.overHeight = p2;
}
}
}

引き続き、クラス初期化の後半です。 イベント処理をおこなう関数を定義しています。

スタイルの変更は最初、foo.style.setAttribute(t,v); のように実行していましたが、Firefox では動作しないようです。 悩んだ結果、対象のイメージ内で eval() を実行するという荒業で実装してみました。 (^-^;

 this.target.evalAttribute = function(t, v, d) {
if (v != "") {
if (d == undefined) {
eval("this." + t + "='" + v + "'");
} else {
eval("this." + t + "='" + d + "'");
}
}
}
this.target.evalAttribute("style.width",
this.Fx(this.width));
this.target.evalAttribute("style.height",
this.Fx(this.height));

this.onmouseover = function() {
this.target.evalAttribute("style.width",
this.Fx(this.overWidth));
this.target.evalAttribute("style.height",
this.Fx(this.overHeight));
this.target.evalAttribute("src", this.overSrc);
}
this.onmouseout = function() {
this.target.evalAttribute("style.width",
this.overWidth, this.Fx(this.width));
this.target.evalAttribute("style.height",
this.overHeight, this.Fx(this.height));
this.target.evalAttribute("src",
this.overSrc, this.src);
}

this.target.aImgTool = this;
this.target.onmouseover = function() {
this.aImgTool.onmouseover();
}
this.target.onmouseout = function() {
this.aImgTool.onmouseout();
}
}

最後にご紹介するのは、prototype で定義した補助関数です。

今回、いったん作成したコードを、Firefox で動作させるのに苦労しました。 特に「FireFoxの標準モードでは、style.width,heightに数値での設定ができない」という制限に気がつくまでが長かった...。

Fx() は上記の制限に対応するための quick hack で、数値を渡すと px 表記に変換して返すだけの補助関数です。

FxMulti() も似たようなもので、px などの余分な文字が指定されていた場合でも、正常に掛け算を実行するだけの補助関数です。 引数に倍率を指定した場合に使用しています。

jp.rinco.ImgTool.prototype.Fx = function(v) {
if (typeof(v) == "number")
return v + "px";
else
return v;
}

jp.rinco.ImgTool.prototype.FxMulti = function(v, m) {
if (typeof(v) == "number")
return eval(v + "*" + m);
var myReg = new RegExp("[0-9]+", "i");
if (!myReg.test(v))
return "error";

var ret = eval(myReg.exec(v) + "*" + m);
return RegExp.leftContext + ret + RegExp.rightContext;
}

ちなみに今回のオブジェクトは、画像が主なターゲットと想定して作成したため、ImgTool という名前になっています。 が、画像以外に使用してもかまいません。

DIV タグに使用した例
 マウスを上に
 マウスをクリック

2007-02-27

[Ajax] 入力フィールドにSuggest機能をつけてみる (2)

週末に時間がとれましたので、前回 の Suggest機能を書き直してみました。 今回はわりと JavaScript っぽくなってきている、気がする。

本サイト右上の検索バーには反映されています。 今のところ、見た目の違いは選択キーワードの背景がグレーになったぐらいですかね。

Suggest付の検索ボックスの例

まずは検索バーの html なんですが、よりシンプルになっています。

<form action="http://www.google.co.jp/search">
<input name="q" size="20" id="my-field" />
<input name="q" type="hidden"
value=" site:http://yamachan-cs.blogspot.com/"/>
<img src="search_glass.gif" alt="Search"
width="48" height="24" align="bottom"
onclick="document.getElementById('my-field')
.form.submit();"
</form>

<div id="my-help"
style="visibility:hidden;display:none;color:blue">
</div>

この部分にはJavaScriptが使用されていない、のが今回のポイントです。 普通の検索バーと違うのは、div要素 "my-help" がひそかに追加されていることぐらいですね。

引き続きましては、Suggest機能を追加する JavaScript 部分です。 辞書の定義部分をのぞけば、記述は1行とシンプルになっていますね。 これで動作します。

<script>
var dic = [
"Ajax",
"aptana",
...途中省略
"unescapeHTML"
];

var ys=new jp.rinco.Suggest('my-field','my-help',dic);
</script>

ただ、このままでは地味ですので、少し拡張してみましょう。 Suggestされたキーワードにマウスがくると、文字は赤色に、背景は灰色になるようにロジックを追加します。

作成したSuggestオブジェクトの onmouseover, onmouseout 関数を、オーバーライドすればOKです。 記述する処理は、ごく普通ですね~

<script>
ys.onmouseover = function() {
this.style.color = 'red';
this.style.backgroundColor = '#ddd';
}
ys.onmouseout = function() {
this.style.color = 'blue';
this.style.backgroundColor = 'white';
}
</script>

これで jp.rinco.Suggest オブジェクトの使い方の説明は終わりです。 前回からの改良点としては、利用方法・拡張方法がシンプルになったこと、そして1ページに複数利用することが可能になったことでしょうか。

実際のコードを簡単にご紹介します。 まずはオブジェクトの生成の部分です。

jp.rinco.Suggest = function(t, h, d) {
if (typeof(t) == 'string')
this.target = document.getElementById(t);
else
this.target = t;
if (typeof(h) == 'string')
this.help = document.getElementById(h);
else
this.help = h;
this.dic = d;

this.target.aYamachanSuggest = this;
this.target.onkeyup = function() {
this.aYamachanSuggest.updateHelp();
}
}

対象のフィールドやヘルプ領域は、id で指定しても、オブジェクトを直接指定してもOKです。 対象フィールドのオブジェクトに aYamachanSuggest なんて名前で、勝手に自分自身を登録しちゃってるのがトリッキーかもしれません。

引き続き、Java でいうところのインスタンス・メソッドの定義部分をご紹介します。 実際には、上記の生成部分に含まれて記述しています。

まずはSuggestキーワードをヘルプ領域に追加するための、keyElement() 補助関数です。

this.keyElement = function(k) {
var ke = document.createElement('div')
ke.innerHTML = k;
ke.aYamachanSuggest = this;
ke.onclick = function(){
this.aYamachanSuggest.click(this);
}
if (ke.aYamachanSuggest.onmouseover)
ke.onmouseover = ke.aYamachanSuggest.onmouseover;
if (ke.aYamachanSuggest.onmouseout)
ke.onmouseout = ke.aYamachanSuggest.onmouseout;
return ke;
}

で、実際にヘルプ領域を更新して表示/非表示する updateHelp() 関数です。

this.updateHelp = function() {
var k = jp.rinco.lastWord(this.target.value);
if (k == "") {
this.help.style.visibility = 'hidden';
this.help.style.display = 'none';
return;
}

var lk = k.toLowerCase();
var counter = 0;
this.help.innerHTML = "";

for (loop = 0; loop < this.dic.length; loop++) {
var dk = this.dic[loop];
if (dk.toLowerCase().indexOf(lk) == 0) {
this.help.appendChild(this.keyElement(dk));
counter++;
}
}

if (counter > 0) {
this.help.style.visibility = 'visible';
this.help.style.display = 'block';
} else {
this.help.style.visibility = 'hidden';
this.help.style.display = 'none';
}
}

最後は Java でいうクラス・メソッドのご紹介です。 ついに登場した prototype って感じですね。これは生成部分の外で記述されています。

click() は、Suggestキーワードがクリックされたときに呼び出される関数です。

jp.rinco.Suggest.prototype.click = function(ke) {
var te = ke.aYamachanSuggest.target;
var k = jp.rinco.lastWord(te.value);
if (k == "") return;

te.value=te.value.substring(0,te.value.length-k.length)
+ ke.innerHTML;
ke.aYamachanSuggest.updateHelp();
te.focus();
}

以上、まだ不安な部分は残しているものの、まぁ JavaScript で書いたプログラムだと言えるレベルになってきたのではないかと...。 そして僕も「趣味のJSプログラマーです」と言っても怒られないかも、というレベルに?

2007-02-26

[JS] ネームスペース jp.rinco

JavaScriptにはJavaのようなネームスペース(名前空間)が無いので不便だな、とか考えていた自分は甘かったです。 先人たちは適切なグローバルオブジェクトを作成し、代用している様子。

そこで僕もJava風に自分のドメインの逆順 jp.rinco というオブジェクトを作成し、関数などを保持するようにしました。 jsファイルの先頭に、以下のような定義文を追加します。

if (typeof jp == "undefined") {
var jp = {};
}
if (typeof jp.rinco == "undefined") {
jp.rinco = {};
}

以前は以下のように関数を定義していました。

function yamachan_display(id) {
...
}

今後は、以下のように関数を定義するようにします。

jp.rinco.display = function(id) {
...
}

過去のコードや記事も順次、修正していきたいと考えています。

2007-02-24

[Ajax] 入力フィールドにSuggest機能をつけてみる (1)

少し前から、本blogの右上に検索ボックスが表示されているのにお気づきでしょうか? この検索ボックスですが、ちょっと仕掛がしてあります。

キーワードを入れると、↓のように候補が下に表示されるようになっています。 キーワードをクリックすると、自動入力されます。 a,r,y とか入力してみてください。

Suggest付の検索ボックスの例

IE などのブラウザにも似た機能がありますが、こちらはAjaxで実装されています。 初めて使用する時も有効なこと、表示されるキーワードはサイト管理者が設定したものであること、あたりがメリットでしょうか。

正直、中身はあまりJavaScriptっぽくないコードで、書き直している最中なのですが、、、とりあえず動いているバージョンを、バックアップも兼ねて貼っておきます。

2007/2/27 追記: 書き直した版 を公開しました

実際の検索ボックスですが、まずは以下のようなscriptが貼ってあります。

<script>
function my_submit() {
var k = y_suggest_target.value;
location.href = 'http://www.google.co.jp/search?
q=site:http://yamachan-cs.blogspot.com/ ' +
encodeURIComponent(k);
}

function my_search_helpLine(key) {
return "<span
onclick=\"yamachan_suggest_click('"+key+"');\"
onmouseover=\"this.style.color='red';\"
onmouseout=\"this.style.color='blue';\">"
+ key + "</span><br />";
}

var my_dic = [
"Ajax",
"aptana",
...途中省略
"unescapeHTML"
];
</script>

my_submit() はSuggest機能とは関係なく、単に検索実行用の関数です。 Google をSite:オプションをつけてキーワード検索する単純なものです。

my_search_helpLine() は表示用の補助関数です。 必須ではありません。 Suggestされるキーワードの上にマウスがある場合、キーワードを赤色にするためのhtml出力を定義しています。

my_dic がSuggestされるキーワード文字列の配列になります。 これらの事前定義に続き、以下のhtmlが貼られています。

<form style="margin:0">
<input size="20" id="my-search-field"
onkeyup="yamachan_suggest(
'my-search-field','my-search-help',
my_dic, my_search_helpLine);"
/>
<img src="search.gif"
width="48" height="24" alt="Search"
onclick="my_submit();"
/>
</form>

<div id="my-search-help"
style="visibility:hidden;display:none;color:blue">
</div>

実際の入力欄に onkeyup が指定されていること、ヘルプ表示用のdivエリアが非表示で用意されていること、の二点がポイントでしょうか。

さて利用されている関数ですが、メイン部分は以下のようになっています。 最初のvarがとても恥ずかしいデスネ。

var yamachan_suggest_target;
var y_suggest_help;
var y_suggest_dic;
var y_suggest_func;

function yamachan_suggest(f, h, dic, func) {
y_suggest_target=document.getElementById(f);
y_suggest_help=document.getElementById(h);
y_suggest_dic = dic;
y_suggest_func = func;
var skey=yamachan_lastWord(y_suggest_target.value);
y_suggest_help.innerHTML=y_suggest_helpHTML(skey);
if (y_suggest_help.innerHTML == "") {
y_suggest_help.style.visibility = 'hidden';
y_suggest_help.style.display = 'none';
} else {
y_suggest_help.style.visibility = 'visible';
y_suggest_help.style.display = 'block';
}
}

yamachan_lastWord() は以前にご紹介したもので、文字列の最後の1単語を得る関数です。 また以下の補助的な関数を使用します。

function yamachan_suggest_helpHTML(skey){
if (skey == "") return "";
var sk = skey.toLowerCase();
var val = "";
for(loop=0;loop<y_suggest_dic.length;loop++){
var k = y_suggest_dic[loop];
if (k.toLowerCase().indexOf(sk) == 0) {
if (y_suggest_func ==null||y_suggest_func=='')
val = val +
"<span onclick=\"yamachan_suggest_click('"
+ k + "');\">" + k + "</span><br />";
else
val = val + y_suggest_func(k);
}
}
return val;
}

function yamachan_suggest_click(k) {
var skey=yamachan_lastWord(y_suggest_target.value);
if (skey != "") {
y_suggest_target.value =
y_suggest_target.value.substring(0,
y_suggest_target.value.length-skey.length) + k;
y_suggest_help.innerHTML =
yamachan_suggest_helpHTML(k);
y_suggest_target.focus();
}
}

yamachan_suggest_helpHTML() はキーワードリストのhtmlを作成する関数です。 yamachan_suggest_click() はその生成されたhtmlに埋め込まれ、キーワードをクリックしたときに呼び出されます。

今回はベタに検索ボックスに使用してみましたが、いろいろ活用方法はあるとおもいます。 例えば製品名を入力する欄で、代表的な製品をSuggestしてみるとか。

2007-02-19

[JS] 最後のwordを得る関数

もうひとつ、紹介し忘れていた関数がありました。 文字列を単語(word)単位で分割し、その最後のwordを返す関数です。

jp.rinco.lastWord = function(str) {
if (str == "") return "";
var s=str.replace(/(\s|\u3000)+/g,' ').split(' ');
return s[s.length - 1];
}

replace() に指定している "/(\s|\u3000)+/g" という正規表現(RegExp)が少し複雑かもしれません。 split()の事前処理として、タブや改行や全角スペースも半角スペースに変換しています。

正規表現について簡単に説明します。 もっとも単純な変換だと str.replace(/\s/, ' '); という感じになるでしょうか。

ただ"\s" だけでは全角スペースに対応できない様子なので、全角スペースのUnicode表記 "\u3000" と組み合わせて "\s|\u3000" という表記にします。

複数連続したスペースが含まれる場合があります。 そこでそれらをまとめ、1つの対象として処理するために、1回以上の繰り返し "+" を使用して "(\s|\u3000)+" という表記にします。

最後に追加される "/g" はよく使いますね。 一致する対象を全て処理するオプション、グローバルマッチングです。

文字列操作において、正規表現は非常に便利ですので、ぜひ活用してみてください。

2007/2/27 追記: jp.rincoオブジェクト へ移動

2007-02-18

[JS] タグを処理する関数

Ajaxっぽいサンプルをいろいろ作成していますが、もう少し時間がかかりそうです。 今回は、その過程で作成した関数をご紹介します。

まずは単に & < > 文字を、&amp; &lt; &gt; に HTMLエスケープする escapeHTML() 関数。 そして戻す unescapeHTML() 関数です。

jp.rinco.escapeHTML = function(str) {
return str.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
jp.rinco.unescapeHTML = function(str) {
return str.replace(/&gt;/g, ">")
.replace(/&lt;/g, "<")
.replace(/&amp;/g, "&");
}

そして文字列から指定したタグの中身を取り出す、tagText() 関数です。

例えば str に "<title>aaa</title>"、tag_s に "<title>"、tag_e に "</title>" を指定し、その間に指定されたタグの中身 "aaa" を取り出します。

最後の引数は通常は false、str に指定した文字列が HTMLエスケープ されている場合に true を指定します。 例えば処理する文字列が "&lt;title&gt;aaa&lt;/title&gt;" のような場合に true にします。

※ これはレアケースのようですが、Domino サイトが対象の場合には有効です

jp.rinco.tagText = function(str,tag_s,tag_e,cflag){
if (tag_s == "" || tag_e == "")
return "";

var key_s=cflag?jp.rinco.escapeHTML(tag_s):tag_s;
var key_e=cflag?jp.rinco.escapeHTML(tag_e):tag_e;
var pos_s = str.indexOf(key_s);
var pos_e = str.indexOf(key_e);
if (pos_s < 0 || pos_e < 0)
return "";
else
return str.substring(pos_s+key_s.length, pos_e);
}

以上、ホントは String に prototype で組み込むのが JavaScript っぽいのですが。 単に関数にしちゃってる、弱気な僕なのでありました。

2007/2/27 追記: jp.rincoオブジェクト へ移動