[Terminal | ターミナル] ディレクトリ か ファイルのみlsで表示する

2012/02/19

こんにちは。きんくまです。

Terminal でディレクトリかファイルのみをlsで表示する方法について調べました。
ファイル数が多くなってくるとディレクトリだけみたいとかありますよね。

元記事はVIVEK GITEさんが書いた記事です。(今回からリスペクト!ということで、なるべく記事の著者名を載せるようにしようと思います。)
HowTo: Linux / UNIX List Just Directories Or Directory Names

ディレクトリのみ表示

詳細表示つき

ls -l | grep ^d

詳細表示なし

ls -F | grep /

ファイルのみ表示

詳細表示つき

ls -l | grep -v ^d

詳細表示なし

ls -F | grep -v /

コマンドの解説

これだけじゃなんなんで、コマンドの解説をします。

詳細表示について

-l オプションで詳細表示したものを、パイプ | を通して、そのうちdからはじまるもの(=ディレクトリ)を表示するということです。

ファイルを表示する場合はgrepのオプションに -v をつけることで、逆のものをあつめます。つまり、dからはじまらない(=ファイル)を表示します。

詳細表示なしについて

-F オプションで、ディレクトリの場合は名前の後ろに「/」をつけて表示します。これをパイプを通して、/がついているものだけgrepでひっぱるということです。

ファイルは-vオプションなので、詳細表示と同じです。

コマンド化する

ワンライナーとはいえ、コマンドとしては長いです。
なので、これに名前をつけてコマンド化してしまいましょう。

~/.bash_profileに下記のコードを付け加えて、Terminalを立ち上げ直します。

alias lf="ls -l | grep -v ^d"
alias ldir="ls -l | grep ^d"

これで、lf コマンドでファイルのみ、 ldir コマンドでディレクトリのみを表示することができるようになります。

unixはこうやって、自分の好きなコマンドを作って、シェルに登録して便利にしていくんですね。
パイプはいくつもつなげられるみたいなんで、例えば今回作ったlfコマンドにさらにlessを足して、

lf | less

みたいなこともできるんですね。なんか面白い。

※2014/03/13 この記事はよく検索でこられてるみたいなので、ターミナルに関する別のおすすめ記事ものせておきます。

>> [Terminal | ターミナル] Terminal で Finder の最前面(現在表示中)のディレクトリを開く

LINEで送る
Pocket

[Terminal | ターミナル] Terminal で Finder の最前面(現在表示中)のディレクトリを開く

2012/02/19

こんにちは。きんくまです。

今回はFinderとTerminalのやりとりです。
Finderで移動していって、そこでターミナル上で作業したいってことがあります。

例えば、Finderで開いているディレクトリ上で git init とかコマンド打ちたいってことがあります。
そんなときに、ターミナルでどうやってそこのディレクトリに移るのかということです。
もちろんcd でパスを書いていくのは基本ですが、面倒くさい。
そこで簡単に移る方法を調べてみました。

いろいろなやり方がありました。3つ紹介します。

1. cdffコマンドを作る

私的に一番おすすめなのが、~/.bash_profileに下記のスクリプトを追加する方法。
cdffとタイプするコマンドを新規作成します。
これを書いた後に、Terminalをログアウトして、もう一回開いてcdffと打ち込めば、
Finderのディレクトリに移っているという!

以下のコマンドを打てば、ログアウトしなくても設定がすぐに反映されて大丈夫です。

source ~/.bash_profile

あ、cdffじゃなくて、自分で好きなコマンド名をつけても全然大丈夫です。

function ff { osascript -e 'tell application "Finder"'\
 -e "if (${1-1} <= (count Finder windows)) then"\
 -e "get POSIX path of (target of window ${1-1} as alias)"\
 -e 'else' -e 'get POSIX path of (desktop as alias)'\
 -e 'end if' -e 'end tell'; };\

function cdff { cd "`ff $@`"; };

各行末に\「バックスラッシュ。日本語だとyenが表示されてる」が入っています。これを入れない場合は、\とそのすぐ後の改行を全て削除して長い1行にしてください。
バックスラッシュはすぐ後の改行をエスケープすることができるので、本来1行で書かなければならないものを、複数行に分けて書くことができます。

全部設定が終わったらターミナル上で cdff とコマンドをうってみます。すると、Finderのディレクトリに移動していることが確認できます!
すげえ!

2. TerminalにFinderのフォルダを直接ドラッグドロップ

この方法はお手軽です。
FinderのフォルダをTerminalに直接ドラッグドロップするとTerminalにパスが表示されます。
これにcdと組み合わせればディレクトリが変更できます。

このあいだディレクトリをFinder上でコピーしたら、ターミナル上でペーストするとパスが表示されたので、こっちの方法でもいいかもしれません。

3. サービスを利用

Lionからはサービスを利用することで、Finder上で右クリックを押すと
・New Terminal at Folder
・New Terminal Tab at Folder
が選べるようになります。

利用するためには、

System Preferences > Keyboard > Keyboard Shortcuts > Services

からそれぞれのチェックボックスにチェックをいれます。

finder_to_terminal_fig1

すると、Finderで右クリックをすると選べるようになります。
ショートカットも設定できるので、一発で開けますね。

finder_to_terminal_fig2

TerminalのカレントディレクトリをFinderで開きたい

まめ知識として、逆のパターンをご紹介。もし、TerminalのカレントディレクトリをFinderで開きたかったら下記のコマンドを入力します。

open .

行末の.「ピリオド」はカレントディレクトリを表します。

■今回の情報元です。どうもです。
osx - Open terminal here in Mac OS finder - Stack Overflow
Switch Terminal dir to frontmost window of certain apps - Mac OS X Hints

クリップボードにパスをコピーしたい

この部分は2014/04/02追記です

ターミナルとか関係なくFinder上で選択しているファイルのパスを、右クリックでクリップボードにコピーしたいとき。
AutomaterのServiceを利用します。

>> Quickly Copy a File or Folder Path to the Clipboard in Mac OS X
(オリジナルの情報もとです。Create a “Copy Path” Service for the Right-Click Menuの段落を参照のこと)

1. Automatorをたちあげて、新しいServiceを作ります
2. 左側からCopy to Clipboardを選択して右側にドラッグしてもっていきます
3. Service recieves selectedをfiles or foldersに設定して、inをFinderにします
4. ファイルを保存します。Copy Pathというような名前など 私はCopy Path to Clipboardとしておきました

LINEで送る
Pocket

[日記] 最近のこととか

2012/02/12

こんにちは。きんくまです。

先々週父が亡くなりまして、そのことを書こうと思います。

父は62歳で、肺癌で他界しました。最近のご時世にしては死ぬには若い年齢だったと思います。
本人もまだ仕事をしていて、仕事が好きだったので、途中になったのは本人としても心残りだったと思います。
私も、せめて退職して何年かゆっくりしてからだったらなと思いました。

2年前の秋に健康診断をしたときには、何も異常なしだったのですが、その次の年明けから少し咳が出ていたそうです。
仕事だゴルフだと忙しかったので、特に医者にもいかないまま、4月頃になっていました。
それで、最初の医者は肺炎だと言って薬をくれたらしいのですが、治らずにゴールデンウィークの前後に別の医者にかかったときには、もう末期の肺がんで治療ができないと診断されました。

それで、昨年の秋ぐらいまでは時々短期に入院をする他は普通にすごせていたのですが、秋に入院をしてからは3ヶ月ちょっとで亡くなってしまいました。

人によっては、もう少しゆっくり進行する場合もあるようなのですが、父の場合はとても速かったです。

最近は本人にもすぐに告知するので、父も病状を知っていたし、私もわかってからすぐに聞きました。
それから先日に亡くなるまでにそれなりに時間があったので、いろんなことを考えました。

父のこともそうなんですが、こうして病気が進行していく父と、5歳と、もうすぐ2歳になる2人の子供のこととか。
子供が産まれるときは、病院にいて、人が産まれるときに立ち会いました。父が亡くなる数時間前まで一緒にいたので、人が亡くなるときにも立ち会いました。
それから、自分は34なので、ちょうどそんな子供と父の中間にいるわけです。もし、父の年齢で死ぬとすれば、折り返しをすぎているわけです。

父が亡くなるちょうどその日に、上の子が初めて歯が抜けまして。そういうこととか。

父と子供と自分。それから仕事のこと。家庭のこと。そのバランス。夢とか。
そんなこととかを考えてました。特に考えがまとまっているわけではないのですが。
悲しいかな、人はあっけなく死んでしまうものだとも思いました。

だからこそ生きている人は、1日1日を大事にしてかないといけないと思いました。
まあ、根詰めてばかりだと楽しくないし、それこそ早死にしちゃいますけどね。
そのバランスは、その人ごとに一番良い位置があるんじゃないでしょうか。

父は健康診断をしていたのですが、やはりあれは目安であって、少しでもおかしいと思ったら早く病院に行った方がいいと思いました。別の人で半年ごとに健康診断を受けてた人でも、その間にやはりガンで亡くなった人の話も聞きました。

亡くなる直前に、医者から詳しく症状を見せてもらったのですが、その症状はとても悪かったです。
お見舞いにいったときには、そんな風に見えなかったので、驚きました。それで、病気とこんなにも戦っていたのかと思い、すごい頑張っていたんだなと思ったり、それに気がついていなかったことを申し訳なく思ったり。
父はとても頑張りやで、気を遣う人だったのだと思います。

LINEで送る
Pocket

FontforgeでTextMateの日本語表示合成フォント作り Andale+Osaka mono

2012/02/12

こんにちは。きんくまです。

いまエディタどれ使おうか迷ってしまっています。
TextMate, Sublime Text, Emacsといろいろあって優柔不断な私には決めがたいところ。

今回はTextMateについてです。
最新版のTextMate2は日本語の表示は普通に表示されていいのですが、なんだかバージョン1とは
使い勝手が違う印象を持ちました。えーと、特に使い込んでいないので、あくまで印象です。

先日amazonでこんな本を注文しました。

>> Textmate: Power Editing for the Mac (Pragmatic Programmers) [ペーパーバック]

それで、もうすぐ届くはずなんですけど、できればバージョン1の方をみながら、やっていきたいなと思いました。

ただし、バージョン1はデフォルトだと日本語入力、表示ともに全然駄目なものなので、調べました。

>> TextMate で日本語をわりとまともに表示する

作者様が、日本語入力を可能にするプラグインを公開してくれたり、さらに日本語表示ができるフォント作りの方法まで買いてくれてます。すばらしい!

私は、去年まではWinユーザーで、かつプログラムするときは、「MSゴシックのアンチエイリアスなし」が大好きでした。だから、Macに移ったときは、なんとかして「MSゴシックのアンチエイリアスなし」環境を実現しようと試行錯誤したことがあります。結局うまくいかなくて、アンチありのMenloフォントで慣れてしまいましたけど。

それで、Terminalでは「Andaleでアンチなし設定」をするとそれっぽい感じでうまく表示されたので、なんとかしてTextMateでもやってみたいと思いました。TextMateのバージョン1の方はアンチなし設定がすぐにできるのです。

なので、
日本語はOsakaの長体50%+英文はAndaleのそのまま
というものを目指しました。

結論からいうとできました!

andale_osaka

やりかた > Fontforgeを使う

FontforgeというUnixのアプリを使います。

インストールはhomebrewが入っていたら下記のコマンドを入力します。

brew update //しなくてもよいかもです。私の場合はしないとうまくいきませんでした。
brew install fontforge

参考サイトです。私の場合は、brew updateでインストールできたのですが、ご参考まで。
プログラミングやコンソールアプリケーションに最適なフォント「Ricty」#LOVEFONT

それで、インストール後にさきほどのhetimaさんのサイトにならってスクリプトを頑張って書いてみたのですが、最後の行間設定が全然うまくいかない!!!!
なので、GUIでやることにしました。

完成までのメモを残しておきます。

参考サイトです。わかりやすいです。
>> Fontforge(その4) – むにむにの日記

X11というアプリケーションを起動。
OsakaMono.ttfを右クリックでFontforge.appから起動します。

Fontforge.appがない場合は、/Applicationsに
/usr/local/Cellar/fontforge/20110222/FontForge.app
というエイリアスを作れば大丈夫です。

ビットマップのダイアログがでますが、Select Allを選択して開きます。

andale_osaka_fig1

ためしに何かの文字をダブルクリックすると、こうなります。
これの定規の縮尺に注目してください。横軸が130ぐらいで縦軸が250くらいになっています。
あとで、フォントを合成するときに、ここがポイントになってきます。

andale_osaka_fig2

横幅を縮めます。
Edit > Select > Select All (Control + AでもオK)
Element > Transforms > Transform

真んなか辺の幅を50%に設定してOKをおします。

andale_osaka_fig3

そしたら長体フォントができあがります。オー簡単。

andale_osaka_fig4

さて、これから英文フォントのAndaleとマージするんですが、Andaleとは縮尺が違うために、そのままマージするとOsakaがものすごく小さい状態でマージされます。ていうか豆粒みたいな文字!
なので、縮尺を合わせます。

Element > Font Info > General > Em Sizeをプルダウンから 256を2048に変更します。

andale_osaka_fig5

この状態で、一度保存します。
まず、File > SaveでOsaka-Mono-Condensed.sfdという名前ででも(拡張子がsfdというこのアプリで使うための形式になっています)保存した後に、
File > Generate Fontsを選択してから、Osaka-Mono-Condensed.ttf とTrueTypeフォントで書き出します。
これで、長体フォントがいつでも利用できます。

あとは、Andaleをマージしてできあがりです。
Andale Mono.ttfを、Fontforgeで開きます。

Element > Merge Fontsででてきたダイアログにさきほど保存した
Osaka-Mono-Condensed.ttfを選択します。
するとマージができました!!!

あとは、できた新しいフォントの情報を設定します。
まずは名前です。
Ctrl+Aで全選択したあとに、Element > Font Infoを押します。

FontNameなんかを設定します。

andale_osaka_fig6

次に、大事な行間の設定です。そのままだと10文字分くらいの行間になってしまっているので、
Generalタブを選択したあとに、Has Vertical Metricsにチェックをいれます。
スクリプトではこれがわからなくてできなかったのですが、何故かGUIでは簡単にできました。

andale_osaka_fig7

あとは、さきほどと同様に、AndaleOsaka.sfdという名前でどこかにもとファイルを保存してから、
File > Generates FontsでAndaleOsaka.ttfという名前で書き出します。

これでフォントの完成です。

今回はTextMate用の特殊なフォントを作りましたけど、いろいろと遊べそうな感じがします。
MacはUnixが中に仕込んであるだけ、Unixの資産がすぐに使えてなかなか便利ですね。

あとMSゴシックばんざい。好きなフォントです!

LINEで送る
Pocket

[SQLite3] MacでSQLite。あとgccでコンパイル

2012/02/8

こんにちは。きんくまです。
SQLiteを業務で必要になったので調べてました。

SQLiteは1ファイルで手軽にデータベースが出来るものです。
そんで、iOSやAndroid、Adobe AIRなんかにも搭載されています。
この間、調べていて知ったのですが、SQLiteというのは、仕様というか規格なので、
iOSで作ったりしたものがAndroidやAIRで読み書きできます。その逆もまた可能。
jpegとかpngも機種依存しないのとおんなじですね。

terminal上で

MacにはSQLiteが標準で組み込まれています。
だから、すぐにデータベースを作れます。
下のはhello.dbという名前のデータベースを作るコードです。
でも、実際にファイルが作られるのは何らかのSQLコマンドが実行された後となります。

sqlite3 hello.db

やめたかったら、”.exit”と打ちます。

SQLiteの設定ファイルは ~/.sqliterc を使います。

.echo ON
.mode column
.headers ON
.nullvalue "NULL"

このファイルを置いておけば、スタート時に読み込まれます。

テーブルを作る

sqlite> create table hello (id integer primary key, message text);

行を追加

sqlite> insert into hello (message) values ('hello');

SQLiteの文字列には注意が必要です。
文字列はシングルクォートで囲んでおいた方が問題がないようです。

データを取得

sqlite> select * from hello;

一番基本的なterminalで打つSQLiteのコマンドたちでした。
これだけでもそれなりに開発できると思います。

それじゃあ、Cでもやってみます。

gccでSQLiteをコンパイル

gccに前から興味がありました。なんでかといいますと、
XCodeを使わないでどうやってCをコンパイルするのかなと思ってまして。
実際はXCodeの中ではgccが走っているみたいですけどね。
でも、わざわざXCodeのプロジェクトを作らんでも
小さめのソースとかだったら気軽にコンパイルできるのは良いのではないかと思いまして。

mysqlite1.c

#include <stdio.h>
#include <string.h>
#include "/usr/include/sqlite3.h"

int main(){
	sqlite3 *db;
	int result, id;
	sqlite3_stmt *stmt;
	char *sql, *name;

	result = sqlite3_open_v2("hello.db", &db, SQLITE_OPEN_READONLY, NULL);
	if(result != SQLITE_OK){
		printf("database opened\n");
		sqlite3_close(db);
		return 1;
	}

	sql = "select * from hello;";
	result = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);

	if(result != SQLITE_OK){
		printf("statement error");
		return 1;
	}

	result = sqlite3_step(stmt);
	while(result == SQLITE_ROW){
		id = sqlite3_column_int(stmt, 0);
		name = (char *)sqlite3_column_text(stmt, 1);
		printf("id = %d, name = %s\n", id, name);
		result = sqlite3_step(stmt);
	}
	sqlite3_finalize(stmt);
	sqlite3_close(db);
	return 0;
}

MacにはSQLite3のライブラリが入ってますので、ヘッダーファイルをインクルードできます。
#include “/usr/include/sqlite3.h”

それじゃあ、terminalでコンパイルです。
うまくいくとmysqlite1という名前のファイルができています。

gcc -Wall -L/usr/lib/sqlite3 mysqlite1.c -lsqlite3 -o mysqlite1

試してみます。

./mysqlite1

makefileを使ってもコンパイルが可能です。
makefileは複数ファイルをコンパイルしたり、ライブラリをリンクしたりするのに便利です。

さきほどのコマンドはmakefileではこんな感じになります。

makefile

CC=gcc
CFLAGS=-Wall
all: mysqlite1

mysqlite1: mysqlite1.o
	$(CC) $(CFLAGS) -L/usr/lib/sqlite3 mysqlite1.o -lsqlite3 -o mysqlite1

mysqlite1.o: mysqlite1.c
	$(CC) $(CFLAGS) -c mysqlite1.c

clean:
	rm -f *.o mysqlite1

makefileを置いた後にこのコマンドを実行します。

make

うまくいけば、同じディレクトリにmysqlite1ができあがります。
cleanコマンドを実行すれば、オブジェクトファイル(*.o)とmysqlite1が削除されます。

make clean

より詳しく

この記事はこれらの本を参考にしています。
>> The Definitive Guide to SQLite
>> An Introduction to GCC: For the GNU Compilers GCC and G++

あとこのサイトも。
>> Makefiles by example

LINEで送る
Pocket

[terminal | ターミナル] 文字コード変換のシェルスクリプト

2012/02/8

こんにちは。きんくまです。

Terminalで文字コード変換のシェルスクリプトです。

同じフォルダにencodedというサブフォルダを作って、
そこに、変換後のファイルが保存されます。
今回は、SJIS から UTF-8に変換します。

sjisToUtf8

#!/bin/sh

mkdir encoded

for fname in "$@"
do
  iconv -f SJIS -t UTF-8 "$fname" > "encoded/$fname"
done

iconvが文字コード変換のコマンドです。
-f は from(変換前) -t は to (変換後)のオプションです。
man iconvと打つとマニュアルが出てくるので、調べてみると良いかもしれません。

さて、実行に向けての準備です。
例えば、今回はさきほどのテキストをsjisToUtf8という名前で保存しました。
どこに置いてもいいのですが、
/usr/local/bin
に置いておくと、いつでもコマンドを呼び出せるので便利です。
そこに置いたあと、実行権をつけておきます。

ファイルを移動(finder上でもOK)

mv sjisToUtf8 /usr/local/bin/

実行権をつける

chmod a+x /usr/local/bin/sjisToUtf8

ここで一度terminalをログアウトしてもう一回開くと、sjisToUtf8がコマンドとして認識されます。

で、ここから変換したいディレクトリに移動した後に、以下のコマンドを打つと
hello.cというファイルが./encoded/hello.cという名前でShiftJISからUTF8に変換されて保存されます。

sjisToUtf8 hello.c

複数ファイルも指定できます。
この場合は拡張子がcのファイルが全て変換されます。

sjisToUtf8 *.c

一度コマンドを実行すると、encodedフォルダができてしまうので、
再度コマンドを実行すると「そのフォルダはすでにありますぜ」という警告が出るのですが、
特に問題なさそうなので、無視しました、、。
encodedフォルダが存在するかどうかのif文を作らなきゃならないのですが、
どうやるかわかりません、、。すみません。

もし、他の文字コードを希望する場合はterminal上で
iconv -l
を打てば、文字コードリストが出るので、スクリプトの該当部分を変えれば大丈夫です。

terminalでこうしたちょっとしたツールが使えるのは便利ですね。

LINEで送る
Pocket

node.jsを使ってflashにも描き込むお絵描きチャット

2012/02/6

こんにちは。きんくまです。

先日の土曜日にF-siteで話す予定でした。
が、金曜日に父が亡くなりまして、急遽行けなくなってしまいました。
当日、会場にこられた方や、スタッフの皆様にはご迷惑をおかけしました。
すみませんでした。

ただ、せっかく作った発表用のものがありますので、アップします。

今回お題はjsflを使って何かをするというものでした。
それで、仕事の効率化みたいなものは、他の方々がすばらしいものを作ってくるだろうなと思ったので、
私はネタ担当と勝手に思い、「jsfl使ってこんなこともできたよ!」というものを作ろうと思いました。

なので、ブラウザからflashのステージ上や同時接続している他のブラウザにも描き込む
お絵描きチャットを作ってみました。

node_canvas_cap1 node_canvas_cap2 node_canvas_cap3

flashとサーバーの通信は、WindowSWF(拡張パネル)からSocket通信をしています。
ブラウザとサーバーはWebosocketを使って通信します。
サーバーはnode.jsを使って立てます。

それで、各フレームに描いたら、swiffyでパブリッシュをします。
ブラウザから描き込んでいるので、iPhoneからも描き込めて、swiffyでパブリッシュなので、変換されたアニメーションも表示することができました。

発表の資料はこちらです。

PDF版
node_canvas.pdf

ソースはnode.jssocket.ioを使っているので、かなり短くすみました。

ソース一式です。

server.js
node.jsのサーバーたちです。

var app = require('http').createServer(handler),
    io = require('socket.io').listen(app),
    fs = require('fs'),
    net = require('net'),
    url = require('url'),
    canvasdata = require('./canvasdata'),
    tlframes = [],
    flashSock;

for(var i = 0, len = 6; i < len; i++){
    tlframes[i] = new canvasdata.TLFrame();
}

var responseFile = function(path, res){
    fs.readFile(__dirname + path, function (err, data) {
        if (err) {
            res.writeHead(500);
            return res.end('Error loading index.html');
        }

        res.writeHead(200);
        res.end(data);
    });
};

function handler(req, res) {
    var path = url.parse(req.url);
    if(path.href === '/'){
        responseFile('/index.html', res);
    }else if(path.href === '/canvasdata.js'){
        responseFile('/canvasdata.js', res);
    }else if(path.href === '/ui_image.png'){
        responseFile('/ui_image.png', res);
    }else if(path.href === '/canvas.swf.html'){
        responseFile('/../flash/flash/canvas.swf.html', res);
    }else{
        res.writeHead(404);
        res.end('not found');
    }
};

app.listen(4000);

io.set('log level', 2);

io.sockets.on('connection', function (socket) {
    //clone
    var frame,
        stroke,
        flashJSON;
    socket.emit('clone', { type:'start' });
    for(var i = 0, len = tlframes.length; i < len; i += 1){
        frame = tlframes[i];
        for(var j = 0, len2 = frame.getStrokeLength(); j < len2; j += 1){
            stroke = frame.getStroke(j);
            socket.emit('clone', {
                type:'add',
                frame:i,
                stroke:j,
                width:stroke.width,
                color:stroke.color,
                points:stroke.points        
            });
        }
    }
    socket.emit('clone', { type:'end' });

    //touch

    socket.on('touch', function(data){
        if(data.type === 'start'){
            frame = tlframes[data.frame];
            data.stroke = frame.getStrokeLength();
            stroke = new canvasdata.Stroke(data.width, data.color);
            stroke.points.push(data.x, data.y);
            frame.addStroke(stroke);
            io.sockets.emit('touch', data);
            
        }else if(data.type === 'move'){
            frame = tlframes[data.frame];
            stroke = frame.getStroke(data.stroke);
            stroke.points.push(data.x, data.y);
            io.sockets.emit('touch', data);

        }else if(data.type === 'end'){
            if(flashSock){
                frame = tlframes[data.frame];
                stroke = frame.getStroke(data.stroke);
                flashJSON = JSON.stringify({
                    "frame":data.frame,
                    "stroke":stroke
                });
                flashSock.write(flashJSON); 
                //JSON.parse(text);
            }
        }
    });
    socket.on('publish', function(data){
        flashJSON = JSON.stringify({"type":"publish"});
        flashSock.write(flashJSON);
    });
});


var sockServer = net.createServer(function(socket){
    console.log('client connected');
    flashSock = socket;

    socket.on('end', function(){
        console.log('client disconnected');
    });
    socket.on('data', function(buf){
        var msg = buf.toString();
        if(msg.indexOf('swiffy complete') !== -1){
            io.sockets.emit('location', {href:'canvas.swf.html'});
            //console.log('complete');
        }
    });
});

sockServer.listen(3000, function(){
    console.log('server listen');
});

canvasdata.js
クライアント、サーバー共通で使います。

var TLFrame = function(){
    this.strokes = [];  
};
TLFrame.prototype.addStroke = function(stroke){
    this.strokes.push(stroke);
};
TLFrame.prototype.getStroke = function(index){
    return this.strokes[index];
};
TLFrame.prototype.getStrokeLength = function(){
    return this.strokes.length;
};
var Stroke = function(width, color){
    this.width = width;
    this.color = color;
    this.points = [];
};

if(exports){
	exports.TLFrame = TLFrame;
	exports.Stroke = Stroke;	
}

index.html
クライアント側です。

<html>
<head>
<meta name="apple-mobile-web-app-capable" content="no">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<script src="/socket.io/socket.io.js"></script>
<script src="canvasdata.js"></script>
<script>

var Painter = function(){
    this.canvas = document.getElementById('mycanvas');
    this.ctx = this.canvas.getContext('2d');
    this.CW = this.canvas.width;
    this.CH = this.canvas.height;
    this.tlframes;
    this.currentFrameIdx = 0;
    this.currentStrokeIdx = 0;
    this.initFrames();

    this.clear();
};
Painter.prototype = {
    initFrames:function(){
        this.tlframes = [];
        for(var i = 0, len = 6; i < len; i++){
            this.tlframes[i] = new TLFrame();
        }
        this.currentFrameIdx = 0;
        this.currentStrokeIdx = 0;
    },
    clear:function(){
        this.ctx.fillStyle = '#ffffff';
        this.ctx.fillRect(0,0,this.CW, this.CH);
    },
    draw:function(frame){
        var stroke,
            points;
        this.clear();
        for(var i = 0, len = frame.getStrokeLength(); i < len; i += 1){
            stroke = frame.getStroke(i);
            points = stroke.points;
            this.ctx.strokeStyle = stroke.color;
            this.ctx.lineWidth = stroke.width;
            this.ctx.beginPath();
            this.ctx.moveTo(points[0], points[1]);
            for(var j = 2, len2 = points.length; j < len2; j += 2){
                this.ctx.lineTo(points[j], points[j+1]);
            }
            this.ctx.stroke();
        }
    },
    onSocketTouchData:function(data){
        var frame,
            stroke;
        if(data.type === 'start'){
            this.currentStrokeIdx = data.stroke;
            frame = this.tlframes[data.frame];
            stroke = new Stroke(data.width, data.color);
            stroke.points.push(data.x, data.y);
            frame.addStroke(stroke);

        }else if(data.type === 'move'){
            frame = this.tlframes[data.frame];
            stroke = frame.getStroke(data.stroke);
            stroke.points.push(data.x, data.y);

            if(data.frame === this.currentFrameIdx){
                this.draw(frame);   
            }
            

        }else if(data.type === 'end'){
            /*
            frame = this.tlframes[data.frame];
            stroke = frame.getStroke(data.stroke);
            stroke.points.push(data.x, data.y);
            this.draw(frame);
            */
        }
    },
    onSocketCloneData:function(data){
        var frame,
            stroke;
        if(data.type === 'start'){
            this.initFrames();

        }else if(data.type === 'add'){
            frame = this.tlframes[data.frame];
            stroke = new Stroke(data.width, data.color);
            stroke.points = data.points;
            frame.addStroke(stroke);

        }else if(data.type === 'end'){
            frame = this.tlframes[this.currentFrameIdx];
            this.draw(frame);
        }
    },
    setCurrentFrameIdx:function(idx){
        this.currentFrameIdx = idx;
        this.draw(this.tlframes[idx]);
    }
};

var Socketio = function(){
    this.socket;
    this.main;
    this.delegate;
};
Socketio.prototype = {
    connect:function(){
        this.socket = io.connect('http://localhost');
        this.registerEvents();
    },
    registerEvents:function(){
        var self = this;
        this.socket.on('touch', function(data){
            if(self.delegate){
                self.delegate.onSocketTouchData(data);
            }
            if(data.type === 'start'){
                self.main.setTimelineBg(data.frame, 'keyframe_element');
            }
        });
        this.socket.on('clone',function(data){
            if(self.delegate){
                self.delegate.onSocketCloneData(data);
            }
            if(data.type === 'add'){
                self.main.setTimelineBg(data.frame, 'keyframe_element');
            }
        });
        this.socket.on('location',function(data){
            location.href = data.href;
        });
    },
    emitTouchStart:function(frameIdx, strokeWidth, strokeColor, point){
        this.socket.emit('touch', {
           type:'start',
           frame:frameIdx,
           x:point.x,
           y:point.y,
           width:strokeWidth,
           color:strokeColor
        });
    },
    emitTouchMove:function(frameIdx, strokeIdx, point){
        this.socket.emit('touch', {
           type:'move',
           frame:frameIdx,
           stroke:strokeIdx,
           x:point.x,
           y:point.y
        });
    },
    emitTouchEnd:function(frameIdx, strokeIdx, point){
        this.socket.emit('touch', {
           type:'end',
           frame:frameIdx,
           stroke:strokeIdx
           //x:point.x,
           //y:point.y
        });
    },
    emitPublish:function(){
        this.socket.emit('publish', {type:'publish'});
    }
};

var TouchUtil = function(){
};
TouchUtil.addListener = function(element, type, listener){
    if('ontouchstart' in window){
        switch(type){
            case 'start':
                element.addEventListener('touchstart', listener);
                break;
            case 'move':
                element.addEventListener('touchmove', listener);
                break;
            case 'end':
                element.addEventListener('touchend', listener);
                break;
            defalt:
                break;
        }
    }else{
        switch(type){
            case 'start':
                element.addEventListener('mousedown', listener);
                break;
            case 'move':
                element.addEventListener('mousemove', listener);
                break;
            case 'end':
                element.addEventListener('mouseup', listener);
                break;
            defalt:
                break;
        }
    }
};
TouchUtil.removeListener = function(element, type, listener){
    if('ontouchstart' in window){
        switch(type){
            case 'start':
                element.removeEventListener('touchstart', listener);
                break;
            case 'move':
                element.removeEventListener('touchmove', listener);
                break;
            case 'end':
                element.removeEventListener('touchend', listener);
                break;
            defalt:
                break;
        }
    }else{
        switch(type){
            case 'start':
                element.removeEventListener('mousedown', listener);
                break;
            case 'move':
                element.removeEventListener('mousemove', listener);
                break;
            case 'end':
                element.removeEventListener('mouseup', listener);
                break;
            defalt:
                break;
        }
    }
};
TouchUtil.getPositionByEvent = function(e){
    if(e.touches && e.touches[0]){
        return {x:e.touches[0].clientX, y:e.touches[0].clientY};
    }else{
        return {x:e.clientX, y:e.clientY};
    }
};

var Main = function(){
    var self = this,
    anoBodyTouchMove = function(e){
        e.preventDefault();
        self.onBodyTouchMove(e);
    },
    anoBodyTouchEnd = function(e){
        e.preventDefault();
        self.onBodyTouchEnd(e);
        TouchUtil.removeListener(self.body, 'move', anoBodyTouchMove);
        TouchUtil.removeListener(self.body, 'end', anoBodyTouchEnd);
    },
    anoMenuTouchMove = function(e){
        self.onMenuTouch(e);
    },
    anoMenuTouchEnd = function(e){
        TouchUtil.removeListener(self.body, 'move', anoMenuTouchMove);
        TouchUtil.removeListener(self.body, 'end', anoMenuTouchEnd);
    };

    this.painter = new Painter();
    this.socket = new Socketio();
    this.socket.main = this;
    this.socket.delegate = this.painter;
    this.socket.connect();

    this.body = document.getElementsByTagName('body')[0];

    TouchUtil.addListener(this.painter.canvas, 'start', function(e){
        e.preventDefault();
        self.onCanvasTouchStart(e);
        TouchUtil.addListener(self.body, 'move', anoBodyTouchMove);
        TouchUtil.addListener(self.body, 'end', anoBodyTouchEnd);
    });

    var timeline = document.getElementById('menu');
    TouchUtil.addListener(timeline, 'start', function(e){
        e.preventDefault();
        TouchUtil.addListener(self.body, 'move', anoMenuTouchMove);
        TouchUtil.addListener(self.body, 'end', anoMenuTouchEnd);
        self.onMenuTouch(e);
    });

    var publishBtn = document.getElementById('publish_link');
    publishBtn.addEventListener('click', function(e){
        e.preventDefault();
        self.socket.emitPublish();
    });
};
Main.prototype = {
    onBodyTouchMove:function(e){
        var point = TouchUtil.getPositionByEvent(e);
        point.y -= 55;
        this.socket.emitTouchMove(this.painter.currentFrameIdx, this.painter.currentStrokeIdx, point);
    },
    onBodyTouchEnd:function(e){
        var point = TouchUtil.getPositionByEvent(e);
        point.y -= 55;
        this.socket.emitTouchEnd(this.painter.currentFrameIdx, this.painter.currentStrokeIdx, point);
    },
    onCanvasTouchStart:function(e){
        var point = TouchUtil.getPositionByEvent(e);
        point.y -= 55;
        this.socket.emitTouchStart(this.painter.currentFrameIdx, 1, '#000000', point);
    },
    setTimelineBg:function(index, type){
        var timeline = document.getElementById('tl_frame'+index);
        if(type === 'empty'){
            
        }else if(type === 'keyframe'){
            timeline.style['background-position'] = '0px -25px';

        }else if(type === 'keyframe_element'){
            timeline.style['background-position'] = '0px -55px';    
        }
    },
    setTimelineCurrentPosition:function(frameInex){
        var tlcurrent = document.getElementById('timeline_current');
        tlcurrent.style['left'] = (frameInex * 50 + 1) + 'px';
    },
    onMenuTouch:function(e){
        var point = TouchUtil.getPositionByEvent(e);
        var tlframeIdx = Math.floor(point.x / 50);
        if(tlframeIdx > 5){
            return;
        }
        if(tlframeIdx !== this.painter.currentFrameIdx){
            this.painter.setCurrentFrameIdx(tlframeIdx);
            this.setTimelineCurrentPosition(tlframeIdx);
        }
    }
};


window.onload = function(){
  var main = new Main();
};


</script>
<style>
body{
	margin: 0px;
	padding: 0px;
	background-color:#000;
}
ul,li{
    margin:0px;
    padding:0px;
}
#wrapper{
    width:320px;
    height:460px;
    text-align:left;
    background-color:#D4D4D4;
}
#menu{
    height:55px;
    position: relative;
    overflow: hidden;
}
#timeline{
    width:350px;
    padding-top:25px;
    margin-left:0px;
}
#timeline li{
    width:50px;
    height:30px;
    display:block;
    float:left;
    background-image:url(ui_image.png);
    background-repeat:no-repeat;
    background-position:0px -25px;
}
#timeline li.empty{
    background:url(ui_image.png) no-repeat -200px -25px;
}
#timeline_ruler{
    width:320px;
    height:25px;
    background:url(ui_image.png) no-repeat 0px 0px;
    position: absolute;
    top:0px;
    left:0px;
}
#timeline_current{
    width:48px;
    height:53px;
    background:url(ui_image.png) no-repeat -260px -100px;
    position: absolute;
    top:2px;
    left:1px;
}
/*
canvas{
    border:1px solid #999;
}
*/
footer{
    width:320px;
    height:45px;
    background:url(ui_image.png) no-repeat 0px -160px;
}
#publish{
    width:64px;
    height:20px;
    padding-top:8px;
    margin-left:6px;
}
#publish a{
    width:64px;
    height:30px;
    display:block;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="menu">
<ul id="timeline">
<li id="tl_frame0"></li>
<li id="tl_frame1"></li>
<li id="tl_frame2"></li>
<li id="tl_frame3"></li>
<li id="tl_frame4"></li>
<li id="tl_frame5"></li>
<li id="tl_frame6" class="empty"></li>
</ul>
<div id="timeline_current"></div>
<div id="timeline_ruler"></div>
</div>
<canvas width="320" height="360" id="mycanvas"></canvas>
<footer>
<div id="publish"><a href="#" id="publish_link"></a></div>
</footer>
</div>
</body>
</html>

NodePainter.as
flash側の拡張パネルです。

package
{
	import adobe.utils.MMExecute;
	
	import com.adobe.serialization.json.JSON;
	import com.bit101.components.PushButton;
	
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.net.Socket;
	
	[SWF(width="250",height="250",frameRate="24",backgroundColor="#aaaaaa")]
	public class NodePainter extends Sprite
	{
		public static const CONNECT_COLOR_RED:int = 0;
		public static const CONNECT_COLOR_GREEN:int = 1;
		
		private var _socket:Socket;
		private var _connectButton:PushButton;
		private var _isconnected:Boolean = false;
		private var _connectCircle:Sprite;
		public const HOST:String = "localhost";
		public const PORT:int = 3000;
		
		public function NodePainter()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			createConnectCircle();
			_connectButton = new PushButton(this, 10, 10, "CONNECT", onConnectClick);
		}
		
		private function onConnectClick(e:MouseEvent):void
		{
			if(_isconnected === false){
				_socket = new Socket();
				_socket.addEventListener(Event.CONNECT, onSocketConnect);
				_socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
				_socket.connect(HOST, PORT);
				_connectButton.label = "DISCONNECT";
			}else{
				_isconnected = false;
				drawConnectCircle(CONNECT_COLOR_RED);
				_socket.removeEventListener(Event.CONNECT, onSocketConnect);
				_socket.removeEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
				_socket.close();
				_socket = null;
				_connectButton.label = "CONNECT";
			}
		}
		
		protected function onSocketData(event:ProgressEvent):void
		{
			var chars:Array = [],
				char:String,
				msg:String;
			while(_socket.bytesAvailable){
				char = _socket.readUTFBytes(1);
				chars.push(char);
			}
			msg = chars.join('');
			var obj:Object = JSON.decode(msg);
			
			if(obj.type === 'publish'){
				this.publishSwiffy();
				_socket.writeUTF('swiffy complete');
				_socket.flush();
				
			}else{
				this.changeFrame(obj.frame);
				this.changeStroke(obj.stroke.width, obj.stroke.color);
				this.drawStagePath(obj.stroke);
			}
			//trace(obj.width, obj.color, obj.points.length);
		}
		
		private function publishSwiffy():void
		{
			var commands:Array = [];
			commands.push('var path = fl.configURI + "Commands/" + "Export as HTML5 (Swiffy).jsfl";');
			commands.push('fl.runScript(path);');
			MMExecute(commands.join(''));
		}
		
		private function changeStroke(strokeWidth:int, strokeColor:String):void
		{
			var commands:Array = [];
			commands.push('var myStroke = fl.getDocumentDOM().getCustomStroke("toolbar");');
			commands.push('myStroke.color = "'+strokeColor+'";');
			commands.push('fl.getDocumentDOM().setCustomStroke(myStroke);');
			MMExecute(commands.join(''));
		}
		
		private function changeFrame(frameIndex:int):void
		{
			var jsfl:String = "fl.getDocumentDOM().getTimeline().currentFrame = "+frameIndex;
			MMExecute(jsfl);
		}
		
		private function drawStagePath(obj:Object):void
		{
			var commands:Array = [],
				i:int = 0,
				len:int = obj.points.length;
			commands.push('var fill = fl.getDocumentDOM().getCustomFill();');
			commands.push('fill.style= "noFill";');
			commands.push('fl.getDocumentDOM().setCustomFill( fill );');
			commands.push('var myPath = fl.drawingLayer.newPath();');
			
			for(i = 0; i < len; i += 2){
				commands.push('myPath.addPoint('+obj.points[i]+', '+obj.points[i+1]+');');
			}
			commands.push('myPath.makeShape();');
			MMExecute(commands.join(''));
		}
		
		protected function onSocketConnect(event:Event):void
		{
			_isconnected = true;
			drawConnectCircle(CONNECT_COLOR_GREEN);
		}
		
		private function createConnectCircle():void
		{
			_connectCircle = new Sprite();
			_connectCircle.x = 120;
			_connectCircle.y = 15;
			addChild(_connectCircle);
			drawConnectCircle(CONNECT_COLOR_RED);
		}
		
		private function drawConnectCircle(colorType:int):void
		{
			var color:int, 
				g:Graphics = _connectCircle.graphics;
			
			if(colorType === CONNECT_COLOR_RED){
				color = 0xFF0022;
				
			}else if(colorType === CONNECT_COLOR_GREEN){
				color = 0x74D062;
			}
			g.clear();
			g.lineStyle(1,0x888888,0.5);
			g.beginFill(color,1);
			g.drawCircle(5, 5, 5);
			g.endFill();
		}
	}
}
LINEで送る
Pocket

ページトップへ戻る