YAWA - Yet Another Wonderfl API v0.16 (SWF素材)

by keim_at_Si
You can get more informations than official API !
The basic idea is from http://wonderfl.net/c/gJXA (created by wh0)
かなり縦横無尽に情報が取れる wonderflAPI.運営様に怒られたら消します.htmlの書式が変わったら使えなくなります.

// basic functions
requestUserRanking(responseHandler, days, order, page);
requestCodeRanking(responseHandler, days, order, page);
requestTagCloud(responseHandler);
requestUserInfo(responseHandler, userID, informationType, page);
requestCodeInfo(responseHandler, codeID, order(N/A), page);
requestTagInfo(responseHandler, tag, page);
(where responseHandeler(data:*) : void {})
// analyze imported libraries
analyzeLibraries(as3code) : *
webpage; http://soundimpulse.sakura.ne.jp/yawa-yet-another-wonderfl-api/
♥22 | Line 679 | Modified 2011-12-28 23:31:48 | MIT License | (replaced)
play

ActionScript3 source code

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

package {
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.system.*;
    import flash.text.*;


    /** Yet Another Wonderfl API */
    public class YAWA extends Sprite {
    // constants
    //--------------------------------------------------
        public const API_VERSION:String = "0.16";
    
        // options
        public const DAYS_1DAY:String      = "days=1day";
        public const DAYS_7DAYS:String     = "days=7days";
        public const DAYS_ALL:String       = "days=all";
        public const DAYS_OPTIONS:Array    = [DAYS_1DAY, DAYS_7DAYS, DAYS_ALL];
        public const ORDER_PV:String       = "order=pv";
        public const ORDER_FAVORITE:String = "order=favorite";
        public const ORDER_FORKED:String   = "order=forked";
        public const ORDER_OPTIONS:Array   = [ORDER_PV, ORDER_FAVORITE, ORDER_FORKED];
        public const USER_INFO_ACTIVITY:String = "";
        public const USER_INFO_CODE:String     = "/codes";
        public const USER_INFO_FAVORITE:String = "/favorites";
        public const USER_INFO_FOLLOWER:String = "/followers";
        public const USER_INFO_OPTIONS:Array   = [USER_INFO_ACTIVITY, USER_INFO_CODE, USER_INFO_FAVORITE, USER_INFO_FOLLOWER];

        // access url
        public const USER_RANKING_URL:String = "http://wonderfl.net/users";
        public const CODE_RANKING_URL:String = "http://wonderfl.net/codes";
        public const TAG_CLOUD_URL:String    = "http://wonderfl.net/tags";
        public const USER_URL:String         = "http://wonderfl.net/user/";
        public const CODE_URL:String         = "http://wonderfl.net/c/";
        public const TAG_URL:String          = "http://wonderfl.net/tag/";
        
        // wonderfl constant
        public var USER_INFO_CODE_PAR_PAGE:int = 12;
        public var CODE_INFO_CODE_PAR_PAGE:int = 8;
        public var TAG_INFO_CODE_PAR_PAGE:int = 40;
        
        // bookmarks of user parser
        public var PROFILE_START:String        = '<section id="sectProfile"';
        public var PROFILE_END  :String        = '<!-- / #sectProfile --></section>';
        public var FOLLOWING_START:String      = '<section id="sectFollowing">';
        public var FOLLOWING_END  :String      = '<!-- / #sectFollowing --></section>';
        public var POSTED_CODES_START:String   = '<div class="unitCodeGroup';
        public var POSTED_CODES_END  :String   = '<!-- /.unitCodeGroup --></div>';
        public var FAVORITE_CODES_START:String = '<div class="unitCodeGroup';
        public var FAVORITE_CODES_END  :String = '<!-- /.unitCodeGroup --></div>';
        public var FOLLOWERS_START:String      = '<div class="follower_box';
        public var FOLLOWERS_END  :String      = '</div>';        
        // bookmarks of code parser
        public var CODE_HEADER_START:String = '<header>';
        public var CODE_HEADER_END:String   = '</header>';
        public var RAW_AS3_START:String     = '<textarea id="raw_as3" style="display:none;">';
        public var RAW_AS3_END:String       = '</textarea>';
        public var TALK_START:String        = '<div id="talk_container"';
        public var TALK_END:String          = '<!-- /.groupTalk.group --></div>';
        public var SWF_START:String         = '<div id="swf">';
        public var SWF_END:String           = '</div>';
        public var FAV_START:String         = '<section id="sectFavBy"';
        public var FAV_END:String           = '<!-- /#sectFavBy --></section>';
        public var TAG_START:String         = '<ul class="listTag group">';
        public var TAG_END:String           = '</ul>';
        public var KEYWORD_START:String     = '<ul class="listKeywords group">';
        public var KEYWORD_END:String       = '</ul>';
        public var FORKED_CODE_START:String = '<div id="boxForkedCode"';
        public var FORKED_CODE_END:String   = '<!-- /#boxForkedCode --></div>';
        // bookmarks of tag parser
        public var TAG_LIST_START:String  = '<div class="unitCodeGroup';
        public var TAG_LIST_END:String    = '<!-- /.unitCodeGroup --></div>';
        public var TAG_COUNT_START:String = '<p class="search_meta">';
        
        
        /** library pathes */
        static public var libraryPathes:* = {
            "com.adobe"               : "as3corelib",
            "caurina.transitions"     : "tweener",
            "com.greensock"           : "tweenmax",
            "com.flashdynamix.motion" : "tweensy",
            "org.libspark.betweenas3" : "betweenas3",
            "net.kawa.tween"          : "ktween",
            "jp.progression"          : "progression",
            "org.libspark.thread"     : "thread",
            "net.hires.debug"         : "stats",
            "org.papervision3d"       : "papervision3d",
            "sandy."                  : "sandy3d",
            "alternativa."            : "alternativa3d",
            "away3d."                 : "away3d",
            "net.badimon.five3d"      : "five3d",
            "Box2D."                  : "box2d",
            "jiglib."                 : "jiglib",
            "org.libspark.swfassist"  : "swfassist",
            "camo.core"               : "flashcamouflage",
            "com.bit101.components"   : "minimalcomps",
            "net.user1"               : "union",
            "org.si.sion"             : "sion",
            "idv.cjcat.stardust"      : "stardust",
            "funnel."                 : "funnel",
            "com.modestmaps"          : "modestmaps",
            "com.google.maps"         : "googlemaps",
            "com.afcomponents.umap"   : "umap",
            "com.sony.jp.felica"      : "felica",
            "org.as3lib.kitchensync"  : "kitchensync",
            "com.actionsnippet.qbox"  : "quickbox2d",
            "net.wonderfl.score"      : "scoreranking",
            "org.libspark.flartoolkit": "flartoolkit",
            "com.desuade.motion"      : "desuademotion",
            "com.desuade.partigen"    : "desuadepartigen",
            "frocessing"              : "frocessing",
            "org.flintparticles"      : "flint",
            "net.wonderfl.utils"      : "wonderflutils",
            "flupie.textanim"         : "textanim",
            "de.polygonal"            : "polygonal",
            "com.pblabs"              : "pushbuttonengine",
            "com.codeazur.as3swf"     : "as3swf",
            "ru.inspirit.surf"        : "assurf",
            "flash.geom.Bezier"       : "bezier",
            "sliz."                   : "miniui",
            "org.libspark.ukiuki"     : "ukiuki",
            "idv.cjcat.signals"       : "cjsignals",
            "com.useitbetter"         : "useitbetteranalytics",
            "com.demonsters.debugger" : "monsterdebugger",
            "org.si.cml"              : "cannonml",
            "org.flixel"              : "flixel"
        };
        /** regular expression for libraries, set null to reconstuct */
        static public var libraryPathesRegExp:RegExp = null;
        
        
        
        
    // variables & properties
    //--------------------------------------------------
        /** proxy url */
        public var proxyURL:String = 'http://www.gmodules.com/ig/proxy?url=';
        /** proxy domain */
        public var proxyDomain:String = 'www.gmodules.com';
        /** api entry in proxy surver */
        public var apiEntry:* = undefined;
        /** maximum trial count when error occurs. */
        public var maxTrialCount:int = 3;

        // suspend while loading api entry
        private var _suspendList:Array = [];
        
        // cache
        private var _userInfoCache:* = {};
        private var _codeInfoCache:* = {};
        private var _tagsInfoCache:* = {};
        
        // loaderInfo.url
        private var loaderInfo_url:String = "http://swf.wonderfl.net/swf/usercode/8/8f/8f46/8f46608d59b94c39bf42b94a89c1819f9e3bce61.swf";
        
        
        
    // constructor
    //--------------------------------------------------
        public function YAWA() {
            var domain:String = new LocalConnection().domain;

            if (domain == proxyDomain) {
                Security.allowDomain("*");
                // execute on proxy
                apiEntry = this;
                addEventListener(Event.ADDED_TO_STAGE, function (e:Event) : void {
                    var tf:TextField = new TextField();
                    tf.width = stage.stageWidth;
                    tf.text = "proxy available (ver" + API_VERSION + ")";
                    addChild(tf);
                });
            } else {
                // execute not on proxy
                var loader:Loader = new Loader();
                loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event) : void {
                    apiEntry = loader.content;
                    for (var i:int=0; i<_suspendList.length; i++) apiEntry._loadOnProxy(_suspendList[i]);
                    _suspendList = [];
                });
                //loader.load(_proxyRequest(loaderInfo.url));
                loader.load(_proxyRequest(loaderInfo_url + "?v=" + API_VERSION));
                _suspendList = [];
                
                // test
                if (stage) _test(this);
            }
        }
        
        
        

    // interfaces
    //--------------------------------------------------
        /** Request to get global user ranking.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @param days range to search
         *  @param order ranking order
         *  @param page page index, 10 users in 1 page
         *  @return URLLoader to load
         */
        public function requestUserRanking(responseHandler:Function, days:String=DAYS_7DAYS, order:String=ORDER_FAVORITE, page:int=1) : URLLoader {
            return load(getUserRankingURL(days, order, page), responseHandler, userRankingParser, [days, order, page]);
        }


        /** Request to get global code ranking.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @param days range to search
         *  @param order ranking order
         *  @param page page index, 10 codes in 1 page
         *  @return URLLoader to load
         */
        public function requestCodeRanking(responseHandler:Function, days:String=DAYS_7DAYS, order:String=ORDER_FAVORITE, page:int=1) : URLLoader {
            return load(getCodeRankingURL(days, order, page), responseHandler, codeRankingParser, [days, order, page]);
        }


        /** Request to get tag cloud information.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @return URLLoader to load
         */
        public function requestTagCloud(responseHandler:Function) : URLLoader {
            return load(getTagCloudURL(), responseHandler, tagCloudParser, null);
        }


        /** Request to get user information.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @param userID user id
         *  @param infoType information type
         *  @param page page index of codes, valid when infoType is USER_INFO_CODES or USER_INFO_FAVORITE, 12 codes par 1 page
         *  @return URLLoader to load
         */
        public function requestUserInfo(responseHandler:Function, userID:String, infoType:String=USER_INFO_CODE, page:int=1) : URLLoader {
            return load(getUserURL(userID, infoType, page), responseHandler, userParser, [userID, infoType, page]);
        }


        /** Request to get code information.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @param codeID code id
         *  @param order order of forked codes (N/A)
         *  @param page page index of forked codes, 12 codes par 1 page
         *  @return URLLoader to load
         */
        public function requestCodeInfo(responseHandler:Function, codeID:String, order:String=ORDER_FAVORITE, page:int=1) : URLLoader {
            return load(getCodeURL(codeID, order, page), responseHandler, codeParser, [codeID, order, page]);
        }


        /** Request to get user information.
         *  @param responseHandler response handler, requires function(data:*) : void.
         *  @param tag tag string
         *  @param page page index of codes, 40 codes par 1 page
         *  @return URLLoader to load
         */
        public function requestTagInfo(responseHandler:Function, tag:String, page:int=1) : URLLoader {
            return load(getTagURL(tag, page), responseHandler, tagParser, [tag, page]);
        }
        
        
        
        
    // cache operations
    //--------------------------------------------------
        /** get cached user info */
        public function getUserInfoCache(userID:String) : * { return _userInfoCache[userID]; }
        
        
        /** get cached code info */
        public function getCodeInfoCache(codeID:String) : * { return _codeInfoCache[codeID]; }
        
        
        /** get cached tag info */
        public function getTagsInfoCache(tag:String) : * { return _tagsInfoCache[tag]; }
        
        
        /** clear all cache */
        public function clearCache() : void {
            _userInfoCache = {};
            _codeInfoCache = {};
            _tagsInfoCache = {};
        }
        



    // url constructor
    //--------------------------------------------------
        public function getUserRankingURL(days:String=DAYS_7DAYS, order:String=ORDER_FAVORITE, page:int=1) : String { return USER_RANKING_URL + "?" + days + "&" + order + "&page=" + page.toString(); }
        public function getCodeRankingURL(days:String=DAYS_7DAYS, order:String=ORDER_FAVORITE, page:int=1) : String { return CODE_RANKING_URL + "?" + days + "&" + order + "&page=" + page.toString(); }
        public function getTagCloudURL() : String { return TAG_CLOUD_URL + "?a=0"; }
        public function getUserURL(userID:String, infoType:String=USER_INFO_CODE, page:int=1) : String { return USER_URL + userID + infoType + "?page=" + page.toString(); }
        public function getCodeURL(codeID:String, order:String=ORDER_FAVORITE, page:int=1) : String { return CODE_URL + codeID + "?" + order + "&page=" + page.toString(); }
        public function getTagURL(tag:String, page:int=1) : String { return TAG_URL + tag + "?page=" + page.toString(); }
        
        
        
        
    // XHTML parser
    //--------------------------------------------------
        /** user ranking parser, args=[days, order, page]; */
        public function userRankingParser(html:String, args:Array) : * {
            var list:XMLList = getElementByClass(_cutout(html)..div, "unitUserRank"),
                result:Array = [], i:int, imax:int = list.length(), j:int, jmax:int, elem:XML, codes:XMLList, 
                user:*, propName:String, codeInfo:Array, str:String;
            result["html"] = html;
            result.length = imax;
            switch (args[1]) {
            case ORDER_PV:       propName = "codes_count";     break;
            case ORDER_FAVORITE: propName = "favorited_count"; break;
            case ORDER_FORKED:   propName = "forked_count";    break;
            }
            for (i=0; i<imax; i++) {
                elem = list[i];
                codes = $class(elem.dl, 'unitRankCode');
                jmax = codes.length();
                codeInfo = new Array(jmax);
                for (j=0; j<jmax; j++) codeInfo[j] = _smallCodeInfo(codes[j].dt.a[0]);
                user = _smallUserInfo($class(elem.p, "user")[0]);
                user[propName] = getNumber(String($class(elem.ul, "data").li.a[0]));
                result[i] = {
                    "rank"  : int($class(elem.p, 'txtRank')),
                    "user"  : user,
                    "codes" : codeInfo};
            }
            return result;
        }
        
        
        /** code ranking parser, args=[days, order, page]; */
        public function codeRankingParser(html:String, args:Array) : * {
            var list:XMLList = getElementByClass(_cutout(html)..div, "unitRank"),
                result:Array = [], code:*, i:int, imax:int = list.length(), elem:XML, data:XMLList;
            result["html"] = html;
            result.length = imax;
            for (i=0; i<imax; i++) {
                elem = list[i];
                result[i] = {
                    "rank" : int($class(elem.p, 'txtRank')),
                    "user" : _smallUserInfo($class(elem.p, "user")[0]),
                    "code" : _codeInfo(elem)
                };
            }
            return result;
        }
        
        
        /** tag cloud parser, args=null; */
        public function tagCloudParser(html:String, args:Array) : * {
            var list:XMLList = $class(_cutout(html)..ul, "listTag").li,
                result:Array = [], i:int, imax:int = list.length(), elem:XML, count:String;
            result["html"] = html;
            for (i=0; i<imax; i++) {
                elem = list[i].a[0];
                count = String(elem.span);
                result[i] = {
                    "tag"   : String(elem.@title),
                    "url"   : "http://wonderfl.net/tag/" + encodeURIComponent(elem.@title),
                    "count" : int(count.substring(1,count.length-1))
                };
            }
            return result;
        }
        
        
        /** user info parser, args=[userID, infoType, page]; */
        public function userParser(html:String, args:Array) : * {
            var prof:XML, result:*, elem:XML, basic:XML, data:XMLList, i:int, imax:int, array:Array,
                userID:String = args[0], codeCount:int, favoritesCount:int;
            if (!(userID in _userInfoCache)) {
                prof = _cutout(html, PROFILE_START, PROFILE_END);
                if (!prof) return {"error" : "Invalid user ID"};
                basic = $id(prof.div, 'boxProfBasic')[0];
                data  = $class($id(prof.div, 'boxProfInfo').ul, 'data')[0].li;
                codeCount      = getNumber(data[0].a[0] || data[0]);
                favoritesCount = getNumber(data[2].a[0] || data[2]);
                _userInfoCache[userID] = {
                    "html" : html,
                    "name" : userID, 
                    "url"  : "http://wonderfl.net/user/" + userID,
                    "icon" : "http://wonderfl.net" + String(basic..img[0].@src),
                    "codes_count": codeCount,
                    "forked_count": getNumber(data[1].a[0] || data[1]),
                    "favorites_count" : favoritesCount,
                    "following_count" : getNumber(data[3].a[0] || data[3]),
                    "followers_count" : getNumber(data[4].a[0] || data[4]),
                    "codes_pages"     : int((codeCount + USER_INFO_CODE_PAR_PAGE - 1) / USER_INFO_CODE_PAR_PAGE),
                    "favorites_pages" : int((favoritesCount + USER_INFO_CODE_PAR_PAGE - 1) / USER_INFO_CODE_PAR_PAGE),
                    "external_url": String(data[5].a[0] || null),
                    "description": String(data[6]).replace(/<.+?>/g,""),
                    "cached_codes_count" : 0,
                    "cached_favorites_count" : 0
               };
            }
            result = _userInfoCache[userID];
            if ("current_page" in result) delete result["current_page"];

            switch(args[1]) {
            case USER_INFO_ACTIVITY:
                result = {error:"USER_INFO_ACTIVITY is not supported currently"};
                break;
            case USER_INFO_FOLLOWER:
                if (!("followers" in result)) result["followers"] = _getUserInfomations(html, FOLLOWERS_START, FOLLOWERS_END);
                break;
            case USER_INFO_CODE:
                result["cached_codes_count"] += _getCodeInfomations(html, POSTED_CODES_START, POSTED_CODES_END, result, "codes", args[2]);
                result["current_page"] = args[2];
                break;
            case USER_INFO_FAVORITE:
                result["cached_favorites_count"] += _getCodeInfomations(html, FAVORITE_CODES_START, FAVORITE_CODES_END, result, "favorites", args[2], true);
                result["current_page"] = args[2];
                break;
            }

            return result;
        }
        private function _getUserInfomations(html:String, start:String, end:String) : Array {
            var xhtml:XML = _cutout(html, start, end);
            if (!xhtml) return [];
            var data:XMLList = xhtml.ul.li.span.a, elem:XML; 
            var i:int, imax:int = data.length(), array:Array = new Array(imax);
            for (i=0; i<imax; i++) {
                elem = data[i];
                array[i] = {
                    "name" : String(elem.@title),
                    "url"  : String(elem.@href),
                    "icon" : "http://wonderfl.net" + String(elem.img.@src)
                };
            }
            return array;
        }
        private function _getCodeInfomations(html:String, start:String, end:String, result:*, prop:String, page:int, withUserInfo:Boolean=false) : int {
            var data:XMLList = $class(_cutout(html, start, end).div, "unitCode"), codeIndex:int, elem:XML, url:String, 
                i:int, imax:int = data.length(), codeList:Array = result[prop] || new Array();
            codeIndex = (page-1) * USER_INFO_CODE_PAR_PAGE;
            for (i=0; i<imax; i++, codeIndex++) {
                elem = data[i];
                codeList[codeIndex] = _codeInfo(elem);
                if (withUserInfo) {
                    elem = $class(elem.p, "user")[0];
                    url = String(elem.a[0].@href);
                    codeList[codeIndex]["user"] = {
                        "url"  : url,
                        "name" : url.substr(url.lastIndexOf("/") + 1),
                        "icon" : "http://wonderfl.net" + String(elem..img.@src)
                    };
                }
            }
            result[prop] = codeList;
            return imax;
        }
        
        
        /** code info parser, args=[codeID, order, page]; */
        public function codeParser(html:String, args:Array) : * {
            var header:XML, codeID:String = args[0], page:int = args[2], 
                swfCont:XML, tagCont:XML, keyCont:XML, frkCont:XML, forkList:XMLList, forkArray:Array, forkedCount:int, thumbBase:String, date:String, 
                tlkCont:XML, commentList:XMLList, comments:Array, favCont:XML, favUserList:XMLList, favUser:Array, userInfo:*, 
                result:*, divForkedFrom:XML, divHeaderInfo:XML, data:XMLList, parent:* = null, url:String, raw_as3:String = "", startIndex:int, endIndex:int,
                i:int, imax:int, codeIndex:int, userInfoList:XMLList;
               
            if (!(codeID in _codeInfoCache)) {
                header = _cutout(html, CODE_HEADER_START, CODE_HEADER_END);
                if (!header) return {"error" : "Invalid code ID. [" + codeID + "]"};

                divForkedFrom = $id(header.div, 'boxForked')[0];
                if (divForkedFrom) {
                    url = String(divForkedFrom..a[1].@href);
                    parent = {"title"    : String(divForkedFrom..a[1]),
                              "url"      : url,
                              "id"       : url.substr(url.lastIndexOf("/")+1),
                              "user"     : { "name":String(divForkedFrom..a[0].@title), "url":String(divForkedFrom..a[0].@href) },
                              "diff"     : getNumber(divForkedFrom..a[2])};
                }

                divHeaderInfo = getElementByClass(header.div, "headerInfo")[0];
                data = divHeaderInfo.ul[0].ul[0].li;
                startIndex = html.indexOf(RAW_AS3_START);
                if (startIndex >= 0) {
                    endIndex = html.indexOf(RAW_AS3_END, startIndex);
                    if (endIndex >= 0) raw_as3 = html.substring(startIndex+RAW_AS3_START.length, endIndex);
                }
                
                swfCont = _cutout(html, SWF_START, SWF_END);
                tlkCont = _cutout(html, TALK_START, TALK_END);
                favCont = _cutout(html, FAV_START, FAV_END);
                tagCont = _cutout(html, TAG_START, TAG_END);
                keyCont = _cutout(html, KEYWORD_START, KEYWORD_END);

                comments = [];
                if (tlkCont) {
                    commentList = getElementByClass(tlkCont.div, "unitTalk");
                    imax = commentList.length();
                    for (i=0; i<imax; i++) {
                        userInfoList = commentList[i].ul[0].li;
                        comments.push({"text" : decodeHtmlEscape(String(commentList[i].p[0]).replace(/<.+?>/g,"")),
                                       "user" : {"name" : String(userInfoList[0].a[0]),
                                                 "url"  : String(userInfoList[0].a[0].@href),
                                                 "icon" : "http://wonderfl.net" + String(userInfoList[0].span[0].a[0].img.@src)},
                                       "date" : String(userInfoList[1]).substr(3)});
                    }
                }

                favUser = [];
                if (favCont) {
                    favUserList = $class(favCont..div, "unitFavBy");
                    imax = favUserList.length();
                    for (i=0; i<imax; i++) favUser.push(_smallUserInfo(favUserList[i].span[0]));
                    favUserList = getElementByClass(favCont.div, "unitFavUserL");
                    imax = favUserList.length();
                    for (i=0; i<imax; i++) {
                        userInfo = _smallUserInfo(favUserList[i].p[0].span[0]);
                        userInfo["comment"] = String(favUserList[i].p[1].text()[0] || "");
                        userInfo["tags"] = _getTagsInFavComment(favUserList[i].p[1].a);
                        favUser.push(userInfo);
                    }
                }

                forkedCount = getNumber(data[0].a[0] || data[0]);
                thumbBase = _thumbBaseURL(swfCont.img.@src);
                date = date$;
                
                _codeInfoCache[codeID] = {
                    "html"        : html,
                    "id"          : codeID,
                    "url"         : "http://wonderfl.net/c/" + codeID,
                    "title"       : String(divHeaderInfo.h1[0]),
                    "description" : String($class(divHeaderInfo.p, "description")[0]).replace(/<.+?>/g,""),
                    "thumb"       : thumbBase + ".jpg?t=" + date,
                    "thumb_w"     : thumbBase + "_w.jpg?t=" + date,
                    "thumb_100"   : thumbBase + "_100.jpg?t=" + date,
                    "user"        : _smallUserInfo($class(divHeaderInfo.p, "user").span[0]),
                    "forked_count"    : forkedCount,
                    "favorited_count" : getNumber(data[1].a[0] || data[1]),
                    "lines"           : getNumber(data[2].a[0] || data[2]),
                    "license"         : String(data[3].a[0]) || getString(data[3]),
                    "modified"        : getString(data[4]),
                    "forked_pages"    : int((forkedCount + CODE_INFO_CODE_PAR_PAGE - 1) / CODE_INFO_CODE_PAR_PAGE),
                    "parent"          : parent,
                    "as3"             : decodeHtmlEscape(raw_as3), 
                    "comments"        : comments,
                    "favorited"       : favUser,
                    "tags"            : (tagCont) ? _tagInfo(tagCont.li) : [],
                    "keywords"        : (keyCont) ? _tagInfo(keyCont.li) : [],
                    "cached_forked_count" : 0
                };
            }
            result = _codeInfoCache[codeID];

            if (!("forked" in result)) result["forked"] = [];
            
            frkCont = _cutout(html, FORKED_CODE_START, FORKED_CODE_END);
            if (frkCont) {
                forkArray = result["forked"];
                forkList = $class(frkCont.div[0].div, "unitCode");
                imax = forkList.length();
                codeIndex = (page - 1) * CODE_INFO_CODE_PAR_PAGE;
                for (i=0; i<imax; i++, codeIndex++) forkArray[codeIndex] = _codeInfo(forkList[i], false);
                result["cached_forked_count"] += imax;
            }
            
            return result;
        }
        private function _getTagsInFavComment(atags:XMLList) : Array {
            var imax:int=atags.length(), res:Array=new Array(imax), i:int;
            for (i=0; i<imax; i++) res[i] = String(atags[i]);
            return res; 
        }

        
        /** tag info parser, args=[tag, page]; */
        public function tagParser(html:String, args:Array) : * {
            var list:XMLList = $class(_cutout(html)..div, "unitCode"), 
                tag:String = args[0], page:int = args[1],  
                i:int, imax:int = list.length(), codeIndex:int, result:*, codes:Array;
            if (!(tag in _tagsInfoCache)) {
                var tagCountStart:int = html.indexOf(TAG_COUNT_START);
                if (tagCountStart == -1) return {"error":"Invalid tag"};
                var count:int = int(html.substr(tagCountStart+TAG_COUNT_START.length, 30).match(/\d+/g)[2]);
                _tagsInfoCache[tag] = { "html"        : html,
                                        "tag"         : tag,
                                        "url"         : "http://wonderfl.net/tag/" + encodeURIComponent(tag),
                                        "codes_count" : count,
                                        "codes_pages" : int((count + TAG_INFO_CODE_PAR_PAGE - 1) / TAG_INFO_CODE_PAR_PAGE),
                                        "codes"       : [],
                                        "cached_codes_count" : 0};
            }
            result = _tagsInfoCache[tag];
            codes = result["codes"];
            codeIndex = (page-1) * TAG_INFO_CODE_PAR_PAGE;
            for (i=0; i<imax; i++, codeIndex++) codes[codeIndex] = _codeInfo(list[i]);
            result["cached_codes_count"] += imax;
            return result;
        }
        
        
        
        
    // html parser sub routines
    //--------------------------------------------------
        // small code information
        private function _smallCodeInfo(atag:XML) : * { 
            var url:String = String(atag.@href),
                thumbBase:String = _thumbBaseURL(atag.img[0].@src),
                date:String = date$;
            return {"thumb"     : thumbBase + ".jpg?t=" + date, 
                    "thumb_w"   : thumbBase + "_w.jpg?t=" + date, 
                    "thumb_100" : thumbBase + "_100.jpg?t=" + date, 
                    "title" : String(atag.@title),
                    "url"   : url,
                    "id"    : url.substr(url.lastIndexOf("/")+1)};
        }
        
        
        // code information
        private function _codeInfo(elem:XML, tag:Boolean=true) : * { 
            var data:XMLList = $class(elem..ul, 'data')[0].li;
            var code:* = _smallCodeInfo($class(elem..p, 'thumb')[0].a[0]);
            code["pv"]     = int($class(elem..p, 'pv').a.span[0]);
            code["forked_count"]    = getNumber(data[0].a[0] || data[0]);
            code["favorited_count"] = getNumber(data[1].a[0] || data[1]);
            code["lines"]           = getNumber(data[2].a[0] || data[2]);
            if (tag) code["tags"] = (data.length() == 4) ? _tagInfo(data[3].ul.li) : [];
            else     code["diff"] = getNumber(data[2].strong[0].a[0] || data[2].strong[0]);
            return code;
        }
        
        
        // small user information
        private function _smallUserInfo(ue:XML) : * {
            return {"name" : String(ue.a[0].@title), 
                    "url"  : String(ue.a[0].@href),
                    "icon" : "http://wonderfl.net" + String(ue..img.@src)};
        }
        
        
        // tag infomation
        private function _tagInfo(te:XMLList) : Array { 
            var imax:int = te.length(), res:Array = new Array(imax), i:int;
            for (i=0; i<imax; i++) res[i] = String(te[i].a[0]);
            return res; 
        }
        
        
        // normalize thumbnail url
        private function _thumbBaseURL(url:String) : String {
            return url.replace(/_?(100|w)\.jpg(\?t=\d+)?$/, "");
        }
        
        
        
        
    // utilities
    //--------------------------------------------------
        /** load over crossdomain policy
         *  @param url url to load
         *  @param responseHandler function to deal result, function(data:*) : void
         *  @param parser function to parse, function(html:String, parserArgs:Array) : *
         *  @param parserArgs arguments for parser.
         */
        public function load(url:String, responseHandler:Function, parser:Function=null, parserArgs:Array=null) : URLLoader {
            var req:RequestHandler = new RequestHandler(url, responseHandler, _onRequestError, parser, parserArgs);
            if (apiEntry) return apiEntry._loadOnProxy(req);
            _suspendList.push(req);
            return req.loader;
        }
        
                
        /** add indent to json text */
        public function jsonIndentor(json:String, tabIndent:int=2) : String { 
            var indent:int = 0, tab:String = "", i:int;
            return json.replace(/[,[\]{}]/g, function(str:String, idx:int, tgt:String) : String {
                switch (str) {
                case "[": case "{": 
                    indent += tabIndent;
                    for (tab="", i=0; i<indent; i++) tab += " ";
                    break;
                case "]": case "}": 
                    indent -= tabIndent;
                    for (tab="", i=0; i<indent; i++) tab += " ";
                    break;
                }
                return str + "\n" + tab;
            });
        }
        
        
        /** decode HTML escape */
        public function decodeHtmlEscape(str:String) : String { 
            if (!_htmlEscapeRex) {
                var keys:Array=[];
                for (var key:String in _htmlEscapeHash) keys.push(key);
                _htmlEscapeRex = new RegExp("&(" + keys.join("|") + ";)", "g");
            }
            return str.replace(_htmlEscapeRex, function(s:String, c:String, i:int, t:String) : String {
                return (c in _htmlEscapeHash) ? _htmlEscapeHash[c] : s;
            });
        }
        protected var _htmlEscapeHash:* = { "nbsp":" ", "lt":"<" , "gt":">", "amp":"&", "quot":'"', "apos":"'" };
        private   var _htmlEscapeRex:RegExp = null;
        
        
        /** analyze libraries */
        public function analyzeLibraries(as3code:String) : * {
            if (!libraryPathesRegExp) {
                var keys:Array = [];
                for (var key:String in libraryPathes) keys.push(key.replace(/\./g, "\\."));
                libraryPathesRegExp = new RegExp("^\\s*import\\s+(" + keys.join("|") + ")", "gm");
            }
            var res:*, libs:* = {};
            while (res = libraryPathesRegExp.exec(as3code)) libs[libraryPathes[res[1]]] = true;
            return libs;
        }
        
        
        /** Unix time String */
        public function get date$() : String { return String(uint(new Date().time)); }
        
        
        // XHTML operators
        public function getElementByClass(xhtml:XMLList, className:String) : XMLList { return xhtml.(attribute('class').toString().search(className) != -1); }
        public function getNumber(str:String) : int { return int(str.match(/\d+/)[0]); }
        public function getString(str:String) : String { return /:\s*(.+)/.exec(str)[1]; }
        public function $class(xhtml:XMLList, className:String) : XMLList { return xhtml.(attribute('class') == className); }
        public function $id(xhtml:XMLList, idName:String) : XMLList { return xhtml.(attribute('id') == idName); }
        
        
        
        
    // internal use
    //--------------------------------------------------
        // convert html to xhtml
        private function _cutout(html:String, start:String="<body", end:String="</body>") : XML {
            var startIndex:int = html.indexOf(start);
            if (startIndex == -1) return null;
            var endIndex:int = html.indexOf(end, startIndex);
            if (endIndex == -1) return null;
            return new XML(html.substring(startIndex, endIndex + end.length));
        }
        
        
        // Google proxy Copyright wh0 ( http://wonderfl.net/c/gJXA )
        private function _proxyRequest(url:String) : URLRequest {
            return new URLRequest(proxyURL + encodeURIComponent(url));
        }
        
        
        /** @private */
        public function _loadOnProxy(request:*) : URLLoader {
            request.loader.load(_proxyRequest(request.url+"&t=" + date$));
            return request.loader;
        }
        
        
        //
        private function _onRequestError(request:RequestHandler) : Boolean {
            if (++request.trialCount == maxTrialCount) return true;
            apiEntry._loadOnProxy(request);
            return false;
        }
    }
}



import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.net.URLLoader;
import com.bit101.components.*;


// request handler
class RequestHandler {
    public var url:String;
    public var trialCount:int = 0;
    public var loader:URLLoader = new URLLoader();
    private var _responseHandler:Function, _errorHandler:Function;
    private var _parser:Function;
    private var _parserArgs:Array;
    function RequestHandler(url:String, responseHandler:Function, errorHandler:Function, parser:Function, parserArgs:Array) {
        this.url = url;
        _responseHandler = responseHandler;
        _errorHandler = errorHandler;
        _parser = parser || _defaultParser;
        _parserArgs = parserArgs || [];
        loader.addEventListener(Event.COMPLETE, onLoaded);
        loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
        loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
    }
    public function onLoaded(e:Event) : void {
        var data:* = _parser(e.target.data, _parserArgs);
        if ("error" in data) _error(data["error"]);
        else {
            _responseHandler(data);
            _removeAllHandlers();
        }
    }
    public function onError(e:ErrorEvent)  : void { _error(e.text); }
    private function _defaultParser(html:String, args:Array) : * { return html; }
    private function _error(errorText:String) : void {
         if (_errorHandler(this)) {
             _responseHandler({"error" : errorText, "trial_count" : trialCount});
             _removeAllHandlers();
         }
    }
    private function _removeAllHandlers() : void {
        loader.removeEventListener(Event.COMPLETE, onLoaded);
        loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
        loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
    }
}


// test
var apiIndex:int = 0;
var page:InputText;
var order:ComboBox;
var days:ComboBox;
var userID:InputText;
var type:ComboBox;
var codeID:InputText;
var tag:InputText;
var showhtml:CheckBox;
var getter:PushButton;

function _test(api:*) : void {
    var parent:Sprite = api, 
        tf:TextField, 
        p1:HBox = new HBox(parent, 60, 20), 
        p3:HBox = new HBox(parent, 60, 20), 
        p4:HBox = new HBox(parent, 60, 20),
        p5:HBox = new HBox(parent, 60, 20);
    p3.visible = p4.visible = p5.visible = !(p1.visible = true);
    Component.initStage(parent.stage);
    tf = new TextField();
    tf.y = 40;
    tf.width = parent.stage.stageWidth;
    tf.height = parent.stage.stageHeight - tf.y;
    tf.multiline = true;
    tf.defaultTextFormat = new TextFormat("_sans", 12);
    
    tf.text = parent.loaderInfo.url; //parent.proxyURL + encodeURIComponent(parent.loaderInfo.url);
    parent.addChild(tf);

    _hradio(parent, 0, 0, ["UserRanking", "CodeRanking", "TagCloud", "UserInfo", "CodeInfo", "TagInfo"], "api", function(e:Event) : void {
        switch (apiIndex = e.target.tag) {
        case 0: 
        case 1: p3.visible = p4.visible = p5.visible = !(p1.visible = true); break;
        case 2: p3.visible = p4.visible = p5.visible = p1.visible = false;   break;
        case 3: p1.visible = p4.visible = p5.visible = !(p3.visible = true); break;
        case 4: p3.visible = p1.visible = p5.visible = !(p4.visible = true); break;
        case 5: p3.visible = p4.visible = p1.visible = !(p5.visible = true); break;
        }
    });
    new Label(parent, 0, 20, "page");
    page = new InputText(parent, 24, 22, "1");
    page.width = 34;
    getter = new PushButton(parent, 400, 20, "getData", _getData);
    getter.width = 65;
    showhtml = new CheckBox(parent, 350, 24, "html");
    showhtml.width = 50;
    order = new ComboBox(p1, 0, 0, "ORDER_FAVORITE");
    order.items = ["ORDER_PV", "ORDER_FAVORITE", "ORDER_FORKED"];
    order.selectedIndex = 1;
    days  = new ComboBox(p1, 0, 0, "DAYS_7DAYS");
    days.items = ["DAYS_1DAY", "DAYS_7DAYS", "DAYS_ALL"];
    days.selectedIndex = 1;
    userID = new InputText(p3, 0, 0, parent.loaderInfo.parameters["viewer.displayName"]);
    type = new ComboBox(p3, 0, 0, "USER_INFO_ACTIVITY");
    type.items = ["USER_INFO_ACTIVITY", "USER_INFO_CODE", "USER_INFO_FAVORITE", "USER_INFO_FOLLOWER"];
    type.selectedIndex = 0;
    codeID = new InputText(p4, 0, 0, "krt3");
    tag    = new InputText(p5, 0, 0, "sion");
    
    function _hradio(doc:DisplayObjectContainer, xpos:Number, ypos:Number, labels:Array, groupName:String, selected:Function) : HBox {
        var hbox:HBox = new HBox(doc, xpos, ypos), i:int, imax:int = labels.length;
        for (i=0; i<imax; i++) {
            var rb:RadioButton = new RadioButton(hbox, 0, 0, labels[i], (i==0), selected);
            rb.groupName = groupName;
            rb.tag = i;
        }
        return hbox;
    }
    
    function _getData(e:Event) : void {
        var pageIndex:int = int(page.text) || 1;
        switch (apiIndex) {
        case 0: api.requestUserRanking(_response, api.DAYS_OPTIONS[days.selectedIndex], api.ORDER_OPTIONS[order.selectedIndex], pageIndex); break;
        case 1: api.requestCodeRanking(_response, api.DAYS_OPTIONS[days.selectedIndex], api.ORDER_OPTIONS[order.selectedIndex], pageIndex); break;
        case 2: api.requestTagCloud(_response); break;
        case 3: api.requestUserInfo(_response, userID.text, api.USER_INFO_OPTIONS[type.selectedIndex], pageIndex); break;
        case 4: api.requestCodeInfo(_response, codeID.text, api.ORDER_FAVORITE, pageIndex); break;
        case 5: api.requestTagInfo(_response, tag.text, pageIndex); break;
        }
    }
    
    function _response(data:*) : void {
        if (showhtml.selected) tf.text = data.html;
        else {
            delete data.html;
            if ("as3" in data) {
                data["library"] = api.analyzeLibraries(data["as3"]);
                data["as3"] = "...snip...";
            }
            tf.text = api.jsonIndentor(JSON.stringify(data));
        }
        api.clearCache();
    }
}