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

HTML→XHTML変換

フィードを解析するときに内容が(整形式でない)HTMLだとAS3のXMLクラスで扱えないので
HTMLを正規表現でXHTMLに変換するテスト

ネストに対応してみた
Get Adobe Flash player
by riafeed 27 Dec 2010
/**
 * Copyright riafeed ( http://wonderfl.net/user/riafeed )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dmfG
 */

/*
フィードを解析するときに内容が(整形式でない)HTMLだとAS3のXMLクラスで扱えないので
HTMLを正規表現でXHTMLに変換するテスト

ネストに対応してみた
*/
package {
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldType;
    import flash.display.Sprite;
    import flash.events.Event;
    import com.bit101.components.PushButton;

    public class FlashTest extends Sprite {
        private var _text:TextField;
        private var _text2:TextField;
        private var _btn:PushButton;

        public function FlashTest() {
            // write as3 code here..
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        public function init(e:Event = null):void {
            _text = new TextField();
            _text.type = TextFieldType.INPUT;
            _text.border = true;
            _text.multiline = true
            _text.width = 465;
            _text.height = 120;
            _text.wordWrap = true;
            _text.y = 0;
            addChild(_text);

            _btn = new PushButton(stage, 0, 120, "Convert", PushEventHandler);

            _text2 = new TextField();
            _text2.width = _text2.height = 465;
            _text2.wordWrap = true;
            _text2.y = 150;
            addChild(_text2);
        }

        private function PushEventHandler(e:Event):void {
            var ret:String = convHTML(_text.text);
            _text2.text = ret;
            try {
                var xmltest:XML = new XML(ret);
                _text2.textColor = Number(0x000000);
            }catch(e:Error){
                _text2.textColor = Number(0xff0000);
                _text2.appendText("\nこのXMLは整形式になっていません:" + e);
            }
        }

        public function convHTML(src:String):String {
            var temp:String = src;

            //HTMLでは終了タグの省略が認められていた要素に終了タグを付加(<li>~ → <li>~</li>)
            /*
            考え方:
              終了タグが省略できる要素だけでネストすることは物理的にありえない(判別できないので)ことを利用して
              ベースとなる親要素(tr要素のベースはtbody要素といった感じで)と共にネストの深さをカウントし、
              ネストの深さが同じ時にだけ終了タグを補完することで間違った位置に終了タグを補完する現象を避ける
            */
            var buf:String = "";
            var tag:String;
            var tagname:String;
            var idx:int = 0;
            var pin:int = 0;

            //関係タグのネスト数 
            var tagnest:Object = 
            {
               html:0,
               head:0,
               body:-1,  //bodyタグの自動補完は最初の1回だけ行うようにするために初期値が-1
               p:0,
               uol:0,    //ulタグとolタグはまとめてカウント
               li:0,
               table:0,
               tr:0,
               tdh:0,    //tdタグとthタグはまとめてカウント
               tbody:0,
               thead:0,
               tfoot:0,
               colgroup:0,
               option:0,
               dl:0,
               dd:0
            }

            //tdタグとthタグはあえてまとめてカウントしているのでどっちの終了タグを補完するかを記憶するための配列
            var tdhnest:Array = [];
            //dtタグは仕様上ネストごとに出てきたり出てこなかったりする場合がありネストごとに個別にカウントする必要があるので専用の配列を使う
            var dtnest:Array = [];
            
            var stack:Array = [];

            while(true){
                //タグの開始を検索
                pin = temp.indexOf("<", idx);

                //なかったら終了
                if(pin == -1) {
                    buf += temp.substring(idx);
                    if(tagnest["p"] == 1){
                        buf += "</p>";
                    }
                    if(tagnest["head"] == 1){
                        buf += "</head>";
                    }
                    if(tagnest["body"] == 1){
                        buf += "</body>";
                    }
                    if(tagnest["html"] == 1){
                        buf += "</html>";
                    }
                    break;                   
                }
                
                //あったらひとまずタグの前までの文字列を追加
                buf += temp.substring(idx, pin);
                
                //XML宣言などを読み飛ばす
                if(temp.substr(pin, 2) == "<?") {
                    var qend:int = temp.indexOf("?>", pin + 1);
                    if(qend == -1) {
                        buf += temp.substring(pin);
                        break;
                    }
                    buf += temp.substring(pin, qend + 2);
                    idx = comend + 3;
                    continue;
                }

                //コメントを読み飛ばす
                if(temp.substr(pin, 4) == "<!--") {
                    var comend:int = temp.indexOf("-->", pin + 1);
                    if(comend == -1) {
                        buf += temp.substring(pin);
                        break;
                    }
                    buf += temp.substring(pin, comend + 3);
                    idx = comend + 3;
                    continue;
                }

                //CDATAセクションなどを読み飛ばす
                if(temp.substr(pin, 3) == "<![") {
                    var secend:int = temp.indexOf("]>", pin + 1);
                    if(secend == -1) {
                        buf += temp.substring(pin);
                        break;
                    }
                    buf += temp.substring(pin, secend + 2);
                    idx = secend + 2;
                    continue;
                }

                //タグの取得
                tag = temp.substring(pin, temp.indexOf(">", pin) + 1);
                var taglength:int = tag.length;

                //タグ内から改行を取り除く
                tag = tag.replace(/[\r\n]/ig, "");

                //タグ名の取得
                var end:int = tag.indexOf(" ");
                if(end == -1) end = tag.length - 1;
                
                tagname = tag.substring(1, end).toLowerCase();
                
                if(!tagname.match(/[a-z]+/)) {
                    buf += "&lt;" + tag.substring(1, tag.length - 1) + "&gt;";
                    idx = pin + taglength;
                    continue;
                }

                //htmlタグの自動補完                
                if(tagnest["html"] == 0 && tagname != "html") {
                    if(tagname != "body" && tagname != "head") {
                        buf = "<html><body>" + buf;
                        tagnest["body"] = 1;
                    } else {
                        buf = "<html>" + buf;
                    }
                    tagnest["html"] = 1;
                }

                //引用符のない属性値に引用符を付加(width=200 → width="200")
                while (tag.match(/(<[^>]*? [a-z]+=)([^"'][^ >]*?)([ >])/i)) {
                    tag = tag.replace(/(<[^>]*? [a-z]+=)([^"'][^ >]*?)([ >])/i, "$1\"$2\"$3");
                }
                
                //短縮された属性値をXML形式に変換(checked → checked="checked")
                while (tag.match(/(<[^>]*? )([a-z]+?)([ >])/i)) {
                    tag = tag.replace(/(<[^>]*? )([a-z]+?)([ >])/i, "$1$2=\"$2\"$3");
                }

                //タグごとに処理
                switch(tagname) {
                    //空要素をXML形式に変換(<img src=""> → <img src="" />)
                    case "hr":
                    case "br":
                    case "img":
                    case "input":
                    case "param":
                    case "col":
                    case "area":
                    case "base":
                    case "link":
                    case "isindex":
                    case "meta":
                    case "basefont":{
                        if(tag.charAt(tag.length-2) != '/'){
                            tag = tag.replace(/<(.*?)>/ig, "<$1 />");
                        }
                        break;
                    }
                    case "head":
                    case "body":
                        //headタグ内でhead,bodyが出てきたらheadの閉じタグを補完
                        if(tagnest["head"] == 1) {
                            buf += "</head>";
                            tagnest["head"] = 0;
                        }
                        //bodyタグ内でhead,bodyが出てきたらbodyの閉じタグを補完(文法違反だけど一応)
                        if(tagnest["body"] == 1) {
                            buf += "</body>";
                            tagnest["body"] = 0;
                        }
                    case "html":
                        tagnest[tagname] = 1;
                        break;
                    case "/html":
                        if(tagnest["p"] == 1) {
                            buf += "</p>";
                            tagnest["p"] = 0;
                        }
                        if(tagnest["head"] == 1) {
                            buf += "</head>";
                            tagnest["head"] = 0;
                        }
                        if(tagnest["body"] == 1) {
                            buf += "</body>";
                            tagnest["body"] = 0;
                        }
                        tagnest["html"] = 0;
                        break;
                    case "/body":
                        if(tagnest["p"] == 1) {
                            buf += "</p>";
                            tagnest["p"] = 0;
                        }
                    case "/head":
                        tagnest[tagname.substring(1)] = 0;
                        break;

                    //段落タグ
                    case "p":
                        if(tagnest["p"] == 1) {
                            buf += "</p>";
                        } else {
                            tagnest["p"] = 1;
                        }
                        break;
                    case "/p":
                        tagnest["p"] = 0;
                        break;

                    //リストタグ
                    case "ul":
                    case "ol":
                        tagnest["uol"]++;
                        break;
                    case "/ul":
                    case "/ol":
                        if(tagnest["li"] == tagnest["uol"]) {
                            buf += "</li>";
                            tagnest["li"]--;
                        }
                        tagnest["uol"]--;
                        break;
                    case "li":
                        if(tagnest["li"] == tagnest["uol"]) {
                            buf += "</li>";
                        } else { 
                            tagnest["li"]++;
                        }
                        break;
                    case "/li":
                        tagnest["li"]--;
                        break;

                    //フォームの選択タグ
                    case "option":
                        if(tagnest["option"] == 1) {
                            buf += "</option>";
                        } else {
                            tagnest["option"] = 1;
                        }
                        break;
                    case "/option":
                        tagnest["option"] = 0;
                        break;
                    case "optgroup":
                    case "/select":
                    case "/optgroup":
                        if(tagnest["option"] == 1) {
                            buf += "</option>";
                            tagnest["option"] = 0;
                        }
                        break;
                        
                    //構造化リストタグ
                    case "dl":
                        tagnest["dl"]++;
                        break;
                    case "dt":
                    case "dd":
                    case "/dl":
                        if(tagnest["dd"] == tagnest["dl"]) {
                            buf += "</dd>"
                            tagnest["dd"]--;
                        }else if(dtnest[tagnest["dl"]] == 1) {
                            buf += "</dt>"
                            dtnest[tagnest["dl"]] = 0;
                        }

                        if(tagname == "/dl"){ 
                            tagnest["dl"]--;
                        }else if(tagname == "dt"){
                            dtnest[tagnest["dl"]] = 1;
                        }else{
                            tagnest["dd"]++;
                        }
                        break;
                        
                    //テーブルタグ
                    case "table":
                        tagnest["table"]++;
                        break;
                    case "/table":
                        if(tagnest["table"] > 0) {
                            if(tagnest["colgroup"] == tagnest["table"]) {
                                buf += "</colgroup>";
                                tagnest["colgroup"]--;
                            }

                            if(tagnest["tdh"] == tagnest["table"]) {
                                buf += "</"+ tdhnest[tagnest["tdh"]] +">";
                                tagnest["tdh"]--;
                            }
    
                            if(tagnest["tr"] == tagnest["table"]) {
                                buf += "</tr>";
                                tagnest["tr"]--;
                            }
                            
                            if(tagnest["tbody"] == tagnest["table"]) {
                                buf += "</tbody>";
                                tagnest["tbody"]--;
                            }

                            if(tagnest["thead"] == tagnest["table"]) {
                                buf += "</thead>";
                                tagnest["thead"]--;
                            }

                            if(tagnest["tfoot"] == tagnest["table"]) {
                                buf += "</tfoot>";
                                tagnest["tfoot"]--;
                            }

                            tagnest["table"]--;
                        }
                        break;
                    case "tr":
                        if(tagnest["colgroup"] == tagnest["table"]) {
                            buf += "</colgroup>";
                            tagnest["colgroup"]--;
                        }

                        if(tagnest["tdh"] == tagnest["table"]) {
                            buf += "</"+ tdhnest[tagnest["tdh"]] +">";
                            tagnest["tdh"]--;
                        }

                        if(tagnest["tr"] == tagnest["table"]) {
                            buf += "</tr>";
                        } else { 
                            if(tagnest["tbody"] < tagnest["table"] && tagnest["thead"] < tagnest["table"] && tagnest["tfoot"] < tagnest["table"]) {
                                buf += "<tbody>";
                                tagnest["tbody"]++;
                            }

                            tagnest["tr"]++;
                        }
                        break;
                    case "/tr":
                        if(tagnest["tdh"] == tagnest["table"]) {
                            buf += "</"+ tdhnest[tagnest["tdh"]] +">";
                            tagnest["tdh"]--;
                        }

                        tagnest["tr"]--;
                        break;
                    case "td":
                    case "th":
                        if(tagnest["tdh"] == tagnest["table"]) {
                            buf += "</"+ tdhnest[tagnest["tdh"]] +">";
                            tdhnest[tagnest["tdh"]] = tagname;
                        } else {
                            tagnest["tdh"]++;
                            tdhnest[tagnest["tdh"]] = tagname;
                        }
                        break;
                    case "/td":
                    case "/th":
                        tagnest["tdh"]--;
                        break;
                    case "thead":
                    case "tfoot":
                    case "tbody":
                        if(tagnest["colgroup"] == tagnest["table"]) {
                            buf += "</colgroup>";
                            tagnest["colgroup"]--;
                        }

                        if(tagnest["tbody"] == tagnest["table"]) {
                            buf += "</tbody>";
                            tagnest["tbody"]--;
                        }

                        if(tagnest["thead"] == tagnest["table"]) {
                            buf += "</thead>";
                            tagnest["thead"]--;
                        }

                        if(tagnest["tfoot"] == tagnest["table"]) {
                            buf += "</tfoot>";
                            tagnest["tfoot"]--;
                        }
                        tagnest[tagname]++;
                        break;
                    case "/thead":
                    case "/tfoot":
                    case "/tbody":
                        if(tagnest["tdh"] == tagnest["table"]) {
                            buf += "</"+ tdhnest[tagnest["tdh"]] +">";
                            tagnest["tdh"]--;
                        }

                        if(tagnest["tr"] == tagnest["table"]) {
                            buf += "</tr>";
                            tagnest["tr"]--;
                        }
                        if(tagnest[tagname.substring(1)] == tagnest["table"]) tagnest[tagname.substring(1)]--;
                        break;
                }
                
                //タグ名を強制的に小文字にする
                buf += "<" + tagname + tag.substring(end);

                idx = pin + taglength;
            }

            temp = buf;
            
            /*
            これだとネストに対応できない...
            if(temp.match(/<\/p>/i) == null) {
                temp = temp.replace(/(<p.*?>)/ig, "</p>$1");
                temp = temp.replace(/(<p.*?>.*?)(<\/body>)/isg, "$1</p>$2");
                temp = temp.replace(/(<body>.*?)<\/p>/isg, "$1");
            }

            if(temp.match(/<\/li>/i) == null) {
                temp = temp.replace(/(<li.*?>)/ig, "</li>$1");
                temp = temp.replace(/(<li.*?>.*?)(<\/ul>|<\/ol>)/isg, "$1</li>$2");
                temp = temp.replace(/(<ul.*?>|<ol.*?>)(.*?)<\/li>/isg, "$1$2");
                temp = temp.replace(/(<\/ul>|<\/ol>)([^<>]*?)<\/li>/isg, "$1$2");
            }

            if(temp.match(/<\/tr>/i) == null) {
                temp = temp.replace(/(<tr.*?>)/ig, "</tr>$1");
                temp = temp.replace(/(<tr.*?>.*?)(<\/thead>|<\/tfoot>|<\/tbody>|<\/table>)/isg, "$1</tr>$2");
                temp = temp.replace(/(<thead.*?>|<tfoot.*?>|<tbody.*?>|<table.*?>)(.*?)<\/tr>/isg, "$1$2");
                temp = temp.replace(/(<\/thead>|<\/tfoot>|<\/tbody>|<\/table>)([^<>]*?)<\/tr>/isg, "$1$2");
            }

            if(temp.match(/<\/td>/i) == null) {
                temp = temp.replace(/(<td.*?>)/ig, "</td>$1");
                temp = temp.replace(/(<td.*?>.*?)(<\/tr>)/isg, "$1</td>$2");
                temp = temp.replace(/(<tr.*?>.*?)<\/td>/isg, "$1");
                temp = temp.replace(/(<\/tr>[^<>]*?)<\/td>/isg, "$1");
            }

            if(temp.match(/<\/th>/i) == null) {
                temp = temp.replace(/(<th.*?>)/ig, "</th>$1");
                temp = temp.replace(/(<th.*?>.*?)(<\/tr>)/isg, "$1</th>$2");
                temp = temp.replace(/(<tr.*?>.*?)<\/th>/isg, "$1");
                temp = temp.replace(/(<\/tr>[^<>]*?)<\/th>/isg, "$1");
            }

            if(temp.match(/<\/colgroup>/i) == null) {
                temp = temp.replace(/(<colgroup.*?>)/ig, "</colgroup>$1");
                temp = temp.replace(/(<colgroup.*?>.*?)(<tr.*?>|<thead.*?>|<tfoot.*?>|<tbody.*?>|<\/table>)/isg, "$1</colgroup>$2");
                temp = temp.replace(/(<table.*?>)(.*?)<\/colgroup>/isg, "$1$2");
            }

            if(temp.match(/<\/option>/i) == null) {
                temp = temp.replace(/(<option.*?>)/ig, "</option>$1");
                temp = temp.replace(/(<option.*?>.*?)(<\/select>|<\/optgroup>)/isg, "$1</option>$2");
                temp = temp.replace(/(<select.*?>|<optgroup.*?>)(.*?)<\/option>/isg, "$1$2");
                temp = temp.replace(/(<\/optgroup>)([^<>]*?)<\/option>/isg, "$1$2");
            }

            if(temp.match(/<\/(dt|dd)>/i) == null) {
                temp = temp.replace(/(<dt.*?>|<dd.*?>)/ig, "</dt></dd>$1");
                temp = temp.replace(/(<dt.*?>|<dd.*?>)(.*?)(<\/dl>)/isg, "$1$2</dt></dd>$3");
                temp = temp.replace(/(<dl.*?>)(.*?)<\/dt><\/dd>/isg, "$1$2");
                temp = temp.replace(/(<dt.*?>)(.*?)(<\/dt>)<\/dd>/isg, "$1$2$3");
                temp = temp.replace(/(<dd.*?>)(.*?)<\/dt>(<\/dd>)/isg, "$1$2$3");
                temp = temp.replace(/(<\/dl>)([^<>]*?)<\/dt><\/dd>/isg, "$1$2");
            }
            */
            return temp;
        }
    }
}