スタンプシールを作りました。

描いたストロークをスタンプのように再利用できるシールを作りました。
コピペシールとも言えなくもない。

ダウンロード

stamp.moon.zip

使い方

スタンプにしたい絵などを描きます。
(もっとかわいい猫を描いてください)


シール台帳からスタンプシールを貼付けます。


ページ内のストロークデータがシールのストロークになります。
(本当はストロークの位置を変えずに、シールの位置を合わせるように移動したかったけど、
onattachでシールの位置を移動しても描画更新されなくておかしくなるので断念しました。)
シールのサイズはストロークの大きさにフィットするようにしています。


実行するとシールの位置にストロークのデータが書き込まれます。


シール台帳に保存しておくと再利用できて便利です。


Stamp!Stamp!Stamp!

今後

12月にUEIさんがシールアップロード機能を追加してくれる予定なので(つ、つくってるよね?)
enchantMOONユーザでいろんなスタンプの共有ができるといいなー

背景ブロックはじめました

まず始めに背景ブロックを使えるようにしたMOONBlockを置いておきます。
MOONBlockのアップデートと同じで上書きするだけでOKです。
(ご利用は自己責任でお願いします)
MOONBlock-v0.3.3-BGBlock.zip


MOONBlockのソースを眺めていると、こんなのを見つけました。
MOONBlock/js/puppet.blocks.block.enchant.js

enchant.ENV.BG_IMAGE_DICTIONARY = pairsToOption([
    [ 'blocks.bgImages.default', null ],
    [ 'blocks.bgImages.beach', 'beach.png' ],
    [ 'blocks.bgImages.desert', 'desert.png' ],
    [ 'blocks.bgImages.sky', 'sky.png' ],
    [ 'blocks.bgImages.hollywood', 'hollywood.png' ],
    [ 'blocks.bgImages.eclipse', 'eclipse.png' ],
    [ 'blocks.bgImages.space', 'spacebg.png' ],
    [ 'blocks.bgImages.table', 'table.png' ],
    [ 'blocks.bgImages.rpg', 'rpg.png' ],
    [ 'blocks.bgImages.race', 'race.png' ],
    [ 'blocks.bgImages.black', 'black.png' ],
    [ 'blocks.bgImages.blockg', 'blockg.png' ],
    [ 'blocks.bgImages.actiong', 'actiong.png' ]
]);

enchant.block.blocks.game.BackgroundBlock = enchant.Class.create(enchant.block.Block, {
    initialize: function() {
        enchant.block.Block.call(this, '#ffcc33');
        this.addLabel(RES('blocks.BackgroundBlock.name'));
        this.addSelectForm(enchant.ENV.BG_IMAGE_DICTIONARY, 'bgsrc');
        this.script = 'enchant.puppet.Theatre.changeScreen("<% bgsrc %>");';
    }
});

これは明らかに背景ブロックですね〜
せっかくなんで使えるようにしてあげましょう。


"BackgroundBlock"でgrepすると背景ブロックを抑止している部分を見つけました。
MOONBlock/main.js

        var akbar = new ActionKitBar();
        akbar.box[RES('blocks.categories.game')].removeItem('BackgroundBlock'); // ここで背景ブロックが消されています
        akbar.box.JavaScript.removeItem('BlackBlock'); // こっちはブラックボックスブロックを消しているようです
        akbar.x = 8;
        akbar.y = uiMargin;
        manager.registerDragTarget(akbar);
        akbar.addEventListener('blockreceived', function(e) {
            var b = e.block;
            if (isAllowedRemoving(b)) {
                blockGroup.removeChild(e.block);
            }
        });
        scene.addChild(akbar);

ActionKitBarというのがMOONBlock上部に表示されるブロックの入っている箱達のようです。
こいつの生成直後にremoveItemされちゃってるので、
ここをコメントアウトしてやれば背景ブロックが出るはず。



はい。背景ブロックは出ましたが、
ブロックがつんつるてんでどこにもハマりません。。。


実行してみても案の定何も起きません。
この子大丈夫かしら。




どうしようもないので、ほかのブロックを参考にしてみることに。
最近追加されたサウンドブロックを見てみます。

enchant.block.blocks.game.SEBlock = enchant.Class.create(enchant.block.Block, {
    initialize: function() {
        enchant.block.Block.call(this, '#3399cc');
        this.setConnectTarget('evalable'); // こいつ怪しい
        this.addLabel(RES('blocks.SEBlock.name'));
        this.addSelectForm(enchant.ENV.SE_DICTIONARY, 'sesrc');
        this._playing = null;
        this.addLabel('\u25B6')
            .style({
                font: '32px selif',
                color: '#00ff00'
            })
            .on(enchant.Event.TOUCH_END, function() {
                this.parentNode.preview();
            });
        this.iteratize(); // こいつも怪しい
        this.script = 'enchant.Core.instance.assets["<% sesrc %>"].clone().play();';
    },
    preview: function() {
        if (this._playing) {
            this._playing.stop();
        }
        this._playing = enchant.Core.instance.assets[this.getSentence('sesrc')].clone();
        this._playing.play();
    }
});

うーん以下の二つが怪しいですね。
this.setConnectTarget('evalable');
this.iteratize();


調べてみると、どちらもブロック基本クラスのfunctionのようです。
MOONBlock/js/block.enchant.js

enchant.block.Block = enchant.Class.create(enchant.block.Draggable, {
    ...
    /**
     [lang:ja]
     * ブロックが接続できるReceptorのタイプを設定する.
     * @see enchant.block.Connector
     * @see enchant.block.Receptor
     * @see enchant.block.MultipleReceptor
     * @param {String|Array.<String>} type 接続できるようにしたいReceptorのtype.
     [/lang]
     */
    setConnectTarget: function(type) {
        if (this.connectable) {
            this.connector.type = type;
        } else {
            this.connector = new enchant.block.Connector(type);
            this.connector.parentNode = this;
            this.connectable = true;
        }
    },
    ...
    /**
     [lang:ja]
     * ブロックを直列に接続できるように設定する.
     * {@link enchant.block.Block#setConnectTarget}で設定したタイプと同じタイプのブロックが下に接続できるようになる.
     * connectTargetを設定していない場合は, 引数にtypeを指定する必要がある.
     * ブロックに特別なReceptorを設定するため, iteratizeの呼び出し後にレイアウトを追加することはできない.
     * @param {String} [type] タイプ.
     [/lang]
     */
    iteratize: function(type) {
        if (this.connectable) {
            if (this._lastLine.length) {
                this.addBR();
            }
            var receptor = new enchant.block.Receptor(this.connector.type, this);
            var C_B_OFST = getBGAsset(this.edgeColor, 'C_B').height / 2;
            receptor.width = receptor._defaultWidth = C_B_OFST;
            receptor.height = receptor._defaultHeight = C_B_OFST;
            this._nextReceptor = receptor;
            this._addElement(receptor);
            this.iteratable = true;
        } else if (type) {
            this.setConnectTarget(type);
            this.iteratize();
        } else {
            throw new Error('Block.iteratize() should call after set connect target');
        }
    },
    ...

setConnectTargetはブロックのコネクタタイプを設定するfunctionらしい。
ブロックにはコネクタ(挿す側)とレセプタ(挿される側)があり、
このタイプが一致しないとブロック同士が接続できないようです。
とりあえずサウンドブロックと同じ感じで使えればいいので、setConnectTargetには'evalable'を指定しておきます。
タイプは今のところ7つあるようですが、まぁ詳しくは追々調べましょう。

evalable:評価?処理?
expression:式
number:数値
string:文字列
behavior:ビヘイビア(振る舞い)
property:変数
startPin:開始位置


iteratizeはコメントの通り、同じタイプのブロックが下に接続できるようになります。
これもサウンドブロックと同様に呼んでおきましょう。
こいつは呼び出し順に制約があるようなのでscriptの前で呼ぶようにしておきます。


はい、こんな感じになりました。
MOONBlock/js/puppet.blocks.block.enchant.js

enchant.block.blocks.game.BackgroundBlock = enchant.Class.create(enchant.block.Block, {
    initialize: function() {
        enchant.block.Block.call(this, '#ffcc33');
        this.setConnectTarget('evalable'); // 追加
        this.addLabel(RES('blocks.BackgroundBlock.name'));
        this.addSelectForm(enchant.ENV.BG_IMAGE_DICTIONARY, 'bgsrc');
        this.iteratize(); // 追加
        this.script = 'enchant.puppet.Theatre.changeScreen("<% bgsrc %>");';
    }
});


MOONBlockで開くと、サウンドブロックと同じ形になっているのが確認できました。


さて、使い方ですが、こんな風にシールブロックのタップされたときに繋げても、
何故か起動時に一瞬表示されるだけでまともに動きません。(なんでだろ。。。)


以下のように、0秒後に実行や、スコアボードが画面に現れたタイミングに設定してやるとちゃんと動きました。
少し使い方が微妙な感じがしますが、使えなくはないのでOKでしょう!


Javaメモ lib/ext

久しぶりにJavaに触れてすっかり忘れてたことをメモ。


lib/extにjarを置いておくと実行時にパスを指定しなくてもいい。
lib/extの場所がわからない場合は以下を実行すればOK。

public class ExtensionPath {
    public static void main (String[] args) {
        System.out.println(System.getProperty("java.ext.dirs"));
    }
}
拡張フォルダのパスを取得する:JavaTips 〜JSP/サーブレット編 - @IT

XML::FeedPPで更新日付が取得できない

PerlXML::FeedPPってモジュール使って、RSSを取得するプログラム作ってたんだけど、あるサイトだけ更新日付が取得できない。
更新日付入ってないのかな?と思ってとりあえずRSSのソースを見てみる。

だけ抜粋。

<item>
<title>
たいとるだよ!</title>
<link>
http://hogehoge.com/
</link>
<dc:date>
2010-10-10T12:34:56+09:00</dc:date>
<content:encoded><![CDATA[
ほげほげー
]]></content:encoded>
</item>

なんか変な所に改行入ってるけどにはちゃんと入ってる。
改行のせいかなーと思って内の改行を消してみたら取得できた!

修正する為にデバッガで追っかけてみたらこのサブルーチンが原因だった。
XML::FeedPP::Util::get_w3cdtf

sub get_w3cdtf {
    my $date = shift;
    return unless defined $date;
    if ( $date =~ /^\d+$/s ) {
        return &epoch_to_w3cdtf($date);
    }
    elsif ( $date =~ $rfc1123_regexp ) {
        return &rfc1123_to_w3cdtf($date);
    }
    elsif ( $date =~ $w3cdtf_regexp ) {
        return $date;
    }
    undef;
}

#$rfc1123_regexpと$w3cdtf_regexpの定義
my $rfc1123_regexp = qr{
    ^(?:[A-Za-z]+,\s*)? (\d+)\s+ ([A-Za-z]+)\s+ (\d+)\s+
    (\d+):(\d+)(?::(\d+)(?:\.\d*)?)?\s*
    ([\+\-]\d+:?\d{2} | [ECMP][DS]T )?
}xi;
my $w3cdtf_regexp = qr{
    ^(\d+)-(\d+)-(\d+)
    (?:T(\d+):(\d+)(?::(\d+)(?:\.\d*)?\:?)?\s*
    ([\+\-]\d+:?\d{2})?|$)
}x;

日付のフォーマットを判定するところで、日付の前後に改行とかスペースが含まれていることが想定されて無いみたい。
なので、判定する前に日付の前後の改行と空白文字を取り除いてやったらうまく行った!

sub get_w3cdtf {
    my $date = shift;
    return unless defined $date;
    $date =~ s/(^[\s\R]*)|([\s\R]*$)//g; # 前後の改行と空白文字を取り除く
    if ( $date =~ /^\d+$/s ) {
        return &epoch_to_w3cdtf($date);
    }
    elsif ( $date =~ $rfc1123_regexp ) {
        return &rfc1123_to_w3cdtf($date);
    }
    elsif ( $date =~ $w3cdtf_regexp ) {
        return $date;
    }
    undef;
}

SendmailでGmailに転送

実験用のLinux機が欲しい!ということでここを参考にサーバを作り始める。
が、しかし、しょっぱなroot宛のメールを転送する設定でつまずく・・・
書いてあるとおりにやったのに!大人はうそつきだ!

結局1日がかりでごにょごにょしてました。
参考にしたのはこのへん。

OP25B(Outbound Port 25 Blocking)対策 - CentOSで自宅サーバー構築
sendmailでGmailへ転送すること - ワインのばか
http://james-lloyd.com/getting-sendmail-use-gmail-as-a-relay-2/
Configuring Sendmail to relay through Gmail SMTP | AppGirlAppGirl

途中経過書いても仕方ないので最終的にやったことだけ書く。
まず環境
CentOS 5.4
Sendmail 8.13.8-2.el5

んで設定
まず /etc/mail/sendmail.mc の編集
dnl define(`SMART_HOST', `smtp.your.provider')dnl の次の行に以下を追加。


define(`SMART_HOST', `smtp.gmail.com')dnl
define(`ESMTP_MAILER_ARGS', `TCP $h 587')
define(`RELAY_MAILER_ARGS', `TCP $h 587')
FEATURE(authinfo, DATABASE_MAP_TYPE` -o 'MAIL_SETTINGS_DIR`authinfo')
define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')
TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')

そんで
dnl DAEMON_OPTIONS(`Port=submission, Name=MSA, M=Ea')dnl のコメントを外す。


DAEMON_OPTIONS(`Port=submission, Name=MSA, M=Ea')dnl

最後に
dnl FEATURE(masquerade_envelope)dnlの行の次に以下を追加。


FEATURE(masquerade_envelope)dnl
FEATURE(genericstable, DATABASE_MAP_TYPE` -o 'MAIL_SETTINGS_DIR`genericstable')
GENERICS_DOMAIN_FILE(MAIL_SETTINGS_DIR`genericsdomain')
FEATURE(`generics_entire_domain')
/etc/mail/sendmail.mc の編集はこれで終わり。


sendmail.mcからsendmail.cfを作る。


m4 /usr/share/sendmail-cf/m4/cf.m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf


次はauthinfoファイルを作る。
中身はこんな感じ。


AuthInfo:smtp.gmail.com "U:root" "I:hoge@gmail.com" "P:アカウントのパスワード"
作ったらauthinfo.dbの作成

makemap hash /etc/mail/authinfo.db < /etc/mail/authinfo


次はgenericsdomainファイルを作る。


echo localhost >> /etc/mail/genericsdomain
echo `hostname` >> /etc/mail/genericsdomain
これでOK。


次はgenericstableファイルを作る。


echo root hoge@gmail.com > /etc/mail/genericstable
でgenericstable.dbを作る。

makemap hash /etc/mail/genericstable.db < /etc/mail/genericstable

hoge@gmail.comは送信に使うgmailアカウントなので各自置き換えること。
sendmailの設定はこれで終わりなので設定を反映させる。


/etc/rc.d/init.d/sendmail reload


最後に送信先を設定する。
/etc/aliasesのroot:なんたらかんたら って行を書き換える。
デフォルトではコメントになってた気がする。


#root: michael

root: huga@gmail.com
に書き換える。
これでroot宛のメールはhuga@gmail.comに送られる。



newaliases
でaliasesの設定を反映させて、

echo test|mail root
ってやってメールがhuga@gmail.comに届けばOK。

name属性の廃止

XHTMLを解説しているページの多くで、

XHTML1.0からはname属性は廃止です。
name属性はid属性にかえましょう。

的な事が書いてあるんで、すべてのname属性が廃止なんだと思ってた。


でも、あれ?じゃあチェックボックスラジオボタンのグループ化ってどうやってやんの?
と思って調べていたら、name属性の廃止は一部の要素についてのみということが判明。


紛らわしい書き方なんで、他にも誤解してる人いっぱい居そう・・・
え?プロにとっては常識?そうですか・・・


以下にname属性を使える要素についてまとめてみました。
本やサイトなどから引っ張ってきたのでなく、きちんとDTDを読んだので正確だと思います。
○:name属性使用可
×:name属性廃止
‐:要素自体が廃止

要素 XHTML1.0 XHTML1.1
frameset transitional strict
a ×
map ×
img × ×
form × ×
iframe - -
applet - -
frame - - -
meta
object
param
input
select
textarea
button