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

Let's study English!!

Google TTSを利用した学習用タイピングゲームです。
Get Adobe Flash player
by shohei909 17 Dec 2010

    Talk

    paq at 15 Dec 2010 12:35
    Google TTS APIはリファラが存在すると弾かれます。現状ではサーバーを介して取得するしかなさそうです。
    kozaklukasz at 15 Dec 2010 12:49
    wow, awesome :>

    Tags

    Embed
/**
 * Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jQMb
 */

//ブラウザによっては、発音が聞こえないみたいです。
package {
    import flash.net.SharedObject;
    import flash.text.TextFormat;
    import flash.text.TextField;
    import flash.events.Event;
    import flash.display.Sprite;
    import com.bit101.components.*;
    import flash.net.URLRequest;
    import flash.net.navigateToURL;
    import flash.utils.escapeMultiByte;
    
    [SWF(backgroundColor="0x001133")]
    public class FlashTest extends Sprite {
        private var page1:Sprite = new Sprite();
        private var list:List;
        
        private var page2:Sprite = new Sprite();
        private var typing:Typing;
        private var manager:TypeManager;
        private var monitor:TypeMonitor;
        
        private var page3:Sprite = new Sprite();
        
        
        private var menu:Array = [];
        private var titles:Array = [ "Katakana", "FF", "Baceball" ];
        private var score:TextField = new TextField();
        private var time:TextField = new TextField();
        
        private var score2:TextField = new TextField();
        private var highScores:Object = {};
        private var selected:String = ""
        
        private var wordsList:Object = {
            study:[
                { word:"スケーリングの誤解。同じ材質の大きい橋と小さい橋では大きい方が壊れやすい。", kana:"scaling fallicy" },
                { word:"逆向きに考えろ。困った時の思考法。", kana:"Work backwards." },
                { word:"帰納的に考えろ。例示をたくさんすることで問題がわかりやすくなる事がある。", kana:"Think inductively." },
                { word:"偽薬。薬効のないラムネでも、思い込みで効くことがある。"                    , kana:"placebo" },
                { word:"北京の蝶のはばたきがニューヨークで嵐をおこす。天気予報などの未来の予測が難しい原因。", kana:"butterfly effect" },
                { word:"車輪の再発明。付加価値のないものにコストをかけてしまうことを表す慣用句。"    , kana:"reinventing the wheel" },
                { word:"割れ窓理論。割れた窓を放置するとますます治安が悪くなる。"                , kana:"broken windows theory" },
                { word:"見えざる手。需要と供給のバランスは自然に調節される。"                    , kana:"invisible hand" },
                { word:"サンクコスト。もう回収できない費用。サンクコストに縛られて行動するべきではない。"    , kana:"sunk cost" },
                { word:"囚人のジレンマ。各個人が自分の利益を最大化しようと行動すると、損をする事例。", kana:"prisoners' dilemma" },
                { word:"オッカムの剃刀。ある事象を説明するために、必要以上に仮定を増やすべきではない。", kana:"Occam's razor" },
                { word:"人間原理。宇宙が人間にとって都合がいいのは、そうでないと人間が観測できないから。", kana:"anthropic principle" },
                { word:"", kana:"" },
                { word:"", kana:"" },
                { word:"", kana:"" },
                { word:"", kana:"" },
                { word:"", kana:"" },
                { word:"", kana:"" }
            ], Katakana : [
                { word : "才能。才能のある人々、タレント。" ,                         kana : "talent" },
                { word : "容器、包むもの。コンテナ。" ,                         kana : "container" },
                { word : "混乱させる、困らせる。難問、パズル。",                     kana : "puzzle" },
                { word : "舌。言葉、言語能力。舌の肉、タン。" ,                     kana : "tongue" },
                { word : "消化する。整理する、要約する。" ,                         kana : "disest" },
                { word : "展示する、陳列する。表示。誇示。展示。コンピューターのディスプレイ" ,    kana : "display" },
                { word : "感覚。感じ、気持ち。意味。常識。勘。" ,                     kana : "sense" },
                { word : "だます、あざむく。たくらみ、策略。意外に難しい。癖。いたずら。手品。",    kana : "trick" },
                { word : "計画する。計画する、予想する。投げ出す、投影する。表現する。計画、事業。" ,    kana : "project" },
                { word : "鉄。アイロンをかける。アイロン。" ,                         kana : "iron" },
                { word : "主催者、主人。主催者を務める。亭主。" ,                     kana : "host" },
                { word : "ジグザグ。" ,                                 kana : "zigzag" },
                { word : "商業上の。営利用の、営業用の。広告用の。" ,                     kana : "commercial" },
                { word : "魅力。引きつける力。引力。呼び物。" ,                        kana : "attraction" },
                { word : "割合。大きさ、広さ。釣り合わせる、比例させる。" ,                 kana : "propotion" },
                { word : "強調する。緊張させる。圧力、圧迫。強調、力説。緊張、ストレス。" ,        kana : "stress" },
                { word : "載せる、積み込む。重くする。課する。負荷。積み荷。読み込み、ロード。" ,     kana : "load" },
                { word : "反応。反響。反作用。反射。" ,                         kana : "reaction" },
                { word : "傾斜させる、勾配をつける。坂、斜面、スロープ。傾斜。微分係数。" ,        kana : "slope" },
                { word : "一様な。お揃いの。規則正しい。制服、ユニフォーム。軍人" ,             kana : "uniform" },
                { word : "道理にかなった。分別ある。あまり値段が高くない。" ,                kana : "reasonable" },
                { word : "便利、好都合。便利なもの。" ,                         kana : "convenience" },
                { word : "押すこと、圧迫。圧力、気圧、血圧。苦悩。圧力をかける。" ,            kana : "pressure" },
                { word : "1組、1対。カップル。結びつける。つなぐ。二,三。" ,                kana : "couple" },
                { word : "複雑な、錯綜した。多くのものが複合した。複合体。コンプレックス。" ,         kana : "complex" },
                { word : "唯一の。唯一の人。匹敵するもののない。" ,                     kana : "unique" },
                { word : "ユーモア、面白さ。人の気質。気分。液。" ,                     kana : "humor" },
                { word : "移動しやすい、固定されていない。気分が変わりやすい。機動力がある。" ,     kana : "mobile" },
                { word : "賢い、効果的な。生意気な。おしゃれな。きびきびした。" ,             kana : "smart" },
                { word : "世間知らずの、単純な。考えが甘い。経験不足の。うぶな。" ,             kana : "naive" },
                { word : "豪邸、大邸宅、館。" ,                                kana : "mansion" },
                { word : "人気のある。普及している。大衆の。大衆向けの。" ,                 kana : "popular" },
                { word : "似合う。好みに合う。好都合な。ふさわしい。訴訟。求婚。スーツ、服。一式" ,    kana : "suit" },
                { word : "整理する。並べる。手配する。準備する" ,                     kana : "arrange" },
                { word : "鼓舞する。呼び起こす。吹き込む、吸い込む、息をする。" ,            kana : "inspire" },
                { word : "足跡、跡。見取り図。さかのぼる。たどる。透写する、なぞる。" ,         kana : "trace" },
                { word : "柱、円柱。円柱状のもの。行列。新聞などの縦長の欄。コラム。" ,         kana : "column" },
                { word : "支配する。" ,                            kana : "control" },
                { word : "選択肢。選択、選択権。標準装備以外のもの。" ,                 kana : "option" },
                { word : "忠告する。助言する。" ,                            kana : "advise" },
                { word : "自慢。自尊心、誇り。うぬぼれ。自慢する。誇る。" ,                 kana : "pride" },
                { word : "水路。水道管。側溝。経路。方針。水路を作る。水路で運ぶ。" ,             kana : "channel" },
                { word : "具体的な、明確な。実際の、現実の。固める。具体化する。コンクリート。" ,     kana : "concrete" }
            ],FF : [
                { word : "排水口。流し出す。排水する。" ,             kana : "drain" },
                { word : "追い払う。一掃する。" ,                 kana : "dispel" },
                { word : "酸。すっぱい。" ,                     kana : "acid" },
                { word : "水薬" ,                         kana : "potion" },
                { word : "震える、おののく、揺れる。震え、地震。" ,         kana : "quake" },
                { word : "隕石、流星。" ,                     kana : "meteor" },
                { word : "急ぐこと、迅速。あわてること、性急、軽率。" ,     kana : "haste" },
                { word : "気息音(ハヒフヘホのHの音などのこと)。気息音を発音する。吸い込む。" , kana : "aspirate" },
                { word : "困惑させる。混乱させる。混同する。間違える。" ,     kana : "confuse" },
                { word : "消える。見えなくなる。絶滅する。隠す。" ,         kana : "vanish" },
                { word : "ヒキガエル" ,                     kana : "toad" },
                { word : "もや、霧。" ,                     kana : "mist" },
                { word : "瞬きする。点滅する。瞬き、ちらつき。" ,         kana : "blink" },
                { word : "上にあげる。上昇させる。元気づける。蘇らせる。" ,     kana : "raise" },
                { word : "抵抗する。" ,                     kana : "resist" },
                { word : "再生する。改心させる。再生した。改心した。" ,     kana : "regenerate" },
                { word : "重力。重大さ。" ,                     kana : "gravity" },
                { word : "世話、介護。注意、関心。世話。気づかう。" ,         kana : "care" },
                { word : "神聖な。高徳な。" ,                     kana : "holy" },
                { word : "能力、才能、実力、力量。" ,                 kana : "ability" },
                { word : "暗殺者。" ,                         kana : "assassin" },
                { word : "泥棒、こそどろ。" ,                     kana : "thief" },
                { word : "たまねぎ。" ,                     kana : "onion" },
                { word : "結晶。水晶。" ,                     kana : "crystal" },
                { word : "鉄。" ,                         kana : "iron" },
                { word : "不可視の、見ることができない。目につかないほど小さい。" , kana : "invisible" },
                { word : "毒。" ,                         kana : "poison" },
                { word : "毒牙、牙。とがったもの。" ,                kana : "fang" },
                { word : "親愛なる。かわいい。いとしい人。高価な。" ,         kana : "dear" },
                { word : "猛吹雪。" ,                         kana : "blizzard" },
                { word : "洪水、大水" ,                     kana : "flood" },
                { word : "毒牙、牙。とがったもの。" ,                kana : "fang" },
                { word : "保護する。守る。保険をかける。" ,             kana : "protect" },
                { word : "兵器、武器。攻撃手段。" ,                kana : "weapon" },
                { word : "最後の、究極の。根本的な。最終段階。最高の" ,        kana : "ultimate" },
                { word : "能力。腕前、力量。法的資格。" ,            kana : "ability" },
                { word : "要素の、単一の、元素の。基本的な。地水火風の霊。" ,    kana : "elemental" },
                { word : "大気。空気。気圧。雰囲気。" ,                kana : "atmosphere" },
                { word : "ノアの方舟。避難所。箱。" ,                kana : "ark" },
                { word : "非常に硬いもの。非常に堅い。不動の、断固な。" ,     kana : "adamant" },
                { word : "空中浮揚する。空中浮揚させる。" ,             kana : "levitate" },
                { word : "図書館。資料室。蔵書。知識の宝庫、情報源。" ,     kana : "library" },
                { word : "信じること。信頼。自信。義務、責任。宗教、信仰。" ,     kana : "faith" },
                { word : "お守り、魔よけ。" ,                     kana : "amulet" },
                { word : "ほうき星。" ,                     kana : "comet" },
                { word : "つぶれる。崩壊させる。折りたためる。倒壊。挫折。" ,     kana : "collapse" },
                { word : "たつまき、雷雨、大旋風。" ,                kana : "tornado" },
                { word : "次元。大きさ、寸法。" ,                 kana : "dimension" },
                { word : "反射する。反響する。像を映す、反映する。" ,        kana : "reflect" },
                { word : "逃げる。もれる。逃亡、現実逃避" ,            kana : "escape" },
                { word : "狂暴な" ,                         kana : "berserk" }
            ],
            Baceball : [
                { word : "変える。改心させる。改宗する。転向する。両替する。"             , kana : "convert" },
                { word : "公正な、正当な。適切な。かなり良い。まっすぐに。"             , kana : "fair" },
                { word : "間違い。思い違い。過失。過ち。誤差。"                    , kana : "error" },
                { word : "土台。底。基礎。出発点。基地。"                     , kana : "base" },
                { word : "空を飛ぶ。舞い上がる。ハエ、飛ぶ昆虫。"                 , kana : "fly" },
                { word : "土手。塚。古墳。"                             , kana : "mound" },
                { word : "こっそり盗む。盗みをする。そっと行く。お買い得品"             , kana : "steal" },
                { word : "打つ、叩く。衝突する。胸を打つ、感心させる。向う。根づく。"        , kana : "strike" },
                { word : "食器のフォーク。フォーク状のもの。音叉。熊手。分岐する。"         , kana : "fork" },
                { word : "沈むもの、沈む人。井戸掘り人。重り。"                 , kana : "sinker" },
                { word : "滑るもの、滑る人。"                             , kana : "slider" },
                { word : "悪臭のある。腐った。汚い。ひどく不快な。不正な。"             , kana : "foul" },
                { word : "救う。守る。むだづかいしないようにする。手間を省く。"         , kana : "save" },
                { word : "指の関節。げんこつ。"                         , kana : "knuckle" },
                { word : "頭突き。突く、押す。"                         , kana : "bunt" },
                { word : "ひとそろい、一組。電池、バッテリー。殴打。砲台。"             , kana : "battery" },
                { word : "程よい時間での、時を得た。好都合な時に。"                 , kana : "timely" },
                { word : "急に止まる。立ち往生する。妨害する。じゃま物。"             , kana : "balk" },
                { word : "平均値。平均する。平均の"                         , kana : "average" },
                { word : "強く打つ人。強打者。"                         , kana : "slugger" }
            ]
        };
        
        public var so: SharedObject = SharedObject.getLocal("savedata");
        function FlashTest() {
            if(so){ highScores = so.data; }
            
            var lbl:Label= new Label( page1, 1,1, "Let's study English!!" )
            lbl.scaleX = lbl.scaleY = 2;
            
            //page1
            for each( var str:String in titles ){ 
                var data:Object = { title:str, words:wordsList[str], highScore:highScores[str] };
                menu.push( data )
            }
            list = new List( page1,5, 40 );
            list.listItemClass = Item;
            list.items = menu;
            list.listItemHeight = 55;
            list.setSize( 455, 420 );
            list.addEventListener(Event.SELECT, onMenuSelect);
            addChild( page1 );
            
            
            //page2 
            typing = new Typing( stage );
            TypeStyle.setStyle( TypeStyle.DARK )
            //keyboard
            var keyboard:TypeKeyboard = new TypeKeyboard( typing );
            page2.addChild( keyboard );
            keyboard.scaleX = keyboard.scaleY = 0.7;
            keyboard.x = 20;
            keyboard.y = 270;
            //panel
            var panel1:TypePanel = new TypePanel( typing, 464, 30, 0, 0, 1 );
            panel1.y = 180;
            page2.addChild( panel1 );
            var panel2:TypePanel = new TypePanel( typing, 464, 16, 0, 1, 0 );
            panel2.y = 220;
            page2.addChild( panel2 );
            //monitor
            monitor = new TypeMonitor( typing, 445, 100, 0.25, 0.45, 0.30 );
            monitor.x = 10;
            monitor.y = 10;
            page2.addChild( monitor );
            manager = new TypeManager( typing, wordsList["FFantasy"] );
            manager.timeLimit = 60;
            score.defaultTextFormat = new TextFormat( null,28,0xFF2200,true );
            score.y = 435;
            score.width = 465;
            time.defaultTextFormat = new TextFormat( null,58,0xFF1100,true, null, null, null, null, "right", null, null, null, null)
            time.y = 405;
            time.width = 465;
            page2.addChild( score );
            page2.addChild( time );       
            page2.visible = false;
            addChild( page2 );
            
            
            score2.defaultTextFormat = new TextFormat( null,58,0xFFFFFF,true, null, null, null, null, "center", null, null, null, null)
            score2.y = 140;
            score2.width = 465;
            page3.addChild( score2 );  
            new PushButton( page3, 115, 260, "tweet", tweet );
            new PushButton( page3, 250, 260, "back", back );
            page3.visible = false;
            addChild( page3 );  
            
            addEventListener( "exitFrame", onFrame );
            typing.addEventListener( "stop", onStop );
            typing.addEventListener( "missed", onMiss );
            
            speak( "Let's study English!!" );
        }
        
        
        
        private function onMenuSelect( e:Event ):void{
            if( list.selectedItem ){
                selected = list.selectedItem.title;
                var words:Array = list.selectedItem.word;
                manager.setWordList( wordsList[selected] );
                page1.visible = false;
                page2.visible = true;
                typing.addEventListener( "wordAdded", onAdded );
                
                typing.init();
                typing.start();
            }
        }
        
        private function onAdded( e:TypeEvent ):void{
            speak( e.word.kana )
        }

        private function onStop( e:TypeEvent ):void{
            typing.removeEventListener( "wordAdded", onAdded );
            page2.alpha = 0.1;
            page3.visible = true;
            score2.text = "SCORE: " + monitor.typeCount;
            if( highScores[selected] == null || highScores[selected] < monitor.typeCount ){
                highScores[selected] = monitor.typeCount;
            }
        }

        private function onFrame( e:Event ):void{
            score.text = "SCORE: " + monitor.typeCount;
            if( manager.timeCount < 60 ){ time.text = "" + (60 - manager.timeCount).toFixed(3); }
            else{ time.text = "Finish" }
        }
        
        private function onMiss( e:Event ):void{ manager.timeCount++; }
        
        private function tweet( e:Event ):void{
            var str:String = "SCORE:" + monitor.typeCount + " " + "http://wonderfl.net/c/jQMb/" + " " + "#wonderfl ";
            navigateToURL(new URLRequest("http://twitter.com/home?status=" + escapeMultiByte(str)), "_blank");
        }
        
        private function back( e:Event ):void{
            page1.visible = true;
            page2.alpha = 1;
            
            menu = [];
            for each( var str:String in titles ){ 
                var data:Object = { title:str, words:wordsList[str], highScore:highScores[str] };
                menu.push( data )
            }
            list.listItemClass = Item;
            list.items = menu;
            list.listItemHeight = 55;
            list.setSize( 455, 420 );
            
            page2.visible = false;
            page3.visible = false;
        }

    }
}

import flash.events.Event;
import flash.display.DisplayObjectContainer;
import flash.media.Sound;
import flash.net.URLRequest;
import com.bit101.components.*;

function speak( _text:String ):void {    
        var req:URLRequest = new  URLRequest(
            "http://www.google.com/gwt/n?u=http%3A%2F%2Ftranslate.google.com%2Ftranslate_tts%3Ftl%3Den%26q%3D" + encodeURI(_text)
        );
        var snd:Sound = new Sound(req)
        snd.play();
}

class Item extends ListItem{
    private var _highScore:Label;
    function Item(parent: DisplayObjectContainer = null, xpos: Number = 0, ypos: Number = 0, data: Object = null) { 
        super(parent, xpos, ypos, data);
        _highScore = new Label( this, 5 ,12  );
    }
    
    override public function draw():void{
        dispatchEvent(new Event(Component.DRAW));
        graphics.clear();
        graphics.beginFill(_selected ? _selectedColor : (_mouseOver ? _rolloverColor : _defaultColor));
        graphics.drawRect(0, 0, _width, _height);
        
        if( data == null ){ return; }
       _label.text = "" + _data.title  + " (" + _data.words.length + " words)"
       if( _data.highScore ){ _highScore.text = "high score: " + _data.highScore }
       else{  _highScore.text = "high score: -" }
    }
}


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, 0xFFFFFF, true, null, null, null, null, "center", null, null, null, null)
    static public var nextFormat:TextFormat = new TextFormat( null, null, 0x666666 );
    static public var typedFormat:TextFormat = new TextFormat( null, null, 0xFFFFFF );
    static public var typingFormat:TextFormat = new TextFormat( null, null, 0x001133 );

    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 {
    public 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 = 10;
    
    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 typeCount:int = 0;
    
    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 = null ):void{
        this.typing = typing;
        if( wordList ){ setWordList( wordList ) }
        typing.addEventListener( TypeEvent.INITED, init );
        typing.addEventListener( "start", onStart );
    }
    
    public function setWordList( wordList:Array ): void{
        this.wordList = new Vector.<TypeWord>();
        for each( var w:Object in wordList ) { this.wordList.push(new TypeWord(w)); }
        init( null );
    }

    public function add():void{
        if( !wordList || wordList.length==0 ){ return; }
        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 l:int = wordList.length;
        if(! wordList || l == 0 ){ return; }
        var next:Vector.<TypeWord> = this.next;
        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{
        typing.stage.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;
        _currentTime = time; 
        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 > -1 ) { 
                if( tl > 0 && 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 ) }
        }
    }
}