In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: IME Typing

Microsoft IMEのローマ字入力に対応したタイピングゲームです。
日本語入力モードをオフにしてプレイします。
Get Adobe Flash player
by yamat1011 05 Apr 2011
/**
 * Copyright yamat1011 ( http://wonderfl.net/user/yamat1011 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/93fN
 */

// forked from shohei909's IME Typing
/* 
Microsoft IMEのローマ字入力に対応したタイピングゲームです。
日本語入力モードをオフにしてプレイします。
*/
package {
    import flash.display.Sprite;
    [SWF(backgroundColor="0x113399")]
    public class FlashTest extends Sprite {
        private var words:Array = [
                { word : "今日から夏の大バーゲンセール!" ,         kana : "きょうからなつのだいばーげんせーる!" },
                { word : "くわばらくわばら。" ,             kana : "くわばらくわばら。" },
                { word : "蟻が鯛なら芋虫ゃクジラ" ,             kana : "ありがたいならいもむしゃくじら" },
                { word : "赤信号みんなで渡れば怖くない。" ,         kana : "あかしんごうみんなでわたればこわくない。" },
                { word : "どうってことないさ。" ,             kana : "どうってことないさ" },
                { word : "何か用かの九日十日" ,             kana : "なにかようかのここのかとおか" },
                { word : "悪事に手を染める。" ,             kana : "あくじにてをそめる。" },
                { word : "その手は桑名の焼き蛤" ,             kana : "そのてはくはなのやきはまぐり" },
                { word : "黄金虫は金持ちだ。" ,             kana : "こがねむしはかねもちだ。" },
                { word : "全米ナンバーワン大ヒット映画が日本上陸!" ,     kana : "ぜんべいなんばーわんだいひっとえいががにほんじょうりく!" },
                { word : "驚き桃の木山椒の木" ,             kana : "おどろきもものきさんしょのき" },
                { word : "犯罪から足を洗う。" ,             kana : "はんざいからあしをあらう。" },
                { word : "このぬれせんべい、湿気ってる。" ,         kana : "このぬれせんべいしけってる。" },
                { word : "やーい、お前んち、おっばけやーしきー。" ,     kana : "やーい、おまえんち、おっばけやーしきー。" },
                { word : "明後日からがんばる。" ,             kana : "明後日からがんばる。" },
                { word : "聞き流すだけで、驚くほど英語が話せるようになる!" , kana : "ききながすだけでおどろくほどえいごがはなせるようになる!" },
                { word : "竹垣に丈高い竹立てかけた。" ,         kana : "たけがきにたけたかいたけたてかけた。" },
                { word : "掘ったイモいじるな。" ,             kana : "ほったいもいじるな。" },
                { word : "割っちゃいねぇ。" ,                 kana : "わっちゃいねぇ。" },
                { word : "今なら五個セットでサンキュッパです。" ,     kana : "いまならごこせっとでさんきゅっぱです。" },
                { word : "あら安い。" ,                 kana : "あらやすい。" },
                { word : "目の上のこぶた" ,                 kana : "めのうえのこぶた" },
                { word : "濡れ手で泡々" ,                 kana : "ぬれてであわあわ" },
                { word : "だから小さいすずらにしとけって言ったのに。" , kana : "だからちいさいすずらにしとけっていったのに。" },
                { word : "まずい!ずらかるぞ!" ,             kana : "まずい!ずらかるぞ!" },
                { word : "あながち間違ってなかったね。" ,         kana : "あながちまちがってなかったね。" },
                { word : "そこはかとなくすばらしい。" ,         kana : "そこはかとなくすばらしい。" },
                { word : "どっこいどっこい" ,                 kana : "どっこいどっこい" },
                { word : "竹垣に竹立てかけたら怒られた。" ,         kana : "たけがきにたけたてかけたらおこられた。" },
                { word : "すきやき焼きすぎた。" ,             kana : "すきやきやきすぎた。" },
                { word : "富士山麓、オウム鳴く。" ,             kana : "ふじさんろくおうむなく。" },
                { word : "これは鉛筆ですか?いいえ、机です。" ,     kana : "これはえんぴつですか?いいえ、つくえです。" },
                { word : "ややこしい。" ,                 kana : "ややこしい。" },
                { word : "しめしめ" ,                     kana : "しめしめ" },
                { word : "明日できることは明日やる。" ,         kana : "あしたできることはあしたやる。" },
                { word : "電球「おれ切れるとマジ周りとか見えなくなるからな」" ,     kana : "でんきゅう「おれきれるとまじまわりとかみえなくなるからな」" },
                { word : "ここぞとばかり" ,                 kana : "ここぞとばかり" },
                { word : "ナウなヤングにバカウケ" ,             kana : "なうなやんぐにばかうけ" },
                { word : "あばらバラバラ",                 kana : "あばらばらばら" },
                { word : "ガダルカナル" ,    kana : "がだるかなる" },
                { word : "ヤンバルクイナ" ,    kana : "やんばるくいな" },
                { word : "プノンペン" ,        kana : "ぷのんぺん" },
                { word : "ワイナイナ" ,        kana : "わいないな" },
                { word : "肉きゅう" ,        kana : "にくきゅう" },
                { word : "痛しかゆし" ,        kana : "いたしかゆし" },
                { word : "へなちょこ" ,     kana : "へなちょこ" },
                { word : "小腹" ,        kana : "こばら" },
                { word : "遮二無二" ,         kana : "しゃにむに" },
                { word : "マカロン" ,         kana : "まかろん" },
                { word : "老若男女" ,         kana : "ろうにゃくなんにょ" },
                { word : "すこぶる" ,         kana : "すこぶる" },
                { word : "キャッサバ粉" ,     kana : "きゃっさばこ" },
                { word : "似たり寄ったり" ,     kana : "にたりよったり" },
                { word : "ぬらりひょん" ,     kana : "ぬらりひょん" },
                { word : "あっけらかん" ,     kana : "あっけらかん" },
                { word : "叙情詩" ,         kana : "じょじょうし" },
                { word : "からきし" ,         kana : "からきし" },
                { word : "オコジョ" ,         kana : "おこじょ" },
                { word : "こちょこちょ" ,    kana : "こちょこちょ" },
                { word : "あんぽんたん" ,     kana : "あんぽんたん" },
                { word : "カッパドキア" ,     kana : "かっぱどきあ" },
                { word : "ちちんぷいぷい" ,     kana : "ちちんぷいぷい" },
                { word : "アブラカタブラ" ,     kana : "あぶらかたぶら" },
                { word : "情状酌量" ,         kana : "じょうじょうしゃくりょう" },
                { word : "金屏風" ,         kana : "きんびょうぶ" },
                { word : "ニイニイゼミ" ,     kana : "にいにいぜみ" },
                { word : "ブッポウソウ" ,     kana : "ぶっぽうそう" },
                { word : "ツクツクボウシ" ,     kana : "つくつくぼうし" },
                { word : "マチュピチュ" ,     kana : "まちゅぴちゅ" },
                { word : "高速増殖炉もんじゅ" , kana : "こうそくぞうしょくろもんじゅ" },
                { word : "貨客船マンギョンボン号" , kana : "かきゃくせんまんぎょんぼんごう" },
                { word : "じゃばら" ,         kana : "じゃばら" },
                { word : "鎖骨" ,         kana : "さこつ" },
                { word : "タクラマカン砂漠" ,     kana : "たくらまかんさばく" },
                { word : "アルファルファ" ,     kana : "あるふぁるふぁ" },
                { word : "すっとこどっこい" ,     kana : "すっとこどっこい" },
                { word : "御意" ,         kana : "ぎょい" },
                { word : "支笏湖" ,         kana : "しこつこ" },
                { word : "すたこらさっさ" ,     kana : "すたこらさっさ" },
                { word : "ざっくばらん" ,     kana : "ざっくばらん" },
                { word : "腕っぷし" ,         kana : "うでっぷし" },
                { word : "行き当たりばったり" , kana : "いきあたりばったり" },
                { word : "アルパカ" ,         kana : "あるぱか" },
                { word : "笑止" ,         kana : "しょうし" },
                { word : "ちょんまげ" ,     kana : "ちょんまげ" },
                { word : "ブギウギ" ,         kana : "ぶぎうぎ" },
                { word : "お得意様" ,         kana : "おとくいさま" },
                { word : "猫だまし" ,         kana : "ねこだまし" },
                { word : "イグノーベル賞" ,     kana : "いぐのーべるしょう" },
                { word : "ハラペーニョ" ,     kana : "はらぺーにょ" },
                { word : "なれの果て" ,     kana : "なれのはて" },
                { word : "抗う" ,         kana : "あらがう" },
                { word : "デイダラボッチ" ,     kana : "でいだらぼっち" },
                { word : "なしくずし" ,     kana : "なしくずし" },
                { word : "チョベリバ" ,     kana : "ちょべりば" },
                { word : "ダブルラリアット" ,     kana : "だぶるらりあっと" },
                { word : "がらくた" ,         kana : "がらくた" },
                { word : "わたがし" ,         kana : "わたがし" },
                { word : "チャンチャラおかしい" , kana : "ちゃんちゃらおかしい" },
                { word : "プルコギ" ,         kana : "ぷるこぎ" },
                { word : "ちりとてちん" ,     kana : "ちりとてちん" },
                { word : "つまようじ" ,     kana : "つまようじ" },
                { word : "ンジャメナ" ,        kana : "んじゃめな" },
                { word : "きかんぼう" ,     kana : "きかんぼう" },
                { word : "度肝を抜く" ,     kana : "どぎもをぬく" },
                { word : "やっつけ仕事" ,     kana : "やっつけしごと" },
                { word : "そこそこ" ,         kana : "そこそこ" },
                { word : "キッパリ" ,         kana : "きっぱり" },
                { word : "へっぽこ" ,         kana : "へっぽこ" },
                { word : "エルニーニョ" ,     kana : "えるにーにょ" },
                { word : "えげつない" ,     kana : "えげつない" },
                { word : "嘘八百" ,         kana : "うそはっぴゃく" },
                { word : "夕焼け小焼け" ,     kana : "ゆうやけこやけ" },
                { word : "八方塞がり" ,     kana : "はっぽうふさがり" },
                { word : "アンドロイド" ,     kana : "あんどろいど" },
                { word : "もやしキムチ" ,     kana : "もやしきむち" },
                { word : "チャンバラごっこ" , kana : "ちゃんばらごっこ" },
                { word : "チョコレート" ,     kana : "ちょこれーと" },
                { word : "おっちょこちょい" ,     kana : "おっちょこちょい" },
                { word : "しゃらくさい" ,     kana : "しゃらくさい" },
                { word : "小ざかしい" ,     kana : "こざかしい" },
                { word : "トムソンガゼル" ,     kana : "とむそんがぜる" },
                { word : "しくじる" ,         kana : "しくじる" },
                { word : "くじける" ,         kana : "くじける" },
                { word : "癇癪玉" ,         kana : "かんしゃくだま" },
                { word : "ひっけらかす" ,     kana : "ひっけらかす" },
                { word : "かまいたち" ,     kana : "かまいたち" },
                { word : "いたちごっこ" ,     kana : "いたちごっこ" },
                { word : "大目玉を食らう" ,     kana : "おおめだまをくらう" },
                { word : "てんてこまい" ,     kana : "てんてこまい" },
                { word : "じゃじゃ馬" ,     kana : "じゃじゃうま" },
                { word : "サモトラケのニケ" ,     kana : "さもとらけのにけ" },
                { word : "とんずら" ,         kana : "とんずら" },
                { word : "カヤック" ,         kana : "かやっく" },
                { word : "岡持ち" ,         kana : "おかもち" },
                { word : "イソギンチャク" ,     kana : "いそぎんちゃく" },
                { word : "オリゴ糖" ,         kana : "おりごとう" },
                { word : "原っぱ" ,         kana : "はらっぱ" },
                { word : "枝豆" ,         kana : "えだまめ" },
                { word : "ジャングルジム" ,     kana : "じゃんぐるじむ" },
                { word : "フラメンコ" ,     kana : "ふらめんこ" },
                { word : "水族館" ,         kana : "すいぞくかん" },
                { word : "かっぱらう" ,     kana : "かっぱらう" },
                { word : "にんまり" ,         kana : "にんまり" },
                { word : "タイピングなう" ,         kana : "たいぴんぐなう" },
                { word : "ガビーン" ,         kana : "がびーん" }
        ];
        
        function FlashTest() {{
                var typing:Typing = new Typing( stage );
                
                TypeStyle.setStyle( TypeStyle.DARK );
                
                //accessary
                var keyboard:TypeKeyboard = new TypeKeyboard( typing );
                addChild( keyboard );
                keyboard.scaleX = keyboard.scaleY = 0.7;
                keyboard.x = 20;
                keyboard.y = 300;
                
                //panel
                var panel:TypePanel = new TypePanel( typing, 464, 55, 0.25, 0.45, 0.30 );
                panel.y = 200;
                addChild( panel );
                
                //monitor
                var monitor:TypeMonitor = new TypeMonitor( typing, 445, 100, 0.25, 0.45, 0.30 );
                monitor.x = 10;
                monitor.y = 10;
                //addChild( monitor );
                
                //manager
                new TypeManager( typing, words );
                
                //start
                typing.start();
            }
        }
    }
}
import flash.utils.Timer;
import flash.display.Shape;

import flash.text.TextFormat;
import flash.text.TextField;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.utils.getTimer;
import flash.display.Stage;

class TypeData {
    /** 半角文字のリスト */
    static public const HANKAKU:String = "1234567890-^\\qwertyuiop@[asdfghjkl;:]zxcvbnm,./!\"#$%&'()=~|QWERTYUIOP`{ASDFGHJKL+*}ZXCVBNM<>?_ ";
    
    /** HANKAKUに対応する全角文字のリスト */
    static public const ZENKAKU:String = "1234567890ー^¥qwertyuiop@[asdfghjkl;:]zxcvbnm,./!”#$%&’()=~|QWERTYUIOP‘{ASDFGHJKL+*}ZXCVBNM<>?_ ";
    
    /** かなからローマ字への変換表*/
    static public const ROMANS:Object = {
        "あ":["a"], "う":["u", "wu", "whu"], "い":["i", "yi"], "え":["e"], "お":["o"],
        "か":["ka", "ca"], "き":["ki"], "く":["ku", "cu"], "け":["ke"], "こ":["ko", "co"],
        "さ":["sa"], "し":["si", "shi", "ci"], "す":["su"], "せ":["se", "ce"], "そ":["so"],
        "た":["ta"], "ち":["ti", "chi"], "つ":["tu", "tsu"], "て":["te"], "と":["to"], 
        "な":["na"], "に":["ni"], "ぬ":["nu"], "ね":["ne"], "の":["no"],
        "は":["ha"], "ひ":["hi"], "ふ":["fu", "hu"], "へ":["he"], "ほ":["ho"],
        "ま":["ma"], "み":["mi"], "む":["mu"], "め":["me"],"も":["mo"],
        "や":["ya"], "ゆ":["yu"], "よ":["yo"],
        "ら":["ra"], "り":["ri"],  "る":["ru"], "れ":["re"],"ろ":["ro"],
        "わ":["wa"], "を":["wo"], "ん":["nn"],
        "が":["ga"], "ぎ":["gi"], "ぐ":["gu"], "げ":["ge"],"ご":["go"],
        "ざ":["za"], "じ":["ji", "zi"], "ず":["zu"], "ぜ":["ze"], "ぞ":["zo"],
        "だ":["da"], "ぢ":["di"], "づ":["du"], "で":["de"], "ど":["do"],
        "ば":["ba"], "び":["bi"], "ぶ":["bu"], "べ":["be"], "ぼ":["bo"],
        "ぱ":["pa"], "ぴ":["pi"], "ぷ":["pu"], "ぺ":["pe"], "ぽ":["po"],
        "しゃ":["sya", "sha"], "しゅ":["syu", "shu"], "しょ":["syo", "sho"], "しぃ":["syi"], "しぇ":["sye", "she"],
        "きぃ":["kyi"], "きゃ":["kya"], "きゅ":["kyu"], "きぇ":["kye"], "きょ":["kyo"],
        "にゃ":["nya"], "にぃ":["nyi"],  "にゅ":["nyu"], "にぇ":["nye"], "にょ":["nyo"],
        "ちゃ":["tya", "cha", "cya"], "ちぃ":["tyi", "cyi"], "ちゅ":["tyu", "chu", "cyu"], "ちぇ":["tye", "che", "cye"], "ちょ":["tyo", "cho", "cyo"],
        "とぁ":["twa"], "とぃ":["twi"], "とぅ":["twu"], "とぇ":["twe"], "とぉ":["two"],
        "てゃ":["tha"], "てぃ":["thi"], "てゅ":["thu"], "てぇ":["the"], "てょ":["tho"],
        "ひゃ":["hya"], "ひぃ":["hyi"], "ひょ":["hyo"], "ひぇ":["hye"], "ひゅ":["hyu"],
        "りゃ":["rya"], "りぃ":["ryi"], "りゅ":["ryu"], "りぇ":["rye"], "りょ":["ryo"],
        "うぁ":["wa", "wha"], "うぃ":["wi", "whi"], "うぇ":["we", "whe"], "うぉ":["wo", "who"],
        "ヴぁ":["va"], "ヴぃ":["vi"], "ヴ":["vu"], "ヴぇ":["ve"], "ヴぉ":["vo"], "ヴゃ":["vya"], "ヴゅ":["vyu"], "ヴょ":["vyo"],
        "くぁ":["qa", "kwa", "qwa"],  "くぃ":["qi", "qwi", "qyi"],  "くぅ":["qwu", "qwu"], "くぇ":["qe", "qwe", "qye"], "くぉ":["qo", "qwo"], "くゃ":["qya"], "くゅ":["qyu"], "くょ":["qyo"],
        "すぁ":["swa"], "すぃ":["swi"], "すぅ":["swu"], "すぇ":["swe"], "すぉ":["swo"],
        "ふぁ":["fa", "fwa"], "ふぃ":["fi", "fwi", "fyi"], "ふぅ":["fwu"], "ふぇ":["fe", "fwe", "fye"], "ふぉ":["fo", "fwo", "fwo"], "ふゃ":["fya"], "ふゅ":["fyu"], "ふょ":["fyo"],
        "ぎゃ":["gya"], "ぎぃ":["gyi"], "ぎゅ":["gyu"], "ぎぇ":["gye"], "ぎょ":["gyo"],
        "じゃ":["ja", "zya", "jya"], "じぃ":["zyi", "jyi"], "じゅ":["ju", "zyu", "jyu"], "じぇ":["je", "zye", "jye"], "じょ":["jo", "zyo", "jyo"],
        "ぐぁ":["gwa"], "ぐぃ":["gwi"], "ぐぅ":["gwu"], "ぐぇ":["gwe"], "ぐぉ":["gwo"],
        "ぢゃ":["dya"], "ぢぃ":["dyi"], "ぢゅ":["dyu"], "ぢぇ":["dye"], "ぢょ":["dyo"],
        "どぁ":["dwa"], "どぃ":["dwi"], "どぅ":["dwu"], "どぇ":["dwe"], "どぉ":["dwo"],
        "でゃ":["dha"], "でぃ":["dhi"], "でゅ":["dhu"], "でぇ":["dhe"], "でょ":["dho"],
        "びゃ":["bya"], "びぃ":["byi"], "びゅ":["byu"], "びぇ":["bye"], "びょ":["byo"],
        "ぴゃ":["pya"], "ぴぃ":["pyi"], "ぴょ":["pyo"], "ぴぇ":["pye"], "ぴゅ":["pyu"],
        "つぁ":["tsa"], "つぃ":["tsi"], "つぇ":["tse"], "つぉ":["tso"],
        "いぇ":["ye"],
        "ぁ":["la", "xa"], "ぃ":["li", "xi", "lyi", "xyi"], "ぅ":["lu", "xu"], "ぇ":["le","xe","lye","xye"], "ぉ":["lo","xo"],
        "ゃ":["lya","xya"],"ゅ":["lyu","xyi"],"ょ":["lyo","xyu"],"ゎ":["lwa","xwa"],
        "っ":["ltu","ltsu"],"ヶ":["lke","xke"],"ヵ":["lka","xka"],
        "「":["["], "」":["]"], "。":["."], "、":[","]
    }
    
    /** ローマ字からかなへの変換表  */
    static public const KANA:Object = {
        qwa:"くぁ", qa:"くぁ", bye:"びぇ", kwa:"くぁ", sho:"しょ", syu:"しゅ", byo:"びょ", byu:"びゅ", syi:"しぃ", pya:"ぴゃ",
        sye:"しぇ", qi:"くぃ", shu:"しゅ", ti:"ち", qwi:"くぃ", qe:"くぇ", qyi:"くぃ", pyo:"ぴょ", cu:"く", tu:"つ", qwu:"くぅ",
        kya:"きゃ", pyi:"ぴぃ", qo:"くぉ", chi:"ち", pyu:"ぴゅ", kyi:"きぃ", she:"しぇ", qye:"くぇ", tsa:"つぁ", pye:"ぴぇ",
        qwe:"くぇ", qya:"くゃ", kyu:"きゅ", tsu:"つ", tsi:"つぃ", kye:"きぇ", te:"て", nu:"ぬ", qyu:"くゅ", to:"と", qwo:"くぉ",
        ne:"ね", tse:"つぇ", kyo:"きょ", "]":"」", no:"の", tso:"つぉ", "[":"「", qyo:"くょ", xa:"ぁ", na:"な", ha:"は", ye:"いぇ",
        swa:"すぁ", li:"ぃ", nya:"にゃ", swo:"すぉ", la:"ぁ", swi:"すぃ", lyi:"ぃ", nyi:"にぃ", xyi:"ゅ", swe:"すぇ", ku:"く", 
        swu:"すぅ", ke:"け", nyu:"にゅ", fa:"ふぁ", myi:"みぃ", nyo:"にょ", xu:"ぅ", nye:"にぇ", ni:"に", lu:"ぅ", mya:"みゃ",
        e:"え", hi:"ひ", fyi:"ふぃ", fwa:"ふぁ", i:"い", fwi:"ふぃ", myo:"みょ", fu:"ふ", le:"ぇ", fi:"ふぃ", xi:"ぃ", hu:"ふ",
        xe:"ぇ", mye:"みぇ", o:"お", lye:"ぇ", ho:"ほ", he:"へ", u:"う", myu:"みゅ", sa:"さ", cha:"ちゃ", fwu:"ふぅ", cya:"ちゃ",
        xye:"ぇ", co:"こ", fe:"ふぇ", ma:"ま", tyi:"ちぃ", fwe:"ふぇ", tya:"ちゃ", cyi:"ちぃ", fye:"ふぇ", mi:"み", xya:"ゃ",
        lo:"ぉ", tyu:"ちゅ", lya:"ゃ", xo:"ぉ", lyu:"ゅ", fwo:"ふぉ", cyu:"ちゅ", mo:"も", fo:"ふぉ", lyo:"ょ", chu:"ちゅ",
        mu:"む", xyu:"ょ", ya:"や", che:"ちぇ", fyu:"ふゅ", me:"め", fya:"ふゃ", cye:"ちぇ", tye:"ちぇ", shi:"し", a:"あ",
        xwa:"ゎ", fyo:"ふょ", ri:"り", lwa:"ゎ", yu:"ゆ", cho:"ちょ", gya:"ぎゃ", ru:"る", tyo:"ちょ", yo:"よ", ci:"し",
        ltu:"っ", twi:"とぃ", cyo:"ちょ", ra:"ら", lka:"ヵ", gyu:"ぎゅ", gyi:"ぎぃ", xka:"ヵ", lke:"ヶ", ltsu:"っ", ja:"じゃ",
        xke:"ヶ", ko:"こ", re:"れ", twa:"とぁ", zya:"じゃ", gyo:"ぎょ", gye:"ぎぇ", jya:"じゃ", twu:"とぅ", jyi:"じぃ", su:"す",
        zyi:"じぃ", twe:"とぇ", ro:"ろ", ju:"じゅ", si:"し", two:"とぉ", wa:"うぁ", jyu:"じゅ", zyu:"じゅ", tha:"てゃ", wo:"うぉ",
        je:"じぇ", thu:"てゅ", thi:"てぃ", nn:"ん", jye:"じぇ", tho:"てょ", gi:"ぎ", ga:"が", jo:"じょ", zyo:"じょ", zye:"じぇ",
        jyo:"じょ", hyi:"ひぃ", ge:"げ", the:"てぇ", gwa:"ぐぁ", hyo:"ひょ", hya:"ひゃ", zi:"じ", gu:"ぐ", hye:"ひぇ", za:"ざ",
        go:"ご", gwu:"ぐぅ", hyu:"ひゅ", ji:"じ", gwe:"ぐぇ", ce:"せ", rya:"りゃ", gwo:"ぐぉ", zu:"ず", gwi:"ぐぃ", ryi:"りぃ",
        dya:"ぢゃ", ze:"ぜ", se:"せ", dyi:"ぢぃ", zo:"ぞ", ".":"。", so:"そ", dyu:"ぢゅ", da:"だ", rye:"りぇ", ",":"、", dye:"ぢぇ",
        di:"ぢ", ryo:"りょ", ryu:"りゅ", dyo:"ぢょ", du:"づ", wha:"うぁ", ta:"た", whi:"うぃ", de:"で", wi:"うぃ", dwa:"どぁ",
        "do":"ど", whe:"うぇ", dwi:"どぃ", ba:"ば", who:"うぉ", ki:"き", dwu:"どぅ", we:"うぇ", va:"ヴぁ", dwe:"どぇ", bu:"ぶ",
        bi:"び", vi:"ヴぃ", dwo:"どぉ", be:"べ", ka:"か", ca:"か", dha:"でゃ", bo:"ぼ", vu:"ヴ", dhi:"でぃ", pa:"ぱ", ve:"ヴぇ",
        dhu:"でゅ", pi:"ぴ", vo:"ヴぉ", dhe:"でぇ", pu:"ぷ", vya:"ヴゃ", dho:"でょ", pe:"ぺ", wu:"う", yi:"い", bya:"びゃ",
        po:"ぽ",vyo:"ヴょ",vyu:"ヴゅ",byi:"びぃ",sya:"しゃ",whu:"う",sha:"しゃ",syo:"しょ"
    }
    
    /** nのうしろに来てもnが「ん」にならない文字 */
    static public const NN:String = "euioany";
    
    /** 二つ続くと「っ」になる文字 */
    static public const LTU:String = "qwrtypsdfghjklzxcvbm";
    
    static public const CODE48:String = "0123456789"; 
    static public const CODE65:String = "abcdefghijklmnopqrstuvwxyz"; 
    static public const CODE186:String = ":;,-./@"; 
    static public const CODE219:String = "[¥]^"; 
    static public const CODE226:String = "\\"; 
    
    static public const QWERTY_KEY:Array = [ "1234567890-^¥", "qwertyuiop@[", "asdfghjkl;:]", "zxcvbnm,./\\" ];
    static public const QWERTY_SHIFT:Array = [ "!\"#$%&'() =~|", "QWERTYUIOP`{", "ASDFGHJKL+*}", "ZXCVBNM<>?_" ];
    static public const DVORAK_KEY:Array = [ "1234567890[]¥", ":,.pyfgcrl/@", "aoeuidhtns-^", ";qjkxbmwvz\\" ];
    static public const DVORAK_SHIFT:Array = [ "!\"#$%&'() {}|", "*<>PYFGCRL?`", "AOEUIDHTNS=~", "+QJKXBMWVZ_" ]
    static public const BOARD_INDENT:Array = [ 1, 1.5, 1.8, 2.3, 5 ]
}

class TypeUtil
{
    // romanのかなに変換可能な部分をかなに変換して返します
    static public function kana( roman:String, option:TypeOption = null ):String {
        if( roman == "" ){ return "" }
        var kana:String;
        for( var i:uint = 3; i > 0; i-- ){
            var subroman:String = roman.substr( 0, i );
            if( option ){ kana = option.kana( subroman ); }
            if( !kana ){ kana = TypeData.KANA[subroman]; }
            if( !kana && i == 1 ){ 
                var next:String = roman.substr( i, 1 );
                var hankaku:int = TypeData.HANKAKU.indexOf( subroman );
                if( subroman == next && TypeData.LTU.indexOf(subroman) != -1 ){ kana = "っ" }
                else if( subroman == "n" && TypeData.NN.indexOf(next) == -1 ){ kana = "ん" }
                else if( hankaku != -1 ){ kana = TypeData.ZENKAKU.substr( hankaku,1 ) }
            }
            if( kana ){ return kana + TypeUtil.kana( roman.substr(i), option ) }
        }
        return "";
    }
    
    //kanaに対してromanが入力されているときに、次にタイプできるキーの候補を返します。
    static public function next( kana:String, roman:String = "", option:TypeOption = null ):Array{
        var next:Array = [];
        if( kana == "" ){ return [] }
        for( var i:uint = 2; i > 0; i-- ){
            var roman2:String = roman;
            var subkana:String = kana.substr( 0, i );
            var nextKana:String = kana.substr( i );
            var subromans:Array = [];
            
            if ( option ) { ArrayUtil.blend( subromans, option.romans( subkana ) ); }
            
            if ( i == 1 ) {
                var zenkaku:int = TypeData.ZENKAKU.indexOf( subkana );
                var hankaku:int = TypeData.HANKAKU.indexOf( subkana );
                if( subkana == "っ" ){ ArrayUtil.blend( subromans, _preLtu( nextKana, option ) ) }
                else if ( subkana == "ん" ) { ArrayUtil.blend( subromans, _preN( nextKana, option )) }
                else if ( zenkaku != -1 ) { subromans.push( TypeData.HANKAKU.substr( zenkaku,1 ) ); }
                else if ( hankaku != -1 ) { subromans.push( TypeData.HANKAKU.substr( hankaku,1 ) ); }
            }
            ArrayUtil.blend( subromans, TypeData.ROMANS[subkana] );
            var l:uint = subromans.length;
            var flag:Boolean = false;
            while( l > 0 ){
                if( roman2.length == 0 ){ ArrayUtil.blend( next, _firsts(subromans) ); break; }
                var first:String = roman2.substr(0, 1);
                for( var j:uint = 0; j < l; j++ ){
                    if( subromans[j].substr( 0, 1 ) == first ){ 
                        subromans[j] = subromans[j].substr( 1 );
                        if ( subromans[j] == "" ) {    
                            ArrayUtil.blend( next, TypeUtil.next( nextKana, roman2.substr(1), option ) );
                            subromans.splice(j,1); l--; j--;
                        }
                    }else{
                        subromans.splice(j,1); l--; j--;
                    }
                }
                roman2 = roman2.substr(1);
            }
        }
        return next;
    }


    //かなを渡すとそのかなの手前に来ることで「っ」になりうるローマ字を返します。
    static private function _preLtu( kana:String, option:TypeOption ):Array{
        var next:Array = next( kana, "", option );
        var pre:Array = [];
        var l:uint = next.length;
        const LTU:String = TypeData.LTU;

        for( var i:uint = 0; i<l; i++ ){
            var n:String = next[i];
            if( LTU.indexOf(n) != -1 ){ pre.push(n); }
        }
        return pre;
    }

    //かなを渡すとそのかなの手前にn来ることで「ん」になる場合[n]を返します。
    static private function _preN( kana:String, option:TypeOption ):Array{
        var next:Array = next( kana, "", option );
        var l:uint = next.length;
        const NN:String = TypeData.NN;

        for( var i:uint = 0; i<l; i++ ){
            var n:String = next[i];
            if( NN.indexOf(n) == -1 ){ return ["n"]; }
        }
        return [];
    }

    //arrの一文字目の配列を作ります。
    static private function _firsts( arr:Array ):Array {
        var l:uint = arr.length;
        var firsts:Array = [];
        for( var i:uint; i<l; i++ ){
            firsts.push( arr[i].substr(0,1) );
        }
        return firsts;
    }

    /**
     * 入力されているときのかなに対するもっとも簡単なローマ字のふりかたを返します。
     * @param    kana
     * @param    roman
     * @param    option
     * @return
     */
    static public function simplestRoman( kana:String, roman:String = "", option:TypeOption = null ):String {
        if ( roman == "" ) { return _simplestRoman( kana, option); }
        for ( var i:uint = 2; i > 0; i-- ) {
            var roman2:String = roman;
            var subkana:String = kana.substr( 0, i );
            var nextKana:String = kana.substr( i );
            var subromans:Array = [];
            
            if ( option ) { ArrayUtil.blend( subromans, option.romans( subkana ) ); }
            
            if ( i == 1 ) {
                var zenkaku:int = TypeData.ZENKAKU.indexOf( subkana );
                var hankaku:int = TypeData.HANKAKU.indexOf( subkana );
                if( subkana == "っ" ){ ArrayUtil.blend( subromans, _preLtu( nextKana, option ) ) }
                else if ( subkana == "ん" ) { ArrayUtil.blend( subromans, _preN( nextKana, option )) }
                else if ( zenkaku != -1 ) { subromans.push( TypeData.HANKAKU.substr( zenkaku,1 ) ); }
                else if ( hankaku != -1 ) { subromans.push( TypeData.HANKAKU.substr( hankaku,1 ) ); }
            }
            ArrayUtil.blend( subromans, TypeData.ROMANS[subkana] );

            var l:uint = subromans.length;
            for( var j:uint = 0; j < l; j++ ){
                var subroman:String = subromans[j];
                var rl:uint = subroman.length;
                var c:uint = 0;
                while ( true ) {
                    var one:String = roman2.substr( c, 1 );
                    if( subroman.indexOf( one, c ) == c ){ ;
                        if ( rl == c + 1 ) {
                            var next:String = simplestRoman( nextKana, roman2.substr(rl), option );
                            if ( next != null ) { return subroman + next }
                            break
                        }
                    }else { break; }
                    c++;
                }
            }
        }
        return null;
    }
    
    //かなに対するもっとも簡単なローマ字のふりかたを返します。
    static private function _simplestRoman( kana:String, option:TypeOption = null ):String {
        if ( kana == "" ) { return "" }
        var roman:String; 
        for ( var i:uint = 2; i > 0; i-- ) {
            var subkana:String = kana.substr( 0, i );
            var nextKana:String = kana.substr( i );
            
            if ( option ) { roman = option.romans( subkana )[0]; }                
            if ( !roman && i == 1 ) {
                var zenkaku:int = TypeData.ZENKAKU.indexOf( subkana );
                var hankaku:int = TypeData.HANKAKU.indexOf( subkana );
                if( subkana == "っ" ){ roman = _preLtu( nextKana, option )[0]  }
                else if ( subkana == "ん" ) { roman = _preN( nextKana, option )[0] }
                else if ( zenkaku != -1 ) { roman = TypeData.HANKAKU.substr( zenkaku,1 ); }
                else if ( hankaku != -1 ) { roman = TypeData.HANKAKU.substr( hankaku,1 ); }
            }
            
            if ( !roman ) { 
                var romans:Array = TypeData.ROMANS[subkana];
                if( romans ){ roman = romans[0] }
            }
            if ( roman ) { 
                var next:String = _simplestRoman( nextKana, option );
                if ( next != null ) { return roman + next } 
            }
        }
        return null;
    }
}
class ArrayUtil{
    /**
     * arr2のうち、arr1にまだ含まれないもののみを配列の前方に追加します。
     * @param    arr1
     * @param    arr2
     * @return
     */
    static public function blend( arr1:Array, arr2:Array ):Array {
        if ( !arr2 ) { return arr1; }
        var l:uint = arr2.length;
        for ( var i:uint = 0; i < l; i++ ) {
            var e:* = arr2[i];
            if ( arr1.indexOf( e ) == -1 ) { arr1.push( e ); }
        }
        return arr1;
    }
}

class TypeConfig{
    static public var option:TypeOption;
    static public var dovrak:Boolean = false;
    static public var space:Boolean = true;
} 

class TypeStyle{
    /** クリーム系の配色です。 */
    static public const CREAM:Object = { fill:0xEEDDCC, activeFill:0xDDAA77, activeFill2:0xDDBB99, disableFill:0x776655, line:0x554433 }
    /** 暗いの配色です。 */
    static public const DARK:Object = { fill:0x111116, activeFill:0xDD4411, activeFill2:0x1144DD, disableFill:0xCCCCCC, line:0xDDDDEE }
    
    static public const DEFAULT:Object = CREAM; 
    
    /** アクセサリーの通常時のfillColorです。 */
    static public var fill:uint =  DEFAULT.fill;
    /** アクセサリーがフォーカスを得た時などに使われるfillColorです。 */
    static public var activeFill:uint = DEFAULT.activeFill;
    /** fillとactiveFillの中間にあたるfillColorです。 */
    static public var activeFill2:uint = DEFAULT.activeFill2;
    /** アクセサリーがの時などに使われるfillColorです。 */
    static public var disableFill:uint = DEFAULT.disableFill;
    /** 枠線に使われる色です。 */
    static public var line:uint = DEFAULT.line;
    
    static public var defaultTextFormat:TextFormat = new TextFormat( "メイリオ", 70, 0, true, null, null, null, null, "center", null, null, null, null)
    static public var nextFormat:TextFormat = new TextFormat(  );
    static public var typedFormat:TextFormat = new TextFormat( null, null, 0 );
    static public var typingFormat:TextFormat = new TextFormat( null, null, 0x666666);

    static public function setStyle( style:Object ):void {
        TypeStyle.fill = style.fill;
        TypeStyle.activeFill = style.activeFill;
        TypeStyle.activeFill2 = style.activeFill2;
        TypeStyle.disableFill = style.disableFill;
        TypeStyle.line = style.line;
    }
}

//===========================================================================================================
dynamic class TypeWord{
    public var word:String;
    public var kana:String = "";
    public var chosen:Boolean;
    public var isTarget:Boolean;
    public var typable:Boolean;
    public var roman:String;
    public var defaultRoman:String;
    public var next:Array;

    function TypeWord( obj:Object ):void { 
        for ( var str:String in obj ) { 
            this[str] = obj[str]
        }
        if( TypeConfig.space ){ kana += " " }
        this.defaultRoman = TypeUtil.simplestRoman( kana, "", TypeConfig.option );
    }
}


class TypeOption{
    /** [[かな,roman],...] */
    public var options:Array = [];
    static public const IROHA:Array = [["ゑ","we"],["ゐ","wi"]];
    static public const CHA:Array = [["ちゃ", "cha"], ["ち", "chi"], ["ちゅ", "tyu"], ["ちぇ", "che"], ["ちょ", "cho"]];
    
    function TypeOption( ...arrays ) {
        options = options.concat.apply( null, arrays );
    }
    
    public function romans( kana:String ):Array {
        var l:uint = options.length; 
        var romans:Array = [];
        for ( var i:uint = 0; i < l; i++ ) {
            var option:Array = options[i]
            if ( option[0] == kana ) { romans.push( option[1] ); }
        }
        return romans;
    }
    
    public function kana( roman:String ):String {
        var l:uint = options.length; 
        for ( var i:uint = 0; i < l; i++ ) {
            var option:Array = options[i]
            if ( option[1] == kana ) { return option[0]; }
        }
        return null;
    }
}


class TypeEvent extends Event{
    public var lastTyped:String;
    public var word:TypeWord;
    
    static public const WORD_REMOVED:String = "wordRemoved";
    static public const WORD_ADDED:String = "wordAdded";
    static public const MISSED:String = "missed";
    static public const INITED:String = "inited";
    static public const TYPED:String = "typed";
    static public const START:String = "start";
    static public const STOP:String = "stop";
    
    function TypeEvent( type:String ) { super( type ); }
}
//===========================================================================================





class Typing extends EventDispatcher {
    private var _stage:Stage;
    public var target:Vector.<TypeWord>;
    public var active:Vector.<TypeWord>;
    
    private var _running:Boolean;
    public function get running():Boolean{ return _running; }
    private var _typed:String;
    public function get typed():String{ return _typed; }
    private var _next:Array;
    public function get next():Array{ return _next; }

    function Typing( stage:Stage ){
        this._stage = stage;
        init();
    }
    
    /** 初期化を行います */
    public function init():void {
        _next = [];
        _typed = "";
        _running = false;
        target = new Vector.<TypeWord>();
        active = new Vector.<TypeWord>();
        dispatchEvent( new TypeEvent( TypeEvent.INITED ) );
    }
    
    /** targetに新たな単語を登録します。 */
    public function addTarget( word:TypeWord ):void{
        target.push( word );
        word.next = TypeUtil.next( word.kana, typed, TypeConfig.option );
        word.typable = ( word.next.length != 0 );
        if( word.typable ){ active.push( word ); }
        word.roman = TypeUtil.simplestRoman( word.kana, typed, TypeConfig.option );
        ArrayUtil.blend( _next, word.next );
        
        
        var e:TypeEvent = new TypeEvent( TypeEvent.WORD_ADDED );
        e.word = word;
        dispatchEvent( e );
    }
    
    /** キーボードの監視をスタートさせます。 */
    public function start():void {
        if ( !_running ) {
            _running = true;
            _stage.addEventListener( "keyDown", onKeyDown );
            dispatchEvent( new TypeEvent( TypeEvent.START ) );
        }
    }
    
    /** キーボードの監視を停止させます。 */
    public function stop():void {
        if ( _running ) {
            _running = false;
            _stage.removeEventListener( "keyDown", onKeyDown );
            dispatchEvent( new TypeEvent( TypeEvent.STOP ) );
        }
    }
    
    /** キーをタイプしたときに呼び出されるイベントハンドラです。この関数を呼び出すことでキーをタイプしたときと同じ挙動をさせることが可能になります。 */
    public function onKeyDown( evt:KeyboardEvent ):void{
        var s:String = String.fromCharCode( evt.charCode );
        var nx:int = next.indexOf( s );
        var e:TypeEvent;
        
        if ( nx == -1 ) {
            e = new TypeEvent( TypeEvent.MISSED );
            e.lastTyped = s;
            dispatchEvent( e );
        }else {
            _typed += s;
            var flag:Boolean = false;
            var l:uint = active.length;
            var c:uint = 0;
            _next = [];
            
            for( var i:uint = 0; i < l; i++ ) {
                var word:TypeWord = active[i];
                if( word.typable ){
                    word.next = TypeUtil.next( word.kana, typed, TypeConfig.option );
                    word.typable = word.next == null || ( word.next.length != 0 );
                    ArrayUtil.blend( _next, word.next ); 
                    if ( word.roman == typed ) {
                        target.splice( target.indexOf(word), 1 );
                        active.splice( i, 1 );
                        flag = true; 
                        i--; l--;
                    }
                    if ( word.typable ) { word.roman = TypeUtil.simplestRoman( word.kana, typed, TypeConfig.option ); }
                    else{ word.roman = word.defaultRoman; }
                }else {
                    active.splice( i, 1 );
                    i--; l--;
                }
            }
            
            e = new TypeEvent( TypeEvent.TYPED );
            e.lastTyped = s;
            dispatchEvent( e );
            
            if ( flag ) { 
                wordsInit();
                e = new TypeEvent( TypeEvent.WORD_REMOVED );
                e.lastTyped = s;
                e.word = word;
                dispatchEvent( e );
            }
        }
    }
    
    private function wordsInit():void {
        var l:uint = target.length;
        active = new Vector.<TypeWord>();
        _typed = "";
        _next = [];
        for ( var i:uint = 0; i < l; i++ ) {
            var word:TypeWord = target[i];
            active.push( word );
            word.next = TypeUtil.next( word.kana, typed, TypeConfig.option );
            ArrayUtil.blend( _next, word.next ); 
            word.typable = ( word.next.length != 0 );
            word.roman = word.defaultRoman;
        }
    }
}


class TypeManager extends EventDispatcher{
    public var typing:Typing;
    public var wordList:Vector.<TypeWord>;
    
    public var next:Vector.<TypeWord>;
    public var nextMax:int = 20;
    
    public var timeLimit:Number = -1;
    public var timeCount:Number = 0;
    private var _useTimeLimit:Boolean = false;
    
    public var addLimit:int = -1;
    public var addCount:int = 0;

    public var removeLimit:int = -1;
    public var removeCount:int = 0;
    
    public var cycleLimit:int = -1;
    public var cycleCount:int;
    
    public var zeroPost:Boolean = true;
    public var timePost:Number = -1;
    public var shuffle:Boolean =  false;
    private var _currentTime:Number = 0;
    private var _lastAdd:Number = 0;
    
    
    function TypeManager( typing:Typing, wordList:Array ):void{
        this.wordList = new Vector.<TypeWord>();
        for each( var w:Object in wordList ) { this.wordList.push(new TypeWord(w)); }
        this.typing = typing;
        typing.addEventListener( TypeEvent.INITED, init );
        typing.addEventListener( "start", onStart );
        init( null )
    }

    public function add():void{
        if ( addLimit != addCount ) {
            addCount++;
            setNext();
            next.reverse();
            typing.addTarget( next.pop() );
            next.reverse();
            setNext();
        }
    }
    
    public function setNext():void {
        var wordList:Vector.<TypeWord> = this.wordList;
        var next:Vector.<TypeWord> = this.next;
        var l:int = wordList.length;
        var c:int = 0;
        while ( next.length < nextMax ) {
            var w:TypeWord = wordList[ (Math.random() * l) >>> 0 ];
            if ( c > 10 || next.indexOf(w) == -1 ) { next.push( w ); c = 0; }
            c++;
        }
    }


    public function onStart( e:Event ):void{
        this.addEventListener( "enterFrame", onFrame );
        typing.addEventListener( "wordAdded", onWordAdded );
        typing.addEventListener( "wordRemoved", onWordRemoved );

        _currentTime = getTimer() / 1000;
    }
    
    public function init( e:Event ):void{
        timeCount = 0;
        addCount = 0;
        removeCount = 0;
        cycleCount = 0;
        _useTimeLimit = ( timeLimit != -1 );
        next = new Vector.<TypeWord>();
        setNext();
        add();
    }
        
    private function onFrame( e:Event ):void{
        var time:Number = getTimer() * 0.001;
        timeCount += time - _currentTime;
        if( _useTimeLimit && timeCount >= timeLimit){ complete() }
        if( 0 < timePost && timePost < time - _lastAdd ){ add() }
    }

    private function onWordAdded( e:Event ):void{
        _lastAdd = getTimer() * 0.001;
    }
    
    private function onWordRemoved( e:Event ):void{
        if( removeLimit == removeCount++ ){ complete(); }
        if( typing.target.length == 0 ){ 
            if( addLimit == addCount ){ complete(); }
            else if( zeroPost ){ add(); }    
        }
    }
    
    public function complete():void{ 
        this.removeEventListener( "enterFrame", onFrame );
        typing.removeEventListener( "wordAdded", onWordAdded );
        typing.removeEventListener( "wordRemoved",onWordRemoved)
        typing.stop();
        this.dispatchEvent( new Event( Event.COMPLETE ) );
    }
}


class TypeKeyboard extends Sprite{
    private var _typing:Typing;
    public function get typing():Typing{ return _typing }
    
    public var keys:Array = TypeData.QWERTY_KEY;
    public var shiftKeys:Array = TypeData.QWERTY_SHIFT;
    
    private var _dvorak:Boolean = false;
    /**
     * Dvorak配列を用いるかどうかのBooleanです。
     */
    public function get dvorak():Boolean{ return _dvorak; }
    public function set dvorak( b:Boolean ):void{ 
        if( _dvorak != b ){
            _dvorak = b;
            if( b ){
                keys = TypeData.DVORAK_KEY;
                shiftKeys = TypeData.DVORAK_SHIFT;
            }else{
                keys = TypeData.QWERTY_KEY;
                shiftKeys = TypeData.QWERTY_SHIFT;
            }
            init();
        }
    }
        
    static public var indent:Array = TypeData.BOARD_INDENT;
    static private const keySize:Number = 40;

    public var typeKeys:Vector.<TypeKey> = new Vector.<TypeKey>;
    public var shiftKey1:TypeKey;
    public var shiftKey2:TypeKey;
    
    private var _shifted:Boolean = false;
    public function get shifted():Boolean{ return _shifted; }
    
    
    function TypeKeyboard( typing:Typing = null ) { 
        this._typing = typing;
        init();
    }
    
    //typingの現状を読み込んで次に打つべきキーのヒントを出力します。
    private function read( e:TypeEvent ):void {
        var next:Array = _typing.next;
        var l:int = next.length;
        
        if( _shifted ){ unshift() }
        for each( var key1:TypeKey in typeKeys ){ key1.up(); }
        
        for ( var i:int = 0; i < l; i++ ) {
            var str:String = next[i];
            for each( var key:TypeKey in typeKeys ){
                if( key.key == str ){
                    if ( i == 0) { key.down(); }
                    else if( key.state == "up" && shifted == false ){ key.over(); }
                }else if ( key.shiftKey == str ) { 
                    if ( i == 0) { key.down(); shiftKey1.down(); shiftKey2.down(); shift() }
                    else if( key.state == "up" && shifted ){ key.over(); }
                }
            }
        }
    }
    
    private function init():void{
        if ( typing ) {
            typing.addEventListener( TypeEvent.TYPED, read );
            typing.addEventListener( TypeEvent.WORD_ADDED, read );
            typing.addEventListener( TypeEvent.WORD_REMOVED, read );
            if ( typing.running ) { read( null ) }
            else{ typing.addEventListener( TypeEvent.START, read ); }
        }
        
        while( this.numChildren > 0 ){ this.removeChildAt(0); }
        var h:int = keys.length;
        var s:Number = keySize;
        for( var i:int = 0; i < h; i++ ){
            var w:int = keys[i].length
            for( var j:int = 0; j < w; j++ ){
                var str:String = keys[i].substr(j,1)
                var code:int = 48 + TypeData.CODE48.indexOf( str );
                if( code == 47 ){ code = 65 + TypeData.CODE65.indexOf( str ) }
                if( code == 64 ){ code = 186 + TypeData.CODE186.indexOf( str ) }
                if( code == 185 ){ code = 219 + TypeData.CODE219.indexOf( str ) }
                if( code == 218 ){ code = 226 + TypeData.CODE226.indexOf( str ) }
                var mark:Boolean = i == 2 &&( j == 3  || j == 6 ) 
                var key:TypeKey= new TypeKey( s-1, s-1, str, shiftKeys[i].substr(j,1), code, mark );
                key.x = ( j + indent[i] ) * s;
                key.y = i * s;
                addChild( key );
                typeKeys.push( key );
            }
        }   
        
        
        for( i = 0; i < 3; i++ ){
            key = new TypeKey( s*indent[i]-1, s-1, "", null, [229,9,22][i] );
            key.y = i * s;
            addChild( key ); 
        }
        
        var w0:Number = keys[0].length + indent[0]; 
        var w1:Number = keys[1].length + indent[1]; 
        var w2:Number = keys[2].length + indent[2]; 
        var w3:Number = keys[3].length + indent[3]; 
        
        //back space
        key = new TypeKey( s-1, s-1, "BS", "BS", 8 );
        key.x = w0 * s;
        addChild( key );
        typeKeys.push( key );
        
        //shift
        key = new TypeKey( indent[3]*s- 1, s-1, "Shift", "Shift", 16 );
        key.y = 3 * s;
        addChild( key );
        typeKeys.push( key );
        shiftKey1 = key;
        
        key = new TypeKey(  (w0-w3+1)*s -1,   s-1, "Shift", "Shift", 16 );
        key.x = w3 * s;
        key.y = 3 * s;
        addChild( key ); 
        typeKeys.push( key );
        shiftKey2 = key;
        
        //enter
        key = new TypeEnterKey( (w0-w1+1)*s -1, (w0-w2+1)*s -1,  s-1, 2*s-1 );
        key.x = w1 * s;
        key.y = 1 * s;
        addChild( key );
        typeKeys.push( key );
        
        //space
        key = new TypeKey( 4*s -1, s-1,  " ", " ", 32 );
        key.x = indent[4] * s;
        key.y = 4 * s;
        addChild( key );
        typeKeys.push( key );
    }
    
    
    public function shift():void {
        _shifted = true;
        for each( var key:TypeKey in typeKeys ){ key.shift() }
    }
    public function unshift():void {
        _shifted = false;
        for each( var key:TypeKey in typeKeys ){ key.unshift() }
    }
}
class TypeKey extends Sprite{
    public var upFill:uint = TypeStyle.fill;
    public var downFill:uint = TypeStyle.activeFill;
    public var overFill:uint = TypeStyle.activeFill2;
    public var disableFill:uint = TypeStyle.disableFill;
    
    public var fillColor:uint = upFill;
    public var lineColor:uint = TypeStyle.line;
    
    public var state:String = "up";
    
    public var textField:TextField = new TextField();
    
    protected var w:Number;
    protected var h:Number;
    
    public var key:String;
    public var shiftKey:String; 
    public var code:int; 
    public var shifted:Boolean; 
    public var mark:Boolean;
    
    function TypeKey( width:Number, height:Number, key:String, shiftKey:String, code:Number, mark:Boolean = false ){
        w = width; h = height; this.key = key; this.shiftKey = shiftKey; this.code = code; this.mark = mark
        
        var format:TextFormat = new TextFormat( "_monospace", h/2.33, lineColor, true );
        textField.defaultTextFormat = format;
        textField.x = 6;
        textField.y = 1;
        textField.selectable = false;
        
        draw();
        
        addChild( textField );
        drawText();
    }

    public function draw():void{
        var g:Graphics = this.graphics;
        g.clear();
        g.beginFill( fillColor, 1 );
        g.lineStyle( 2, lineColor );
        g.drawRoundRect( 0,0, w, h, 8 );
        g.endFill(); 
        
        if( mark ){
            var cw:Number = w/2;
            g.lineStyle( 1, lineColor, 0.8 );
            g.moveTo( cw + 4, h - 13 );
            g.lineTo( cw - 4, h - 13 );
        }

        g.lineStyle( 1, 0xFFFFFF, 0.5 );
        g.moveTo( 5, 1 );
        g.lineTo( 5, h-10 );
        g.lineTo( w-5 , h-10 );
        g.lineTo( w-5 , 1 );
    }
    
    public function drawText():void {
        if( shifted ){ textField.text = shiftKey; }
        else { textField.text = key; }
    }
    public function down():void{
        fillColor = downFill;
        state = "down";
        draw();
    }
    public function over():void{
        fillColor = overFill;
        state = "over";
        draw();
    }
    public function up():void{
        fillColor = upFill;
        state = "up";
        draw();
    }
    public function disable():void{
        fillColor = disableFill;
        state = "disable";
        draw();
    }
    public function shift():void{
        shifted = true;
        drawText();
    }
    public function unshift():void{
        shifted = false;
        drawText();
    }
}

class TypeEnterKey extends TypeKey{
    private var w2:Number;
    private var h2:Number;
    function TypeEnterKey ( width:Number, width2:Number, height:Number, height2:Number ){
        w2 = width2; h2 = height2;
        super( width, height, "Enter", "Enter", 13 )
        
        addChild( textField );
        drawText();
    }
    override public function draw():void{
        var g:Graphics = this.graphics;
        g.clear();
        g.beginFill( fillColor, 1 );
        g.lineStyle( 2, lineColor );
        var l:Function = g.lineTo;
        var c:Function = g.curveTo;
        var r:Number = 8;
        var x0:Number = 0, x1:Number = w - w2, x2:Number = w;
        var y0:Number = 0, y1:Number = h, y2:Number = h2;
        g.moveTo( x0, y0 + r );
        l( x0, y1 - r ); c( x0, y1, x0 + r, y1 );
        l( x1 , y1 );
        l( x1, y2 - r ); c( x1, y2, x1 + r, y2 );
        l( x2 - r, y2 ); c( x2, y2, x2, y2 - r );
        l( x2, y0 + r ); c( x2, y0, x2 - r, y0 );
        l( x0 + r, y0 ); c( x0, y0, x0, y0 + r );
        
        g.endFill(); 
        g.lineStyle( 1, 0xFFFFFF, 0.5 );  
        x0 = 4, x1 = w - w2 + 4, x2 = w - 4;
        y0 = 1, y1 = h - 10, y2 = h2 - 10;
        g.moveTo( x0, y0 );
        l( x0, y1 ); l( x1, y1 ); l( x1, y2 ); l( x2, y2 ); l( x2, y0 );    
    }
}

class TypePanel extends Sprite{
    public var typing:Typing;
    public var kanaField:TextField = new TextField();
    public var wordField:TextField = new TextField();
    public var romanField:TextField = new TextField();
    private var w:Number, h:Number;
    private var nextFormat:TextFormat = TypeStyle.nextFormat;
    private var typedFormat:TextFormat = TypeStyle.typedFormat;
    private var typingFormat:TextFormat = TypeStyle.typingFormat;

    function TypePanel( typing:Typing, width:Number, height:Number, kanaWeight:Number = 0.2, wordWeight:Number = 0.5, romanWeight:Number = 0.3 ){
        this.typing = typing;
        w = width; h = height;
        
        var y:Number = 0;
        var th:Number;
        var format:TextFormat;
        var weights:Array = [ kanaWeight, wordWeight, romanWeight ];
        var fields:Array  = [ kanaField, wordField, romanField ]; 

        for( var i:int = 0; i < 3; i++ ){
            var weight:Number = weights[i];
            var field:TextField = fields[i];
            if( weight > 0 ){
                th = h * weight;
                format = TypeStyle.defaultTextFormat;
                field.scaleX = field.scaleY = th * 0.01;
                field.width = w / field.scaleX;
                field.height = th / field.scaleY;
                field.y = y;
                field.selectable = false;
                field.defaultTextFormat = format;
                addChild( field );
                
                y += th;
            }
        }
        update(null);
        typing.addEventListener( "wordAdded", update );
        typing.addEventListener( "wordRemoved", update );
        typing.addEventListener( "typed", update );
    }

    private function update( e:Event ):void {
        if( typing.active.length == 1 ){
            var word:TypeWord = typing.active[0];
            var wordNum:Number = 0;
            kanaField.text = word.kana;
            wordField.text = word.word;
            romanField.text = word.roman;
            romanField.setTextFormat( typingFormat, -1, -1 );
            
            var tl:int = typing.typed.length;
            if ( tl > 0 ) { 
                if( tl <  word.roman.length ){ romanField.setTextFormat( typedFormat, -1, tl); }
                if( tl + 1 <  word.roman.length){ romanField.setTextFormat( nextFormat, tl, tl + 1 ); }
            }
        } 
    }
}

class TypeMonitor extends Sprite{
    public var typing:Typing;
    public var maxText:TextField = new TextField();
    public var minText:TextField = new TextField();
    public var text:TextField = new TextField();
    private var w:Number, h:Number;
    private var graph:Shape = new Shape;
    private var gw:Number, gh:Number;
    
    public var time:int;
    public var typeCount:int;
    public var speed:Number;
    public var miss:Number;
    private var currentTime:int;
    
    private var typeHistory: Vector.<int>;
    private var missHistory: Vector.<int>;
    
    private var timer:Timer = new Timer( 100 );
    
    private var type3Graph:Vector.<Number>; 
    private var miss3Graph:Vector.<Number>; 
    private var graphMax:Number = 0.05;
    private var graphLen:int = 0;
    
    function TypeMonitor( typing:Typing, width:Number, height:Number, kanaWeight:Number = 0.2, wordWeight:Number = 0.5, romanWeight:Number = 0.3 ){
        this.typing = typing;
        w = width; h = height;
        
        var g:Graphics = this.graphics;
        g.beginFill( 0x222222, 1 );
        g.lineStyle( 1, 0x444444, 1 );
        g.drawRect( 0,0, w, h );
        g.endFill(); 
        
        var m:Number = 8;
        gh = h - 2 * m - 10;   gw = w - 2 * m - 60; 
        this.addChild( graph );
        graph.x = 60 + m; graph.y = m;
        
        g.beginFill( 0, 1 );
        g.lineStyle( 1, 0x444444, 1 );
        g.drawRect( graph.x, graph.y, gw, gh );
        
        text = new TextField();
        text.width = w - m;
        text.y = gh + m; 
        text.defaultTextFormat = new TextFormat( null, 10, 0xAAAAAA, null, null, null, null, null, "right" )
        addChild( text );
        
        maxText = new TextField();
        maxText.width = 60;
        maxText.x = m; 
        maxText.defaultTextFormat = new TextFormat( null, 10, 0xAAAAAA, null, null, null, null, null, "right" )
        addChild( maxText );
        
        minText = new TextField();
        minText.width = 60;
        minText.x = m; 
        minText.y = gh; 
        minText.defaultTextFormat = new TextFormat( null, 10, 0xAAAAAA, null, null, null, null, null, "right" )
        minText.text = "0key/s"; 
        addChild( minText );
        
        timer.addEventListener( "timer", update );
        typing.addEventListener( "inited", onInited );
        typing.addEventListener( "start", onStart );
        typing.addEventListener( "stop", onStop );
        typing.addEventListener( "typed", onTyped );
        typing.addEventListener( "missed", onMissed );
        onInited(null);
    }
    
    private function onInited( e:Event ):void{
        time = 0;
        typeCount = 0;
        miss = 0;
        speed = 0;
        typeHistory = new Vector.<int>();
        missHistory = new Vector.<int>();
        type3Graph = new Vector.<Number>();
        miss3Graph = new Vector.<Number>();
        graphMax = 0.01;
        graphLen = 0;
        draw();
    }
    
    private function onStart( e:Event ):void{ 
        currentTime = getTimer();
        timer.start();
    }
    
    private function onStop( e:Event ):void{ 
        timer.stop();
    }
    
    private function onTyped( e:Event ):void{ 
        time += getTimer() - currentTime;
        currentTime = getTimer();
        
        typeHistory.push( time );
        
        typeCount++;
    }
    private function onMissed( e:Event ):void{ 
        time += getTimer() - currentTime;
        currentTime = getTimer();
        missHistory.push( time );
    }
    
    private function update( e:Event ):void{
        time += getTimer() - currentTime;
        currentTime = getTimer();
        var t:Number = time / 1000;
        text.text = "  Time:"+ t.toFixed(1)+"sec" + "  Miss:" + miss.toFixed(2) + "key/s"+ "  CurrentSpeed:" + speed.toFixed(2) + "key/s" +"  AverageSpeed:" + (typeCount/t).toFixed(2) + "key/s"; 
        maxText.text = "" + ( graphMax * 10 ).toFixed(2) + "key/s"; 
        
        while( t > ( graphLen + 3 ) / 10 ){ 
            tgPush();
            mgPush();
            graphLen += 3;
        } 
        
        draw();
    }
    
    private function tgPush():void{
        var t:int = currentTime;
        var start:int = (graphLen - 30) * 100;
        var end:int = graphLen * 100; 
        var c:int = 0;
        
        for( var i:int = typeHistory.length - 1; i >= 0; i--){
            t = typeHistory[i]
            if( end > t ){
                if( t > start ){ c++ }
                else{ typeHistory.splice( 0, i+1 ); i = -1; }
            }
        }
        var s:Number;
        if( graphLen > 30 ){
            s = c / 30;
            if( graphMax < s ){ graphMax = s; } 
            type3Graph.push( s );
            speed = s*10;
            if( type3Graph.length > gw ){ 
                type3Graph.reverse();
                type3Graph.pop();
                type3Graph.reverse();
            }
        }
    }
    
    
    private function mgPush():void{
        var t:int = currentTime;
        var start:int = (graphLen - 30) * 100;
        var end:int = graphLen * 100; 
        var c:int = 0;
        
        for( var i:int = missHistory.length - 1; i >= 0; i--){
            t = missHistory[i]
            if( end > t ){  
                if( t > start ){ c++; } 
                else{ missHistory.splice( 0, i+1 ); i = -1; }
            }
        }
        
        var s:Number;
        if( graphLen > 30 ){
            s = c / 30;
            if( graphMax < s ){ graphMax = s; } 
            miss3Graph.push( s );
            miss = s*10;
            if( miss3Graph.length > gw ){ 
                miss3Graph.reverse();
                miss3Graph.pop();
                miss3Graph.reverse();
            }
        }
    }
    
    
    private function draw():void{
        var g:Graphics = graph.graphics;
        g.clear();
        g.lineStyle( 1, 0xAAAAAA, 0.8 );
        var l:int; var i:int;
        
        l = miss3Graph.length;
        g.lineStyle( 0.5, 0xFF0000, 0.8 );
        for( i = 0; i < l; i++ ){
            if( i == 0 ){ g.moveTo( l-i, gh * ( graphMax - miss3Graph[i] ) / graphMax ) }
            else{ g.lineTo( l-i, gh * ( graphMax - miss3Graph[i] ) / graphMax ) }
        }
        
        l = type3Graph.length;
        g.lineStyle( 0.5, 0xFF00, 0.8 );
        for( i = 0; i < l; i++ ){
            if( i == 0 ){ g.moveTo( l-i, gh * ( graphMax - type3Graph[i] ) / graphMax ) }
            else{ g.lineTo( l-i, gh * ( graphMax - type3Graph[i] ) / graphMax ) }
        }
        
        
    }
}