swf2js

swf2js is Flash Player Runtime Engine written in pure JavaScript

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/466069/1189642/swf2js.js

// @license MIT

/*jshint bitwise: false*/
/**
 * swf2js (version 0.7.24)
 * Develop: https://github.com/ienaga/swf2js
 * ReadMe: https://github.com/ienaga/swf2js/blob/master/README.md
 * Web: https://swf2js.wordpress.com
 * Contact: ienaga@tvon.jp
 * Copyright (c) 2013 Toshiyuki Ienaga. Licensed under the MIT License.
 */
if (!("swf2js" in window)){(function(window)
{
    var _document = window.document;
    var _Math = Math;
    var _min = _Math.min;
    var _max = _Math.max;
    var _floor = _Math.floor;
    var _ceil = _Math.ceil;
    var _pow = _Math.pow;
    var _random = _Math.random;
    var _atan2 = _Math.atan2;
    var _sqrt = _Math.sqrt;
    var _cos = _Math.cos;
    var _sin = _Math.sin;
    var _log = _Math.log;
    var _abs = _Math.abs;
    var _SQRT2 = _Math.SQRT2;
    var _LN2 = _Math.LN2;
    var _LN2_2 = _LN2 / 2;
    var _LOG1P = 0.29756328478758615;
    var _PI = _Math.PI;
    var _Number = Number;
    var _fromCharCode = String.fromCharCode;
    var _isNaN = isNaN;
    var _setTimeout = setTimeout;
    var _clearTimeout = clearTimeout;
    var _setInterval = setInterval;
    var _clearInterval = clearInterval;
    var Func = Function;
    var alert = window.alert;
    var console = window.console;
    var isBtoa = ("btoa" in window);
    var isWebGL = (window.WebGLRenderingContext &&
        _document.createElement("canvas").getContext("webgl")) ? true : false;
    isWebGL = false; // TODO
    var requestAnimationFrame =
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.setTimeout;

    // params
    var resizeId = 0;
    var stageId = 0;
    var stages = [];
    var loadStages = [];
    var instanceId = 0;
    var tmpContext;
    var StartDate = new Date();
    var _navigator = window.navigator;
    var ua = _navigator.userAgent;
    var isAndroid = (ua.indexOf("Android") > 0);
    var isAndroid4x = (ua.indexOf("Android 4.") > 0);
    var isiOS = (ua.indexOf("iPhone") > 0 || ua.indexOf("iPod") > 0);
    var isChrome = (ua.indexOf("Chrome") > 0);
    var isTouch = (isAndroid || isiOS);
    var xmlHttpRequest = new XMLHttpRequest();
    var isXHR2 = (typeof xmlHttpRequest.responseType !== "undefined");
    var isArrayBuffer = window.ArrayBuffer;
    var quality = (isWebGL) ? 1 : 0.8;
    var devicePixelRatio = window.devicePixelRatio || 1;
    var _devicePixelRatio = devicePixelRatio * quality;
    var _event = null;
    var _keyEvent = null;
    var startEvent = "mousedown";
    var moveEvent = "mousemove";
    var endEvent = "mouseup";
    if (isTouch) {
        startEvent = "touchstart";
        moveEvent = "touchmove";
        endEvent = "touchend";
    }

    // Alpha Bug
    var isAlphaBug = isAndroid;
    var chkCanvas = _document.createElement("canvas");
    chkCanvas.width = 1;
    chkCanvas.height = 1;
    tmpContext = chkCanvas.getContext("2d");
    if (isAndroid) {
        var imageData = tmpContext.createImageData(1, 1);
        var pixelArray = imageData.data;
        pixelArray[0] = 128;
        pixelArray[3] = 128;
        tmpContext.putImageData(imageData, 0, 0);
        imageData = tmpContext.getImageData(0, 0, 1, 1);
        pixelArray = imageData.data;
        isAlphaBug = (pixelArray[0] === 255);
        imageData = null;
        pixelArray = null;
    }

    if (typeof Object.defineProperty !== "function") {
        Object.defineProperty = function (obj, prop, desc)
        {
            if ("value" in desc) {
                obj[prop] = desc.value;
            }
            if ("get" in desc) {
                obj.__defineGetter__(prop, desc.get);
            }
            if ("set" in desc) {
                obj.__defineSetter__(prop, desc.set);
            }
            return obj;
        };
    }

    if (typeof Object.defineProperties !== "function") {
        Object.defineProperties = function (obj, descs)
        {
            for (var prop in descs) {
                if (descs.hasOwnProperty(prop)) {
                    Object.defineProperty(obj, prop, descs[prop]);
                }
            }
            return obj;
        };
    }

    if (typeof Object.getPrototypeOf !== "function") {
        Object.getPrototypeOf = function (obj)
        {
            return obj.__proto__;
        };
    }

    if (typeof Object.setPrototypeOf !== "function") {
        Object.setPrototypeOf = function (obj, proto) {
            obj.__proto__ = proto;
            return obj;
        };
    }

    // shift-jis
    var JCT11280 = new Func('var a="zKV33~jZ4zN=~ji36XazM93y!{~k2y!o~k0ZlW6zN?3Wz3W?{EKzK[33[`y|;-~j^YOTz$!~kNy|L1$353~jV3zKk3~k-4P4zK_2+~jY4y!xYHR~jlz$_~jk4z$e3X5He<0y!wy|X3[:~l|VU[F3VZ056Hy!nz/m1XD61+1XY1E1=1y|bzKiz!H034zKj~mEz#c5ZA3-3X$1~mBz$$3~lyz#,4YN5~mEz#{ZKZ3V%7Y}!J3X-YEX_J(3~mAz =V;kE0/y|F3y!}~m>z/U~mI~j_2+~mA~jp2;~m@~k32;~m>V}2u~mEX#2x~mBy+x2242(~mBy,;2242(~may->2&XkG2;~mIy-_2&NXd2;~mGz,{4<6:.:B*B:XC4>6:.>B*BBXSA+A:X]E&E<~r#z+625z s2+zN=`HXI@YMXIAXZYUM8X4K/:Q!Z&33 3YWX[~mB`{zKt4z (zV/z 3zRw2%Wd39]S11z$PAXH5Xb;ZQWU1ZgWP%3~o@{Dgl#gd}T){Uo{y5_d{e@}C(} WU9|cB{w}bzvV|)[} H|zT}d||0~{]Q|(l{|x{iv{dw}(5}[Z|kuZ }cq{{y|ij}.I{idbof%cu^d}Rj^y|-M{ESYGYfYsZslS`?ZdYO__gLYRZ&fvb4oKfhSf^d<Yeasc1f&a=hnYG{QY{D`Bsa|u,}Dl|_Q{C%xK|Aq}C>|c#ryW=}eY{L+`)][YF_Ub^h4}[X|?r|u_ex}TL@YR]j{SrXgo*|Gv|rK}B#mu{R1}hs|dP{C7|^Qt3|@P{YVV |8&}#D}ef{e/{Rl|>Hni}R1{Z#{D[}CQlQ||E}[s{SG_+i8eplY[=[|ec[$YXn#`hcm}YR|{Ci(_[ql|?8p3]-}^t{wy}4la&pc|3e{Rp{LqiJ],] `kc(]@chYnrM`O^,ZLYhZB]ywyfGY~aex!_Qww{a!|)*lHrM{N+n&YYj~Z b c#e_[hZSon|rOt`}hBXa^i{lh|<0||r{KJ{kni)|x,|0auY{D!^Sce{w;|@S|cA}Xn{C1h${E]Z-XgZ*XPbp]^_qbH^e[`YM|a||+=]!Lc}]vdBc=j-YSZD]YmyYLYKZ9Z>Xcczc2{Yh}9Fc#Z.l{}(D{G{{mRhC|L3b#|xK[Bepj#ut`H[,{E9Yr}1b{[e]{ZFk7[ZYbZ0XL]}Ye[(`d}c!|*y`Dg=b;gR]Hm=hJho}R-[n}9;{N![7k_{UbmN]rf#pTe[x8}!Qcs_rs[m`|>N}^V})7{^r|/E}),}HH{OYe2{Skx)e<_.cj.cjoMhc^d}0uYZd!^J_@g,[[[?{i@][|3S}Yl3|!1|eZ|5IYw|1D}e7|Cv{OHbnx-`wvb[6[4} =g+k:{C:}ed{S]|2M]-}WZ|/q{LF|dYu^}Gs^c{Z=}h>|/i|{W]:|ip{N:|zt|S<{DH[p_tvD{N<[8Axo{X4a.^o^X>Yfa59`#ZBYgY~_t^9`jZHZn`>G[oajZ;X,i)Z.^~YJe ZiZF^{][[#Zt^|]Fjx]&_5dddW]P0C[-]}]d|y {C_jUql] |OpaA[Z{lp|rz}:Mu#]_Yf6{Ep?f5`$[6^D][^u[$[6^.Z8]]ePc2U/=]K^_+^M{q*|9tYuZ,s(dS{i=|bNbB{uG}0jZOa:[-]dYtu3]:]<{DJ_SZIqr_`l=Yt`gkTnXb3d@kiq0a`Z{|!B|}e}Ww{Sp,^Z|0>_Z}36|]A|-t}lt{R6pi|v8hPu#{C>YOZHYmg/Z4nicK[}hF_Bg|YRZ7c|crkzYZY}_iXcZ.|)U|L5{R~qi^Uga@Y[xb}&qdbd6h5|Btw[}c<{Ds53[Y7]?Z<|e0{L[ZK]mXKZ#Z2^tavf0`PE[OSOaP`4gi`qjdYMgys/?[nc,}EEb,eL]g[n{E_b/vcvgb.{kcwi`~v%|0:|iK{Jh_vf5lb}KL|(oi=LrzhhY_^@`zgf[~g)[J_0fk_V{T)}I_{D&_/d9W/|MU[)f$xW}?$xr4<{Lb{y4}&u{XJ|cm{Iu{jQ}CMkD{CX|7A}G~{kt)nB|d5|<-}WJ}@||d@|Iy}Ts|iL|/^|no|0;}L6{Pm]7}$zf:|r2}?C_k{R(}-w|`G{Gy[g]bVje=_0|PT{^Y^yjtT[[[l!Ye_`ZN]@[n_)j3nEgMa]YtYpZy].d-Y_cjb~Y~[nc~sCi3|zg}B0}do{O^{|$`_|D{}U&|0+{J3|8*]iayx{a{xJ_9|,c{Ee]QXlYb]$[%YMc*]w[aafe]aVYi[fZEii[xq2YQZHg]Y~h#|Y:thre^@^|_F^CbTbG_1^qf7{L-`VFx Zr|@EZ;gkZ@slgko`[e}T:{Cu^pddZ_`yav^Ea+[#ZBbSbO`elQfLui}.F|txYcbQ`XehcGe~fc^RlV{D_0ZAej[l&jShxG[ipB_=u:eU}3e8[=j|{D(}dO{Do[BYUZ0/]AYE]ALYhZcYlYP/^-^{Yt_1_-;YT`P4BZG=IOZ&]H[e]YYd[9^F[1YdZxZ?Z{Z<]Ba2[5Yb[0Z4l?]d_;_)a?YGEYiYv`_XmZs4ZjY^Zb]6gqGaX^9Y}dXZr[g|]Y}K aFZp^k^F]M`^{O1Ys]ZCgCv4|E>}8eb7}l`{L5[Z_faQ|c2}Fj}hw^#|Ng|B||w2|Sh{v+[G}aB|MY}A{|8o}X~{E8paZ:]i^Njq]new)`-Z>haounWhN}c#{DfZ|fK]KqGZ=:u|fqoqcv}2ssm}.r{]{nIfV{JW)[K|,Z{Uxc|]l_KdCb%]cfobya3`p}G^|LZiSC]U|(X|kBlVg[kNo({O:g:|-N|qT}9?{MBiL}Sq{`P|3a|u.{Uaq:{_o|^S}jX{Fob0`;|#y_@[V[K|cw[<_ }KU|0F}d3|et{Q7{LuZttsmf^kYZ`Af`}$x}U`|Ww}d]| >}K,r&|XI|*e{C/a-bmr1fId4[;b>tQ_:]hk{b-pMge]gfpo.|(w[jgV{EC1Z,YhaY^q,_G[c_g[J0YX]`[h^hYK^_Yib,` {i6vf@YM^hdOKZZn(jgZ>bzSDc^Z%[[o9[2=/YHZ(_/Gu_`*|8z{DUZxYt^vuvZjhi^lc&gUd4|<UiA`z]$b/Z?l}YI^jaHxe|;F}l${sQ}5g}hA|e4}?o{ih}Uz{C)jPe4]H^J[Eg[|AMZMlc}:,{iz}#*|gc{Iq|/:|zK{l&}#u|myd{{M&v~nV};L|(g|I]ogddb0xsd7^V})$uQ{HzazsgxtsO^l}F>ZB]r|{7{j@cU^{{CbiYoHlng]f+nQ[bkTn/}<-d9q {KXadZYo+n|l[|lc}V2{[a{S4Zam~Za^`{HH{xx_SvF|ak=c^[v^7_rYT`ld@]:_ub%[$[m](Shu}G2{E.ZU_L_R{tz`vj(f?^}hswz}GdZ}{S:h`aD|?W|`dgG|if{a8|J1{N,}-Ao3{H#{mfsP|[ bzn+}_Q{MT{u4kHcj_q`eZj[8o0jy{p7}C|[}l){MuYY{|Ff!Ykn3{rT|m,^R|,R}$~Ykgx{P!]>iXh6[l[/}Jgcg{JYZ.^qYfYIZl[gZ#Xj[Pc7YyZD^+Yt;4;`e8YyZVbQ7YzZxXja.7SYl[s]2^/Ha$[6ZGYrb%XiYdf2]H]kZkZ*ZQ[ZYS^HZXcCc%Z|[(bVZ]]:OJQ_DZCg<[,]%Zaa [g{C00HY[c%[ChyZ,Z_`PbXa+eh`^&jPi0a[ggvhlekL]w{Yp^v}[e{~;k%a&k^|nR_z_Qng}[E}*Wq:{k^{FJZpXRhmh3^p>de^=_7`|ZbaAZtdhZ?n4ZL]u`9ZNc3g%[6b=e.ZVfC[ZZ^^^hD{E(9c(kyZ=bb|Sq{k`|vmr>izlH[u|e`}49}Y%}FT{[z{Rk}Bz{TCc/lMiAqkf(m$hDc;qooi[}^o:c^|Qm}a_{mrZ(pA`,}<2sY| adf_%|}`}Y5U;}/4|D>|$X{jw{C<|F.hK|*A{MRZ8Zsm?imZm_?brYWZrYx`yVZc3a@f?aK^ojEd {bN}/3ZH]/$YZhm^&j 9|(S|b]mF}UI{q&aM]LcrZ5^.|[j`T_V_Gak}9J[ ZCZD|^h{N9{~&[6Zd{}B}2O|cv]K}3s}Uy|l,fihW{EG`j_QOp~Z$F^zexS`dcISfhZBXP|.vn|_HYQ|)9|cr]<`&Z6]m_(ZhPcSg>`Z]5`~1`0Xcb4k1{O!bz|CN_T{LR|a/gFcD|j<{Z._[f)mPc:1`WtIaT1cgYkZOaVZOYFrEe[}T$}Ch}mk{K-^@]fH{Hdi`c*Z&|Kt{if[C{Q;{xYB`dYIX:ZB[}]*[{{p9|4GYRh2ao{DS|V+[zd$`F[ZXKadb*A] Ys]Maif~a/Z2bmclb8{Jro_rz|x9cHojbZ{GzZx_)]:{wAayeDlx}<=`g{H1{l#}9i|)=|lP{Qq}.({La|!Y{i2EZfp=c*}Cc{EDvVB|;g}2t{W4av^Bn=]ri,|y?|3+}T*ckZ*{Ffr5e%|sB{lx^0]eZb]9[SgAjS_D|uHZx]dive[c.YPkcq/}db{EQh&hQ|eg}G!ljil|BO]X{Qr_GkGl~YiYWu=c3eb}29v3|D|}4i||.{Mv})V{SP1{FX}CZW6{cm|vO{pS|e#}A~|1i}81|Mw}es|5[}3w{C`h9aL]o{}p[G`>i%a1Z@`Ln2bD[$_h`}ZOjhdTrH{[j_:k~kv[Sdu]CtL}41{I |[[{]Zp$]XjxjHt_eThoa#h>sSt8|gK|TVi[Y{t=}Bs|b7Zpr%{gt|Yo{CS[/{iteva|cf^hgn}($_c^wmb^Wm+|55jrbF|{9^ q6{C&c+ZKdJkq_xOYqZYSYXYl`8]-cxZAq/b%b*_Vsa[/Ybjac/OaGZ4fza|a)gY{P?| I|Y |,pi1n7}9bm9ad|=d{aV|2@[(}B`d&|Uz}B}{`q|/H|!JkM{FU|CB|.{}Az}#P|lk}K{|2rk7{^8^?`/|k>|Ka{Sq}Gz}io{DxZh[yK_#}9<{TRdgc]`~Z>JYmYJ]|`!ZKZ]gUcx|^E[rZCd`f9oQ[NcD_$ZlZ;Zr}mX|=!|$6ZPZYtIo%fj}CpcN|B,{VDw~gb}@hZg`Q{LcmA[(bo`<|@$|o1|Ss}9Z_}tC|G`{F/|9nd}i=}V-{L8aaeST]daRbujh^xlpq8|}zs4bj[S`J|]?G{P#{rD{]I`OlH{Hm]VYuSYUbRc*6[j`8]pZ[bt_/^Jc*[<Z?YE|Xb|?_Z^Vcas]h{t9|Uwd)_(=0^6Zb{Nc} E[qZAeX[a]P^|_J>e8`W^j_Y}R{{Jp__]Ee#e:iWb9q_wKbujrbR}CY`,{mJ}gz{Q^{t~N|? gSga`V_||:#mi}3t|/I`X{N*|ct|2g{km}gi|{={jC}F;|E}{ZZjYf*frmu}8Tdroi{T[|+~}HG{cJ}DM{Lp{Ctd&}$hi3|FZ| m}Kr|38}^c|m_|Tr{Qv|36}?Up>|;S{DV{k_as}BK{P}}9p|t`jR{sAm4{D=b4pWa[}Xi{EjwEkI}3S|E?u=X0{jf} S|NM|JC{qo^3cm]-|JUx/{Cj{s>{Crt[UXuv|D~|j|d{YXZR}Aq}0r}(_{pJfi_z}0b|-vi)Z mFe,{f4|q`b{}^Z{HM{rbeHZ|^x_o|XM|L%|uFXm}@C_{{Hhp%a7|0p[Xp+^K}9U{bP}: tT}B|}+$|b2|[^|~h{FAby[`{}xgygrt~h1[li`c4vz|,7p~b(|mviN}^pg[{N/|g3|^0c,gE|f%|7N{q[|tc|TKA{LU}I@|AZp(}G-sz{F |qZ{}F|f-}RGn6{Z]_5})B}UJ{FFb2]4ZI@v=k,]t_Dg5Bj]Z-]L]vrpdvdGlk|gF}G]|IW}Y0[G| /bo|Te^,_B}#n^^{QHYI[?hxg{[`]D^IYRYTb&kJ[cri[g_9]Ud~^_]<p@_e_XdNm-^/|5)|h_{J;{kacVopf!q;asqd}n)|.m|bf{QW|U)}b+{tL|w``N|to{t ZO|T]jF}CB|0Q{e5Zw|k |We}5:{HO{tPwf_uajjBfX}-V_C_{{r~gg|Ude;s+}KNXH}! `K}eW{Upwbk%ogaW}9EYN}YY|&v|SL{C3[5s.]Y]I]u{M6{pYZ`^,`ZbCYR[1mNg>rsk0Ym[jrE]RYiZTr*YJ{Ge|%-lf|y(`=[t}E6{k!|3)}Zk} ][G{E~cF{u3U.rJ|a9p#o#ZE|?|{sYc#vv{E=|LC}cu{N8`/`3`9rt[4|He{cq|iSYxY`}V |(Q|t4{C?]k_Vlvk)BZ^r<{CL}#h}R+[<|i=}X|{KAo]|W<`K{NW|Zx}#;|fe{IMr<|K~tJ_x}AyLZ?{GvbLnRgN}X&{H7|x~}Jm{]-| GpNu0}.ok>|c4{PYisrDZ|fwh9|hfo@{H~XSbO]Odv]%`N]b1Y]]|eIZ}_-ZA]aj,>eFn+j[aQ_+]h[J_m_g]%_wf.`%k1e#Z?{CvYu_B^|gk`Xfh^M3`afGZ-Z|[m{L}|k3cp[it ^>YUi~d>{T*}YJ{Q5{Jxa$hg|%4`}|LAgvb }G}{P=|<;Ux{_skR{cV|-*|s-{Mp|XP|$G|_J}c6cM{_=_D|*9^$ec{V;|4S{qO|w_|.7}d0|/D}e}|0G{Dq]Kdp{}dfDi>}B%{Gd|nl}lf{C-{y}|ANZr}#={T~|-(}c&{pI|ft{lsVP}){|@u}!W|bcmB{d?|iW|:dxj{PSkO|Hl]Li:}VYk@|2={fnWt{M3`cZ6|)}|Xj}BYa?vo{e4|L7|B7{L7|1W|lvYO}W8nJ|$Vih|{T{d*_1|:-n2dblk``fT{Ky|-%}m!|Xy|-a{Pz}[l{kFjz|iH}9N{WE{x,|jz}R {P|{D)c=nX|Kq|si}Ge{sh|[X{RF{t`|jsr*fYf,rK|/9}$}}Nf{y!1|<Std}4Wez{W${Fd_/^O[ooqaw_z[L`Nbv[;l7V[ii3_PeM}.h^viqYjZ*j1}+3{bt{DR[;UG}3Og,rS{JO{qw{d<_zbAh<R[1_r`iZTbv^^a}c{iEgQZ<exZFg.^Rb+`Uj{a+{z<[~r!]`[[|rZYR|?F|qppp]L|-d|}K}YZUM|=Y|ktm*}F]{D;g{uI|7kg^}%?Z%ca{N[_<q4xC]i|PqZC]n}.bDrnh0Wq{tr|OMn6tM|!6|T`{O`|>!]ji+]_bTeU}Tq|ds}n|{Gm{z,f)}&s{DPYJ`%{CGd5v4tvb*hUh~bf]z`jajiFqAii]bfy^U{Or|m+{I)cS|.9k:e3`^|xN}@Dnlis`B|Qo{`W|>||kA}Y}{ERYuYx`%[exd`]|OyiHtb}HofUYbFo![5|+]gD{NIZR|Go}.T{rh^4]S|C9_}xO^i`vfQ}C)bK{TL}cQ|79iu}9a];sj{P.o!f[Y]pM``Jda^Wc9ZarteBZClxtM{LW}l9|a.mU}KX}4@{I+f1}37|8u}9c|v${xGlz}jP{Dd1}e:}31}%3X$|22i<v+r@~mf{sN{C67G97855F4YL5}8f{DT|xy{sO{DXB334@55J1)4.G9A#JDYtXTYM4, YQD9;XbXm9SX]IB^4UN=Xn<5(;(F3YW@XkH-X_VM[DYM:5XP!T&Y`6|,^{IS-*D.H>:LXjYQ0I3XhAF:9:(==.F*3F1189K/7163D,:@|e2{LS36D4hq{Lw/84443@4.933:0307::6D7}&l{Mx657;89;,K5678H&93D(H<&<>0B90X^I;}Ag1{P%3A+>><975}[S{PZE453?4|T2{Q+5187;>447:81{C=hL6{Me^:=7ii{R=.=F<81;48?|h8}Uh{SE|,VxL{ST,7?9Y_5Xk3A#:$%YSYdXeKXOD8+TXh7(@>(YdXYHXl9J6X_5IXaL0N?3YK7Xh!1?XgYz9YEXhXaYPXhC3X`-YLY_XfVf[EGXZ5L8BXL9YHX]SYTXjLXdJ: YcXbQXg1PX]Yx4|Jr{Ys4.8YU+XIY`0N,<H%-H;:0@,74/:8546I=9177154870UC]d<C3HXl7ALYzXFXWP<<?E!88E5@03YYXJ?YJ@6YxX-YdXhYG|9o{`iXjY_>YVXe>AYFX[/(I@0841?):-B=14337:8=|14{c&93788|di{cW-0>0<097/A;N{FqYpugAFT%X/Yo3Yn,#=XlCYHYNX[Xk3YN:YRT4?)-YH%A5XlYF3C1=NWyY}>:74-C673<69545v {iT85YED=64=.F4..9878/D4378?48B3:7:7/1VX[f4{D,{l<5E75{dAbRB-8-@+;DBF/$ZfW8S<4YhXA.(5@*11YV8./S95C/0R-A4AXQYI7?68167B95HA1*<M3?1/@;/=54XbYP36}lc{qzSS38:19?,/39193574/66878Yw1X-87E6=;964X`T734:>86>1/=0;(I-1::7ALYGXhF+Xk[@W%TYbX7)KXdYEXi,H-XhYMRXfYK?XgXj.9HX_SX]YL1XmYJ>Y}WwIXiI-3-GXcYyXUYJ$X`Vs[7;XnYEZ;XF! 3;%8;PXX(N3Y[)Xi1YE&/ :;74YQ6X`33C;-(>Xm0(TYF/!YGXg8 9L5P01YPXO-5%C|qd{{/K/E6,=0144:361:955;6443@?B7*7:F89&F35YaX-CYf,XiFYRXE_e{}sF 0*7XRYPYfXa5YXXY8Xf8Y~XmA[9VjYj*#YMXIYOXk,HHX40YxYMXU8OXe;YFXLYuPXP?EB[QV0CXfY{:9XV[FWE0D6X^YVP*$4%OXiYQ(|xp|%c3{}V`1>Y`XH00:8/M6XhQ1:;3414|TE|&o@1*=81G8<3}6<|(f6>>>5-5:8;093B^3U*+*^*UT30XgYU&7*O1953)5@E78--F7YF*B&0:%P68W9Zn5974J9::3}Vk|-,C)=)1AJ4+<3YGXfY[XQXmT1M-XcYTYZXCYZXEYXXMYN,17>XIG*SaS|/eYJXbI?XdNZ+WRYP<F:R PXf;0Xg`$|1GX9YdXjLYxWX!ZIXGYaXNYm6X9YMX?9EXmZ&XZ#XQ>YeXRXfAY[4 ;0X!Zz0XdN$XhYL XIY^XGNXUYS/1YFXhYk.TXn4DXjB{jg|4DEX]:XcZMW=A.+QYL<LKXc[vV$+&PX*Z3XMYIXUQ:ZvW< YSXFZ,XBYeXMM)?Xa XiZ4/EXcP3%}&-|6~:1(-+YT$@XIYRBC<}&,|7aJ6}bp|8)K1|Xg|8C}[T|8Q.89;-964I38361<=/;883651467<7:>?1:.}le|:Z=39;1Y^)?:J=?XfLXbXi=Q0YVYOXaXiLXmJXO5?.SFXiCYW}-;|=u&D-X`N0X^,YzYRXO(QX_YW9`I|>hZ:N&X)DQXP@YH#XmNXi$YWX^=!G6YbYdX>XjY|XlX^XdYkX>YnXUXPYF)FXT[EVTMYmYJXmYSXmNXi#GXmT3X8HOX[ZiXN]IU2>8YdX1YbX<YfWuZ8XSXcZU%0;1XnXkZ_WTG,XZYX5YSX Yp 05G?XcYW(IXg6K/XlYP4XnI @XnO1W4Zp-9C@%QDYX+OYeX9>--YSXkD.YR%Q/Yo YUX].Xi<HYEZ2WdCE6YMXa7F)=,D>-@9/8@5=?7164;35387?N<618=6>7D+C50<6B03J0{Hj|N9$D,9I-,.KB3}m |NzE0::/81YqXjMXl7YG; [.W=Z0X4XQY]:MXiR,XgM?9$9>:?E;YE77VS[Y564760391?14941:0=:8B:;/1DXjFA-564=0B3XlH1+D85:0Q!B#:-6&N/:9<-R3/7Xn<*3J4.H:+334B.=>30H.;3833/76464665755:/83H6633:=;.>5645}&E|Y)?1/YG-,93&N3AE@5 <L1-G/8A0D858/30>8<549=@B8] V0[uVQYlXeD(P#ID&7T&7;Xi0;7T-$YE)E=1:E1GR):--0YI7=E<}n9|aT6783A>D7&4YG7=391W;Zx<5+>F#J39}o/|cc;6=A050EQXg8A1-}D-|d^5548083563695D?-.YOXd37I$@LYLWeYlX<Yd+YR A$;3-4YQ-9XmA0!9/XLY_YT(=5XdDI>YJ5XP1ZAW{9>X_6R(XhYO65&J%DA)C-!B:97#A9;@?F;&;(9=11/=657/H,<8}bz|j^5446>.L+&Y^8Xb6?(CYOXb*YF(8X`FYR(XPYVXmPQ%&DD(XmZXW??YOXZXfCYJ79,O)XnYF7K0!QXmXi4IYFRXS,6<%-:YO(+:-3Q!1E1:W,Zo}Am|n~;3580534*?3Zc4=9334361693:30C<6/717:<1/;>59&:4}6!|rS36=1?75<8}[B|s809983579I.A.>84758=108564741H*9E{L{|u%YQ<%6XfH.YUXe4YL@,>N}Tv|ve*G0X)Z;/)3@A74(4P&A1X:YVH97;,754*A66:1 D739E3553545558E4?-?K17/770843XAYf838A7K%N!YW4.$T19Z`WJ*0XdYJXTYOXNZ 1XaN1A+I&Xi.Xk3Z3GB&5%WhZ1+5#Y[X<4YMXhQYoQXVXbYQ8XSYUX4YXBXWDMG0WxZA[8V+Z8X;D],Va$%YeX?FXfX[XeYf<X:Z[WsYz8X_Y]%XmQ(!7BXIZFX]&YE3F$(1XgYgYE& +[+W!<YMYFXc;+PXCYI9YrWxGXY9DY[!GXiI7::)OC;*$.>N*HA@{C|}&k=:<TB83X`3YL+G4XiK]i}(fYK<=5$.FYE%4*5*H*6XkCYL=*6Xi6!Yi1KXR4YHXbC8Xj,B9ZbWx/XbYON#5B}Ue}+QKXnF1&YV5XmYQ0!*3IXBYb71?1B75XmF;0B976;H/RXU:YZX;BG-NXj;XjI>A#D3B636N;,*%<D:0;YRXY973H5)-4FXOYf0:0;/7759774;7;:/855:543L43<?6=E,.A4:C=L)%4YV!1(YE/4YF+ F3%;S;&JC:%/?YEXJ4GXf/YS-EXEYW,9;E}X$}547EXiK=51-?71C%?57;5>463553Zg90;6447?<>4:9.7538XgN{|!}9K/E&3-:D+YE1)YE/3;37/:05}n<}:UX8Yj4Yt864@JYK..G=.(A Q3%6K>3(P3#AYE$-6H/456*C=.XHY[#S.<780191;057C)=6HXj?955B:K1 E>-B/9,;5.!L?:0>/.@//:;7833YZ56<4:YE=/:7Z_WGC%3I6>XkC*&NA16X=Yz2$X:Y^&J48<99k8}CyB-61<18K946YO4{|N}E)YIB9K0L>4=46<1K0+R;6-=1883:478;4,S+3YJX`GJXh.Yp+Xm6MXcYpX(>7Yo,/:X=Z;Xi0YTYHXjYmXiXj;*;I-8S6N#XgY}.3XfYGO3C/$XjL$*NYX,1 6;YH&<XkK9C#I74.>}Hd`A748X[T450[n75<4439:18A107>|ET}Rf<1;14876/Yb983E<5.YNXd4149>,S=/4E/<306443G/06}0&}UkYSXFYF=44=-5095=88;63844,9E6644{PL}WA8:>)7+>763>>0/B3A545CCnT}Xm|dv}Xq1L/YNXk/H8;;.R63351YY747@15YE4J8;46;.38.>4A369.=-83,;Ye3?:3@YE.4-+N353;/;@(X[YYD>@/05-I*@.:551741Yf5>6A443<3535;.58/86=D4753442$635D1>0359NQ @73:3:>><Xn?;43C14 ?Y|X611YG1&<+,4<*,YLXl<1/AIXjF*N89A4Z576K1XbJ5YF.ZOWN.YGXO/YQ01:4G38Xl1;KI0YFXB=R<7;D/,/4>;$I,YGXm94@O35Yz66695385.>:6A#5}W7n^4336:4157597434433<3|XA}m`>=D>:4A.337370?-6Q96{`E|4A}C`|Qs{Mk|J+~r>|o,wHv>Vw}!c{H!|Gb|*Ca5}J||,U{t+{CN[!M65YXOY_*B,Y[Z9XaX[QYJYLXPYuZ%XcZ8LY[SYPYKZM<LMYG9OYqSQYM~[e{UJXmQYyZM_)>YjN1~[f3{aXFY|Yk:48YdH^NZ0|T){jVFYTZNFY^YTYN~[h{nPYMYn3I]`EYUYsYIZEYJ7Yw)YnXPQYH+Z.ZAZY]^Z1Y`YSZFZyGYHXLYG 8Yd#4~[i|+)YH9D?Y^F~Y7|-eYxZ^WHYdYfZQ~[j|3>~[k|3oYmYqY^XYYO=Z*4[]Z/OYLXhZ1YLZIXgYIHYEYK,<Y`YEXIGZI[3YOYcB4SZ!YHZ*&Y{Xi3~[l|JSY`Zz?Z,~[m|O=Yi>??XnYWXmYS617YVYIHZ(Z4[~L4/=~[n|Yu{P)|];YOHHZ}~[o33|a>~[r|aE]DH~[s|e$Zz~[t|kZFY~XhYXZB[`Y}~[u|{SZ&OYkYQYuZ2Zf8D~[v}% ~[w3},Q[X]+YGYeYPIS~[y}4aZ!YN^!6PZ*~[z}?E~[{3}CnZ=~[}}EdDZz/9A3(3S<,YR8.D=*XgYPYcXN3Z5 4)~[~}JW=$Yu.XX~] }KDX`PXdZ4XfYpTJLY[F5]X~[2Yp}U+DZJ::<446[m@~]#3}]1~]%}^LZwZQ5Z`/OT<Yh^ -~]&}jx[ ~m<z!%2+~ly4VY-~o>}p62yz!%2+Xf2+~ly4VY-zQ`z (=] 2z~o2",C={" ":0,"!":1},c=34,i=2,p,s="",u=String.fromCharCode,t=u(12539);for(;++c<127;)C[u(c)]=c^39&&c^92?i++:0;i=0;for(;0<=(c=C[a.charAt(i++)]);)if(16===c)if((c=C[a.charAt(i++)])<87){if(86===c)c=1879;for(;c--;)s+=u(++p)}else s+=s.substr(8272,360);else if(c<86)s+=u(p+=c<51?c-16:(c-55)*92+C[a.charAt(i++)]);else if((c=((c-86)*92+C[a.charAt(i++)])*92+C[a.charAt(i++)])<49152)s+=u(p=c<40960?c:c|57344);else{c&=511;for(;c--;)s+=t;p=12539}return s')();

    /**
     * resize
     */
    function resizeCanvas()
    {
        for (var i in stages) {
            if (!stages.hasOwnProperty(i)) {
                continue;
            }
            var stage = stages[i];
            if (!stage.isLoad) {
                continue;
            }
            stage.resize();
        }
    }

    /**
     * @param audio
     * @param soundInfo
     */
    function startSound(audio, soundInfo)
    {
        if (soundInfo.SyncStop) {
            audio.pause();
        } else {
            if (soundInfo.HasLoops) {
                audio.loopCount = soundInfo.LoopCount;
                var loopSound = function ()
                {
                    audio.loopCount--;
                    if (!this.loopCount) {
                        audio.removeEventListener("ended", loopSound);
                    } else {
                        audio.currentTime = 0;
                        if (soundInfo.HasInPoint) {
                            audio.currentTime = soundInfo.InPoint;
                        }
                        audio.play();
                    }
                };
                audio.addEventListener("ended", loopSound);
            }

            if (soundInfo.HasInPoint) {
                audio.addEventListener("canplay", function ()
                {
                    this.currentTime = soundInfo.InPoint;
                });
            }

            audio.play();
        }
    }

    /**
     * resize event
     */
    window.addEventListener("resize", function ()
    {
        _clearTimeout(resizeId);
        resizeId = _setTimeout(resizeCanvas, 300);
    });

    /**
     * unload event
     */
    window.addEventListener("unload", function ()
    {
        stages = void 0;
        loadStages = void 0;
    });

    /**
     * @constructor
     */
    var VectorToCanvas = function () {};

    /**
     * Function
     */
    VectorToCanvas.prototype.FUNCTION = Function;

    /**
     * @param src
     * @returns {{}}
     */
    VectorToCanvas.prototype.clone = function (src)
    {
        var execute = function (src, obj)
        {
            var prop;
            for (prop in src) {
                if (!src.hasOwnProperty(prop)) {
                    continue;
                }
                var value = src[prop];
                if (value instanceof Array) {
                    obj[prop] = [];
                    execute(value, obj[prop]);
                } else if (value instanceof Object) {
                    obj[prop] = {};
                    execute(value, obj[prop]);
                } else {
                    obj[prop] = value;
                }
            }
        };

        var obj = {};
        execute(src, obj);
        return obj;
    };

    /**
     * @param shapes
     * @param isMorph
     * @returns {Array}
     */
    VectorToCanvas.prototype.convert = function (shapes, isMorph)
    {
        var _this = this;
        var lineStyles = shapes.lineStyles.lineStyles;
        var fillStyles = shapes.fillStyles.fillStyles;
        var records = shapes.ShapeRecords;
        var idx = 0;
        var obj = {};
        var cache = [];
        var AnchorX = 0;
        var AnchorY = 0;
        var MoveX = 0;
        var MoveY = 0;
        var LineX = 0;
        var LineY = 0;
        var FillStyle0 = 0;
        var FillStyle1 = 0;
        var LineStyle = 0;
        var fills0 = [];
        var fills1 = [];
        var lines = [];
        var stack = [];
        var depth = 0;
        var length = records.length;
        for (var i = 0; i < length; i++) {
            var record = records[i];
            if (!record) {
                stack = _this.setStack(stack, _this.fillMerge(fills0, fills1, isMorph));
                stack = _this.setStack(stack, lines);
                break;
            }

            if (record.isChange) {
                depth++;
                if (record.StateNewStyles) {
                    AnchorX = 0;
                    AnchorY = 0;
                    stack = _this.setStack(stack, _this.fillMerge(fills0, fills1, isMorph));
                    stack = _this.setStack(stack, lines);
                    fills0 = [];
                    fills1 = [];
                    lines = [];

                    if (record.NumFillBits) {
                        fillStyles = record.FillStyles.fillStyles;
                    }
                    if (record.NumLineBits) {
                        lineStyles = record.LineStyles.lineStyles;
                    }
                }

                MoveX = AnchorX;
                MoveY = AnchorY;
                if (record.StateMoveTo) {
                    MoveX = record.MoveX;
                    MoveY = record.MoveY;
                }
                LineX = MoveX;
                LineY = MoveY;

                if (record.StateFillStyle0) {
                    FillStyle0 = record.FillStyle0;
                }
                if (record.StateFillStyle1) {
                    FillStyle1 = record.FillStyle1;
                }
                if (record.StateLineStyle) {
                    LineStyle = record.LineStyle;
                }
                continue;
            }

            AnchorX = record.AnchorX;
            AnchorY = record.AnchorY;
            var ControlX = record.ControlX;
            var ControlY = record.ControlY;
            var isCurved = record.isCurved;
            if (FillStyle0) {
                idx = FillStyle0 - 1;
                if (!(idx in fills0)) {
                    fills0[idx] = [];
                }

                if (!(depth in fills0[idx])) {
                    fills0[idx][depth] = {
                        obj: fillStyles[idx],
                        startX: MoveX,
                        startY: MoveY,
                        endX: 0,
                        endY: 0,
                        cache: []
                    };
                }

                obj = fills0[idx][depth];
                cache = obj.cache;
                cache[cache.length] = _this.clone(record);
                obj.endX = AnchorX;
                obj.endY = AnchorY;
            }

            if (FillStyle1) {
                idx = FillStyle1 - 1;
                if (!(idx in fills1)) {
                    fills1[idx] = [];
                }

                if (!(depth in fills1[idx])) {
                    fills1[idx][depth] = {
                        obj: fillStyles[idx],
                        startX: MoveX,
                        startY: MoveY,
                        endX: 0,
                        endY: 0,
                        cache: []
                    };
                }

                obj = fills1[idx][depth];
                cache = obj.cache;
                cache[cache.length] = _this.clone(record);
                obj.endX = AnchorX;
                obj.endY = AnchorY;
            }

            if (LineStyle) {
                idx = LineStyle - 1;
                if (!(idx in lines)) {
                    lines[idx] = {
                        obj: lineStyles[idx],
                        cache: []
                    };
                }

                obj = lines[idx];
                cache = obj.cache;
                cache[cache.length] = [0, LineX, LineY];
                var code = [2, AnchorX, AnchorY];
                if (isCurved) {
                    code = [1, ControlX, ControlY, AnchorX, AnchorY];
                }
                cache[cache.length] = code;
            }

            LineX = AnchorX;
            LineY = AnchorY;
        }

        return stack;
    };

    /**
     * @param fills0
     * @param fills1
     * @param isMorph
     * @returns {*}
     */
    VectorToCanvas.prototype.fillMerge = function (fills0, fills1, isMorph)
    {
        var _this = this;
        fills0 = _this.fillReverse(fills0);
        if (fills0.length) {
            for (var i in fills0) {
                if (!fills0.hasOwnProperty(i)) {
                    continue;
                }
                var fills = fills0[i];
                if (i in fills1) {
                    var fill1 = fills1[i];
                    for (var depth in fills) {
                        if (!fills.hasOwnProperty(depth)) {
                            continue;
                        }
                        fill1[fill1.length] = fills[depth];
                    }
                } else {
                    fills1[i] = fills;
                }
            }
        }
        return _this.coordinateAdjustment(fills1, isMorph);
    };

    /**
     * @param fills0
     * @returns {*}
     */
    VectorToCanvas.prototype.fillReverse = function (fills0)
    {
        if (!fills0.length) {
            return fills0;
        }

        for (var i in fills0) {
            if (!fills0.hasOwnProperty(i)) {
                continue;
            }
            var fills = fills0[i];
            for (var depth in fills) {
                if (!fills.hasOwnProperty(depth)) {
                    continue;
                }
                var AnchorX = 0;
                var AnchorY = 0;
                var obj = fills[depth];
                var cacheX = obj.startX;
                var cacheY = obj.startY;
                var cache = obj.cache;
                var length = cache.length;
                if (length) {
                    for (var idx in cache) {
                        if (!cache.hasOwnProperty(idx)) {
                            continue;
                        }
                        var recode = cache[idx];
                        AnchorX = recode.AnchorX;
                        AnchorY = recode.AnchorY;
                        recode.AnchorX = cacheX;
                        recode.AnchorY = cacheY;
                        cacheX = AnchorX;
                        cacheY = AnchorY;
                    }
                    var array = [];
                    if (length > 0) {
                        while (length--) {
                            array[array.length] = cache[length];
                        }
                    }
                    obj.cache = array;
                }

                cacheX = obj.startX;
                cacheY = obj.startY;
                obj.startX = obj.endX;
                obj.startY = obj.endY;
                obj.endX = cacheX;
                obj.endY = cacheY;
            }
        }
        return fills0;
    };

    /**
     * @param fills1
     * @param isMorph
     */
    VectorToCanvas.prototype.coordinateAdjustment = function (fills1, isMorph)
    {
        for (var i in fills1) {
            if (!fills1.hasOwnProperty(i)) {
                continue;
            }
            var array = [];
            var fills = fills1[i];

            for (var depth in fills) {
                if (!fills.hasOwnProperty(depth)) {
                    continue;
                }
                array[array.length] = fills[depth];
            }

            var adjustment = [];
            if (array.length > 1 && !isMorph) {
                while (true) {
                    if (!array.length) {
                        break;
                    }

                    var fill = array.shift();
                    if (fill.startX === fill.endX && fill.startY === fill.endY) {
                        adjustment[adjustment.length] = fill;
                        continue;
                    }

                    var mLen = array.length;
                    if (mLen < 0) {
                        break;
                    }

                    var isMatch = 0;
                    while (mLen--) {
                        var comparison = array[mLen];
                        if (comparison.startX === fill.endX && comparison.startY === fill.endY) {
                            fill.endX = comparison.endX;
                            fill.endY = comparison.endY;
                            var cache0 = fill.cache;
                            var cache1 = comparison.cache;
                            var cLen = cache1.length;
                            for (var cIdx = 0; cIdx < cLen; cIdx++) {
                                cache0[cache0.length] = cache1[cIdx];
                            }
                            array.splice(mLen, 1);
                            array.unshift(fill);
                            isMatch = 1;
                            break;
                        }
                    }

                    if (!isMatch) {
                        array.unshift(fill);
                    }
                }
            } else {
                adjustment = array;
            }

            var aLen = adjustment.length;
            var cache = [];
            var obj = {};
            for (var idx = 0; idx < aLen; idx++) {
                var data = adjustment[idx];
                obj = data.obj;
                var caches = data.cache;
                var cacheLength = caches.length;
                cache[cache.length] = [0, data.startX, data.startY];
                for (var compIdx = 0; compIdx < cacheLength; compIdx++) {
                    var r = caches[compIdx];
                    var code = [2, r.AnchorX, r.AnchorY];
                    if (r.isCurved) {
                        code = [1, r.ControlX, r.ControlY, r.AnchorX, r.AnchorY];
                    }
                    cache[cache.length] = code;
                }
            }

            fills1[i] = {cache: cache, obj: obj};
        }
        return fills1;
    };

    /**
     * @param stack
     * @param array
     * @returns {*}
     */
    VectorToCanvas.prototype.setStack = function (stack, array)
    {
        var _this = this;
        var _buildCommand = _this.buildCommand;
        if (array.length) {
            for (var i in array) {
                if (!array.hasOwnProperty(i)) {
                    continue;
                }
                var data = array[i];
                stack[stack.length] = {
                    obj: data.obj,
                    cmd: _buildCommand.call(_this, data.cache)
                };
            }
        }
        return stack;
    };

    /**
     * @param cache
     * @returns {*}
     */
    VectorToCanvas.prototype.buildCommand = function (cache)
    {
        var _this = this;
        if (isWebGL) {
            return _this.toCanvas2D(cache); // TODO toWebGL
        } else {
            return _this.toCanvas2D(cache);
        }
    };

    /**
     * @param cache
     * @returns {*}
     */
    VectorToCanvas.prototype.toCanvas2D = function (cache)
    {
        var length = cache.length;
        var str = "";
        var i = 0;
        while (i < length) {
            var a = cache[i];
            switch (a[0]) {
                case 0:
                    str += "ctx.moveTo(" + a[1] + "," + a[2] + ");";
                    break;
                case 1:
                    str += "ctx.quadraticCurveTo(" + a[1] + "," + a[2] + "," + a[3] + "," + a[4] + ");";
                    break;
                case 2:
                    str += "ctx.lineTo(" + a[1] + "," + a[2] + ");";
                    break;
                case 3:
                    str += "ctx.bezierCurveTo(" + a[1] + "," + a[2] + "," + a[3] + "," + a[4] + "," + a[5] + "," + a[6] + ");";
                    break;
                case 4:
                    str += "ctx.moveTo(" + (a[1] + a[3]) + "," + a[2] + ");";
                    str += "ctx.arc(" + a[1] + "," + a[2] + "," + a[3] + ",0 , Math.PI*2, false);";
                    break;

                // Graphics
                case 5: // fillStyle
                    str += "var r = Math.max(0, Math.min(("+ a[1] +" * ct[0]) + ct[4], 255))|0;";
                    str += "var g = Math.max(0, Math.min(("+ a[2] +" * ct[1]) + ct[5], 255))|0;";
                    str += "var b = Math.max(0, Math.min(("+ a[3] +" * ct[2]) + ct[6], 255))|0;";
                    str += "var a = Math.max(0, Math.min(("+ a[4] +" * 255 * ct[3]) + ct[7], 255)) / 255;";
                    str += "ctx.fillStyle = 'rgba('+r+', '+g+', '+b+', '+a+')';";
                    break;
                case 6: // strokeStyle
                    str += "var r = Math.max(0, Math.min(("+ a[1] +" * ct[0]) + ct[4], 255))|0;";
                    str += "var g = Math.max(0, Math.min(("+ a[2] +" * ct[1]) + ct[5], 255))|0;";
                    str += "var b = Math.max(0, Math.min(("+ a[3] +" * ct[2]) + ct[6], 255))|0;";
                    str += "var a = Math.max(0, Math.min(("+ a[4] +" * 255 * ct[3]) + ct[7], 255)) / 255;";
                    str += "ctx.strokeStyle = 'rgba('+r+', '+g+', '+b+', '+a+')';";
                    break;
                case 7: // fill
                    str += "if (!isClip) { ctx.fill(); }";
                    break;
                case 8: // stroke
                    str += "if (!isClip) { ctx.stroke(); }";
                    break;
                case 9: // width
                    str += "ctx.lineWidth = "+ a[1] +";";
                    break;
                case 10: // lineCap
                    str += "ctx.lineCap = '"+ a[1] +"';";
                    break;
                case 11: // lineJoin
                    str += "ctx.lineJoin = '"+ a[1] +"';";
                    break;
                case 12: // miterLimit
                    str += "ctx.lineJoin = '"+ a[1] +"';";
                    break;
                case 13: // beginPath
                    str += "ctx.beginPath();";
                    break;
            }
            i++;
        }
        return new this.FUNCTION("ctx", "ct", "isClip", str);
    };

    /**
     * @param cache
     * @returns {*}
     */
    VectorToCanvas.prototype.toWebGL = function (cache)
    {
        var length = cache.length;
        var polygons = [];
        var curves = [];
        var polygon = [];
        var position = -1;
        var lastPosition = {x:0, y:0};
        var str = "";
        for (var i = 0; i < length; i++) {
            var a = cache[i];
            switch (a[0]) {
                case 0: // moveTo
                    if (position > 0) {
                        polygons[position] = polygon;
                    }

                    position++;
                    polygons[position + 1] = [];
                    polygon = [];
                    polygon[polygon.length] = a[1];
                    polygon[polygon.length] = a[2];
                    lastPosition.x = a[1];
                    lastPosition.y = a[2];
                    break;
                case 1: // quadraticCurveTo
                    curves[curves.length] = lastPosition.x;
                    curves[curves.length] = lastPosition.y;
                    curves[curves.length] = a[1];
                    curves[curves.length] = a[2];
                    curves[curves.length] = a[3];
                    curves[curves.length] = a[4];

                    polygon[polygon.length] = a[3];
                    polygon[polygon.length] = a[4];
                    lastPosition.x = a[3];
                    lastPosition.y = a[4];
                    break;
                case 2: // lineTo
                    polygon[polygon.length] = a[1];
                    polygon[polygon.length] = a[2];
                    lastPosition.x = a[1];
                    lastPosition.y = a[2];
                    break;
                case 3: // bezierCurveTo
                    // TODO bezierCurveTo
                    break;
                case 4: // arc
                    // TODO arc
                    break;
            }
        }

        return new this.FUNCTION("ctx", str);
    };

    var vtc = new VectorToCanvas();

    /**
     * @constructor
     */
    var CacheStore = function ()
    {
        this.store = {};
        this.pool = [];
        this.size = 73400320; // 70M
    };

    /**
     * reset
     */
    CacheStore.prototype.reset = function ()
    {
        var _this = this;
        var store = _this.store;
        for (var key in store) {
            if (!store.hasOwnProperty(key)) {
                continue;
            }
            var value = store[key];
            if (!(value instanceof CanvasRenderingContext2D)) {
                continue;
            }
            _this.destroy(value);
        }
        _this.store = {};
        _this.size = 73400320;
    };

    /**
     * @param ctx
     */
    CacheStore.prototype.destroy = function (ctx)
    {
        var pool = this.pool;
        var canvas = ctx.canvas;
        if (isWebGL) {
            ctx.clear(ctx.STENCIL_BUFFER_BIT | ctx.COLOR_BUFFER_BIT);
        } else {
            ctx.clearRect(0, 0, canvas.width + 1, canvas.height + 1);
        }
        canvas.width = canvas.height = 1;
        pool[pool.length] = canvas;
    };

    /**
     * @returns {*}
     */
    CacheStore.prototype.getCanvas = function ()
    {
        return this.pool.pop() || _document.createElement("canvas");
    };

    /**
     * @param key
     * @returns {*}
     */
    CacheStore.prototype.getCache = function (key)
    {
        return this.store[key];
    };

    /**
     * @param key
     * @param value
     */
    CacheStore.prototype.setCache = function (key, value)
    {
        var _this = this;
        if (value instanceof CanvasRenderingContext2D) {
            var canvas = value.canvas;
            _this.size -= (canvas.width * canvas.height);
        }
        this.store[key] = value;
    };

    /**
     * @param name
     * @param id
     * @param matrix
     * @param cxForm
     * @returns {string}
     */
    CacheStore.prototype.generateKey = function (name, id, matrix, cxForm)
    {
        var key = name + "_" + id;
        if (matrix instanceof Array) {
            key += "_" + matrix.join("_");
        }
        if (cxForm instanceof Array) {
            key += "_" + cxForm.join("_");
        }
        return key;
    };
    var cacheStore = new CacheStore();

    /**
     * @constructor
     */
    var BitIO = function ()
    {
        var _this = this;
        _this.data = null;
        _this.bit_offset = 0;
        _this.byte_offset = 0;
        _this.bit_buffer = null;
    };

    /**
     * @param data
     */
    BitIO.prototype.init = function (data)
    {
        var _this = this;
        var length = data.length;
        var array = _this.createArray(length);
        var i = 0;
        while (i < length) {
            array[i] = data.charCodeAt(i) & 0xff;
            i++;
        }
        _this.data = array;
    };

    /**
     * @param str
     * @returns {XML|string|void|*}
     */
    BitIO.prototype.decodeToShiftJis = function (str)
    {
        return str.replace(/%(8[1-9A-F]|[9E][0-9A-F]|F[0-9A-C])(%[4-689A-F][0-9A-F]|%7[0-9A-E]|[@-~])|%([0-7][0-9A-F]|A[1-9A-F]|[B-D][0-9A-F])/ig,
            function (s)
            {
                var c = parseInt(s.substring(1, 3), 16);
                var l = s.length;
                return 3 === l ? _fromCharCode(c < 160 ? c : c + 65216) : JCT11280.charAt((c < 160 ? c - 129 : c - 193) * 188 + (4 === l ? s.charCodeAt(3) - 64 : (c = parseInt(s.substring(4), 16)) < 127 ? c - 64 : c - 65));
            }
        );
    };

    /**
     * @param compressed
     * @returns {Array}
     */
    BitIO.prototype.unlzma = function (compressed)
    {
        return [];
    };

    /**
     * @param compressed
     * @param isDeCompress
     * @returns {Array}
     */
    BitIO.prototype.unzip = function (compressed, isDeCompress)
    {
        var _this = this;
        var sym = 0;
        var i = 0;
        var length = 0;
        var data = [];
        var bitLengths = [];
        var bitio = new BitIO();
        bitio.setData(compressed);

        var ORDER =
            [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];

        var LEXT = [
            0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
            3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
        ];

        var LENS = [
            3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
            35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
        ];

        var DEXT = [
            0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
            7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
        ];

        var DISTS = [
            1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
            257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
            8193, 12289, 16385, 24577
        ];

        if (isArrayBuffer) {
            ORDER = new Uint8Array(ORDER);
            LEXT = new Uint8Array(LEXT);
            LENS = new Uint16Array(LENS);
            DEXT = new Uint8Array(DEXT);
            DISTS = new Uint16Array(DISTS);
        }

        var startOffset = 2;
        if (isDeCompress) {
            startOffset = 10;
        }
        bitio.setOffset(startOffset, 8);

        for (var flag = 0; !flag;) {
            flag = bitio.readUB(1);
            var type = bitio.readUB(2);
            var distTable = {};
            var litTable = {};
            var fixedDistTable = false;
            var fixedLitTable = false;

            if (type) {
                if (type === 1) {
                    distTable = fixedDistTable;
                    litTable = fixedLitTable;

                    if (!distTable) {
                        bitLengths = [];
                        for (i = 32; i--;) {
                            bitLengths[bitLengths.length] = 5;
                        }
                        distTable = fixedDistTable = _this.buildHuffTable(bitLengths);
                    }

                    if (!litTable) {
                        bitLengths = [];
                        i = 0;
                        while (i < 144) {
                            i++;
                            bitLengths[bitLengths.length] = 8;
                        }
                        while (i < 256) {
                            i++;
                            bitLengths[bitLengths.length] = 9;
                        }
                        while (i < 280) {
                            i++;
                            bitLengths[bitLengths.length] = 7;
                        }
                        while (i < 288) {
                            i++;
                            bitLengths[bitLengths.length] = 8;
                        }
                        litTable = fixedLitTable = _this.buildHuffTable(bitLengths);
                    }
                } else {
                    var numLitLengths = bitio.readUB(5) + 257;
                    var numDistLengths = bitio.readUB(5) + 1;
                    var numCodeLengths = bitio.readUB(4) + 4;
                    var codeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
                    if (isArrayBuffer) {
                        codeLengths = new Uint8Array(codeLengths);
                    }
                    for (i = 0; i < numCodeLengths; i++) {
                        codeLengths[ORDER[i]] = bitio.readUB(3);
                    }
                    var codeTable = _this.buildHuffTable(codeLengths);
                    codeLengths = null;

                    var litLengths = [];
                    var prevCodeLen = 0;
                    var maxLengths = numLitLengths + numDistLengths;
                    while (litLengths.length < maxLengths) {
                        sym = _this.decodeSymbol(bitio, codeTable);
                        switch (sym) {
                            case 16:
                                i = bitio.readUB(2) + 3;
                                while (i--) {
                                    litLengths[litLengths.length] = prevCodeLen;
                                }
                                break;
                            case 17:
                                i = bitio.readUB(3) + 3;
                                while (i--) {
                                    litLengths[litLengths.length] = 0;
                                }
                                break;
                            case 18:
                                i = bitio.readUB(7) + 11;
                                while (i--) {
                                    litLengths[litLengths.length] = 0;
                                }
                                break;
                            default:
                                if (sym <= 15) {
                                    litLengths[litLengths.length] = sym;
                                    prevCodeLen = sym;
                                }
                                break;
                        }
                    }
                    distTable = _this.buildHuffTable(litLengths.splice(numLitLengths, numDistLengths));
                    litTable = _this.buildHuffTable(litLengths);
                }

                sym = 0;
                while (sym !== 256) {
                    sym = _this.decodeSymbol(bitio, litTable);
                    if (sym < 256) {
                        data[data.length] = sym;
                    } else if (sym > 256) {
                        var mapIdx = sym - 257;
                        length = LENS[mapIdx] + bitio.readUB(LEXT[mapIdx]);
                        var distMap = _this.decodeSymbol(bitio, distTable);
                        var dist = DISTS[distMap] + bitio.readUB(DEXT[distMap]);
                        i = data.length - dist;
                        while (length--) {
                            data[data.length] = data[i++];
                        }
                    }
                }
            } else {
                bitio.bit_offset = 8;
                bitio.bit_buffer = null;
                length = bitio.readNumber(2);
                bitio.readNumber(2); // nlen
                while (length--) {
                    data[data.length] = bitio.readNumber(1);
                }
            }
        }
        return data;
    };

    /**
     * @param data
     * @returns {{}}
     */
    BitIO.prototype.buildHuffTable = function (data)
    {
        var length = data.length;
        var blCount = [];
        var nextCode = [];
        var table = {};
        var code = 0;
        var len = 0;
        var maxBits = 0;
        for (var i = 0; i < length; i++) {
            maxBits = _max(maxBits, data[i]);
        }
        maxBits++;

        i = length;
        while (i--) {
            len = data[i];
            blCount[len] = (blCount[len] || 0) + (len > 0);
        }

        for (i = 1; i < maxBits; i++) {
            len = i - 1;
            if (!(len in blCount)) {
                blCount[len] = 0;
            }
            code = (code + blCount[len]) << 1;
            nextCode[i] = code|0;
        }

        for (i = 0; i < length; i++) {
            len = data[i];
            if (len) {
                table[nextCode[len]] = {length: len, symbol: i};
                nextCode[len]++;
            }
        }
        return table;
    };

    /**
     * @param bitio
     * @param table
     * @returns {*}
     */
    BitIO.prototype.decodeSymbol = function (bitio, table)
    {
        var code = 0;
        var len = 0;
        while (true) {
            code = (code << 1) | bitio.readUB(1);
            len++;
            if (!(code in table)) {
                continue;
            }
            var entry = table[code];
            if (entry.length === len) {
                return entry.symbol;
            }
        }
    };

    /**
     * @param length
     * @returns {Array}
     */
    BitIO.prototype.createArray = function (length)
    {
        return (isArrayBuffer) ? new Uint8Array(length) : [];
    };

    /**
     * @param data
     */
    BitIO.prototype.setData = function (data)
    {
        this.data = data;
    };

    /**
     * @returns {string}
     */
    BitIO.prototype.getHeaderSignature = function ()
    {
        var _this = this;
        var str = "";
        var count = 3;
        while (count) {
            var code = _this.getUI8();
            switch (code) { // trim
                case 32:
                case 96:
                case 127:
                    continue;
                default:
                    break;
            }
            str += _fromCharCode(code);
            count--;
        }
        return str;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getVersion = function ()
    {
        return this.getUI8();
    };

    /**
     * byteAlign
     */
    BitIO.prototype.byteAlign = function ()
    {
        var _this = this;
        if (!_this.bit_offset) {
            return;
        }
        _this.byte_offset += ((_this.bit_offset + 7) / 8) | 0;
        _this.bit_offset = 0;
    };

    /**
     * @param length
     * @returns {Array}
     */
    BitIO.prototype.getData = function (length)
    {
        var _this = this;
        _this.byteAlign();
        var array = _this.createArray(length);
        var key = 0;
        var data = _this.data;
        var limit = length;
        while (limit--) {
            array[key++] = data[_this.byte_offset++];
        }
        return array;
    };

    /**
     * @param value
     * @param isJis
     * @returns {string}
     */
    BitIO.prototype.getDataUntil = function (value, isJis)
    {
        var _this = this;
        var data = _this.data;

        _this.byteAlign();
        var bo = _this.byte_offset;
        var offset = 0;
        if (value === null) {
            offset = -1;
        } else {
            var length = data.length;
            while (true) {
                var val = data[bo + offset];
                offset++;
                if (val === 0 || (bo + offset) >= length) {
                    break;
                }
            }
        }

        var n = (offset === -1) ? data.length - bo : offset;
        var array = [];
        var ret = "";
        var _join = Array.prototype.join;
        var i = 0;
        if (value !== null) {
            for (i = 0; i < n; i++) {
                var code = data[bo + i];
                if (code === 10 || code === 13) {
                    array[array.length] = "\n";
                }
                if (code < 32) {
                    continue;
                }
                array[array.length] = "%" + code.toString(16);
            }

            if (array.length) {
                var str = _join.call(array, "");
                if (str.length > 5 && str.substr(-5) === "\n") {
                    str = str.slice(0, -5);
                }

                if (isJis) {
                    ret = _this.decodeToShiftJis(str);
                } else {
                    try {
                        ret = decodeURIComponent(str);
                    } catch (e) {
                        ret = _this.decodeToShiftJis(str);
                    }
                }
            }
        } else {
            for (i = 0; i < n; i++) {
                ret += _fromCharCode(data[bo + i]);
            }
        }
        _this.byte_offset = bo + n;
        return ret;
    };

    /**
     * byteCarry
     */
    BitIO.prototype.byteCarry = function ()
    {
        var _this = this;
        if (_this.bit_offset > 7) {
            _this.byte_offset += ((_this.bit_offset + 7) / 8) | 0;
            _this.bit_offset &= 0x07;
        } else {
            while (_this.bit_offset < 0) {
                _this.byte_offset--;
                _this.bit_offset += 8;
            }
        }
    };

    /**
     * @param n
     * @returns {number}
     */
    BitIO.prototype.getUIBits = function (n)
    {
        var value = 0;
        var _this = this;
        var _getUIBit = _this.getUIBit;
        while (n--) {
            value <<= 1;
            value |= _getUIBit.call(_this);
        }
        return value;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUIBit = function ()
    {
        var _this = this;
        _this.byteCarry();
        return (_this.data[_this.byte_offset] >> (7 - _this.bit_offset++)) & 0x1;
    };

    /**
     * @param n
     * @returns {number}
     */
    BitIO.prototype.getSIBits = function (n)
    {
        var _this = this;
        var value = _this.getUIBits(n);
        var msb = value & (0x1 << (n - 1));
        if (msb) {
            var bitMask = (2 * msb) - 1;
            return -(value ^ bitMask) - 1;
        }
        return value;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUI8 = function ()
    {
        var _this = this;
        _this.byteAlign();
        return _this.data[_this.byte_offset++];
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUI16 = function ()
    {
        var _this = this;
        var data = _this.data;
        _this.byteAlign();
        return (data[_this.byte_offset++] | (data[_this.byte_offset++]) << 8);
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUI24 = function ()
    {
        var _this = this;
        var data = _this.data;
        _this.byteAlign();
        return (data[_this.byte_offset++] | (data[_this.byte_offset++]
            | (data[_this.byte_offset++]) << 8) << 8);
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUI32 = function ()
    {
        var _this = this;
        var data = _this.data;
        _this.byteAlign();
        return (data[_this.byte_offset++] | (data[_this.byte_offset++]
            | (data[_this.byte_offset++] | (data[_this.byte_offset++]) << 8) << 8) << 8);
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getUI16BE = function ()
    {
        var _this = this;
        var data = _this.data;
        _this.byteAlign();
        return (((data[_this.byte_offset++]) << 8) | (data[_this.byte_offset++]));
    };

    /**
     * @returns {*}
     */
    BitIO.prototype.getFloat16 = function ()
    {
        var data = this.getData(2);
        var float = 0;
        for (var i = 2; i--;) {
            float |= data[i] << (i * 8);
        }
        return float;
    };

    /**
     * @returns {*}
     */
    BitIO.prototype.getFloat32 = function ()
    {
        var data = this.getData(4);
        var rv = 0;
        for (var i = 4; i--;) {
            rv |= data[i] << (i * 8);
        }
        var sign = rv & 0x80000000;
        var exp = (rv >> 23) & 0xff;
        var fraction = rv & 0x7fffff;
        if (!rv || rv === 0x80000000) {
            return 0;
        }
        return (sign ? -1 : 1) * (fraction | 0x800000) * _pow(2, (exp - 127 - 23));
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getFloat64 = function ()
    {
        var _this = this;
        var upperBits = _this.getUI32();
        var lowerBits = _this.getUI32();
        var sign = upperBits >>> 31 & 0x1;
        var exp = upperBits >>> 20 & 0x7FF;
        var upperFraction = upperBits & 0xFFFFF;
        return (!upperBits && !lowerBits) ? 0 : ((sign === 0) ? 1 : -1) *
            (upperFraction / 1048576 + lowerBits / 4503599627370496 + 1) *
                _pow(2, exp - 1023);
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getFloat64LittleEndian = function ()
    {
        var _this = this;
        var signBits = 1;
        var exponentBits = 11;
        var fractionBits = 52;
        var min = -1022;
        var max = 1023;

        var data = _this.data;
        var str = "";
        for (var i = 0; i < 8; i++) {
            var bits = data[_this.byte_offset++].toString(2);
            while (bits.length < 8) {
                bits = "0" + bits;
            }
            str = bits + str;
        }

        var sign = (str.charAt(0) === "1") ? -1 : 1;
        var exponent = parseInt(str.substr(signBits, exponentBits), 2) - max;
        var significandBase = str.substr(signBits + exponentBits, fractionBits);
        var significandBin = "1"+ significandBase;

        var val = 1;
        var significand = 0;
        if (exponent === -max) {
            if (significandBase.indexOf("1") === -1) {
                return 0;
            } else {
                exponent = min;
                significandBin = "0"+ significandBase;
            }
        }

        var l = 0;
        while (l < significandBin.length) {
            var sb = significandBin.charAt(l);
            significand += val * +sb;
            val = val / 2;
            l++;
        }

        return sign * significand * _pow(2, exponent);
    };

    /**
     * @param data
     * @returns {number}
     */
    BitIO.prototype.toUI16 = function (data)
    {
        return data[0] + (data[1] << 8);
    };

    /**
     * @param data
     * @returns {number}
     */
    BitIO.prototype.toSI16LE = function (data)
    {
        var _this = this;
        var value = _this.toUI16(data);
        return (value < 0x8000) ? value : (value - 0x10000);
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getSI8 = function ()
    {
        var _this = this;
        var value = _this.getUI8();
        if (value >> 7) { // nBits = 8;
            value -= 256; // Math.pow(2, 8)
        }
        return value;
    };

    /**
     * @returns {*}
     */
    BitIO.prototype.getSI24 = function ()
    {
        var _this = this;
        var value = _this.getUI24();
        if (value >> 23) { // nBits = 24;
            value -= 16777216; // Math.pow(2, 24)
        }
        return value;
    };

    /**
     * @param byteInt
     * @param bitInt
     */
    BitIO.prototype.incrementOffset = function (byteInt, bitInt)
    {
        var _this = this;
        _this.byte_offset += byteInt;
        _this.bit_offset += bitInt;
        _this.byteCarry();
    };

    /**
     * @param byteInt
     * @param bitInt
     */
    BitIO.prototype.setOffset = function (byteInt, bitInt)
    {
        var _this = this;
        _this.byte_offset = byteInt;
        _this.bit_offset = bitInt;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getU30 = function ()
    {
        var _this = this;
        var value = 0;
        var data = _this.data;
        for (var i = 0; i < 5; i++) {
            var num = data[_this.byte_offset++];
            value |= ((num & 0x7f) << (7 * i));
            if (!(num & 0x80)) {
                break;
            }
        }
        return value;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.getS30 = function ()
    {
        var _this = this;
        var startOffset = _this.byte_offset;
        var value = _this.getU30();
        var nBits = (_this.byte_offset - startOffset) * 8;
        if (value >> (nBits - 1)) {
            value -= _pow(2, nBits);
        }
        return value;
    };

    /**
     * @param offset
     * @returns {number}
     */
    BitIO.prototype.ReadU30 = function (offset)
    {
        var _this = this;
        var value = 0;
        var data = _this.data;
        for (var i = 0; i < 5; i++) {
            var num = data[offset++];
            value |= ((num & 0x7f) << (7 * i));
            if (!(num & 0x80)) {
                break;
            }
        }
        return value;
    };

    /**
     * @returns {string}
     */
    BitIO.prototype.AbcReadString = function ()
    {
        var _this = this;
        var offset = _this.byte_offset;
        var data = _this.data;
        var length = _this.ReadU30(offset) + 1;
        var ret = [];
        for (var i = 0; i < length; i++) {
            var code = data[_this.byte_offset++];
            if (code < 33) {
                continue;
            }

            switch (code) {
                default:
                    break;
                case 34:
                case 35:
                case 36:
                case 37:
                case 38:
                case 39:
                case 43:
                case 45:
                    continue;
            }

            ret[ret.length] = _fromCharCode(code);
        }
        return ret.join("");
    };

    /**
     * @param length
     * @returns {number}
     */
    BitIO.prototype.readUB = function (length)
    {
        var _this = this;
        var value = 0;
        for (var i = 0; i < length; i++) {
            if (_this.bit_offset === 8) {
                _this.bit_buffer = _this.readNumber(1);
                _this.bit_offset = 0;
            }
            value |= (_this.bit_buffer & (0x01 << _this.bit_offset++) ? 1 : 0) << i;
        }
        return value;
    };

    /**
     * @returns {number}
     */
    BitIO.prototype.readNumber = function (n)
    {
        var _this = this;
        var value = 0;
        var o = _this.byte_offset;
        var i = o + n;
        while (i > o) {
            value = (value << 8) | _this.data[--i];
        }
        _this.byte_offset += n;
        return value;
    };

    /**
     * @param size
     * @param mode
     */
    BitIO.prototype.deCompress = function (size, mode)
    {
        var _this = this;
        var cacheOffset = _this.byte_offset;
        _this.byte_offset = 0;

        var data = _this.getData(cacheOffset);
        var deCompress = (mode === "ZLIB") ? _this.unzip(_this.data, true) : _this.unlzma(_this.data);

        var i = 0;
        var key = 0;
        var array = _this.createArray(size);
        var length = data.length;
        while (i < length) {
            array[key++] = data[i];
            i++;
        }

        i = 0;
        length = deCompress.length;
        while (i < length) {
            array[key++] = deCompress[i];
            i++;
        }

        _this.data = array;
        _this.byte_offset = cacheOffset;
    };

    /**
     * @constructor
     */
    var PlaceObject = function ()
    {
        var _this = this;
        _this.matrix = _this.cloneArray([1, 0, 0, 1, 0, 0]);
        _this.colorTransform = _this.cloneArray([1, 1, 1, 1, 0, 0, 0, 0]);
        _this.filters = null;
        _this.blendMode = "normal";
    };

    /**
     * @param src
     * @returns {Array}
     */
    PlaceObject.prototype.cloneArray = function(src)
    {
        var arr = [];
        var length = src.length;
        for (var i = 0; i < length; i++) {
            arr[i] = src[i];
        }
        return arr;
    };

    /**
     * @param blendMode
     * @returns {String}
     */
    PlaceObject.prototype.getBlendName = function (blendMode)
    {
        var mode = null;
        switch (blendMode) {
            case 1:
            case "normal":
                mode = "normal";
                break;
            case 2:
            case "layer":
                mode = "layer";
                break;
            case 3:
            case "multiply":
                mode = "multiply";
                break;
            case 4:
            case "screen":
                mode = "screen";
                break;
            case 5:
            case "lighten":
                mode = "lighten";
                break;
            case 6:
            case "darken":
                mode = "darken";
                break;
            case 7:
            case "difference":
                mode = "difference";
                break;
            case 8:
            case "add":
                mode = "add";
                break;
            case 9:
            case "subtract":
                mode = "subtract";
                break;
            case 10:
            case "invert":
                mode = "invert";
                break;
            case 11:
            case "alpha":
                mode = "alpha";
                break;
            case 12:
            case "erase":
                mode = "erase";
                break;
            case 13:
            case "overlay":
                mode = "overlay";
                break;
            case 14:
            case "hardlight":
                mode = "hardlight";
                break;
        }
        return mode;
    };

    /**
     * @returns {PlaceObject}
     */
    PlaceObject.prototype.clone = function ()
    {
        var _this = this;
        var placeObject = new PlaceObject();
        placeObject.setMatrix(_this.getMatrix());
        placeObject.setColorTransform(_this.getColorTransform());
        placeObject.setFilters(_this.getFilters());
        placeObject.setBlendMode(_this.getBlendMode());
        return placeObject;
    };

    /**
     * @returns {*}
     */
    PlaceObject.prototype.getMatrix = function ()
    {
        return this.matrix;
    };

    /**
     * @param matrix
     */
    PlaceObject.prototype.setMatrix = function (matrix)
    {
        var _this = this;
        _this.matrix = _this.cloneArray(matrix);
    };

    /**
     * @returns {*}
     */
    PlaceObject.prototype.getColorTransform = function ()
    {
        return this.colorTransform;
    };

    /**
     * @param colorTransform
     */
    PlaceObject.prototype.setColorTransform = function (colorTransform)
    {
        var _this = this;
        _this.colorTransform = _this.cloneArray(colorTransform);
    };

    /**
     * @returns {*}
     */
    PlaceObject.prototype.getFilters = function ()
    {
        return this.filters;
    };

    /**
     * @param filters
     */
    PlaceObject.prototype.setFilters = function (filters)
    {
        this.filters = filters;
    };

    /**
     * @returns {string}
     */
    PlaceObject.prototype.getBlendMode = function ()
    {
        return this.blendMode;
    };

    /**
     * @param blendMode
     */
    PlaceObject.prototype.setBlendMode = function (blendMode)
    {
        var _this = this;
        _this.blendMode = _this.getBlendName(blendMode);
    };

    /**
     * @param stage
     * @param bitio
     * @constructor
     */
    var SwfTag = function (stage, bitio)
    {
        var _this = this;
        _this.stage = stage;
        _this.bitio = bitio;
        _this.currentPosition = {x: 0, y: 0};
        _this.jpegTables = null;
    };

    /**
     * @param mc
     * @returns {Array}
     */
    SwfTag.prototype.parse = function (mc)
    {
        var _this = this;
        var length = _this.bitio.data.length;
        return _this.parseTags(length, mc.characterId);
    };

    /**
     * @param tags
     * @param parent
     */
    SwfTag.prototype.build = function (tags, parent)
    {
        var _this = this;
        var length = tags.length;
        if (length) {
            var _showFrame = _this.showFrame;
            var originTags = [];
            for (var frame in tags) {
                if (!tags.hasOwnProperty(frame)) {
                    continue;
                }
                var tag = tags[frame];
                _showFrame.call(_this, tag, parent, originTags);
            }
        }
    };

    /**
     * @param obj
     * @param mc
     * @param originTags
     */
    SwfTag.prototype.showFrame = function (obj, mc, originTags)
    {
        var _this = this;
        var _buildTag = _this.buildTag;
        var newDepth = [];
        var i;
        var tag;
        var frame = obj.frame;
        var stage = _this.stage;

        if (!(frame in originTags)) {
            originTags[frame] = [];
        }
        mc.setTotalFrames(_max(mc.getTotalFrames(), frame));

        // add ActionScript
        var actions = obj.actionScript;
        if (actions.length) {
            for (i in actions) {
                if (!actions.hasOwnProperty(i)) {
                    continue;
                }
                mc.setActions(frame, actions[i]);
            }
        }

        // add label
        var labels = obj.labels;
        if (labels.length) {
            for (i in labels) {
                if (!labels.hasOwnProperty(i)) {
                    continue;
                }
                var label = labels[i];
                mc.addLabel(label.frame, label.name);
            }
        }

        // add sounds
        var sounds = obj.sounds;
        if (sounds.length) {
            for (i in sounds) {
                if (!sounds.hasOwnProperty(i)) {
                    continue;
                }
                mc.addSound(frame, sounds[i]);
            }
        }

        var cTags = obj.cTags;
        if (cTags.length) {
            for (i in cTags) {
                if (!cTags.hasOwnProperty(i)) {
                    continue;
                }
                tag = cTags[i];
                newDepth[tag.Depth] = true;
                _buildTag.call(_this, frame, tag, mc, originTags);
            }
        }

        // remove tag
        var tags = obj.removeTags;
        if (tags.length) {
            mc.setRemoveTag(frame, tags);
            for (i in tags) {
                if (!tags.hasOwnProperty(i)) {
                    continue;
                }
                var rTag = tags[i];
                newDepth[rTag.Depth] = true;
            }
        }

        // copy
        if (frame > 1) {
            var prevFrame = frame - 1;
            var container = mc.container;
            if (prevFrame in container) {
                var prevTags = container[prevFrame];
                if (!(frame in container)) {
                    container[frame] = [];
                }

                var length = prevTags.length;
                if (length) {
                    var parentId = mc.instanceId;
                    for (var d in prevTags) {
                        if (!prevTags.hasOwnProperty(d)) {
                            continue;
                        }
                        if (d in newDepth) {
                            continue;
                        }
                        container[frame][d] = prevTags[d];
                        stage.copyPlaceObject(parentId, d, frame);
                        originTags[frame][d] = originTags[prevFrame][d];
                    }
                }
            }
        }
    };

    /**
     * @param frame
     * @param tag
     * @param parent
     * @param originTags
     */
    SwfTag.prototype.buildTag = function (frame, tag, parent, originTags)
    {
        var _this = this;
        var container = parent.container;
        if (!(frame in container)) {
            container[frame] = [];
        }

        var isCopy = true;
        if (tag.PlaceFlagMove) {
            var oTag = originTags[frame - 1][tag.Depth];
            if (oTag !== undefined) {
                if (tag.PlaceFlagHasCharacter) {
                    if (tag.CharacterId !== oTag.CharacterId) {
                        isCopy = false;
                    }
                } else {
                    tag.PlaceFlagHasCharacter = oTag.PlaceFlagHasCharacter;
                    tag.CharacterId = oTag.CharacterId;
                }

                if (!tag.PlaceFlagHasMatrix && oTag.PlaceFlagHasMatrix) {
                    tag.PlaceFlagHasMatrix = oTag.PlaceFlagHasMatrix;
                    tag.Matrix = oTag.Matrix;
                }

                if (!tag.PlaceFlagHasColorTransform && oTag.PlaceFlagHasColorTransform) {
                    tag.PlaceFlagHasColorTransform = oTag.PlaceFlagHasColorTransform;
                    tag.ColorTransform = oTag.ColorTransform;
                }

                if (!tag.PlaceFlagHasClipDepth && oTag.PlaceFlagHasClipDepth) {
                    tag.PlaceFlagHasClipDepth = oTag.PlaceFlagHasClipDepth;
                    tag.ClipDepth = oTag.ClipDepth;
                }

                if (!tag.PlaceFlagHasClipActions && oTag.PlaceFlagHasClipActions) {
                    tag.PlaceFlagHasClipActions = oTag.PlaceFlagHasClipActions;
                    tag.ClipActionRecords = oTag.ClipActionRecords;
                }

                if (!tag.PlaceFlagHasRatio && !isCopy) {
                    tag.PlaceFlagHasRatio = 1;
                    tag.Ratio = frame - 1;
                }

                if (!tag.PlaceFlagHasFilterList && oTag.PlaceFlagHasFilterList) {
                    tag.PlaceFlagHasFilterList = oTag.PlaceFlagHasFilterList;
                    tag.SurfaceFilterList = oTag.SurfaceFilterList;
                }

                if (!tag.PlaceFlagHasBlendMode && oTag.PlaceFlagHasBlendMode) {
                    tag.PlaceFlagHasBlendMode = oTag.PlaceFlagHasBlendMode;
                    tag.BlendMode = oTag.BlendMode;
                }
            }
        }

        originTags[frame][tag.Depth] = tag;
        var buildObject = _this.buildObject(tag, parent, isCopy, frame);
        if (buildObject) {
            var stage = _this.stage;
            var placeObject = _this.buildPlaceObject(tag);
            stage.setPlaceObject(placeObject, parent.instanceId, tag.Depth, frame);
            container[frame][tag.Depth] = buildObject.instanceId;
        }
    };

    /**
     * @param tag
     * @param parent
     * @param isCopy
     * @param frame
     * @returns {*}
     */
    SwfTag.prototype.buildObject = function (tag, parent, isCopy, frame)
    {
        var _this = this;
        var stage = _this.stage;
        var char = stage.getCharacter(tag.CharacterId);
        var tagType = char.tagType;
        var isMorphShape = false;
        if (tagType === 46 || tagType === 84) {
            isMorphShape = true;
        }

        var obj = {};
        if (!isMorphShape && tag.PlaceFlagMove && isCopy) {
            var id = parent.container[frame - 1][tag.Depth];
            obj = stage.getInstance(id);
        } else {
            if (char instanceof Array) {
                obj = _this.buildMovieClip(tag, char, parent);
            } else {
                switch (tagType) {
                    case 11: // DefineText
                    case 33: // DefineText2
                        obj = _this.buildText(tag, char);
                        break;
                    case 37: // DefineEditText
                        obj = _this.buildTextField(tag, char, parent);
                        break;
                    case 2:  // DefineShape
                    case 22: // DefineShape2
                    case 32: // DefineShape3
                    case 83: // DefineShape4
                        obj = _this.buildShape(tag, char);
                        break;
                    case 46: // MorphShape
                    case 84: // MorphShape2
                        var MorphShape = _this.buildMorphShape(char, tag.Ratio);
                        MorphShape.tagType = tagType;
                        obj = _this.buildShape(tag, MorphShape);
                        break;
                    case 7: // DefineButton
                    case 34: // DefineButton2
                        obj = _this.buildButton(char, tag, parent);
                        break;
                    default:
                        return 0;
                }
            }
            obj.setParent(parent);
            obj.setStage(stage);
            obj.setCharacterId(tag.CharacterId);
            obj.setRatio(tag.Ratio || 0);
            obj.setLevel(tag.Depth);
        }

        if (tag.PlaceFlagHasClipDepth) {
            obj.isClipDepth = true;
            obj.clipDepth = tag.ClipDepth;
        }

        return obj;
    };

    /**
     * @param tag
     * @returns {PlaceObject}
     */
    SwfTag.prototype.buildPlaceObject = function (tag)
    {
        var placeObject = new PlaceObject();
        // Matrix
        if (tag.PlaceFlagHasMatrix) {
            placeObject.setMatrix(tag.Matrix);
        }
        // ColorTransform
        if (tag.PlaceFlagHasColorTransform) {
            placeObject.setColorTransform(tag.ColorTransform);
        }
        // Filter
        if (tag.PlaceFlagHasFilterList) {
            placeObject.setFilters(tag.SurfaceFilterList);
        }
        // BlendMode
        if (tag.PlaceFlagHasBlendMode) {
            placeObject.setBlendMode(tag.BlendMode);
        }
        return placeObject;
    };


    /**
     * @param tag
     * @param character
     * @param parent
     * @returns {MovieClip}
     */
    SwfTag.prototype.buildMovieClip = function (tag, character, parent)
    {
        var _this = this;
        var stage = _this.stage;
        var mc = new MovieClip();
        mc.setStage(stage);
        mc._url = parent._url;
        var target = "instance" + mc.instanceId;
        if (tag.PlaceFlagHasName) {
            mc.setName(tag.Name);
            target = tag.Name;
        }
        mc.setTarget(parent.getTarget() + "/" + target);
        _this.build(character, mc);

        if (tag.PlaceFlagHasClipActions) {
            var ClipActionRecords = tag.ClipActionRecords;
            var length = ClipActionRecords.length;
            var eventName;
            for (var i = 0; i < length; i++) {
                var actionRecord = ClipActionRecords[i];
                var eventFlag = actionRecord.EventFlags;
                for (eventName in eventFlag) {
                    if (!eventFlag.hasOwnProperty(eventName)) {
                        continue;
                    }
                    if (!eventFlag[eventName]) {
                        continue;
                    }
                    var action = mc.createActionScript(actionRecord.Actions);
                    mc.addEventListener(eventName, action);
                }
            }
        }

        return mc;
    };

    /**
     * @param tag
     * @param character
     * @param parent
     * @returns {TextField}
     */
    SwfTag.prototype.buildTextField = function (tag, character, parent)
    {
        var _this = this;
        var stage = _this.stage;

        var textField = new TextField();
        textField.setStage(stage);
        textField.setParent(parent);
        textField.setInitParams();
        textField.setTagType(character.tagType);
        textField.setBounds(character.bounds);
        var target = "instance" + textField.instanceId;
        if (tag.PlaceFlagHasName) {
            textField.setName(tag.Name);
            target = tag.Name;
        }
        textField.setTarget(parent.getTarget() + "/" + target);

        var data = character.data;
        var obj = {};
        var fontData = null;
        var fontId = data.FontID;
        if (data.HasFont) {
            fontData = stage.getCharacter(fontId);
        }

        textField.fontId = fontId;
        textField.fontScale = data.FontHeight / 1024;
        if (fontData && fontData.ZoneTable) {
            textField.fontScale /= 20;
        }
        textField.initialText = data.InitialText;
        obj.autoSize = data.AutoSize;
        obj.border = data.Border;
        if (obj.border) {
            obj.background = 1;
        }
        obj.bottomScroll = 1;
        obj.condenseWhite = 0;
        obj.embedFonts = (data.HasFont && data.UseOutlines && fontData.FontFlagsHasLayout && !data.Password) ? 1 : 0;
        obj.hscroll = 0;
        obj.maxscroll = 0;
        obj.scroll = 0;
        obj.maxhscroll = 0;
        obj.html = data.HTML;
        obj.htmlText = (data.HTML) ? data.InitialText : null;
        obj.length = 0;
        obj.maxChars = 0;
        obj.multiline = data.Multiline;
        obj.password = data.Password;
        obj.selectable = data.NoSelect;
        obj.tabEnabled = 0;
        obj.tabIndex = 0;
        obj.text = data.InitialText;
        obj.textColor = data.TextColor;
        obj.textHeight = 0;
        obj.textWidth = 0;
        obj.type = data.ReadOnly ? "dynamic" : "input";

        var variable = data.VariableName;
        obj.variable = variable;
        if (variable) {
            parent.setVariable(variable, data.InitialText);
        }

        obj.wordWrap = data.WordWrap;

        // TextFormat
        obj.blockIndent = 0;
        obj.bullet = 0;

        if (fontData) {
            obj.bold = fontData.FontFlagsBold;
            var font = textField.getVariable("font");
            obj.font = "'" + fontData.FontName + "', " + font;
            obj.italic = fontData.FontFlagsItalic;
        }

        if (data.HasLayout) {
            switch (data.Align) {
                case 1:
                    obj.align = "right";
                    break;
                case 2:
                    obj.align = "center";
                    break;
                case 3:
                    obj.align = "justify";
                    break;
            }
            obj.leftMargin = data.LeftMargin;
            obj.rightMargin = data.RightMargin;
            obj.indent = data.Indent;
            obj.leading = (14400 > data.Leading) ? data.Leading : data.Leading - 65535;
        }

        obj.size = data.FontHeight / 20;
        obj.tabStops = [];
        obj.target = null;
        obj.underline = 0;
        obj.url = null;

        for (var name in obj) {
            if (!obj.hasOwnProperty(name)) {
                continue;
            }
            textField.setProperty(name, obj[name]);
        }

        if (obj.type === "input") {
            textField.setInputElement();
        }

        return textField;
    };

    /**
     * @param tag
     * @param character
     * @returns {StaticText}
     */
    SwfTag.prototype.buildText = function (tag, character)
    {
        var _this = this;
        var stage = _this.stage;
        var staticText = new StaticText();
        staticText.setTagType(character.tagType);
        staticText.setBounds(character.bounds);

        var records = character.textRecords;
        var length = records.length;
        var offsetX = 0;
        var offsetY = 0;
        var scale = 1;
        var textHeight = 0;
        var ShapeTable = null;
        var cMatrix = character.matrix;
        var color = null;
        var isZoneTable = false;
        for (var i = 0; i < length; i++) {
            var record = records[i];
            if ("FontId" in record) {
                var fontId = record.FontId;
                var fontData = stage.getCharacter(fontId);
                ShapeTable = fontData.GlyphShapeTable;
                isZoneTable = false;
                if ("ZoneTable" in fontData) {
                    isZoneTable = true;
                }
            }
            if ("XOffset" in record) {
                offsetX = record.XOffset;
            }
            if ("YOffset" in record) {
                offsetY = record.YOffset;
            }
            if ("TextColor" in record) {
                color = record.TextColor;
            }
            if ("TextHeight" in record) {
                textHeight = record.TextHeight;
                if (isZoneTable) {
                    textHeight /= 20;
                }
            }

            var entries = record.GlyphEntries;
            var count = record.GlyphCount;
            scale = textHeight / 1024;
            for (var idx = 0; idx < count; idx++) {
                var entry = entries[idx];
                var shapes = ShapeTable[entry.GlyphIndex];
                var data = vtc.convert(shapes);
                var matrix = [scale, cMatrix[1], cMatrix[2], scale, cMatrix[4] + offsetX, cMatrix[5] + offsetY];
                var textRecode = new TextRecord();
                textRecode.setData(data);
                textRecode.setColor(color);
                textRecode.setMatrix(matrix);
                staticText.addRecord(textRecode);
                offsetX += entry.GlyphAdvance;
            }
        }

        return staticText;
    };

    /**
     * @param tag
     * @param character
     * @returns {Shape}
     */
    SwfTag.prototype.buildShape = function (tag, character)
    {
        var shape = new Shape();
        shape.setTagType(character.tagType);
        shape.setBounds(character.bounds);
        shape.setData(character.data);
        return shape;
    };

    /**
     * @param character
     * @param tag
     * @param parent
     * @returns {SimpleButton}
     */
    SwfTag.prototype.buildButton = function (character, tag, parent)
    {
        var _this = this;
        var stage = _this.stage;
        var characters = character.characters;
        var button = new SimpleButton();
        button.setStage(stage);
        button.setParent(parent);
        button.setLevel(tag.Depth);

        if ("actions" in character) {
            button.setActions(character.actions);
        }

        var target = "instance" + button.instanceId;
        if (tag.PlaceFlagHasName) {
            button.setName(tag.Name);
            target = tag.Name;
        }
        button.setTarget(parent.getTarget() + "/" + target);

        var downState = button.getSprite("down");
        if (character.ButtonStateDownSoundId) {
            downState.soundId = character.ButtonStateDownSoundId;
            downState.soundInfo = character.ButtonStateDownSoundInfo;
        }

        var hitState = button.getSprite("hit");
        if (character.ButtonStateHitTestSoundId) {
            hitState.soundId = character.ButtonStateHitTestSoundId;
            hitState.soundInfo = character.ButtonStateHitTestSoundInfo;
        }

        var overState = button.getSprite("over");
        if (character.ButtonStateOverSoundId) {
            overState.soundId = character.ButtonStateOverSoundId;
            overState.soundInfo = character.ButtonStateOverSoundInfo;
        }

        var upState = button.getSprite("up");
        if (character.ButtonStateUpSoundId) {
            upState.soundId = character.ButtonStateUpSoundId;
            upState.soundInfo = character.ButtonStateUpSoundInfo;
        }

        for (var depth in characters) {
            if (!characters.hasOwnProperty(depth)) {
                continue;
            }

            var tags = characters[depth];
            for (var idx in tags) {
                if (!tags.hasOwnProperty(idx)) {
                    continue;
                }

                var bTag = tags[idx];
                var obj = _this.buildObject(bTag, button, false, 1);
                var placeObject = _this.buildPlaceObject(bTag);
                var Depth = bTag.Depth;
                if (bTag.ButtonStateDown) {
                    downState.addTag(Depth, obj);
                    stage.setPlaceObject(placeObject, downState.instanceId, Depth, 0);
                }
                if (bTag.ButtonStateHitTest) {
                    hitState.addTag(Depth, obj);
                    stage.setPlaceObject(placeObject, hitState.instanceId, Depth, 0);
                }
                if (bTag.ButtonStateOver) {
                    overState.addTag(Depth, obj);
                    stage.setPlaceObject(placeObject, overState.instanceId, Depth, 0);
                }
                if (bTag.ButtonStateUp) {
                    upState.addTag(Depth, obj);
                    stage.setPlaceObject(placeObject, upState.instanceId, Depth, 0);
                }
            }
        }

        button.setSprite("down", downState);
        button.setSprite("hit", hitState);
        button.setSprite("over", overState);
        button.setSprite("up", upState);
        button.setTagType(character.tagType);
        return button;
    };

    /**
     * @param frame
     * @param characterId
     * @returns {{ }}
     */
    SwfTag.prototype.generateDefaultTagObj = function (frame, characterId)
    {
        return {
            frame: frame,
            characterId: characterId,
            cTags: [],
            removeTags: [],
            actionScript: [],
            labels: [],
            sounds: []
        };
    };

    /**
     * @param dataLength
     * @param characterId
     * @returns {Array}
     */
    SwfTag.prototype.parseTags = function (dataLength, characterId)
    {
        var _this = this;
        var _parseTag = _this.parseTag;
        var _addTag = _this.addTag;
        var _generateDefaultTagObj = _this.generateDefaultTagObj;
        var frame = 1;
        var tags = [];
        var tagType = 0;
        var bitio = _this.bitio;

        // default set
        tags[frame] = _generateDefaultTagObj.call(_this, frame, characterId);

        while (bitio.byte_offset < dataLength) {
            var tagStartOffset = bitio.byte_offset;
            if (tagStartOffset + 2 > dataLength) {
                break;
            }

            var tagLength = bitio.getUI16();
            tagType = tagLength >> 6;

            // long
            var length = tagLength & 0x3f;
            if (length === 0x3f) {
                if (tagStartOffset + 6 > dataLength) {
                    bitio.byte_offset = tagStartOffset;
                    bitio.bit_offset = 0;
                    break;
                }
                length = bitio.getUI32();
            }

            var tagDataStartOffset = bitio.byte_offset;
            if (tagType === 1) {
                frame++;
                if (dataLength > tagDataStartOffset + 2) {
                    tags[frame] = _generateDefaultTagObj.call(_this, frame, characterId);
                }
            }

            var tag = _parseTag.call(_this, tagType, length);

            var o = bitio.byte_offset - tagDataStartOffset;
            if (o !== length) {
                if (o < length) {
                    var eat = (length - o);
                    if (eat > 0) {
                        bitio.byte_offset += eat;
                    }
                }
            }

            if (tag) {
                tags = _addTag.call(_this, tagType, tags, tag, frame);
            }

            bitio.bit_offset = 0;
        }

        return tags;
    };

    /**
     * @param tagType
     * @param length
     * @returns {*}
     */
    SwfTag.prototype.parseTag = function (tagType, length)
    {
        var _this = this;
        var obj = null;
        var bitio = _this.bitio;
        var stage = _this.stage;

        switch (tagType) {
            case 0: // End
                break;
            case 1: // ShowFrame
                break;
            case 2:  // DefineShape
            case 22: // DefineShape2
            case 32: // DefineShape3
            case 83: // DefineShape4
                if (length < 10) {
                    bitio.byte_offset += length;
                } else {
                    _this.parseDefineShape(tagType);
                }
                break;
            case 9: // BackgroundColor
                if (stage.bgcolor) {
                    stage.backgroundColor = stage.bgcolor;
                } else {
                    stage.setBackgroundColor(
                        bitio.getUI8(),
                        bitio.getUI8(),
                        bitio.getUI8()
                    );
                }
                break;
            case 10: // DefineFont
            case 48: // DefineFont2
            case 75: // DefineFont3
                _this.parseDefineFont(tagType, length);
                break;
            case 13: // DefineFontInfo
            case 62: // DefineFontInfo2
                _this.parseDefineFontInfo(tagType, length);
                break;
            case 11: // DefineText
            case 33: // DefineText2
                _this.parseDefineText(tagType);
                break;
            case 4: // PlaceObject
            case 26: // PlaceObject2
            case 70: //PlaceObject3
                obj = _this.parsePlaceObject(tagType, length);
                break;
            case 37: // DefineEditText
                _this.parseDefineEditText(tagType);
                break;
            case 39: // DefineSprite
                _this.parseDefineSprite(bitio.byte_offset + length);
                break;
            case 12: // DoAction
                obj = _this.parseDoAction(length);
                break;
            case 59: // DoInitAction
                _this.parseDoInitAction(length);
                break;
            case 5: // RemoveObject
            case 28: // RemoveObject2
                obj = _this.parseRemoveObject(tagType);
                break;
            case 7: // DefineButton
            case 34: // DefineButton2
                obj = _this.parseDefineButton(tagType, length);
                break;
            case 43: // FrameLabel
                obj = _this.parseFrameLabel();
                break;
            case 88: // DefineFontName
                _this.parseDefineFontName();
                break;
            case 20: // DefineBitsLossless
            case 36: // DefineBitsLossless2
                _this.parseDefineBitsLossLess(tagType, length);
                break;
            case 6: // DefineBits
            case 21: // DefineBitsJPEG2
            case 35: // DefineBitsJPEG3
            case 90: // DefineBitsJPEG4
                _this.parseDefineBits(tagType, length, _this.jpegTables);
                _this.jpegTables = null;
                break;
            case 8: // JPEGTables
                _this.jpegTables = _this.parseJPEGTables(length);
                break;
            case 56: // ExportAssets
                _this.parseExportAssets();
                break;
            case 46: // DefineMorphShape
            case 84: // DefineMorphShape2
                _this.parseDefineMorphShape(tagType);
                break;
            case 40: // NameCharacter
                bitio.getDataUntil("\0"); // NameCharacter
                break;
            case 24: // Protect
                bitio.byteAlign();
                break;
            case 63: // DebugID
                bitio.getUI8(); // UUID
                break;
            case 64: // EnableDebugger2
                bitio.getUI16(); // Reserved
                bitio.getDataUntil("\0"); // Password
                break;
            case 65: // ScriptLimits
                bitio.getUI16(); // MaxRecursionDepth
                bitio.getUI16(); // ScriptTimeoutSeconds
                break;
            case 69: // FileAttributes
                _this.parseFileAttributes();
                break;
            case 77: // MetaData
                bitio.getDataUntil("\0"); // MetaData
                break;
            case 86: // DefineSceneAndFrameLabelData
                obj = _this.parseDefineSceneAndFrameLabelData();
                break;
            case 18: // SoundStreamHead
            case 45: // SoundStreamHead2
                obj = _this.parseSoundStreamHead(tagType);
                break;
            case 72: // DoABC
            case 82: // DoABC2
                _this.parseDoABC(tagType, length);
                break;
            case 76: // SymbolClass
                _this.parseSymbolClass();
                break;
            case 14: // DefineSound
                _this.parseDefineSound(tagType, length);
                break;
            case 15: // StartSound
            case 89: // StartSound2
                obj = _this.parseStartSound(tagType);
                break;
            case 17: // DefineButtonSound
                _this.parseDefineButtonSound();
                break;
            case 73: // DefineFontAlignZones
                _this.parseDefineFontAlignZones();
                break;
            case 74: // CSMTextSettings
                _this.parseCSMTextSettings(tagType);
                break;
            case 19: // SoundStreamBlock
                _this.parseSoundStreamBlock(tagType, length);
                break;
            case 60: // DefineVideoStream
                _this.parseDefineVideoStream(tagType);
                break;
            case 61: // VideoFrame
                _this.parseVideoFrame(tagType, length);
                break;
            case 78: // DefineScalingGrid
                _this.parseDefineScalingGrid();
                break;
            case 41: // ProductInfo
                bitio.getUI32(); // ProductID
                bitio.getUI32(); // Edition
                bitio.getUI8(); // MajorVersion
                bitio.getUI8(); // MinorVersion
                bitio.getUI32(); // BuildLow
                bitio.getUI32(); // BuildHigh
                bitio.getUI32(); // CompilationDate
                bitio.getUI32(); // TODO
                break;
            case 3:  // FreeCharacter
            case 16: // StopSound
            case 23: // DefineButtonCxform
            case 25: // PathsArePostScript
            case 29: // SyncFrame
            case 31: // FreeAll
            case 38: // DefineVideo
            case 42: // DefineTextFormat
            case 44: // DefineBehavior
            case 47: // FrameTag
            case 49: // GeProSet
            case 52: // FontRef
            case 53: // DefineFunction
            case 54: // PlaceFunction
            case 55: // GenTagObject
            case 57: // ImportAssets
            case 58: // EnableDebugger
            case 66: // SetTabIndex
            case 71: // ImportAssets2
            case 87: // DefineBinaryData
            case 91: // DefineFont4
            case 93: // EnableTelemetry
                console.log("[base] tagType -> " + tagType);
                break;
            case 27: // 27 (invalid)
            case 30: // 30 (invalid)
            case 67: // 67 (invalid)
            case 68: // 68 (invalid)
            case 79: // 79 (invalid)
            case 80: // 80 (invalid)
            case 81: // 81 (invalid)
            case 85: // 85 (invalid)
            case 92: // 92 (invalid)
                break;
            default: // null
                break;
        }

        return obj;
    };

    /**
     * @param tagType
     * @param tags
     * @param tag
     * @param frame
     * @returns {*}
     */
    SwfTag.prototype.addTag = function (tagType, tags, tag, frame)
    {
        var tagsArray = tags[frame];
        switch (tagType) {
            case 4:  // PlaceObject
            case 26: // PlaceObject2
            case 70: // PlaceObject3
                var cTags = tagsArray.cTags;
                tagsArray.cTags[cTags.length] = tag;
                break;
            case 12: // DoAction
                var as = tagsArray.actionScript;
                tagsArray.actionScript[as.length] = tag;
                break;
            case 5: // RemoveObject
            case 28: // RemoveObject2
                var removeTags = tagsArray.removeTags;
                tagsArray.removeTags[removeTags.length] = tag;
                break;
            case 43: // FrameLabel
                var labels = tagsArray.labels;
                tag.frame = frame;
                tagsArray.labels[labels.length] = tag;
                break;
            case 15: // StartSound
            case 89: // StartSound2
                var sounds = tagsArray.sounds;
                tagsArray.sounds[sounds.length] = tag;
                break;
        }

        return tags;
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseDefineShape = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var characterId = bitio.getUI16();
        var bounds = _this.rect();

        if (tagType === 83) {
            var obj = {};
            obj.EdgeBounds = _this.rect();
            bitio.getUIBits(5); // Reserved
            obj.UsesFillWindingRule = bitio.getUIBits(1);
            obj.UsesNonScalingStrokes = bitio.getUIBits(1);
            obj.UsesScalingStrokes = bitio.getUIBits(1);
        }

        var shapes = _this.shapeWithStyle(tagType);
        _this.appendShapeTag(characterId, bounds, shapes, tagType);
    };

    /**
     * @returns {{xMin: number, xMax: number, yMin: number, yMax: number}}
     */
    SwfTag.prototype.rect = function ()
    {
        var bitio = this.bitio;
        bitio.byteAlign();

        var nBits = bitio.getUIBits(5);
        return {
            xMin: bitio.getSIBits(nBits),
            xMax: bitio.getSIBits(nBits),
            yMin: bitio.getSIBits(nBits),
            yMax: bitio.getSIBits(nBits)
        };
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.shapeWithStyle = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var fillStyles;
        var lineStyles;

        if (tagType === 46 || tagType === 84) {
            fillStyles = {fillStyleCount: 0, fillStyles: []};
            lineStyles = {lineStyleCount: 0, lineStyles: []};
        } else {
            fillStyles = _this.fillStyleArray(tagType);
            lineStyles = _this.lineStyleArray(tagType);
        }

        var numBits = bitio.getUI8();
        var NumFillBits = numBits >> 4;
        var NumLineBits = numBits & 0x0f;
        var ShapeRecords = _this.shapeRecords(tagType, {
            FillBits: NumFillBits,
            LineBits: NumLineBits
        });

        return {
            fillStyles: fillStyles,
            lineStyles: lineStyles,
            ShapeRecords: ShapeRecords
        };
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.fillStyleArray = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var fillStyleCount = bitio.getUI8();
        if ((tagType > 2) && (fillStyleCount === 0xff)) {
            fillStyleCount = bitio.getUI16();
        }

        var fillStyles = [];
        for (var i = fillStyleCount; i--;) {
            fillStyles[fillStyles.length] = _this.fillStyle(tagType);
        }

        return {
            fillStyleCount: fillStyleCount,
            fillStyles: fillStyles
        };
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.fillStyle = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        var bitType = bitio.getUI8();
        obj.fillStyleType = bitType;
        switch (bitType) {
            case 0x00:
                if (tagType === 32 || tagType === 83) {
                    obj.Color = _this.rgba();
                } else if (tagType === 46 || tagType === 84) {
                    obj.StartColor = _this.rgba();
                    obj.EndColor = _this.rgba();
                } else {
                    obj.Color = _this.rgb();
                }
                break;
            case 0x10:
            case 0x12:
                if (tagType === 46 || tagType === 84) {
                    obj.startGradientMatrix = _this.matrix();
                    obj.endGradientMatrix = _this.matrix();
                    obj.gradient = _this.gradient(tagType);
                } else {
                    obj.gradientMatrix = _this.matrix();
                    obj.gradient = _this.gradient(tagType);
                }
                break;
            case 0x13:
                obj.gradientMatrix = _this.matrix();
                obj.gradient = _this.focalGradient(tagType);
                break;
            case 0x40:
            case 0x41:
            case 0x42:
            case 0x43:
                obj.bitmapId = bitio.getUI16();
                if (tagType === 46 || tagType === 84) {
                    obj.startBitmapMatrix = _this.matrix();
                    obj.endBitmapMatrix = _this.matrix();
                } else {
                    obj.bitmapMatrix = _this.matrix();
                }
                break;
        }
        return obj;
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.rgb = function ()
    {
        var bitio = this.bitio;
        return {
            R: bitio.getUI8(),
            G: bitio.getUI8(),
            B: bitio.getUI8(),
            A: 1
        };
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.rgba = function ()
    {
        var bitio = this.bitio;
        return {
            R: bitio.getUI8(),
            G: bitio.getUI8(),
            B: bitio.getUI8(),
            A: bitio.getUI8() / 255
        };
    };

    /**
     * @returns {Array}
     */
    SwfTag.prototype.matrix = function ()
    {
        var bitio = this.bitio;
        bitio.byteAlign();

        var result = [1, 0, 0, 1, 0, 0];
        if (bitio.getUIBit()) {
            var nScaleBits = bitio.getUIBits(5);
            result[0] = bitio.getSIBits(nScaleBits) / 0x10000;
            result[3] = bitio.getSIBits(nScaleBits) / 0x10000;
        }

        if (bitio.getUIBit()) {
            var nRotateBits = bitio.getUIBits(5);
            result[1] = bitio.getSIBits(nRotateBits) / 0x10000;
            result[2] = bitio.getSIBits(nRotateBits) / 0x10000;
        }

        var nTranslateBits = bitio.getUIBits(5);
        result[4] = bitio.getSIBits(nTranslateBits);
        result[5] = bitio.getSIBits(nTranslateBits);

        return result;
    };

    /**
     * gradient
     * @param tagType
     * @returns {{SpreadMode: number, InterpolationMode: number, GradientRecords: Array}}
     */
    SwfTag.prototype.gradient = function (tagType)
    {
        var _this = this;
        var SpreadMode = 0;
        var InterpolationMode = 0;
        var NumGradients;
        var bitio = this.bitio;

        bitio.byteAlign();

        if (tagType === 46 || tagType === 84) {
            NumGradients = bitio.getUI8();
        } else {
            SpreadMode = bitio.getUIBits(2);
            InterpolationMode = bitio.getUIBits(2);
            NumGradients = bitio.getUIBits(4);
        }

        var GradientRecords = [];
        for (var i = NumGradients; i--;) {
            GradientRecords[GradientRecords.length] = _this.gradientRecord(tagType);
        }

        return {
            SpreadMode: SpreadMode,
            InterpolationMode: InterpolationMode,
            GradientRecords: GradientRecords
        };
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.gradientRecord = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        if (tagType === 46 || tagType === 84) {
            return {
                StartRatio: bitio.getUI8() / 255,
                StartColor: _this.rgba(),
                EndRatio: bitio.getUI8() / 255,
                EndColor: _this.rgba()
            };
        } else {
            var Ratio = bitio.getUI8();
            var Color = (tagType < 32) ? _this.rgb() : _this.rgba();
            return {Ratio: Ratio / 255, Color: Color};
        }
    };

    /**
     * @param tagType
     * @returns {{SpreadMode: number, InterpolationMode: number, GradientRecords: Array, FocalPoint: number}}
     */
    SwfTag.prototype.focalGradient = function (tagType)
    {
        var bitio = this.bitio;
        bitio.byteAlign();
        var _this = this;
        var SpreadMode = bitio.getUIBits(2);
        var InterpolationMode = bitio.getUIBits(2);
        var numGradients = bitio.getUIBits(4);

        var gradientRecords = [];
        for (var i = numGradients; i--;) {
            gradientRecords[gradientRecords.length] =
                _this.gradientRecord(tagType);
        }
        var FocalPoint = bitio.getFloat16();

        return {
            SpreadMode: SpreadMode,
            InterpolationMode: InterpolationMode,
            GradientRecords: gradientRecords,
            FocalPoint: FocalPoint
        };
    };

    /**
     * @param tagType
     * @returns {{lineStyleCount: number, lineStyles: Array}}
     */
    SwfTag.prototype.lineStyleArray = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var lineStyleCount = bitio.getUI8();
        if ((tagType > 2) && (lineStyleCount === 0xff)) {
            lineStyleCount = bitio.getUI16();
        }

        var array = [];
        for (var i = lineStyleCount; i--;) {
            array[array.length] = _this.lineStyles(tagType);
        }

        return {
            lineStyleCount: lineStyleCount,
            lineStyles: array
        };
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.lineStyles = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};

        obj.fillStyleType = 0;
        if (tagType === 46) {
            obj = {
                StartWidth: bitio.getUI16(),
                EndWidth: bitio.getUI16(),
                StartColor: _this.rgba(),
                EndColor: _this.rgba()
            };
        } else if (tagType === 84) {
            obj.StartWidth = bitio.getUI16();
            obj.EndWidth = bitio.getUI16();

            obj.StartCapStyle = bitio.getUIBits(2);
            obj.JoinStyle = bitio.getUIBits(2);
            obj.HasFillFlag = bitio.getUIBit();
            obj.NoHScaleFlag = bitio.getUIBit();
            obj.NoVScaleFlag = bitio.getUIBit();
            obj.PixelHintingFlag = bitio.getUIBit();
            bitio.getUIBits(5); // Reserved
            obj.NoClose = bitio.getUIBit();
            obj.EndCapStyle = bitio.getUIBits(2);

            if (obj.JoinStyle === 2) {
                obj.MiterLimitFactor = bitio.getUI16();
            }

            if (obj.HasFillFlag) {
                obj.FillType = _this.fillStyle(tagType);
            } else {
                obj.StartColor = _this.rgba();
                obj.EndColor = _this.rgba();
            }
        } else {
            obj.Width = bitio.getUI16();
            if (tagType === 83) {
                // DefineShape4
                obj.StartCapStyle = bitio.getUIBits(2);
                obj.JoinStyle = bitio.getUIBits(2);
                obj.HasFillFlag = bitio.getUIBit();
                obj.NoHScaleFlag = bitio.getUIBit();
                obj.NoVScaleFlag = bitio.getUIBit();
                obj.PixelHintingFlag = bitio.getUIBit();
                bitio.getUIBits(5); // Reserved
                obj.NoClose = bitio.getUIBit();
                obj.EndCapStyle = bitio.getUIBits(2);

                if (obj.JoinStyle === 2) {
                    obj.MiterLimitFactor = bitio.getUI16();
                }

                if (obj.HasFillFlag) {
                    obj.FillType = _this.fillStyle(tagType);
                } else {
                    obj.Color = _this.rgba();
                }
            } else if (tagType === 32) {
                // DefineShape3
                obj.Color = _this.rgba();
            } else {
                // DefineShape1or2
                obj.Color = _this.rgb();
            }
        }

        return obj;
    };

    /**
     * @param tagType
     * @param currentNumBits
     * @returns {Array}
     */
    SwfTag.prototype.shapeRecords = function (tagType, currentNumBits)
    {
        var _this = this;
        var bitio = _this.bitio;
        var shapeRecords = [];
        _this.currentPosition = {x: 0, y: 0};
        var _straightEdgeRecord = _this.straightEdgeRecord;
        var _curvedEdgeRecord = _this.curvedEdgeRecord;
        var _styleChangeRecord = _this.styleChangeRecord;

        while (true) {
            var first6Bits = bitio.getUIBits(6);
            var shape = 0;
            if (first6Bits & 0x20) {
                var numBits = first6Bits & 0x0f;
                if (first6Bits & 0x10) {
                    shape = _straightEdgeRecord.call(_this, tagType, numBits);
                } else {
                    shape = _curvedEdgeRecord.call(_this, tagType, numBits);
                }
            } else if (first6Bits) {
                shape =
                    _styleChangeRecord.call(_this, tagType, first6Bits, currentNumBits);
            }

            shapeRecords[shapeRecords.length] = shape;
            if (!shape) {
                bitio.byteAlign();
                break;
            }
        }
        return shapeRecords;
    };

    /**
     * @param tagType
     * @param numBits
     * @returns {{}}
     */
    SwfTag.prototype.straightEdgeRecord = function (tagType, numBits)
    {
        var _this = this;
        var bitio = _this.bitio;
        var deltaX = 0;
        var deltaY = 0;
        var GeneralLineFlag = bitio.getUIBit();
        if (GeneralLineFlag) {
            deltaX = bitio.getSIBits(numBits + 2);
            deltaY = bitio.getSIBits(numBits + 2);
        } else {
            var VertLineFlag = bitio.getUIBit();
            if (VertLineFlag) {
                deltaX = 0;
                deltaY = bitio.getSIBits(numBits + 2);
            } else {
                deltaX = bitio.getSIBits(numBits + 2);
                deltaY = 0;
            }
        }

        var AnchorX = deltaX;
        var AnchorY = deltaY;
        if (tagType !== 46 && tagType !== 84) {
            AnchorX = _this.currentPosition.x + deltaX;
            AnchorY = _this.currentPosition.y + deltaY;
            _this.currentPosition.x = AnchorX;
            _this.currentPosition.y = AnchorY;
        }

        return {
            ControlX: 0,
            ControlY: 0,
            AnchorX: AnchorX,
            AnchorY: AnchorY,
            isCurved: false,
            isChange: false
        };
    };

    /**
     * @param tagType
     * @param numBits
     * @returns {{}}
     */
    SwfTag.prototype.curvedEdgeRecord = function (tagType, numBits)
    {
        var _this = this;
        var bitio = _this.bitio;
        var controlDeltaX = bitio.getSIBits(numBits + 2);
        var controlDeltaY = bitio.getSIBits(numBits + 2);
        var anchorDeltaX = bitio.getSIBits(numBits + 2);
        var anchorDeltaY = bitio.getSIBits(numBits + 2);

        var ControlX = controlDeltaX;
        var ControlY = controlDeltaY;
        var AnchorX = anchorDeltaX;
        var AnchorY = anchorDeltaY;
        if (tagType !== 46 && tagType !== 84) {
            ControlX = _this.currentPosition.x + controlDeltaX;
            ControlY = _this.currentPosition.y + controlDeltaY;
            AnchorX = ControlX + anchorDeltaX;
            AnchorY = ControlY + anchorDeltaY;

            _this.currentPosition.x = AnchorX;
            _this.currentPosition.y = AnchorY;
        }

        return {
            ControlX: ControlX,
            ControlY: ControlY,
            AnchorX: AnchorX,
            AnchorY: AnchorY,
            isCurved: true,
            isChange: false
        };
    };

    /**
     * @param tagType
     * @param changeFlag
     * @param currentNumBits
     * @returns {{}}
     */
    SwfTag.prototype.styleChangeRecord = function (tagType, changeFlag, currentNumBits)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        obj.StateNewStyles = (changeFlag >> 4) & 1;
        obj.StateLineStyle = (changeFlag >> 3) & 1;
        obj.StateFillStyle1 = (changeFlag >> 2) & 1;
        obj.StateFillStyle0 = (changeFlag >> 1) & 1;
        obj.StateMoveTo = changeFlag & 1;

        if (obj.StateMoveTo) {
            var moveBits = bitio.getUIBits(5);
            obj.MoveX = bitio.getSIBits(moveBits);
            obj.MoveY = bitio.getSIBits(moveBits);
            _this.currentPosition.x = obj.MoveX;
            _this.currentPosition.y = obj.MoveY;
        }

        obj.FillStyle0 = 0;
        if (obj.StateFillStyle0) {
            obj.FillStyle0 = bitio.getUIBits(currentNumBits.FillBits);
        }

        obj.FillStyle1 = 0;
        if (obj.StateFillStyle1) {
            obj.FillStyle1 = bitio.getUIBits(currentNumBits.FillBits);
        }

        obj.LineStyle = 0;
        if (obj.StateLineStyle) {
            obj.LineStyle = bitio.getUIBits(currentNumBits.LineBits);
        }

        if (obj.StateNewStyles) {
            obj.FillStyles = _this.fillStyleArray(tagType);
            obj.LineStyles = _this.lineStyleArray(tagType);
            var numBits = bitio.getUI8();
            currentNumBits.FillBits = obj.NumFillBits = numBits >> 4;
            currentNumBits.LineBits = obj.NumLineBits = numBits & 0x0f;
        }
        obj.isChange = true;
        return obj;
    };

    /**
     * @param characterId
     * @param bounds
     * @param shapes
     * @param tagType
     */
    SwfTag.prototype.appendShapeTag = function (characterId, bounds, shapes, tagType)
    {
        var stage = this.stage;
        stage.setCharacter(characterId, {
            tagType: tagType,
            data: vtc.convert(shapes, false),
            bounds: bounds
        });
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseDefineBitsLossLess = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var startOffset = bitio.byte_offset;
        var CharacterId = bitio.getUI16();
        var format = bitio.getUI8();
        var width = bitio.getUI16();
        var height = bitio.getUI16();

        var isAlpha = (tagType === 36);
        var colorTableSize = 0;
        if (format === 3) {
            colorTableSize = bitio.getUI8() + 1;
        }

        // unCompress
        var sub = bitio.byte_offset - startOffset;
        var compressed = bitio.getData(length - sub);
        var data = bitio.unzip(compressed, false);

        // canvas
        var canvas = cacheStore.getCanvas();
        canvas.width = width;
        canvas.height = height;
        var imageContext = canvas.getContext("2d");
        var imgData = imageContext.createImageData(width, height);
        var pxData = imgData.data;

        var idx = 0;
        var pxIdx = 0;
        var x = width;
        var y = height;
        if (format === 5 && !isAlpha) {
            idx = 0;
            pxIdx = 0;
            for (y = height; y--;) {
                for (x = width; x--;) {
                    idx++;
                    pxData[pxIdx++] = data[idx++];
                    pxData[pxIdx++] = data[idx++];
                    pxData[pxIdx++] = data[idx++];
                    pxData[pxIdx++] = 255;
                }
            }
        } else {
            var bpp = (isAlpha) ? 4 : 3;
            var cmIdx = colorTableSize * bpp;
            var pad = 0;
            if (colorTableSize) {
                pad = ((width + 3) & ~3) - width;
            }

            for (y = height; y--;) {
                for (x = width; x--;) {
                    idx = (colorTableSize) ? data[cmIdx++] * bpp : cmIdx++ * bpp;
                    if (!isAlpha) {
                        pxData[pxIdx++] = data[idx++];
                        pxData[pxIdx++] = data[idx++];
                        pxData[pxIdx++] = data[idx++];
                        idx++;
                        pxData[pxIdx++] = 255;
                    } else {
                        var alpha = (format === 3) ? data[idx + 3] : data[idx++];
                        if (!isAlphaBug) {
                            pxData[pxIdx++] = data[idx++] * 255 / alpha;
                            pxData[pxIdx++] = data[idx++] * 255 / alpha;
                            pxData[pxIdx++] = data[idx++] * 255 / alpha;
                        } else {
                            pxData[pxIdx++] = data[idx++];
                            pxData[pxIdx++] = data[idx++];
                            pxData[pxIdx++] = data[idx++];
                        }
                        pxData[pxIdx++] = alpha;

                        if (format === 3) {
                            idx++;
                        }
                    }
                }
                cmIdx += pad;
            }
        }

        imageContext.putImageData(imgData, 0, 0);
        stage.setCharacter(CharacterId, imageContext);
    };

    /**
     * parseExportAssets
     */
    SwfTag.prototype.parseExportAssets = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var count = bitio.getUI16();

        var exportAssets = stage.exportAssets;
        var packages = stage.packages;
        while (count--) {
            var id = bitio.getUI16();
            var name = bitio.getDataUntil("\0");
            if (name.substr(0, 10) === "__Packages") {
                packages[id] = 1;
            }
            exportAssets[name] = id;
        }

        stage.exportAssets = exportAssets;
    };

    /**
     * @param length
     * @returns {string}
     */
    SwfTag.prototype.parseJPEGTables = function (length)
    {
        return this.bitio.getData(length);
    };

    /**
     * @param tagType
     * @param length
     * @param jpegTables
     */
    SwfTag.prototype.parseDefineBits = function (tagType, length, jpegTables)
    {
        var _this = this;
        var bitio = _this.bitio;
        var startOffset = bitio.byte_offset;
        var CharacterId = bitio.getUI16();
        var sub = bitio.byte_offset - startOffset;

        var ImageDataLen = length - sub;
        if (tagType === 35 || tagType === 90) {
            ImageDataLen = bitio.getUI32();
        }

        if (tagType === 90) {
            var DeblockParam = bitio.getUI16();
            console.log("DeblockParam", DeblockParam);
        }

        var JPEGData = bitio.getData(ImageDataLen);
        var BitmapAlphaData = false;
        if (tagType === 35 || tagType === 90) {
            BitmapAlphaData =
                bitio.getData(length - sub - ImageDataLen);
        }
        bitio.byte_offset = startOffset + length;

        // render
        var stage = _this.stage;
        stage.imgUnLoadCount++;
        var image = _document.createElement("img");
        image.addEventListener("load", function()
        {
            var width = this.width;
            var height = this.height;
            var canvas = cacheStore.getCanvas();
            canvas.width = width;
            canvas.height = height;
            var imageContext = canvas.getContext("2d");
            imageContext.drawImage(this, 0, 0, width, height);

            if (BitmapAlphaData) {
                var data = bitio.unzip(BitmapAlphaData, false);
                var imgData = imageContext.getImageData(0, 0, width, height);
                var pxData = imgData.data;
                var pxIdx = 3;
                var len = width * height;
                for (var i = 0; i < len; i++) {
                    pxData[pxIdx] = data[i];
                    pxIdx += 4;
                }
                imageContext.putImageData(imgData, 0, 0);
            }

            stage.setCharacter(CharacterId, imageContext);
            stage.imgUnLoadCount--;
        });

        if (jpegTables !== null && jpegTables.length > 4) {
            var margeData = [];
            var len = jpegTables.length - 2;
            for (var idx = 0; idx < len; idx++) {
                margeData[margeData.length] = jpegTables[idx];
            }

            len = JPEGData.length;
            for (idx = 2; idx < len; idx++) {
                margeData[margeData.length] = JPEGData[idx];
            }

            JPEGData = margeData;
        }

        image.src = "data:image/jpeg;base64," +
            _this.base64encode(_this.parseJpegData(JPEGData));

        // for android bug
        _setTimeout(function () {}, 0);
    };

    /**
     * @param JPEGData
     * @returns {string}
     */
    SwfTag.prototype.parseJpegData = function (JPEGData)
    {
        var i = 0;
        var idx = 0;
        var str = "";
        var length = JPEGData.length;

        // erroneous
        if (JPEGData[0] === 0xFF && JPEGData[1] === 0xD9 && JPEGData[2] === 0xFF && JPEGData[3] === 0xD8) {
            for (i = 4; i < length; i++) {
                str += _fromCharCode(JPEGData[i]);
            }
        } else if (JPEGData[i++] === 0xFF && JPEGData[i++] === 0xD8) {
            for (idx = 0; idx < i; idx++) {
                str += _fromCharCode(JPEGData[idx]);
            }
            while (i < length) {
                if (JPEGData[i] === 0xFF) {
                    if (JPEGData[i + 1] === 0xD9 && JPEGData[i + 2] === 0xFF && JPEGData[i + 3] === 0xD8) {
                        i += 4;
                        for (idx = i; idx < length; idx++) {
                            str += _fromCharCode(JPEGData[idx]);
                        }
                        break;
                    } else if (JPEGData[i + 1] === 0xDA) {
                        for (idx = i; idx < length; idx++) {
                            str += _fromCharCode(JPEGData[idx]);
                        }
                        break;
                    } else {
                        var segmentLength = (JPEGData[i + 2] << 8) + JPEGData[i + 3] + i + 2;
                        for (idx = i; idx < segmentLength; idx++) {
                            str += _fromCharCode(JPEGData[idx]);
                        }
                        i += segmentLength - i;
                    }
                }
            }
        }
        return str;
    };

    /**
     * @param data
     * @returns {*}
     */
    SwfTag.prototype.base64encode = function (data)
    {
        if (isBtoa) {
            return window.btoa(data);
        }

        var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        var out = [];
        var i = 0;
        var len = data.length;

        while (i < len) {
            var c1 = data.charCodeAt(i++) & 0xff;
            if (i === len) {
                out[out.length] = base64EncodeChars.charAt(c1 >> 2);
                out[out.length] = base64EncodeChars.charAt((c1 & 0x3) << 4);
                out[out.length] = "==";
                break;
            }

            var c2 = data.charCodeAt(i++);
            if (i === len) {
                out[out.length] = base64EncodeChars.charAt(c1 >> 2);
                out[out.length] = base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
                out[out.length] = base64EncodeChars.charAt((c2 & 0xF) << 2);
                out[out.length] = "=";
                break;
            }

            var c3 = data.charCodeAt(i++);
            out[out.length] = base64EncodeChars.charAt(c1 >> 2);
            out[out.length] = base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
            out[out.length] = base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
            out[out.length] = base64EncodeChars.charAt(c3 & 0x3F);
        }

        return out.join("");
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseDefineFont = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var endOffset = bitio.byte_offset + length;
        var i = 0;
        var len = 0;
        var obj = {};
        obj.tagType = tagType;
        obj.FontId = bitio.getUI16();

        var numGlyphs = 0;
        if (tagType === 48 || tagType === 75) {
            var fontFlags = bitio.getUI8();
            obj.FontFlagsHasLayout = (fontFlags >>> 7) & 1;
            obj.FontFlagsShiftJIS = (fontFlags >>> 6) & 1;
            obj.FontFlagsSmallText = (fontFlags >>> 5) & 1;
            obj.FontFlagsANSI = (fontFlags >>> 4) & 1;
            obj.FontFlagsWideOffsets = (fontFlags >>> 3) & 1;
            obj.FontFlagsWideCodes = (fontFlags >>> 2) & 1;
            obj.FontFlagsItalic = (fontFlags >>> 1) & 1;
            obj.FontFlagsBold = (fontFlags) & 1;
            bitio.byteAlign();

            obj.LanguageCode = bitio.getUI8();
            obj.FontNameLen = bitio.getUI8();
            if (obj.FontNameLen) {
                var startOffset = bitio.byte_offset;
                var data = bitio.getData(obj.FontNameLen);
                var str = "";
                len = obj.FontNameLen;
                for (i = 0; i < len; i++) {
                    if (data[i] > 127) {
                        continue;
                    }
                    str += _fromCharCode(data[i]);
                }

                var fontName;
                if (obj.FontFlagsShiftJIS || obj.LanguageCode === 2) {
                    fontName = bitio.decodeToShiftJis(str);
                } else {
                    fontName = decodeURIComponent(str);
                }

                obj.FontName = _this.getFontName(fontName);
                bitio.byte_offset = startOffset + obj.FontNameLen;
            }

            numGlyphs = bitio.getUI16();
            obj.NumGlyphs = numGlyphs;
        }

        // offset
        var offset = bitio.byte_offset;
        if (tagType === 10) {
            numGlyphs = bitio.getUI16();
        }

        if (numGlyphs) {
            var OffsetTable = [];
            if (tagType === 10) {
                OffsetTable[0] = numGlyphs;
                numGlyphs /= 2;
                numGlyphs--;
            }

            if (obj.FontFlagsWideOffsets) {
                for (i = numGlyphs; i--;) {
                    OffsetTable[OffsetTable.length] = bitio.getUI32();
                }
                if (tagType !== 10) {
                    obj.CodeTableOffset = bitio.getUI32();
                }
            } else {
                for (i = numGlyphs; i--;) {
                    OffsetTable[OffsetTable.length] = bitio.getUI16();
                }
                if (tagType !== 10) {
                    obj.CodeTableOffset = bitio.getUI16();
                }
            }

            // Shape
            var GlyphShapeTable = [];
            if (tagType === 10) {
                numGlyphs++;
            }

            for (i = 0; i < numGlyphs; i++) {
                bitio.setOffset(OffsetTable[i] + offset, 0);

                var numBits = bitio.getUI8();
                var NumFillBits = numBits >> 4;
                var NumLineBits = numBits & 0x0f;

                var currentNumBits = {
                    FillBits: NumFillBits,
                    LineBits: NumLineBits
                };

                var shapes = {};
                shapes.ShapeRecords = _this.shapeRecords(tagType, currentNumBits);
                shapes.lineStyles = {
                    lineStyles: [{
                        Color: {R: 0, G: 0, B: 0, A: 1},
                        lineStyleType: 0
                    }]
                };
                shapes.fillStyles = {
                    fillStyles: [{
                        Color: {R: 0, G: 0, B: 0, A: 1},
                        fillStyleType: 0
                    }]
                };

                GlyphShapeTable[GlyphShapeTable.length] = shapes;
            }
            obj.GlyphShapeTable = GlyphShapeTable;

            if (tagType === 48 || tagType === 75) {
                bitio.setOffset(obj.CodeTableOffset + offset, 0);
                var CodeTable = [];
                if (obj.FontFlagsWideCodes) {
                    for (i = numGlyphs; i--;) {
                        CodeTable[CodeTable.length] = bitio.getUI16();
                    }
                } else {
                    for (i = numGlyphs; i--;) {
                        CodeTable[CodeTable.length] = bitio.getUI8();
                    }
                }
                obj.CodeTable = CodeTable;

                if (obj.FontFlagsHasLayout) {
                    obj.FontAscent = bitio.getUI16();
                    obj.FontDescent = bitio.getUI16();
                    obj.FontLeading = bitio.getUI16();

                    var FontAdvanceTable = [];
                    for (i = numGlyphs; i--;) {
                        FontAdvanceTable[FontAdvanceTable.length] = bitio.getUI16();
                    }
                    obj.FontAdvanceTable = FontAdvanceTable;

                    var FontBoundsTable = [];
                    for (i = numGlyphs; i--;) {
                        FontBoundsTable[FontBoundsTable.length] = _this.rect();
                    }
                    obj.FontBoundsTable = FontBoundsTable;

                    if (tagType === 75) {
                        obj.KerningCount = bitio.getUI16();
                        obj.KerningRecord = [];
                        for (i = obj.KerningCount; i--;) {
                            var FontKerningCode1 = (obj.FontFlagsWideCodes) ? bitio.getUI16() : bitio.getUI8();
                            var FontKerningCode2 = (obj.FontFlagsWideCodes) ? bitio.getUI16() : bitio.getUI8();
                            var FontKerningAdjustment = bitio.getSIBits(16);
                            obj.KerningRecord[obj.KerningRecord.length] = {
                                FontKerningCode1: FontKerningCode1,
                                FontKerningCode2: FontKerningCode2,
                                FontKerningAdjustment: FontKerningAdjustment
                            };
                        }
                    }
                }
            }
        }

        bitio.byte_offset = endOffset;
        stage.setCharacter(obj.FontId, obj);
        stage.fonts[obj.FontName] = obj;
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseDefineFontInfo = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var endOffset = bitio.byte_offset + length;

        var obj = {};
        obj.tagType = tagType;
        obj.FontId = bitio.getUI16();
        var len = bitio.getUI8();
        var data = bitio.getData(len);
        var str = "";
        for (var i = 0; i < len; i++) {
            if (data[i] > 127) {
                continue;
            }
            str += _fromCharCode(data[i]);
        }

        obj.FontFlagsReserved = bitio.getUIBits(2);
        obj.FontFlagsSmallText = bitio.getUIBits(1);
        obj.FontFlagsShiftJIS = bitio.getUIBits(1);
        obj.FontFlagsANSI = bitio.getUIBits(1);
        obj.FontFlagsItalic = bitio.getUIBits(1);
        obj.FontFlagsBold = bitio.getUIBits(1);
        obj.FontFlagsWideCodes = bitio.getUIBits(1);
        if (tagType === 62) {
            obj.LanguageCode = bitio.getUI8();
        }

        var fontName;
        if (obj.FontFlagsShiftJIS || obj.LanguageCode === 2) {
            fontName = bitio.decodeToShiftJis(str);
        } else {
            fontName = decodeURIComponent(str);
        }
        obj.FontName = _this.getFontName(fontName);

        var CodeTable = [];
        bitio.byteAlign();
        var tLen = endOffset - bitio.byte_offset;
        if (obj.FontFlagsWideCodes || tagType === 62) {
            while (tLen) {
                CodeTable[CodeTable.length] = bitio.getUI16();
                tLen -= 2;
            }
        } else {
            while (tLen) {
                CodeTable[CodeTable.length] = bitio.getUI8();
                tLen--;
            }
        }
        obj.CodeTable = CodeTable;
    };

    /**
     * @param fontName
     * @returns {string}
     */
    SwfTag.prototype.getFontName = function (fontName)
    {
        var length = fontName.length;
        var str = fontName.substr(length - 1);
        if (str.charCodeAt(0) === 0) {
            fontName = fontName.slice(0, -1);
        }

        switch (fontName) {
            case "_sans":
                return "sans-serif";
            case "_serif":
                return "serif";
            case "_typewriter":
                return "monospace";
            default:
                var ander = fontName.substr(0, 1);
                if (ander === "_") {
                    return "sans-serif";
                }
                return fontName;
        }
    };

    /**
     * parseDefineFontName
     */
    SwfTag.prototype.parseDefineFontName = function ()
    {
        var bitio = this.bitio;
        bitio.getUI16(); // FontId
        bitio.getDataUntil("\0"); // FontName
        bitio.getDataUntil("\0"); // FontCopyright
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseDefineText = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var obj = {};
        var characterId = bitio.getUI16();
        obj.tagType = tagType;
        obj.bounds = _this.rect();
        obj.matrix = _this.matrix();
        var GlyphBits = bitio.getUI8();
        var AdvanceBits = bitio.getUI8();
        obj.textRecords = _this.getTextRecords(tagType, GlyphBits, AdvanceBits);
        stage.setCharacter(characterId, obj);
    };

    /**
     * @param tagType
     * @param GlyphBits
     * @param AdvanceBits
     * @returns {Array}
     */
    SwfTag.prototype.getTextRecords = function (tagType, GlyphBits, AdvanceBits)
    {
        var _this = this;
        var bitio = _this.bitio;
        var array = [];
        while (bitio.getUI8() !== 0) {
            bitio.incrementOffset(-1, 0);

            var obj = {};
            obj.TextRecordType = bitio.getUIBits(1);
            obj.StyleFlagsReserved = bitio.getUIBits(3);
            obj.StyleFlagsHasFont = bitio.getUIBits(1);
            obj.StyleFlagsHasColor = bitio.getUIBits(1);
            obj.StyleFlagsHasYOffset = bitio.getUIBits(1);
            obj.StyleFlagsHasXOffset = bitio.getUIBits(1);
            if (obj.StyleFlagsHasFont) {
                obj.FontId = bitio.getUI16();
            }

            if (obj.StyleFlagsHasColor) {
                if (tagType === 11) {
                    obj.TextColor = _this.rgb();
                } else {
                    obj.TextColor = _this.rgba();
                }
            }

            if (obj.StyleFlagsHasXOffset) {
                obj.XOffset = bitio.getUI16();
            }

            if (obj.StyleFlagsHasYOffset) {
                obj.YOffset = bitio.getUI16();
            }

            if (obj.StyleFlagsHasFont) {
                obj.TextHeight = bitio.getUI16();
            }

            obj.GlyphCount = bitio.getUI8();
            obj.GlyphEntries = _this.getGlyphEntries(
                obj.GlyphCount, GlyphBits, AdvanceBits
            );

            array[array.length] = obj;
        }

        return array;
    };

    /**
     * @param count
     * @param GlyphBits
     * @param AdvanceBits
     * @returns {Array}
     */
    SwfTag.prototype.getGlyphEntries = function (count, GlyphBits, AdvanceBits)
    {
        var bitio = this.bitio;
        var array = [];
        for (var i = count; i--;) {
            array[array.length] = {
                GlyphIndex: bitio.getUIBits(GlyphBits),
                GlyphAdvance: bitio.getSIBits(AdvanceBits)
            };
        }
        return array;
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseDefineEditText = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var obj = {};
        var isJis = false;

        obj.CharacterId = bitio.getUI16();
        var bounds = _this.rect();

        var flag1 = bitio.getUI8();
        obj.HasText = (flag1 >>> 7) & 1;
        obj.WordWrap = (flag1 >>> 6) & 1;
        obj.Multiline = (flag1 >>> 5) & 1;
        obj.Password = (flag1 >>> 4) & 1;
        obj.ReadOnly = (flag1 >>> 3) & 1;
        obj.HasTextColor = (flag1 >>> 2) & 1;
        obj.HasMaxLength = (flag1 >>> 1) & 1;
        obj.HasFont = flag1 & 1;

        var flag2 = bitio.getUI8();
        obj.HasFontClass = (flag2 >>> 7) & 1;
        obj.AutoSize = (flag2 >>> 6) & 1;
        obj.HasLayout = (flag2 >>> 5) & 1;
        obj.NoSelect = (flag2 >>> 4) & 1;
        obj.Border = (flag2 >>> 3) & 1;
        obj.WasStatic = (flag2 >>> 2) & 1;
        obj.HTML = (flag2 >>> 1) & 1;
        obj.UseOutlines = flag2 & 1;

        if (obj.HasFont) {
            obj.FontID = bitio.getUI16();
            var fontData = stage.getCharacter(obj.FontID);
            isJis = (fontData.FontFlagsShiftJIS) ? true : false;
            if (obj.HasFontClass) {
                obj.FontClass = bitio.getDataUntil("\0");
            }
            obj.FontHeight = bitio.getUI16();
        }

        if (obj.HasTextColor) {
            obj.TextColor = _this.rgba();
        }

        if (obj.HasMaxLength) {
            obj.MaxLength = bitio.getUI16();
        }

        if (obj.HasLayout) {
            obj.Align = bitio.getUI8();
            obj.LeftMargin = bitio.getUI16();
            obj.RightMargin = bitio.getUI16();
            obj.Indent = bitio.getUI16();
            obj.Leading = bitio.getUI16();
        }

        var VariableName = bitio.getDataUntil("\0", isJis) + "";
        obj.VariableName = (VariableName === "") ? null : VariableName;
        obj.InitialText = "";
        if (obj.HasText) {
            var text = bitio.getDataUntil("\0", isJis);
            if (obj.HTML) {
                if (text.indexOf("<sbr />") !== -1) {
                    text = text.replace(new RegExp("<sbr />", "gi"), "\n");
                }
                if (text.indexOf("<b>") !== -1) {
                    text = text.replace(new RegExp("<b>", "gi"), "");
                    text = text.replace(new RegExp("</b>", "gi"), "");
                }

                var span = _document.createElement("span");
                span.innerHTML = text;

                var tags = span.getElementsByTagName("p");
                var length = tags.length;
                var tagData = [];
                for (var i = 0; i < length; i++) {
                    tagData[i] = tags[i];
                }
                obj.InitialText = tagData;
            } else {
                obj.InitialText = text;
            }
        }

        stage.setCharacter(obj.CharacterId, {
            data: obj,
            bounds: bounds,
            tagType: tagType
        });
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseDefineMorphShape = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var obj = {};
        obj.tagType = tagType;
        obj.CharacterId = bitio.getUI16();

        obj.StartBounds = _this.rect();
        obj.EndBounds = _this.rect();

        if (tagType === 84) {
            obj.StartEdgeBounds = _this.rect();
            obj.EndEdgeBounds = _this.rect();
            bitio.getUIBits(6); // Reserved
            obj.UsesNonScalingStrokes = bitio.getUIBits(1);
            obj.UsesScalingStrokes = bitio.getUIBits(1);
        }

        var offset = bitio.getUI32();
        var endOffset = bitio.byte_offset + offset;

        obj.MorphFillStyles = _this.fillStyleArray(tagType);
        obj.MorphLineStyles = _this.lineStyleArray(tagType);

        obj.StartEdges = _this.shapeWithStyle(tagType);
        if (bitio.byte_offset !== endOffset) {
            bitio.byte_offset = endOffset;
        }

        obj.EndEdges = _this.shapeWithStyle(tagType);

        // fill1 control
        var startPosition = {x: 0, y: 0};
        var endPosition = {x: 0, y: 0};
        var StartRecords = obj.StartEdges.ShapeRecords;
        var EndRecords = obj.EndEdges.ShapeRecords;
        var StartRecordLength = StartRecords.length;
        var EndRecordLength = EndRecords.length;
        var length = _max(StartRecordLength, EndRecordLength);
        for (var i = 0; i < length; i++) {
            var addRecode = {};
            var StartRecord = StartRecords[i];
            var EndRecord = EndRecords[i];
            if (!StartRecord && !EndRecord) {
                continue;
            }

            if (!StartRecord.isChange && !EndRecord.isChange) {
                if (StartRecord.isCurved) {
                    startPosition.x += StartRecord.ControlX + StartRecord.AnchorX;
                    startPosition.y += StartRecord.ControlY + StartRecord.AnchorY;
                } else {
                    startPosition.x += StartRecord.AnchorX;
                    startPosition.y += StartRecord.AnchorY;
                }

                if (EndRecord.isCurved) {
                    endPosition.x += EndRecord.ControlX + EndRecord.AnchorX;
                    endPosition.y += EndRecord.ControlY + EndRecord.AnchorY;
                } else {
                    endPosition.x += EndRecord.AnchorX;
                    endPosition.y += EndRecord.AnchorY;
                }
                continue;
            }

            if (StartRecord.isChange && !EndRecord.isChange) {
                addRecode = {
                    FillStyle0: StartRecord.FillStyle0,
                    FillStyle1: StartRecord.FillStyle1,
                    LineStyle: StartRecord.LineStyle,
                    StateFillStyle0: StartRecord.StateFillStyle0,
                    StateFillStyle1: StartRecord.StateFillStyle1,
                    StateLineStyle: StartRecord.StateLineStyle,
                    StateMoveTo: StartRecord.StateMoveTo,
                    StateNewStyles: StartRecord.StateNewStyles,
                    isChange: true
                };

                if (StartRecord.StateMoveTo) {
                    addRecode.MoveX = endPosition.x;
                    addRecode.MoveY = endPosition.y;
                    startPosition.x = StartRecord.MoveX;
                    startPosition.y = StartRecord.MoveY;
                }

                EndRecords.splice(i, 0, addRecode);
            } else if (!StartRecord.isChange && EndRecord.isChange) {
                addRecode = {
                    FillStyle0: EndRecord.FillStyle0,
                    FillStyle1: EndRecord.FillStyle1,
                    LineStyle: EndRecord.LineStyle,
                    StateFillStyle0: EndRecord.StateFillStyle0,
                    StateFillStyle1: EndRecord.StateFillStyle1,
                    StateLineStyle: EndRecord.StateLineStyle,
                    StateMoveTo: EndRecord.StateMoveTo,
                    StateNewStyles: EndRecord.StateNewStyles,
                    isChange: true
                };

                if (EndRecord.StateMoveTo) {
                    addRecode.MoveX = startPosition.x;
                    addRecode.MoveY = startPosition.y;
                    endPosition.x = EndRecord.MoveX;
                    endPosition.y = EndRecord.MoveY;
                }

                StartRecords.splice(i, 0, addRecode);
            } else {
                if (StartRecord.StateMoveTo) {
                    startPosition.x = StartRecord.MoveX;
                    startPosition.y = StartRecord.MoveY;
                }

                if (EndRecord.StateMoveTo) {
                    endPosition.x = EndRecord.MoveX;
                    endPosition.y = EndRecord.MoveY;
                }
            }
        }

        var FillType = 0;
        var FillStyle = 0;
        length = obj.StartEdges.ShapeRecords.length;
        for (i = 0; i < length; i++) {
            var record = StartRecords[i];
            if (!record.isChange) {
                continue;
            }
            if (record.StateFillStyle0) {
                FillStyle = record.FillStyle0;
            }

            if (FillStyle) {
                record.StateFillStyle0 = 1;
                record.StateFillStyle1 = 1;
                if (FillType) {
                    record.FillStyle0 = 0;
                    record.FillStyle1 = FillStyle;
                } else {
                    record.FillStyle0 = FillStyle;
                    record.FillStyle1 = 0;
                }
            } else {
                record.StateFillStyle1 = 1;
                record.FillStyle1 = 0;
            }

            FillType = (FillType) ? 0 : 1;
        }

        stage.setCharacter(obj.CharacterId, obj);
    };

    /**
     * @param char
     * @param ratio
     * @returns {{data: Array, bounds: {xMax: number, xMin: number, yMax: number, yMin: number}}}
     */
    SwfTag.prototype.buildMorphShape = function (char, ratio)
    {
        var per = (ratio === undefined) ? 0 : ratio / 65535;
        var startPer = 1 - per;
        var newShapeRecords = [];

        var morphLineStyles = char.MorphLineStyles;
        var lineStyles = morphLineStyles.lineStyles;
        var lineStyleCount = morphLineStyles.lineStyleCount;

        var morphFillStyles = char.MorphFillStyles;
        var fillStyles = morphFillStyles.fillStyles;
        var fillStyleCount = morphFillStyles.fillStyleCount;

        var StartEdges = char.StartEdges;
        var StartShapeRecords = StartEdges.ShapeRecords;

        var EndEdges = char.EndEdges;
        var EndShapeRecords = EndEdges.ShapeRecords;

        var shapes = {
            lineStyles: {
                lineStyleCount: lineStyleCount,
                lineStyles: []
            },
            fillStyles: {
                fillStyleCount: fillStyleCount,
                fillStyles: []
            },
            ShapeRecords: []
        };

        var position = {x: 0, y: 0};
        var len = StartShapeRecords.length;
        for (var i = 0; i < len; i++) {
            var StartRecord = StartShapeRecords[i];
            if (!StartRecord) {
                continue;
            }

            var newRecord = {};
            var EndRecord = EndShapeRecords[i];
            if (StartRecord.isChange) {
                var MoveX = 0;
                var MoveY = 0;

                if (StartRecord.StateMoveTo === 1) {
                    MoveX = StartRecord.MoveX * startPer + EndRecord.MoveX * per;
                    MoveY = StartRecord.MoveY * startPer + EndRecord.MoveY * per;
                    position.x = MoveX;
                    position.y = MoveY;
                }

                newRecord = {
                    FillStyle0: StartRecord.FillStyle0,
                    FillStyle1: StartRecord.FillStyle1,
                    LineStyle: StartRecord.LineStyle,
                    MoveX: MoveX,
                    MoveY: MoveY,
                    StateFillStyle0: StartRecord.StateFillStyle0,
                    StateFillStyle1: StartRecord.StateFillStyle1,
                    StateLineStyle: StartRecord.StateLineStyle,
                    StateMoveTo: StartRecord.StateMoveTo,
                    StateNewStyles: StartRecord.StateNewStyles,
                    isChange: true
                };
            } else {
                var AnchorX = 0;
                var AnchorY = 0;
                var ControlX = 0;
                var ControlY = 0;

                var startAnchorX = StartRecord.AnchorX;
                var startAnchorY = StartRecord.AnchorY;
                var endAnchorX = EndRecord.AnchorX;
                var endAnchorY = EndRecord.AnchorY;

                var startControlX = StartRecord.ControlX;
                var startControlY = StartRecord.ControlY;
                var endControlX = EndRecord.ControlX;
                var endControlY = EndRecord.ControlY;

                if (per > 0 && per < 1 && StartRecord.isCurved !== EndRecord.isCurved) {
                    if (!StartRecord.isCurved) {
                        startAnchorX = StartRecord.AnchorX / 2;
                        startAnchorY = StartRecord.AnchorY / 2;
                        startControlX = startAnchorX;
                        startControlY = startAnchorY;
                    }
                    if (!EndRecord.isCurved) {
                        endAnchorX = EndRecord.AnchorX / 2;
                        endAnchorY = EndRecord.AnchorY / 2;
                        endControlX = endAnchorX;
                        endControlY = endAnchorY;
                    }
                }

                ControlX = startControlX * startPer + endControlX * per + position.x;
                ControlY = startControlY * startPer + endControlY * per + position.y;
                AnchorX = startAnchorX * startPer + endAnchorX * per + ControlX;
                AnchorY = startAnchorY * startPer + endAnchorY * per + ControlY;

                position.x = AnchorX;
                position.y = AnchorY;

                newRecord = {
                    AnchorX: AnchorX,
                    AnchorY: AnchorY,
                    ControlX: ControlX,
                    ControlY: ControlY,
                    isChange: false,
                    isCurved: (StartRecord.isCurved || EndRecord.isCurved)
                };
            }

            newShapeRecords[i] = newRecord;
        }
        newShapeRecords[newShapeRecords.length] = 0;
        shapes.ShapeRecords = newShapeRecords;

        var EndColor;
        var StartColor;
        var color;
        for (i = 0; i < lineStyleCount; i++) {
            var lineStyle = lineStyles[i];
            EndColor = lineStyle.EndColor;
            StartColor = lineStyle.StartColor;
            color = {
                R: _floor(StartColor.R * startPer + EndColor.R * per),
                G: _floor(StartColor.G * startPer + EndColor.G * per),
                B: _floor(StartColor.B * startPer + EndColor.B * per),
                A: StartColor.A * startPer + EndColor.A * per
            };

            var EndWidth = lineStyles[i].EndWidth;
            var StartWidth = lineStyles[i].StartWidth;
            shapes.lineStyles.lineStyles[i] = {
                Width: _floor(StartWidth * startPer + EndWidth * per),
                Color: color,
                fillStyleType: 0
            };
        }

        for (i = 0; i < fillStyleCount; i++) {
            var fillStyle = fillStyles[i];
            var fillStyleType = fillStyle.fillStyleType;

            if (fillStyleType === 0x00) {
                EndColor = fillStyle.EndColor;
                StartColor = fillStyle.StartColor;
                color = {
                    R: _floor(StartColor.R * startPer + EndColor.R * per),
                    G: _floor(StartColor.G * startPer + EndColor.G * per),
                    B: _floor(StartColor.B * startPer + EndColor.B * per),
                    A: StartColor.A * startPer + EndColor.A * per
                };

                shapes.fillStyles.fillStyles[i] = {
                    Color: color,
                    fillStyleType: fillStyleType
                };
            } else {
                var EndGradientMatrix = fillStyle.endGradientMatrix;
                var StartGradientMatrix = fillStyle.startGradientMatrix;
                var matrix = [
                    StartGradientMatrix[0] * startPer + EndGradientMatrix[0] * per,
                    StartGradientMatrix[1] * startPer + EndGradientMatrix[1] * per,
                    StartGradientMatrix[2] * startPer + EndGradientMatrix[2] * per,
                    StartGradientMatrix[3] * startPer + EndGradientMatrix[3] * per,
                    StartGradientMatrix[4] * startPer + EndGradientMatrix[4] * per,
                    StartGradientMatrix[5] * startPer + EndGradientMatrix[5] * per
                ];

                var gRecords = [];
                var gradient = fillStyle.gradient;
                var GradientRecords = gradient.GradientRecords;
                var gLen = GradientRecords.length;
                for (var gIdx = 0; gIdx < gLen; gIdx++) {
                    var gRecord = GradientRecords[gIdx];
                    EndColor = gRecord.EndColor;
                    StartColor = gRecord.StartColor;
                    color = {
                        R: _floor(StartColor.R * startPer + EndColor.R * per),
                        G: _floor(StartColor.G * startPer + EndColor.G * per),
                        B: _floor(StartColor.B * startPer + EndColor.B * per),
                        A: StartColor.A * startPer + EndColor.A * per
                    };

                    gRecords[gIdx] = {
                        Color: color,
                        Ratio: gRecord.StartRatio * startPer + gRecord.EndRatio * per
                    };
                }

                shapes.fillStyles.fillStyles[i] = {
                    gradient: {GradientRecords: gRecords},
                    gradientMatrix: matrix,
                    fillStyleType: fillStyleType
                };
            }
        }

        var EndBounds = char.EndBounds;
        var StartBounds = char.StartBounds;
        var bounds = {
            xMax: StartBounds.xMax * startPer + EndBounds.xMax * per,
            xMin: StartBounds.xMin * startPer + EndBounds.xMin * per,
            yMax: StartBounds.yMax * startPer + EndBounds.yMax * per,
            yMin: StartBounds.yMin * startPer + EndBounds.yMin * per
        };

        return {
            data: vtc.convert(shapes, true),
            bounds: bounds
        };
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.parseFrameLabel = function ()
    {
        return {
            name: this.bitio.getDataUntil("\0"),
            frame: 0
        };
    };

    /**
     * @param tagType
     * @returns {*}
     */
    SwfTag.prototype.parseRemoveObject = function (tagType)
    {
        var bitio = this.bitio;
        if (tagType === 5) {
            console.log("RemoveObject");
            return {
                CharacterId: bitio.getUI16(),
                Depth: bitio.getUI16()
            };
        }
        return {Depth: bitio.getUI16()};
    };

    /**
     * @param tagType
     * @param length
     * @returns {{}}
     */
    SwfTag.prototype.parseDefineButton = function (tagType, length)
    {
        var obj = {};
        obj.tagType = tagType;

        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var endOffset = bitio.byte_offset + length;
        obj.ButtonId = bitio.getUI16();

        var ActionOffset = 0;
        if (tagType !== 7) {
            obj.ReservedFlags = bitio.getUIBits(7);
            obj.TrackAsMenu = bitio.getUIBits(1);
            ActionOffset = bitio.getUI16();
        }

        obj.characters = _this.buttonCharacters();

        // actionScript
        if (tagType === 7) {
            obj.actions = _this.parseDoAction(endOffset - bitio.byte_offset);
        } else if (ActionOffset > 0) {
            obj.actions = _this.buttonActions(endOffset);
        }

        // set layer
        stage.setCharacter(obj.ButtonId, obj);
        if (bitio.byte_offset !== endOffset) {
            bitio.byte_offset = endOffset;
        }

        return obj;
    };

    /**
     * @returns {Array}
     */
    SwfTag.prototype.buttonCharacters = function ()
    {
        var characters = [];
        var _this = this;
        var bitio = _this.bitio;
        while (bitio.getUI8() !== 0) {
            bitio.incrementOffset(-1, 0);
            var record = _this.buttonRecord();
            var depth = record.Depth;
            if (!(record.Depth in characters)) {
                characters[depth] = [];
            }
            characters[depth].push(record);
        }
        return characters;
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.buttonRecord = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};

        bitio.getUIBits(2); // Reserved
        obj.PlaceFlagHasBlendMode = bitio.getUIBits(1);
        obj.PlaceFlagHasFilterList = bitio.getUIBits(1);
        obj.ButtonStateHitTest = bitio.getUIBits(1);
        obj.ButtonStateDown = bitio.getUIBits(1);
        obj.ButtonStateOver = bitio.getUIBits(1);
        obj.ButtonStateUp = bitio.getUIBits(1);
        obj.CharacterId = bitio.getUI16();
        obj.Depth = bitio.getUI16();
        obj.PlaceFlagHasMatrix = 1;
        obj.Matrix = _this.matrix();
        obj.ColorTransform = _this.colorTransform();
        obj.PlaceFlagHasColorTransform = (obj.ColorTransform === undefined) ? 0 : 1;
        if (obj.PlaceFlagHasBlendMode) {
            obj.BlendMode = bitio.getUI8();
        }
        if (obj.PlaceFlagHasFilterList) {
            obj.SurfaceFilterList = _this.getFilterList();
        }
        obj.PlaceFlagHasRatio = 0;
        obj.PlaceFlagHasClipDepth = 0;
        obj.Sound = null;
        return obj;
    };

    /**
     * @param endOffset
     * @returns {Array}
     */
    SwfTag.prototype.buttonActions = function (endOffset)
    {
        var _this = this;
        var bitio = _this.bitio;
        var results = [];

        while (true) {
            var obj = {};
            var startOffset = bitio.byte_offset;
            var CondActionSize = bitio.getUI16();
            obj.CondIdleToOverDown = bitio.getUIBits(1);
            obj.CondOutDownToIdle = bitio.getUIBits(1);
            obj.CondOutDownToOverDown = bitio.getUIBits(1);
            obj.CondOverDownToOutDown = bitio.getUIBits(1);
            obj.CondOverDownToOverUp = bitio.getUIBits(1);
            obj.CondOverUpToOverDown = bitio.getUIBits(1);
            obj.CondOverUpToIdle = bitio.getUIBits(1);
            obj.CondIdleToOverUp = bitio.getUIBits(1);
            obj.CondKeyPress = bitio.getUIBits(7);
            obj.CondOverDownToIdle = bitio.getUIBits(1);

            // ActionScript
            var length = endOffset - bitio.byte_offset + 1;
            obj.ActionScript = _this.parseDoAction(length);
            results[results.length] = obj;

            if (!CondActionSize) {
                break;
            }
            bitio.byte_offset = startOffset + CondActionSize;
        }

        return results;
    };

    /**
     * @param tagType
     * @param length
     * @returns {{}}
     */
    SwfTag.prototype.parsePlaceObject = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var obj = {};
        obj.tagType = tagType;
        var startOffset = bitio.byte_offset;

        if (tagType === 4) {
            obj.CharacterId = bitio.getUI16();
            obj.Depth = bitio.getUI16();
            obj.Matrix = _this.matrix();
            obj.PlaceFlagHasMatrix = 1;

            bitio.byteAlign();
            if ((bitio.byte_offset - startOffset) < length) {
                obj.ColorTransform = _this.colorTransform();
                obj.PlaceFlagHasColorTransform = 1;
            }
        } else {
            obj.PlaceFlagHasClipActions = bitio.getUIBits(1);
            if (stage.getVersion() < 5) {
                obj.PlaceFlagHasClipActions = 0;
            }
            obj.PlaceFlagHasClipDepth = bitio.getUIBits(1);
            obj.PlaceFlagHasName = bitio.getUIBits(1);
            obj.PlaceFlagHasRatio = bitio.getUIBits(1);
            obj.PlaceFlagHasColorTransform = bitio.getUIBits(1);
            obj.PlaceFlagHasMatrix = bitio.getUIBits(1);
            obj.PlaceFlagHasCharacter = bitio.getUIBits(1);
            obj.PlaceFlagMove = bitio.getUIBits(1);

            // PlaceObject3
            if (tagType === 70) {
                bitio.getUIBits(1); // Reserved
                obj.PlaceFlagOpaqueBackground = bitio.getUIBits(1);
                obj.PlaceFlagHasVisible = bitio.getUIBits(1);
                obj.PlaceFlagHasImage = bitio.getUIBits(1);
                obj.PlaceFlagHasClassName = bitio.getUIBits(1);
                obj.PlaceFlagHasCacheAsBitmap = bitio.getUIBits(1);
                obj.PlaceFlagHasBlendMode = bitio.getUIBits(1);
                obj.PlaceFlagHasFilterList = bitio.getUIBits(1);
            }

            obj.Depth = bitio.getUI16();

            if (obj.PlaceFlagHasClassName ||
                (obj.PlaceFlagHasImage && obj.PlaceFlagHasCharacter)
            ) {
                obj.ClassName = bitio.getDataUntil("\0");
            }
            if (obj.PlaceFlagHasCharacter) {
                obj.CharacterId = bitio.getUI16();
            }
            if (obj.PlaceFlagHasMatrix) {
                obj.Matrix = _this.matrix();
            }
            if (obj.PlaceFlagHasColorTransform) {
                obj.ColorTransform = _this.colorTransform();
            }
            if (obj.PlaceFlagHasRatio) {
                obj.Ratio = bitio.getUI16();
            }
            if (obj.PlaceFlagHasName) {
                obj.Name = bitio.getDataUntil("\0");
            }
            if (obj.PlaceFlagHasClipDepth) {
                obj.ClipDepth = bitio.getUI16();
            }

            if (tagType === 70) {
                if (obj.PlaceFlagHasFilterList) {
                    obj.SurfaceFilterList = _this.getFilterList();
                }
                if (obj.PlaceFlagHasBlendMode) {
                    obj.BlendMode = bitio.getUI8();
                }
                if (obj.PlaceFlagHasCacheAsBitmap) {
                    obj.BitmapCache = bitio.getUI8();
                }
                if (obj.PlaceFlagHasVisible) {
                    obj.Visible = bitio.getUI8();
                    obj.BackgroundColor = _this.rgba();
                }
            }

            if (obj.PlaceFlagHasClipActions) {
                bitio.getUI16(); // Reserved
                obj.AllEventFlags = _this.parseClipEventFlags();

                var endLength = startOffset + length;
                var actionRecords = [];
                while (bitio.byte_offset < endLength) {
                    var clipActionRecord = _this.parseClipActionRecord(endLength);
                    actionRecords[actionRecords.length] = clipActionRecord;
                    if (endLength <= bitio.byte_offset) {
                        break;
                    }
                    var endFlag = (stage.getVersion() <= 5) ? bitio.getUI16() : bitio.getUI32();
                    if (!endFlag) {
                        break;
                    }
                    if (stage.getVersion() <= 5) {
                        bitio.byte_offset -= 2;
                    } else {
                        bitio.byte_offset -= 4;
                    }

                    if (clipActionRecord.KeyCode) {
                        bitio.byte_offset -= 1;
                    }
                }
                obj.ClipActionRecords = actionRecords;
            }
        }

        bitio.byteAlign();
        bitio.byte_offset = startOffset + length;

        return obj;
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.parseClipActionRecord = function (endLength)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        var EventFlags = _this.parseClipEventFlags();
        if (endLength > bitio.byte_offset) {
            var ActionRecordSize = bitio.getUI32();
            if (EventFlags.keyPress) {
                obj.KeyCode = bitio.getUI8();
            }
            obj.EventFlags = EventFlags;
            obj.Actions = _this.parseDoAction(ActionRecordSize);
        }
        return obj;
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.parseClipEventFlags = function ()
    {
        var _this = this;
        var obj = {};
        var bitio = _this.bitio;
        var stage = _this.stage;

        obj.keyUp = bitio.getUIBits(1);
        obj.keyDown = bitio.getUIBits(1);
        obj.mouseUp = bitio.getUIBits(1);
        obj.mouseDown = bitio.getUIBits(1);
        obj.mouseMove = bitio.getUIBits(1);
        obj.unload = bitio.getUIBits(1);
        obj.enterFrame = bitio.getUIBits(1);
        obj.load = bitio.getUIBits(1);

        if (stage.getVersion() >= 6) {
            obj.dragOver = bitio.getUIBits(1);
            obj.rollOut = bitio.getUIBits(1);
            obj.rollOver = bitio.getUIBits(1);
            obj.releaseOutside = bitio.getUIBits(1);
            obj.release = bitio.getUIBits(1);
            obj.press = bitio.getUIBits(1);
            obj.initialize = bitio.getUIBits(1);
        }

        obj.data = bitio.getUIBits(1);

        if (stage.getVersion() >= 6) {
            bitio.getUIBits(5); // Reserved
            obj.construct = bitio.getUIBits(1);
            obj.keyPress = bitio.getUIBits(1);
            obj.dragOut = bitio.getUIBits(1);
            bitio.getUIBits(8); // Reserved
        }

        bitio.byteAlign();

        return obj;
    };

    /**
     * @returns {Array}
     */
    SwfTag.prototype.getFilterList = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var result = [];
        var _getFilter = _this.getFilter;
        var NumberOfFilters = bitio.getUI8();
        for (var i = 0; i < NumberOfFilters; i++) {
            var filter = _getFilter.call(_this);
            if (filter) {
                result[result.length] = filter;
            }
        }
        return (result.length) ? result : null;
    };

    /**
     * @return {{}}
     */
    SwfTag.prototype.getFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var filterId = bitio.getUI8();
        var filter;
        switch (filterId) {
            case 0:
                filter = _this.dropShadowFilter();
                break;
            case 1:
                filter = _this.blurFilter();
                break;
            case 2:
                filter = _this.glowFilter();
                break;
            case 3:
                filter = _this.bevelFilter();
                break;
            case 4:
                filter = _this.gradientGlowFilter();
                break;
            case 5:
                filter = _this.convolutionFilter();
                break;
            case 6:
                filter = _this.colorMatrixFilter();
                break;
            case 7:
                filter = _this.gradientBevelFilter();
                break;
        }
        return filter;
    };

    /**
     * @returns {DropShadowFilter}
     */
    SwfTag.prototype.dropShadowFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var rgba = _this.rgba();
        var alpha = rgba.A;
        var color = rgba.R << 16 | rgba.G << 8 | rgba.B;
        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var angle = bitio.getUI32() / 0x10000 * 180 / _PI;
        var distance = bitio.getUI32() / 0x10000;
        var strength = bitio.getFloat16() / 256;
        var inner = (bitio.getUIBits(1)) ? true : false;
        var knockout = (bitio.getUIBits(1)) ? true : false;
        var hideObject = (bitio.getUIBits(1)) ? false : true;
        var quality = bitio.getUIBits(5);

        if (!strength) {
            return null;
        }

        return new DropShadowFilter(
            distance, angle, color, alpha, blurX, blurY,
            strength, quality, inner, knockout, hideObject
        );
    };

    /**
     * @returns {BlurFilter}
     */
    SwfTag.prototype.blurFilter = function ()
    {
        var bitio = this.bitio;
        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var quality = bitio.getUIBits(5);
        bitio.getUIBits(3); // Reserved

        return new BlurFilter(blurX, blurY, quality);
    };

    /**
     * @returns {GlowFilter}
     */
    SwfTag.prototype.glowFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var rgba = _this.rgba();
        var alpha = rgba.A;
        var color = rgba.R << 16 | rgba.G << 8 | rgba.B;
        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var strength = bitio.getFloat16() / 256;
        var inner = (bitio.getUIBits(1)) ? true : false;
        var knockout = (bitio.getUIBits(1)) ? true : false;
        bitio.getUIBits(1); // CompositeSource
        var quality = bitio.getUIBits(5);

        if (!strength) {
            return null;
        }

        return new GlowFilter(
            color, alpha, blurX, blurY,
            strength, quality, inner, knockout
        );
    };

    /**
     * @returns {BevelFilter}
     */
    SwfTag.prototype.bevelFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var rgba;
        rgba = _this.rgba();
        var highlightAlpha = rgba.A;
        var highlightColor = rgba.R << 16 | rgba.G << 8 | rgba.B;

        rgba = _this.rgba();
        var shadowAlpha = rgba.A;
        var shadowColor = rgba.R << 16 | rgba.G << 8 | rgba.B;

        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var angle = bitio.getUI32() / 0x10000 * 180 / _PI;
        var distance = bitio.getUI32() / 0x10000;
        var strength = bitio.getFloat16() / 256;
        var inner = (bitio.getUIBits(1)) ? true : false;
        var knockout = (bitio.getUIBits(1)) ? true : false;
        bitio.getUIBits(1); // CompositeSource
        var OnTop = bitio.getUIBits(1);
        var quality = bitio.getUIBits(4);

        var type = "inner";
        if (!inner) {
            if (OnTop) {
                type = "full";
            } else {
                type = "outer";
            }
        }

        if (!strength) {
            return null;
        }

        return new BevelFilter(
            distance, angle, highlightColor, highlightAlpha,
            shadowColor, shadowAlpha, blurX, blurY,
            strength, quality, type, knockout
        );
    };

    /**
     * @returns {GradientGlowFilter}
     */
    SwfTag.prototype.gradientGlowFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var i;
        var NumColors = bitio.getUI8();

        var colors = [];
        var alphas = [];
        for (i = 0; i < NumColors; i++) {
            var rgba = _this.rgba();
            alphas[alphas.length] = rgba.A;
            colors[colors.length] = rgba.R << 16 | rgba.G << 8 | rgba.B;
        }

        var ratios = [];
        for (i = 0; i < NumColors; i++) {
            ratios[ratios.length] = bitio.getUI8();
        }

        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var angle = bitio.getUI32() / 0x10000 * 180 / _PI;
        var distance = bitio.getUI32() / 0x10000;
        var strength = bitio.getFloat16() / 256;
        var inner = (bitio.getUIBits(1)) ? true : false;
        var knockout = (bitio.getUIBits(1)) ? true : false;
        bitio.getUIBits(1); // CompositeSource
        var OnTop = bitio.getUIBits(1);
        var quality = bitio.getUIBits(4);

        var type = "inner";
        if (!inner) {
            if (OnTop) {
                type = "full";
            } else {
                type = "outer";
            }
        }

        if (!strength) {
            return null;
        }

        return new GradientGlowFilter(
            distance, angle, colors, alphas, ratios,
            blurX, blurY, strength, quality, type, knockout
        );
    };

    /**
     * @returns {ConvolutionFilter}
     */
    SwfTag.prototype.convolutionFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};

        obj.MatrixX = bitio.getUI8();
        obj.MatrixY = bitio.getUI8();
        obj.Divisor = bitio.getFloat16() | bitio.getFloat16();
        obj.Bias = bitio.getFloat16() | bitio.getFloat16();

        var count = obj.MatrixX * obj.MatrixY;
        var MatrixArr = [];
        while (count--) {
            MatrixArr[MatrixArr.length] = bitio.getUI32();
        }
        obj.DefaultColor = _this.rgba();
        bitio.getUIBits(6); // Reserved
        obj.Clamp = bitio.getUIBits(1);
        obj.PreserveAlpha = bitio.getUIBits(1);

        return new ConvolutionFilter(
        );
    };

    /**
     * @returns {GradientBevelFilter}
     */
    SwfTag.prototype.gradientBevelFilter = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var NumColors = bitio.getUI8();

        var i;
        var colors = [];
        var alphas = [];
        for (i = 0; i < NumColors; i++) {
            var rgba = _this.rgba();
            alphas[alphas.length] = rgba.A;
            colors[colors.length] = rgba.R << 16 | rgba.G << 8 | rgba.B;
        }

        var ratios = [];
        for (i = 0; i < NumColors; i++) {
            ratios[ratios.length] = bitio.getUI8();
        }

        var blurX = bitio.getUI32() / 0x10000;
        var blurY = bitio.getUI32() / 0x10000;
        var angle = bitio.getUI32() / 0x10000 * 180 / _PI;
        var distance = bitio.getUI32() / 0x10000;
        var strength = bitio.getFloat16() / 256;

        var inner = (bitio.getUIBits(1)) ? true : false;
        var knockout = (bitio.getUIBits(1)) ? true : false;
        bitio.getUIBits(1); // CompositeSource
        var OnTop = bitio.getUIBits(1);
        var quality = bitio.getUIBits(4);

        var type = "inner";
        if (!inner) {
            if (OnTop) {
                type = "full";
            } else {
                type = "outer";
            }
        }

        if (!strength) {
            return null;
        }

        return new GradientBevelFilter(
            distance, angle, colors, alphas, ratios,
            blurX, blurY, strength, quality, type, knockout
        );
    };

    /**
     * @returns {ColorMatrixFilter}
     */
    SwfTag.prototype.colorMatrixFilter = function ()
    {
        var bitio = this.bitio;
        var MatrixArr = [];
        for (var i = 0; i < 20; i++) {
            MatrixArr[MatrixArr.length] = bitio.getUI32();
        }

        return new ColorMatrixFilter(
        );
    };

    /**
     * @returns {Array}
     */
    SwfTag.prototype.colorTransform = function ()
    {
        var bitio = this.bitio;
        bitio.byteAlign();

        var result = [1, 1, 1, 1, 0, 0, 0, 0];
        var first6bits = bitio.getUIBits(6);
        var HasAddTerms = first6bits >> 5;
        var HasMultiTerms = (first6bits >> 4) & 1;
        var nbits = first6bits & 0x0f;

        if (HasMultiTerms) {
            result[0] = bitio.getSIBits(nbits) / 256;
            result[1] = bitio.getSIBits(nbits) / 256;
            result[2] = bitio.getSIBits(nbits) / 256;
            result[3] = bitio.getSIBits(nbits) / 256;
        }

        if (HasAddTerms) {
            result[4] = bitio.getSIBits(nbits);
            result[5] = bitio.getSIBits(nbits);
            result[6] = bitio.getSIBits(nbits);
            result[7] = bitio.getSIBits(nbits);
        }

        return result;
    };

    /**
     * @param dataLength
     */
    SwfTag.prototype.parseDefineSprite = function (dataLength)
    {
        var _this = this;
        var bitio = _this.bitio;
        var characterId = bitio.getUI16();
        bitio.getUI16(); // FrameCount
        var stage = _this.stage;
        stage.setCharacter(characterId, _this.parseTags(dataLength, characterId));
    };

    /**
     * @param length
     * @returns {ActionScript}
     */
    SwfTag.prototype.parseDoAction = function (length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var data = bitio.getData(length);
        return new ActionScript(data);
    };

    /**
     * @param length
     */
    SwfTag.prototype.parseDoInitAction = function (length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var spriteId = bitio.getUI16();

        var as = new ActionScript(bitio.getData(length - 2), undefined, undefined, true);
        var mc = stage.getParent();
        mc.variables = {};
        var action = mc.createActionScript2(as);
        var packages = stage.packages;
        if (spriteId in packages) {
            mc.active = true;
            action.apply(mc);
            mc.active = false;
        }
        stage.initActions[spriteId] = action;
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.parseDefineSceneAndFrameLabelData = function ()
    {
        var i;
        var bitio = this.bitio;
        var obj = {};
        obj.SceneCount = bitio.getU30();
        obj.sceneInfo = [];
        for (i = 0; i < obj.SceneCount; i++) {
            obj.sceneInfo[i] = {
                offset: bitio.getU30(),
                name: decodeURIComponent(bitio.getDataUntil("\0"))
            };
        }

        obj.FrameLabelCount = bitio.getU30();
        obj.frameInfo = [];
        for (i = 0; i < obj.FrameLabelCount; i++) {
            obj.frameInfo[i] = {
                num: bitio.getU30(),
                label: decodeURIComponent(bitio.getDataUntil("\0"))
            };
        }
        return obj;
    };

    /**
     * @param tagType
     * @returns {{}}
     */
    SwfTag.prototype.parseSoundStreamHead = function (tagType)
    {
        var obj = {};
        obj.tagType = tagType;
        var bitio = this.bitio;

        bitio.getUIBits(4); // Reserved

        // 0 = 5.5kHz, 1 = 11kHz, 2 = 22kHz, 3 = 44kHz
        obj.PlaybackSoundRate = bitio.getUIBits(2);

        // 0 = 8-bit, 1 = 16-bit
        obj.PlaybackSoundSize = bitio.getUIBits(1);

        // 0 = Mono, 1 = Stereo
        obj.PlaybackSoundType = bitio.getUIBits(1);

        // 0 = Uncompressed(native-endian)
        // 1 = ADPCM
        // 2 = MP3
        // 3 = Uncompressed(little-endian)
        // 4 = Nellymoser 16 kHz
        // 5 = Nellymoser 8 kHz
        // 6 = Nellymoser
        // 11 = Speex
        obj.StreamSoundCompression = bitio.getUIBits(4);

        // 0 = 5.5kHz, 1 = 11kHz, 2 = 22kHz, 3 = 44kHz
        obj.StreamSoundRate = bitio.getUIBits(2);

        // 0 = 8-bit, 1 = 16-bit
        obj.StreamSoundSize = bitio.getUIBits(1);

        // 0 = Mono, 1 = Stereo
        obj.StreamSoundType = bitio.getUIBits(1);

        obj.StreamSoundSampleCount = bitio.getUI16();

        if (obj.StreamSoundCompression === 2) {
            obj.LatencySeek = bitio.getSIBits(2);
        }

        return obj;
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseDoABC = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        stage.abcFlag = true;
        var startOffset = bitio.byte_offset;

        var obj = {};
        obj.tagType = tagType;
        obj.Flags = bitio.getUI32();
        obj.Name = bitio.getDataUntil("\0");
        var offset = length - (bitio.byte_offset - startOffset);
        var ABCData = bitio.getData(offset);
        var ABCBitIO = new BitIO();
        ABCBitIO.setData(ABCData);

        // version
        obj.minorVersion = ABCBitIO.getUI16();
        obj.majorVersion = ABCBitIO.getUI16();

        // integer
        obj.integer = _this.ABCInteger(ABCBitIO);

        // uinteger
        obj.uinteger = _this.ABCUinteger(ABCBitIO);

        // double
        obj.double = _this.ABCDouble(ABCBitIO);

        // string_info
        obj.string = _this.ABCStringInfo(ABCBitIO);

        // namespace_info
        obj.namespace = _this.ABCNameSpaceInfo(ABCBitIO);

        // ns_set_info
        obj.nsSet = _this.ABCNsSetInfo(ABCBitIO);

        // multiname_info;
        obj.multiname_info = _this.ABCMultiNameInfo(ABCBitIO);

        var i = 0;

        // method_info
        obj.method = [];
        var methodCount = ABCBitIO.getU30();
        if (methodCount) {
            var method = [];
            for (i = 0; i < methodCount; i++) {
                method[i] = _this.ABCMethodInfo(ABCBitIO);
            }
            obj.method = method;
        }

        // metadata_info
        obj.metadata = [];
        var metadataCount = ABCBitIO.getU30();
        if (metadataCount) {
            var metadataInfo = [];
            for (i = 0; i < metadataCount; i++) {
                metadataInfo[i] = _this.ABCMetadataInfo(ABCBitIO);
            }
            obj.metadata = metadataInfo;
        }

        var classCount = ABCBitIO.getU30();
        obj.instance = [];
        obj.class = [];
        if (classCount) {
            // instance_info
            var instance = [];
            for (i = 0; i < classCount; i++) {
                instance[i] = _this.ABCInstanceInfo(ABCBitIO);
            }
            obj.instance = instance;

            // class_info
            var classInfo = [];
            for (i = 0; i < classCount; i++) {
                classInfo[i] = _this.ABCClassInfo(ABCBitIO);
            }
            obj.class = classInfo;
        }

        // script_info
        obj.script = [];
        var scriptCount = ABCBitIO.getU30();
        if (scriptCount) {
            var script = [];
            for (i = 0; i < scriptCount; i++) {
                script[i] = _this.ABCScriptInfo(ABCBitIO);
            }
            obj.script = script;
        }

        // method_body_info
        obj.methodBody = [];
        var methodBodyCount  = ABCBitIO.getU30();
        if (methodBodyCount) {
            var methodBody = [];
            for (i = 0; i < methodBodyCount; i++) {
                var mBody = _this.ABCMethodBodyInfo(ABCBitIO);
                methodBody[mBody.method] = mBody;
            }
            obj.methodBody = methodBody;
        }

        // build names
        obj = _this.ABCMultinameToString(obj);

        // build instance
        _this.ABCBuildInstance(obj);
    };

    /**
     * @param obj
     */
    SwfTag.prototype.ABCBuildInstance = function (obj)
    {
        var _this = this;
        var instances = obj.instance;
        var length = instances.length;
        var namespaces = obj.namespace;
        var string = obj.string;
        var stage = _this.stage;
        var names = obj.names;
        for (var i = 0; i < length; i++) {
            var instance = instances[i];
            var flag = instance.flags;

            var nsIndex = null;
            if (flag & 0x08) {
                nsIndex = instance.protectedNs;
            }

            var object = {};
            if (nsIndex) {
                var nObj = namespaces[nsIndex];
                object = string[nObj.name];
            } else {
                object = names[instance.name];
            }

            var values = object.split(":");
            var className = values.pop();
            var ns = values.pop();

            // build parent
            var AVM2 = function (mc) { this["__swf2js__::builder"] = mc; };
            var prop = AVM2.prototype;

            // constructor
            prop[className] = _this.ABCCreateActionScript3(obj, instance.iinit, object);

            // prototype
            var traits = instance.trait;
            var tLength = traits.length;
            var register = [];
            var rCount = 1;
            if (tLength) {
                for (var idx = 0; idx < tLength; idx++) {
                    var trait = traits[idx];
                    var tName = names[trait.name];
                    var tNames = tName.split("::");
                    var pName =  tNames.pop();
                    var kind = trait.kind;

                    var val = undefined;
                    switch (kind) {
                        case 0: // Slot
                            register[rCount++] = pName;
                            break;
                        case 1: // Method
                        case 2: // Getter
                        case 3: // Setter
                            val = _this.ABCCreateActionScript3(obj, trait.data.info, object);
                            break;
                        case 4: // Class
                            console.log("build: Class");
                            break;
                        case 5: // Function
                            console.log("build: Function");
                            break;
                        case 6: // Const
                            console.log("build: Const");
                            break;
                    }
                    prop[pName] = val;
                }
            }

            var localName = "__swf2js__:"+ object;
            prop[localName] = {};

            // extends
            var superName = instance.superName;
            prop[localName].extends = names[superName];

            // register
            prop[localName].register = register;

            // build
            var abc = stage.abc;
            var classObj = stage.avm2;
            if (ns) {
                var nss = ns.split(".");
                var nLen = nss.length;
                for (var nIdx = 0; nIdx < nLen; nIdx++) {
                    if (!(nss[nIdx] in classObj)) {
                        classObj[nss[nIdx]] = {};
                        abc[nss[nIdx]] = {};
                    }
                    classObj = classObj[nss[nIdx]];
                    abc = abc[nss[nIdx]];
                }
            }

            abc[className] = AVM2;
            classObj[className] = new AVM2();
        }
    };

    /**
     * @param obj
     * @param methodId
     * @param abcKey
     */
    SwfTag.prototype.ABCCreateActionScript3 = function (obj, methodId, abcKey)
    {
        var stage = this.stage;
        return (function (data, id, ns, stage)
        {
            return function ()
            {
                var as3 = new ActionScript3(data, id, ns, stage);
                as3.caller = this;
                as3.args = arguments;
                return as3.execute();
            };
        })(obj, methodId, abcKey, stage);
    };

    /**
     * @param obj
     * @returns {*}
     */
    SwfTag.prototype.ABCMultinameToString = function (obj)
    {
        var multinames = obj.multiname_info;
        var length = multinames.length;
        var string = obj.string;
        var ns = obj.namespace;
        var names = [];
        for (var i = 1; i < length; i++) {
            var info = multinames[i];
            var str = "";

            switch (info.kind) {
                case 0x07: // QName
                case 0x0D: // QNameA
                    var namespace_info = ns[info.ns];
                    switch (namespace_info.kind) {
                        default:
                            str += string[namespace_info.name];
                            break;
                        case 0x05:
                            str += "private";
                            break;
                    }

                    if (str !== "") {
                        str += "::";
                    }

                    str += string[info.name];
                    break;
                case 0x0F: // RTQName
                case 0x10: // RTQNameA
                    console.log("RTQName", i, info);
                    break;
                case 0x09: // Multiname
                case 0x0E: // MultinameA
                    str = string[info.name];
                    break;
                case 0x1B: // MultinameL
                case 0x1C: // MultinameLA
                    str = null;
                    break;
                case 0x11: // RTQNameL
                case 0x12: // RTQNameLA
                    console.log("RTQNameL", i, info);

                    break;
            }
            names[i] = str;
        }
        obj.names = names;
        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCInteger = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                array[i] = ABCBitIO.getS30();
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCUinteger = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                array[i] = ABCBitIO.getU30();
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCDouble = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                array[i] = ABCBitIO.getFloat64LittleEndian();
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCStringInfo = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                array[i] = ABCBitIO.AbcReadString();
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCNameSpaceInfo = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                array[i] =
                {
                    kind: ABCBitIO.getUI8(),
                    name: ABCBitIO.getU30()
                };
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCNsSetInfo = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                var nsCount = ABCBitIO.getU30();
                var ns = [];
                if (nsCount) {
                    for (var j = 0; j < nsCount; j++) {
                        ns[j] = ABCBitIO.getU30();
                    }
                }
                array[i] = ns;
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCMultiNameInfo = function (ABCBitIO)
    {
        var array = [];
        var count = ABCBitIO.getU30();
        if (count) {
            for (var i = 1; i < count; i++) {
                var obj = {};
                obj.kind = ABCBitIO.getUI8();
                switch (obj.kind) {
                    case 0x07: // QName
                    case 0x0D: // QNameA
                        obj.ns = ABCBitIO.getU30();
                        obj.name = ABCBitIO.getU30();
                        break;
                    case 0x0F: // RTQName
                    case 0x10: // RTQNameA
                        obj.name = ABCBitIO.getU30();
                        break;
                    case 0x09: // Multiname
                    case 0x0E: // MultinameA
                        obj.name = ABCBitIO.getU30();
                        obj.ns_set = ABCBitIO.getU30();
                        break;
                    case 0x1B: // MultinameL
                    case 0x1C: // MultinameLA
                        obj.ns_set = ABCBitIO.getU30();
                        break;
                    case 0x11: // RTQNameL
                    case 0x12: // RTQNameLA
                        break;
                }
                array[i] = obj;
            }
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCMethodInfo = function (ABCBitIO)
    {
        var obj = {};
        var i;
        var count = ABCBitIO.getU30();
        obj.paramCount = count;
        obj.returnType = ABCBitIO.getU30();
        obj.paramType = [];
        if (count) {
            var paramType = [];
            for (i = 0; i < count; i++) {
                paramType[paramType.length] = ABCBitIO.getU30();
            }
            obj.paramType = paramType;
        }

        obj.name = ABCBitIO.getU30();
        obj.flags = ABCBitIO.getUI8();

        obj.options = [];
        if (obj.flags === 0x08) {
            var options = [];
            var optionCount = ABCBitIO.getU30();
            if (optionCount) {
                for (i = 0; i < optionCount; i++) {
                    options[options.length] = {
                        val: ABCBitIO.getU30(),
                        kind: ABCBitIO.getUI8()
                    };
                }
            }
            obj.options = options;
        }

        obj.paramName = [];
        if (obj.flags === 0x80) {
            var paramName = [];
            if (count) {
                for (i = 0; i < count; i++) {
                    paramName[paramName.length] = ABCBitIO.getU30();
                }
            }
            obj.paramName = paramName;
        }

        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCMetadataInfo = function (ABCBitIO)
    {
        var obj = {};
        obj.name = ABCBitIO.getU30();
        obj.items = [];

        var count = ABCBitIO.getU30();
        if (count) {
            var items = [];
            for (var i = 0; i < count; i++) {
                items[items.length] = {
                    key: ABCBitIO.getU30(),
                    value: ABCBitIO.getU30()
                };
            }
            obj.items = items;
        }

        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCInstanceInfo = function (ABCBitIO)
    {
        var obj = {};
        obj.name = ABCBitIO.getU30();
        obj.superName = ABCBitIO.getU30();
        obj.flags = ABCBitIO.getUI8();
        if (obj.flags & 0x08) {
            obj.protectedNs = ABCBitIO.getU30();
        }

        var count = ABCBitIO.getU30();
        obj.interfaces = [];
        if (count) {
            var interfaces = [];
            for (var i = 0; i < count; i++) {
                interfaces[interfaces.length] = ABCBitIO.getU30();
            }
            obj.interfaces = interfaces;
        }

        obj.iinit = ABCBitIO.getU30();
        obj.trait = this.ABCTrait(ABCBitIO);

        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCClassInfo = function (ABCBitIO)
    {
        var obj = {};
        obj.cinit = ABCBitIO.getU30();
        obj.trait = this.ABCTrait(ABCBitIO);
        return obj;
    };

    /**
     * @param ABCBitIO
     */
    SwfTag.prototype.ABCScriptInfo = function (ABCBitIO)
    {
        var obj = {};
        obj.init = ABCBitIO.getU30();
        obj.trait = this.ABCTrait(ABCBitIO);
        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCMethodBodyInfo = function (ABCBitIO)
    {
        var _this = this;
        var obj = {};
        obj.method = ABCBitIO.getU30();
        obj.maxStack = ABCBitIO.getU30();
        obj.localCount = ABCBitIO.getU30();
        obj.initScopeDepth = ABCBitIO.getU30();
        obj.maxScopeDepth = ABCBitIO.getU30();
        var i;
        var count = ABCBitIO.getU30();
        var codes = [];
        if (count) {
            codes = _this.ABCBuildCode(ABCBitIO, count);
        }
        obj.codes = codes;

        count = ABCBitIO.getU30();
        var exceptions = [];
        if (count) {
            for (i = 0; i < count; i++) {
                exceptions[exceptions.length] = _this.ABCException(ABCBitIO);
            }
        }
        obj.exceptions = exceptions;
        obj.trait = _this.ABCTrait(ABCBitIO);
        return obj;
    };

    /**
     * @param ABCBitIO
     * @param count
     * @returns {Array}
     */
    SwfTag.prototype.ABCBuildCode = function (ABCBitIO, count)
    {
        var array = [];
        var cacheOffset;
        for (var i = 0; i < count; i++) {
            var obj = {};
            var offset = 0;

            var code = ABCBitIO.getUI8();
            obj.code = code;
            switch (code) {
                case 0x86: // astype
                case 0x41: // call
                case 0x80: // coerce
                case 0x42: // construct
                case 0x49: // constructsuper
                case 0xf1: // debugfile
                case 0xf0: // debugline
                case 0x94: // declocal
                case 0xc3: // declocal_i
                case 0x6a: // deleteproperty
                case 0x06: // dxns
                case 0x5e: // findproperty
                case 0x5d: // findpropstrict
                case 0x59: // getdescendants
                case 0x6e: // getglobalslot
                case 0x60: // getlex
                case 0x62: // getlocal
                case 0x66: // getproperty
                case 0x6c: // getslot
                case 0x04: // getsuper
                case 0x92: // inclocal
                case 0xc2: // inclocal_i
                case 0x68: // initproperty
                case 0xb2: // istype
                case 0x08: // kill
                case 0x56: // newarray
                case 0x5a: // newcatch
                case 0x58: // newclass
                case 0x40: // newfunction
                case 0x55: // newobject
                case 0x2f: // pushdouble
                case 0x2d: // pushint
                case 0x31: // pushnamespace
                case 0x25: // pushshort
                case 0x2c: // pushstring
                case 0x2e: // pushuint
                case 0x63: // setlocal
                case 0x6f: // setglobalslot
                case 0x61: // setproperty
                case 0x6d: // setslot
                case 0x05: // setsuper
                    cacheOffset = ABCBitIO.byte_offset;
                    obj.value1 = ABCBitIO.getU30();
                    offset += (ABCBitIO.byte_offset - cacheOffset);
                    break;
                case 0x1b: // lookupswitch
                    obj.value1 = ABCBitIO.getSI24();
                    offset += 3;
                    cacheOffset = ABCBitIO.byte_offset;
                    obj.value2 = ABCBitIO.getSI24();
                    offset += (ABCBitIO.byte_offset - cacheOffset);
                    obj.value3 = ABCBitIO.getSI24();
                    offset += 3;
                    break;
                case 0x65: // getscopeobject
                case 0x24: // pushbyte
                    obj.value1 = ABCBitIO.getSI8();
                    offset += 1;
                    break;
                case 0x32: // hasnext2
                    obj.value1 = ABCBitIO.getSI8();
                    obj.value2 = ABCBitIO.getSI8();
                    offset += 2;
                    break;
                case 0x13: // ifeq
                case 0x12: // iffalse
                case 0x18: // ifge
                case 0x17: // ifgt
                case 0x16: // ifle
                case 0x15: // iflt
                case 0x0f: // ifnge
                case 0x0e: // ifngt
                case 0x0d: // ifnle
                case 0x0c: // ifnlt
                case 0x14: // ifne
                case 0x19: // ifstricteq
                case 0x1a: // ifstrictne
                case 0x11: // iftrue
                case 0x10: // jump
                    obj.value1 = ABCBitIO.getSI24();
                    offset += 3;
                    break;
                case 0x43: // callmethod
                case 0x46: // callproperty
                case 0x4c: // callproplex
                case 0x4f: // callpropvoid
                case 0x44: // callstatic
                case 0x45: // callsuper
                case 0x4e: // callsupervoid
                case 0x4a: // constructprop
                case 0xef: // debug
                    cacheOffset = ABCBitIO.byte_offset;
                    obj.value1 = ABCBitIO.getU30();
                    obj.value2 = ABCBitIO.getU30();
                    offset += (ABCBitIO.byte_offset - cacheOffset);
                    break;
            }

            obj.offset = offset;
            array[i] = obj;

            i += offset;
        }
        return array;
    };

    /**
     * @param ABCBitIO
     * @returns {{}}
     */
    SwfTag.prototype.ABCException = function (ABCBitIO)
    {
        var obj = {};
        obj.from = ABCBitIO.getU30();
        obj.to = ABCBitIO.getU30();
        obj.target = ABCBitIO.getU30();
        obj.excType = ABCBitIO.getU30();
        obj.varName = ABCBitIO.getU30();
        return obj;
    };

    /**
     * @param ABCBitIO
     * @returns {Array}
     */
    SwfTag.prototype.ABCTrait = function (ABCBitIO)
    {
        var count = ABCBitIO.getU30();
        var trait = [];
        if (count) {
            for (var i = 0; i < count; i++) {
                var tObj = {};
                tObj.name = ABCBitIO.getU30();
                var tag = ABCBitIO.getUI8();
                var kind = tag & 0x0f;
                var attributes = (kind >> 4) & 0x0f;

                var data = {};
                switch (kind) {
                    default:
                        console.log("ERROR:"+ kind);
                        break;
                    case 0: // Trait_Slot
                    case 6: // Trait_Const
                        data.id = ABCBitIO.getU30();
                        data.name = ABCBitIO.getU30();
                        data.index = ABCBitIO.getU30();
                        data.kind = null;
                        if (data.index !== 0) {
                            data.kind = ABCBitIO.getUI8();
                        }
                        break;
                    case 1: // Trait_Method
                    case 2: // Trait_Getter
                    case 3: // Trait_Setter
                        data.id = ABCBitIO.getU30();
                        data.info = ABCBitIO.getU30();
                        break;
                    case 4: // Trait_Class
                        data.id = ABCBitIO.getU30();
                        data.info = ABCBitIO.getU30();
                        break;
                    case 5: // Trait_Function
                        data.id = ABCBitIO.getU30();
                        data.info = ABCBitIO.getU30();
                        break;
                }
                tObj.kind = kind;
                tObj.data = data;

                if (attributes & 0x04) {
                    var metadataCount = ABCBitIO.getU30();
                    var metadata = [];
                    if (metadataCount) {
                        for (var j = 0; j < metadataCount; j++) {
                            metadata[metadata.length] = ABCBitIO.getU30();
                        }
                    }
                    tObj.metadata = metadata;
                }

                trait[trait.length] = tObj;
            }
        }

        return trait;
    };

    /**
     * parseSymbolClass
     */
    SwfTag.prototype.parseSymbolClass = function ()
    {
        var bitio = this.bitio;
        var stage = this.stage;
        var symbols = stage.symbols;
        var count = bitio.getUI16();
        if (count) {
            while (count--) {
                var tagId = bitio.getUI16();
                symbols[tagId] = bitio.getDataUntil("\0");
            }
        }
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseDefineSound = function (tagType, length)
    {
        var obj = {};
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var startOffset = bitio.byte_offset;

        obj.tagType = tagType;
        obj.SoundId = bitio.getUI16();
        obj.SoundFormat = bitio.getUIBits(4);
        obj.SoundRate = bitio.getUIBits(2);
        obj.SoundSize = bitio.getUIBit();
        obj.SoundType = bitio.getUIBit();
        obj.SoundSampleCount = bitio.getUI32();

        var sub = bitio.byte_offset - startOffset;
        var dataLength = length - sub;
        var data = bitio.getData(dataLength);
        var SoundData = "";
        for (var i = 0; i < dataLength; i++) {
            SoundData += _fromCharCode(data[i]);
        }
        bitio.byte_offset = startOffset + length;

        var mimeType = "";
        switch (obj.SoundFormat) {
            case 0: // Uncompressed native-endian
            case 3: // Uncompressed little-endian
                mimeType = "wave";
                break;
            case 1: // ADPCM ? 32KADPCM
                mimeType = "wave";
                break;
            case 2: // MP3
                mimeType = "mpeg";
                break;
            case 4: // Nellymoser 16
            case 5: // Nellymoser 8
            case 6: //
                mimeType = "nellymoser";
                break;
            case 11: // Speex
                mimeType = "speex";
                break;
            case 15:
                mimeType = "x-aiff";
                break;
        }

        obj.base64 = "data:audio/" + mimeType + ";base64," + window.btoa(SoundData);
        stage.sounds[obj.SoundId] = obj;
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseStartSound = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        var stage = _this.stage;

        obj.tagType = tagType;
        obj.SoundId = bitio.getUI16();
        if (tagType === 89) {
            obj.SoundClassName = bitio.getDataUntil("\0");
        }

        obj.SoundInfo = _this.parseSoundInfo();
        stage.setCharacter(obj.SoundId, obj);

        var sound = stage.sounds[obj.SoundId];
        var audio = _document.createElement("audio");
        audio.onload = function ()
        {
            this.load();
            this.preload = "auto";
            this.autoplay = false;
            this.loop = false;
        };
        audio.src = sound.base64;

        var loadSounds = stage.loadSounds;
        loadSounds[loadSounds.length] = audio;

        return {
            SoundId: obj.SoundId,
            Audio: audio,
            tagType: tagType
        };
    };

    /**
     * parseDefineButtonSound
     */
    SwfTag.prototype.parseDefineButtonSound = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var buttonId = bitio.getUI16();
        var btnObj = stage.getCharacter(buttonId);
        for (var i = 0; i < 4; i++) {
            var soundId = bitio.getUI16();
            if (soundId) {
                var soundInfo = _this.parseSoundInfo();
                switch (i) {
                    case 0:
                        btnObj.ButtonStateUpSoundInfo = soundInfo;
                        btnObj.ButtonStateUpSoundId = soundId;
                        break;
                    case 1:
                        btnObj.ButtonStateOverSoundInfo = soundInfo;
                        btnObj.ButtonStateOverSoundId = soundId;
                        break;
                    case 2:
                        btnObj.ButtonStateDownSoundInfo = soundInfo;
                        btnObj.ButtonStateDownSoundId = soundId;
                        break;
                    case 3:
                        btnObj.ButtonStateHitTestSoundInfo = soundInfo;
                        btnObj.ButtonStateHitTestSoundId = soundId;
                        break;
                }
            }
        }
        stage.setCharacter(buttonId, btnObj);
    };

    /**
     * @returns {{}}
     */
    SwfTag.prototype.parseSoundInfo = function ()
    {
        var obj = {};
        var bitio = this.bitio;
        bitio.getUIBits(2); // Reserved
        obj.SyncStop = bitio.getUIBit();
        obj.SyncNoMultiple = bitio.getUIBit();
        obj.HasEnvelope = bitio.getUIBit();
        obj.HasLoops = bitio.getUIBit();
        obj.HasOutPoint = bitio.getUIBit();
        obj.HasInPoint = bitio.getUIBit();

        if (obj.HasInPoint) {
            obj.InPoint = bitio.getUI32();
        }
        if (obj.HasOutPoint) {
            obj.OutPoint = bitio.getUI32();
        }
        if (obj.HasLoops) {
            obj.LoopCount = bitio.getUI16();
        }
        if (obj.HasEnvelope) {
            obj.EnvPoints = bitio.getUI8();
            obj.EnvelopeRecords = [];
            for (var i = 0; i < obj.EnvPoints; i++) {
                obj.EnvelopeRecords[i] = {
                    Pos44: bitio.getUI32(),
                    LeftLevel: bitio.getUI16(),
                    RightLevel: bitio.getUI16()
                };
            }
        }

        return obj;
    };

    /**
     * parseDefineFontAlignZones
     */
    SwfTag.prototype.parseDefineFontAlignZones = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var FontId = bitio.getUI16();
        var tag = stage.getCharacter(FontId);
        tag.CSMTableHint = bitio.getUIBits(2);
        bitio.getUIBits(6); // Reserved
        var NumGlyphs = tag.NumGlyphs;
        var ZoneTable = [];
        for (var i = 0; i < NumGlyphs; i++) {
            var NumZoneData = bitio.getUI8();
            var ZoneData = [];
            for (var idx = 0; idx < NumZoneData; idx++) {
                ZoneData[idx] = bitio.getUI32();
            }
            ZoneTable[i] = {
                ZoneData: ZoneData,
                Mask: bitio.getUI8()
            };
        }

        bitio.byteAlign();
        tag.ZoneTable = ZoneTable;
        stage.setCharacter(FontId, tag);
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseCSMTextSettings = function (tagType)
    {
        var _this = this;
        var obj = {};
        var bitio = _this.bitio;
        obj.tagType = tagType;
        obj.TextID = bitio.getUI16();
        obj.UseFlashType = bitio.getUIBits(2);
        obj.GridFit = bitio.getUIBits(3);
        bitio.getUIBits(3); // Reserved
        obj.Thickness = bitio.getUI32();
        obj.Sharpness = bitio.getUI32();
        bitio.getUI8(); // Reserved
    };

    /**
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseSoundStreamBlock = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        obj.tagType = tagType;
        obj.compressed = bitio.getData(length);
    };

    /**
     * @param tagType
     */
    SwfTag.prototype.parseDefineVideoStream = function (tagType)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var obj = {};
        obj.tagType = tagType;
        obj.CharacterId = bitio.getUI16();
        obj.NumFrames = bitio.getUI16();
        obj.Width = bitio.getUI16();
        obj.Height = bitio.getUI16();
        bitio.getUIBits(4); // Reserved
        obj.VideoFlagsDeblocking = bitio.getUIBits(3);
        obj.VideoFlagsSmoothing = bitio.getUIBits(1);
        obj.CodecID = bitio.getUI8();
        stage.setCharacter(obj.CharacterId, obj);
        console.log(obj);
    };

    /**
     *
     * @param tagType
     * @param length
     */
    SwfTag.prototype.parseVideoFrame = function (tagType, length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var stage = _this.stage;
        var startOffset = bitio.byte_offset;
        var obj = {};
        obj.tagType = tagType;
        obj.StreamID = bitio.getUI16();
        obj.FrameNum = bitio.getUI16();
        var StreamData = stage.getCharacter(obj.StreamID);
        var sub = bitio.byte_offset - startOffset;
        var dataLength = length - sub;
        var VideoData;
        switch (StreamData.CodecID) {
            case 4:
                VideoData = _this.parseVp6SwfVideoPacket(dataLength);
                break;
        }

        bitio.byte_offset = startOffset + length;

        // obj.base64 = 'data:image/jpeg;base64,' + window.btoa(VideoData);
        stage.videos[obj.StreamID] = obj;
    };

    /**
     * @param length
     * @returns {string}
     */
    SwfTag.prototype.parseVp6SwfVideoPacket = function (length)
    {
        var _this = this;
        var bitio = _this.bitio;
        var VideoData = "";
        var data = bitio.getData(length);

        console.log(data);

        return VideoData;
    };

    /**
     * parseFileAttributes
     */
    SwfTag.prototype.parseFileAttributes = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        bitio.getUIBit(); // Reserved
        obj.UseDirectBlit = bitio.getUIBit();
        obj.UseGPU = bitio.getUIBit();
        obj.HasMetadata = bitio.getUIBit();
        obj.ActionScript3 = bitio.getUIBit();
        obj.Reserved2 = bitio.getUIBits(3);
        obj.UseNetwork = bitio.getUIBit();
        obj.Reserved3 = bitio.getUIBits(24);
    };

    /**
     * parseDefineScalingGrid
     */
    SwfTag.prototype.parseDefineScalingGrid = function ()
    {
        var _this = this;
        var bitio = _this.bitio;
        var obj = {};
        obj.CharacterId = bitio.getUI16();
        obj.Splitter = _this.rect();
    };

    /**
     * @constructor
     */
    var Activation = function () {};


    /**
     * @param data
     * @param id
     * @param ns
     * @param stage
     * @constructor
     */
    var ActionScript3 = function (data, id, ns, stage)
    {
        var _this = this;

        // params
        _this.id = id;
        _this.caller = null;
        _this.parent = null;
        _this.activation = null;
        _this.scopeStack = [];
        _this.currentIndex = 0;
        _this.stage = stage;
        _this.args = [];
        _this.variables = {};

        // ABC code and info
        var methodBody = data.methodBody[id];
        _this.body = methodBody;
        _this.codes = methodBody.codes;
        _this.info = data.method[methodBody.method];

        // pool and data
        _this.names = data.names;
        _this.data = data;

        // ns
        _this.ns = ns;

        // register
        _this.AVM2 = _this.getAVM2();
        _this.register = _this.AVM2["__swf2js__:"+ns].register;
        _this.register[0] = _this.AVM2;

        // trait
        var trait = methodBody.trait;
        var length = trait.length;
        for (var i = 0; i < length; i++) {
            var obj = trait[i];
            var kind = obj.kind;
            switch (kind) {
                case 0:
                    // var key = _this.names[obj.name];

                    break;
            }
        }
    };

    /**
     * @type {{}}
     */
    ActionScript3.prototype.methods = {
        gotoAndStop: 1,
        gotoAndPlay: 1,
        play: 1,
        stop: 1,
        duplicateMovieClip: 1,
        getProperty: 1,
        removeMovieClip: 1,
        setProperty: 1,
        startDrag: 1,
        stopDrag: 1,
        targetPath: 1,
        updateAfterEvent: 1,
        nextFrame: 1,
        nextScene: 1,
        prevFrame: 1,
        prevScene: 1,
        stopAllSounds: 1,
        setMask: 1,
        getURL: 1,
        loadMovie: 1,
        loadMovieNum: 1,
        loadVariables: 1,
        loadVariablesNum: 1,
        unloadMovie: 1,
        unloadMovieNum: 1,
        swapDepths: 1,
        getInstanceAtDepth: 1,
        attachMovie: 1,
        attachAudio: 1,
        attachBitmap: 1,
        getNextHighestDepth: 1,
        getBytesLoaded: 1,
        getBytesTotal: 1,
        ASSetPropFlags: 1,
        lineStyle: 1,
        lineGradientStyle: 1,
        beginFill: 1,
        beginGradientFill: 1,
        beginBitmapFill: 1,
        graphics: 1,
        buttonMode: 1,
        clear: 1,
        moveTo: 1,
        lineTo: 1,
        curveTo: 1,
        endFill: 1,
        hitTest: 1,
        getDepth: 1,
        createEmptyMovieClip: 1,
        createTextField: 1,
        getBounds: 1,
        getRect: 1,
        getSWFVersion: 1,
        getTextSnapshot: 1,
        globalToLocal: 1,
        localToGlobal: 1,
        addFrameScript: 1,
        trace: 1,
        addEventListener: 1,
        removeEventListener: 1,
        x: 1,
        y: 1,
        alpha: 1,
        name: 1,
        blendMode: 1,
        filters: 1,
        visible: 1,
        rotation: 1,
        height: 1,
        width: 1,
        scaleX: 1,
        scaleY: 1,
        mouseX: 1,
        mouseY: 1,
        mask: 1,
        mouseEnabled: 1
    };

    /**
     * @returns {*}
     */
    ActionScript3.prototype.getAVM2 = function ()
    {
        var _this = this;
        var ns = _this.ns;
        var stage = _this.stage;
        var values = ns.split(":");
        var className = values.pop();
        var nLen = values.length;
        var classObj = stage.avm2;
        for (var i = 0; i < nLen; i++) {
            classObj = classObj[values[i]];
        }
        return classObj[className];
    };

    /**
     * @returns {*}
     */
    ActionScript3.prototype.getBuilder = function ()
    {
        return this.AVM2["__swf2js__::builder"];
    };

    /**
     * @returns {*}
     */
    ActionScript3.prototype.getSuperClass = function ()
    {
        var _this = this;
        return _this.AVM2["__swf2js__:"+_this.ns].superClass;
    };

    /**
     * @param superClass
     */
    ActionScript3.prototype.setSuperClass = function (superClass)
    {
        var _this = this;
        _this.AVM2["__swf2js__:"+_this.ns].superClass = superClass;
    };

    /**
     * @returns {*}
     */
    ActionScript3.prototype.getParent = function ()
    {
        return this.parent;
    };

    /**
     * @param name
     * @returns {*}
     */
    ActionScript3.prototype.getProperty = function (name)
    {
        var _this = this;
        var stage = _this.stage;
        var value;

        // local1
        if (_this.activation) {
            value = _this.activation[name];
        }

        // parent
        if (value === undefined) {
            var parent = _this.getParent();
            if (parent) {
                value = parent.getProperty(name);
            }
        }

        // property
        if (value === undefined) {
            var builder = _this.getBuilder();
            if (builder) {
                if (name in _this.methods) {
                    value = builder[name];
                }
                if (value === undefined) {
                    value = builder.getProperty(name);
                }
            }
        }

        // local2
        if (value === undefined && name.indexOf("::") !== -1) {
            var values = name.split("::");
            var className = values.pop();
            var path = values.pop();
            if (path !== "private") {
                var pathArr = path.split(".");
                var pLen = pathArr.length;
                var classObj = stage.avm2;
                for (var pIdx = 0; pIdx < pLen; pIdx++) {
                    classObj = classObj[pathArr[pIdx]];
                }
                value = classObj[className];

                if (value === undefined) {
                    value = _this.AVM2[className];
                }
            } else {
                value = _this.AVM2["private::"+ className];
            }
        }

        // global
        if (value === undefined) {
            value = stage.avm2[name];
        }

        return value;
    };

    /**
     * setOptions
     */
    ActionScript3.prototype.setOptions = function ()
    {
        var _this = this;
        var info = _this.info;
        var paramCount = info.paramCount;
        if (paramCount) {
            var data = _this.data;
            var options = info.options;
            var paramType = info.paramType;
            var stage = _this.stage;

            for (var i = 0; i < paramCount; i++) {
                var value = undefined;

                if (i in options) {
                    var option = options[i];
                    var val = option.val;
                    switch (option.kind) {
                        case 0x01: // string
                            value = data.string[val];
                            break;
                        default:
                            console.log("options", option);
                            break;
                    }
                }

                if (i in paramType) {
                    var pType = paramType[i];
                    if (pType) {
                        var mName = data.multiname_info[pType];
                        var className = null;
                        var path = "";
                        switch (mName.kind) {
                            case 0x07: // QName
                                var ns = data.namespace[mName.ns];
                                switch (ns.kind) {
                                    case 0x16:
                                        path = data.string[ns.name];
                                        break;
                                    default:
                                        console.log("SetOptions", ns);
                                        break;
                                }

                                className = data.string[mName.name];
                                break;
                        }

                        if (path) {
                            var values = path.split(".");
                            var pLen = values.length;
                            var classObj = stage.avm2;
                            for (var idx = 0; idx < pLen; idx++) {
                                classObj = classObj[values[idx]];
                            }
                            value = classObj[className];
                        }
                    }
                }

                if (_this.args[i] === undefined) {
                    _this.args[i] = value;
                }
            }
        }
    };

    /**
     * execute
     */
    ActionScript3.prototype.execute = function ()
    {
        var _this = this;
        var stack = [];
        _this.scopeStack = [];

        var i = 0;
        var offset = 0;
        var codes = _this.codes;
        var length = codes.length;

        _this.setOptions();

        while(i < length) {
            var obj = codes[i];
            switch (obj.code) {
                case 0xa0:
                    _this.ActionAdd(stack);
                    break;
                case 0xc5:
                    _this.ActionAddI(stack);
                    break;
                case 0x86:
                    _this.ActionAsType(stack, obj.value1);
                    break;
                case 0x87:
                    _this.ActionAsTypeLate(stack);
                    break;
                case 0xa8:
                    _this.ActionBitAnd(stack);
                    break;
                case 0x97:
                    _this.ActionBitNot(stack);
                    break;
                case 0xa9:
                    _this.ActionBitOr(stack);
                    break;
                case 0xaa:
                    _this.ActionBitXOr(stack);
                    break;
                case 0x41:
                    _this.ActionCall(stack, obj.value1);
                    break;
                case 0x43:
                    _this.ActionCallMethod(stack, obj.value1, obj.value2);
                    break;
                case 0x46:
                    _this.ActionCallProperty(stack, obj.value1, obj.value2);
                    break;
                case 0x4c:
                    _this.ActionCallPropLex(stack, obj.value1, obj.value2);
                    break;
                case 0x4f:
                    _this.ActionCallPropVoid(stack, obj.value1, obj.value2);
                    break;
                case 0x44:
                    _this.ActionCallStatic(stack, obj.value1, obj.value2);
                    break;
                case 0x45:
                    _this.ActionCallSuper(stack, obj.value1, obj.value2);
                    break;
                case 0x4e:
                    _this.ActionCallSuperVoid(stack, obj.value1, obj.value2);
                    break;
                case 0x78:
                    _this.ActionCheckFilter(stack);
                    break;
                case 0x80:
                    _this.ActionCoerce(stack, obj.value1);
                    break;
                case 0x82:
                    _this.ActionCoerceA(stack);
                    break;
                case 0x85:
                    _this.ActionCoerceS(stack);
                    break;
                case 0x42:
                    _this.ActionConstruct(stack, obj.value1);
                    break;
                case 0x4a:
                    _this.ActionConstructProp(stack, obj.value1, obj.value2);
                    break;
                case 0x49:
                    _this.ActionConstructSuper(stack, obj.value1);
                    break;
                case 0x76:
                    _this.ActionConvertB(stack);
                    break;
                case 0x73:
                    _this.ActionConvertI(stack);
                    break;
                case 0x75:
                    _this.ActionConvertD(stack);
                    break;
                case 0x77:
                    _this.ActionConvertO(stack);
                    break;
                case 0x74:
                    _this.ActionConvertU(stack);
                    break;
                case 0x70:
                    _this.ActionConvertS(stack);
                    break;
                case 0xef:
                    _this.ActionDebug(stack, obj.value1, obj.value2, obj.value3, obj.value4);
                    break;
                case 0xf1:
                    _this.ActionDebugFile(stack, obj.value1);
                    break;
                case 0xf0:
                    _this.ActionDebugLine(stack);
                    break;
                case 0x94:
                    _this.ActionDecLocal(stack, obj.value1);
                    break;
                case 0xc3:
                    _this.ActionDecLocalI(stack, obj.value1);
                    break;
                case 0x93:
                    _this.ActionDecrement(stack);
                    break;
                case 0xc1:
                    _this.ActionDecrementI(stack);
                    break;
                case 0x6a:
                    _this.ActionDeleteProperty(stack, obj.value1);
                    break;
                case 0xa3:
                    _this.ActionDivide(stack);
                    break;
                case 0x2a:
                    _this.ActionDup(stack);
                    break;
                case 0x06:
                    _this.ActionDxns(stack, obj.value1);
                    break;
                case 0x07:
                    _this.ActionDxnsLate(stack);
                    break;
                case 0xab:
                    _this.ActionEquals(stack);
                    break;
                case 0x72:
                    _this.ActionEscXAttr(stack);
                    break;
                case 0x71:
                    _this.ActionEscXElem(stack);
                    break;
                case 0x5e:
                    _this.ActionFindProperty(stack, obj.value1);
                    break;
                case 0x5d:
                    _this.ActionFindPropStrict(stack, obj.value1);
                    break;
                case 0x59:
                    _this.ActionGetDescendAnts(stack, obj.value1);
                    break;
                case 0x64:
                    _this.ActionGetGlobalScope(stack);
                    break;
                case 0x6e:
                    _this.ActionGetGlobalsLot(stack, obj.value1);
                    break;
                case 0x60:
                    _this.ActionGetLex(stack, obj.value1);
                    break;
                case 0x62:
                    _this.ActionGetLocal(stack, obj.value1);
                    break;
                case 0xd0:
                    _this.ActionGetLocal0(stack);
                    break;
                case 0xd1:
                    _this.ActionGetLocal1(stack);
                    break;
                case 0xd2:
                    _this.ActionGetLocal2(stack);
                    break;
                case 0xd3:
                    _this.ActionGetLocal3(stack);
                    break;
                case 0x66:
                    _this.ActionGetProperty(stack, obj.value1);
                    break;
                case 0x65:
                    _this.ActionGetScopeObject(stack, obj.value1);
                    break;
                case 0x6c:
                    _this.ActionGetSlot(stack, obj.value1);
                    break;
                case 0x04:
                    _this.ActionGetSuper(stack, obj.value1);
                    break;
                case 0xb0:
                    _this.ActionGreaterEquals(stack);
                    break;
                case 0xaf:
                    _this.ActionGreaterThan(stack);
                    break;
                case 0x1f:
                    _this.ActionHasNext(stack);
                    break;
                case 0x32:
                    _this.ActionHasNext2(stack, obj.value1, obj.value2);
                    break;
                case 0x12:
                    offset = _this.ActionIfFalse(stack, obj.value1);
                    i += offset;
                    break;
                case 0x18:
                    offset = _this.ActionIfGe(stack, obj.value1);
                    i += offset;
                    break;
                case 0x17:
                    offset = _this.ActionIfGt(stack, obj.value1);
                    i += offset;
                    break;
                case 0x16:
                    offset = _this.ActionIfLe(stack, obj.value1);
                    i += offset;
                    break;
                case 0x15:
                    offset = _this.ActionIfLt(stack, obj.value1);
                    i += offset;
                    break;
                case 0x0f:
                    offset = _this.ActionIfNge(stack, obj.value1);
                    i += offset;
                    break;
                case 0x0e:
                    offset = _this.ActionIfNgt(stack, obj.value1);
                    i += offset;
                    break;
                case 0x0d:
                    offset = _this.ActionIfNle(stack, obj.value1);
                    i += offset;
                    break;
                case 0x0c:
                    offset = _this.ActionIfNlt(stack, obj.value1);
                    i += offset;
                    break;
                case 0x14:
                    offset = _this.ActionIfNe(stack, obj.value1);
                    i += offset;
                    break;
                case 0x19:
                    offset = _this.ActionIfStrictEq(stack, obj.value1);
                    i += offset;
                    break;
                case 0x1a:
                    offset = _this.ActionIfStrictNe(stack, obj.value1);
                    i += offset;
                    break;
                case 0x11:
                    offset = _this.ActionIfTrue(stack, obj.value1);
                    i += offset;
                    break;
                case 0xb4:
                    _this.ActionIn(stack, obj.value1);
                    break;
                case 0x92:
                    _this.ActionIncLocal(stack, obj.value1);
                    break;
                case 0xc2:
                    _this.ActionIncLocalI(stack, obj.value1);
                    break;
                case 0x91:
                    _this.ActionIncrement(stack);
                    break;
                case 0xc0:
                    _this.ActionIncrementI(stack);
                    break;
                case 0x68:
                    _this.ActionInitProperty(stack, obj.value1);
                    break;
                case 0xb1:
                    _this.ActionInstanceOf(stack);
                    break;
                case 0xb2:
                    _this.ActionIsType(stack, obj.value1);
                    break;
                case 0xb3:
                    _this.ActionIsTypeLate(stack);
                    break;
                case 0x10: // ActionJump
                    offset = obj.value1;
                    i += offset;
                    break;
                case 0x08:
                    _this.ActionKill(stack, obj.value1);
                    break;
                case 0x09:
                    _this.ActionLabel(stack);
                    break;
                case 0xae:
                    _this.ActionLessEquals(stack);
                    break;
                case 0xad:
                    _this.ActionLessThan(stack);
                    break;
                case 0x1b:
                    _this.ActionLookupSwitch(stack, obj.value1, obj.value1, obj.value3);
                    break;
                case 0xa5:
                    _this.ActionLShift(stack);
                    break;
                case 0xa4:
                    _this.ActionModulo(stack);
                    break;
                case 0xa2:
                    _this.ActionMultiply(stack);
                    break;
                case 0xc7:
                    _this.ActionMultiplyI(stack);
                    break;
                case 0x90:
                    _this.ActionNeGate(stack);
                    break;
                case 0xc4:
                    _this.ActionNeGateI(stack);
                    break;
                case 0x57:
                    _this.ActionNewActivation(stack);
                    break;
                case 0x56:
                    _this.ActionNewArray(stack, obj.value1);
                    break;
                case 0x5a:
                    _this.ActionNewCatch(stack, obj.value1);
                    break;
                case 0x58:
                    _this.ActionNewClass(stack, obj.value1);
                    break;
                case 0x40:
                    _this.ActionNewFunction(stack, obj.value1);
                    break;
                case 0x55:
                    _this.ActionNewObject(stack, obj.value1);
                    break;
                case 0x1e:
                    _this.ActionNextName(stack);
                    break;
                case 0x23:
                    _this.ActionNextValue(stack);
                    break;
                case 0x02:
                    _this.ActionNop(stack);
                    break;
                case 0x96:
                    _this.ActionNot(stack);
                    break;
                case 0x29:
                    _this.ActionPop(stack);
                    break;
                case 0x1d:
                    _this.ActionPopScope();
                    break;
                case 0x24:
                    _this.ActionPushByte(stack, obj.value1);
                    break;
                case 0x2f:
                    _this.ActionPushDouble(stack, obj.value1);
                    break;
                case 0x27:
                    _this.ActionPushFalse(stack, obj.value1);
                    break;
                case 0x2d:
                    _this.ActionPushInt(stack, obj.value1);
                    break;
                case 0x31:
                    _this.ActionPushNameSpace(stack, obj.value1);
                    break;
                case 0x28:
                    _this.ActionPushNan(stack);
                    break;
                case 0x20:
                    _this.ActionPushNull(stack);
                    break;
                case 0x30:
                    _this.ActionPushScope(stack);
                    break;
                case 0x25:
                    _this.ActionPushShort(stack, obj.value1);
                    break;
                case 0x2c:
                    _this.ActionPushString(stack, obj.value1);
                    break;
                case 0x26:
                    _this.ActionPushTrue(stack);
                    break;
                case 0x2e:
                    _this.ActionPushUInt(stack, obj.value1);
                    break;
                case 0x21:
                    _this.ActionPushUndefined(stack);
                    break;
                case 0x1c:
                    _this.ActionPushWith(stack);
                    break;
                case 0x48: // ActionReturnValue
                    return stack.pop();
                case 0x47: // ReturnVoid
                    return undefined;
                case 0xa6:
                    _this.ActionRShift(stack);
                    break;
                case 0x63:
                    _this.ActionSetLocal(stack, obj.value1);
                    break;
                case 0xd4:
                    _this.ActionSetLocal0(stack);
                    break;
                case 0xd5:
                    _this.ActionSetLocal1(stack);
                    break;
                case 0xd6:
                    _this.ActionSetLocal2(stack);
                    break;
                case 0xd7:
                    _this.ActionSetLocal3(stack);
                    break;
                case 0x6f:
                    _this.ActionSetGlobalSlot(stack, obj.value1);
                    break;
                case 0x61:
                    _this.ActionSetProperty(stack, obj.value1);
                    break;
                case 0x6d:
                    _this.ActionSetSlot(stack, obj.value1);
                    break;
                case 0x05:
                    _this.ActionSetSuper(stack, obj.value1);
                    break;
                case 0xac:
                    _this.ActionStrictEquals(stack);
                    break;
                case 0xa1:
                    _this.ActionSubtract(stack);
                    break;
                case 0xc6:
                    _this.ActionSubtractI(stack);
                    break;
                case 0x2b:
                    _this.ActionSwap(stack);
                    break;
                case 0x03:
                    _this.ActionThrow(stack);
                    break;
                case 0x95:
                    _this.ActionTypeof(stack);
                    break;
                case 0xa7:
                    _this.ActionURShift(stack);
                    break;
            }

            i += obj.offset;
            i += 1;
        }
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionAdd = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 + value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionAddI = function (stack)
    {
        var value2 = +stack.pop();
        var value1 = +stack.pop();
        stack[stack.length] = value1 + value2;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionAsType = function (stack, index)
    {
        var type = this.names[index];
        var value = stack.pop();
        stack[stack.length] = (typeof value === type) ? true : null;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionAsTypeLate = function (stack)
    {
        var cValue = stack.pop(); // class
        var value = stack.pop();
        stack[stack.length] = (typeof cValue === value) ? true : null;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionBitAnd = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 & value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionBitNot = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = ~value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionBitOr = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 | value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionBitXOr = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 ^ value2;
    };

    /**
     * @param stack
     * @param argCount
     */
    ActionScript3.prototype.ActionCall = function (stack, argCount)
    {
        var params = [];
        for (var i = argCount; i--;) {
            params[i] = stack.pop();
        }
        var receiver = stack.pop();
        var func = stack.pop();

        var value;
        if (typeof func === "function") {
            value = func.apply(receiver, params);
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallMethod = function (stack, index, argCount)
    {
        var params = [];
        for (var i = 0; i < argCount; i++) {
            params[params.length] = stack.pop();
        }
        var receiver = stack.pop();
        var value;
        if (typeof receiver === "function") {
            value = receiver.apply(this.caller, params);
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallProperty = function (stack, index, argCount)
    {
        var _this = this;
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }
        var prop = _this.names[index];
        var obj = stack.pop();

        var value;
        if (obj) {
            var func = null;
            if (obj instanceof DisplayObject) {
                if (prop in _this.methods) {
                    func = obj[prop];
                }

                if (!func) {
                    func = obj.getProperty(prop);
                }
            } else {
                func = obj[prop];
            }

            if (func) {
                value = func.apply(_this.caller, params);
            }
        }

        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallPropLex = function (stack, index, argCount)
    {
        var _this = this;
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }

        var prop = _this.names[index];
        var obj = stack.pop();

        var value;
        if (obj) {
            value = obj[prop].apply(_this.getBuilder(), params);
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallPropVoid = function (stack, index, argCount)
    {
        var _this = this;
        var params = [];
        for (var i = argCount; i--;) {
            params[i] = stack.pop();
        }

        var obj = stack.pop();
        var name = _this.names[index];

        var values = name.split("::"); // implements
        var prop = values.pop();
        var ns = values.pop();
        if (ns) {
            // console.log(ns, obj, prop);
        }

        var func = obj[prop];
        if (!func && obj instanceof MovieClip) {
            var stage = obj.getStage();
            var symbol = stage.symbols[obj.getCharacterId()];
            if (symbol) {
                var names = symbol.split(".");
                var classMethod = names.pop();
                var length = names.length;
                var classObj = stage.avm2;
                for (i = 0; i < length; i++) {
                    classObj = classObj[names[i]];
                }

                if (classObj) {
                    var AVM2 = classObj[classMethod];
                    while (true) {
                        func = AVM2[prop];
                        if (func) {
                            break;
                        }
                        AVM2 = AVM2.super;
                        if (!AVM2 || AVM2 instanceof MovieClip) {
                            break;
                        }
                    }
                }
            }
        }

        if (!func) {
            while (true) {
                var SuperClass = obj.super;
                if (!SuperClass) {
                    break;
                }

                if (SuperClass instanceof MovieClip) {
                    obj = _this.caller;
                    func = obj[prop];
                    break;
                }

                func = SuperClass[prop];
                if (func) {
                    break;
                }

                obj = SuperClass;
            }
        }

        // fscommand
        if (prop === "fscommand") {
            obj = _this.stage;
        }

        if (func) {
            func.apply(obj, params);
        }
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallStatic = function (stack, index, argCount)
    {
        console.log("ActionCallStatic");
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }
        var receiver = stack.pop();
        var value;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallSuper = function (stack, index, argCount)
    {
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }
        var porp = this.names[index];
        var receiver = stack.pop();

    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionCallSuperVoid = function (stack, index, argCount)
    {
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }
        var porp = this.names[index];
        var receiver = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionCheckFilter = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionCoerce = function (stack, index)
    {
        var value = stack.pop();
        var str = this.names[index];
        stack[stack.length] = str;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionCoerceA = function(stack)
    {
        var value = stack.pop();
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionCoerceS = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = String(value);
    };

    /**
     * @param stack
     * @param argCount
     */
    ActionScript3.prototype.ActionConstruct = function (stack, argCount)
    {
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }
        var obj = stack.pop();
        stack[stack.length] = obj.construct.apply(obj, params);
    };

    /**
     * @param stack
     * @param index
     * @param argCount
     */
    ActionScript3.prototype.ActionConstructProp = function (stack, index, argCount)
    {
        var _this = this;
        var params = [];
        for (var i = argCount; i--;) {
            params[params.length] = stack.pop();
        }

        var prop = _this.names[index];
        var obj = stack.pop();

        var value;
        var stage = _this.stage;
        var DoABC = stage.abc[prop];
        if (DoABC) {
            var builder = _this.getBuilder();
            var AVM2 = new DoABC(builder);
            stage.avm2[prop] = AVM2;
            AVM2[prop].apply(builder, params);
            value = AVM2;
        } else {
            value = new (Function.prototype.bind.apply(obj[prop], params))();
        }

        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param argCount
     */
    ActionScript3.prototype.ActionConstructSuper = function (stack, argCount)
    {
        var _this = this;
        var params = [];
        for (var i = argCount; i--;) {
            params[i] = stack.pop();
        }

        var obj = stack.pop();
        var SuperClassName = obj["__swf2js__:"+_this.ns].extends;
        var values = SuperClassName.split("::");
        var prop = values.pop();
        var ns = values.pop();
        var stage = _this.stage;
        var abcObj = stage.abc;
        var avmObj = stage.avm2;

        if (ns) {
            var names = ns.split(".");
            var length = names.length;
            for (i = 0; i < length; i++) {
                abcObj = abcObj[names[i]];
                avmObj = avmObj[names[i]];
            }
        }

        var sClass = null;
        var SuperClass = abcObj[prop];
        var builder = _this.getBuilder();
        switch (SuperClass) {
            case MovieClip:
                sClass = new MovieClip();
                sClass.setStage(stage);
                sClass._extend = true;
                break;
            case Sound:
                sClass = new Sound();
                sClass.movieClip = builder;
                break;
            default:
                if (SuperClass in window) { // Object
                    sClass = new (Function.prototype.bind.apply(window[SuperClassName], params))();
                } else {
                    sClass = new SuperClass(builder);
                    avmObj[prop] = sClass;
                    sClass[prop].apply(builder, params);
                }
                break;
        }

        obj["super"] = sClass;
        obj["__swf2js__:"+_this.ns].superClass = sClass;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertB = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = (value) ? true : false;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertI = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = value|0;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertD = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = +value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertO = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = (typeof value === "object") ? value : null;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertU = function (stack)
    {
        var value = stack.pop();
        value = value|0;
        if (value < 0) {
            value *= -1;
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionConvertS = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = String(value);
    };

    /**
     * @param stack
     * @param type
     * @param index
     * @param reg
     * @param extra
     */
    ActionScript3.prototype.ActionDebug = function (stack, type, index, reg, extra)
    {


    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionDebugFile = function (stack, index)
    {


    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDebugLine = function (stack)
    {


    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionDecLocal = function (stack, index)
    {

    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionDecLocalI = function (stack, index)
    {

    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDecrement = function (stack)
    {
        var value = stack.pop();
        value -= 1;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDecrementI = function (stack)
    {
        var value = stack.pop();
        value -= 1;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionDeleteProperty = function (stack, index)
    {
        var prop = this.name[index];
        var obj = stack.pop();
        if (obj) {
            if (prop in obj) {
                delete obj[prop];
            } else {
                // TODO
                console.log("ActionDeleteProperty");
            }
        }
        stack[stack.length] = true;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDivide = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 / value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDup = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = value;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionDxns = function (stack, index)
    {

    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionDxnsLate = function (stack)
    {
        var value = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionEquals = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = (value1 == value2);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionEscXAttr = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = String(value);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionEscXElem = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = String(value);
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionFindProperty = function (stack, index)
    {
        var prop = this.names[index];
        var obj;
        stack[stack.length] = obj;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionFindPropStrict = function (stack, index)
    {
        var _this = this;
        var name = _this.names[index];
        var values = name.split("::");
        var prop = values.pop();
        var ns = values.pop();
        var obj = null;

        // caller
        var caller = _this.caller;
        if (caller && !(caller instanceof MovieClip)) {
            if (name in caller) {
                obj = caller;
            }
        }

        if (!obj) {
            var AVM2 = _this.AVM2;
            if (ns) {
                var names = ns.split(".");
                var length = names.length;
                if (length > 1) {
                    var avmObj = _this.stage.avm2;
                    for (var i = 0; i < length; i++) {
                        avmObj = avmObj[names[i]];
                    }
                    AVM2 = avmObj;
                }
            }

            // local
            if (prop in AVM2) {
                obj = AVM2;
            }
        }

        // find avm
        if (!obj) {
            var avm2s = _this.stage.avm2;
            if (prop in avm2s) {
                obj = avm2s[prop];
            }
        }

        // builder
        if (!obj) {
            var builder = _this.getBuilder();
            if (builder) {
                if (builder instanceof MovieClip) {
                    if (prop in _this.methods) {
                        obj = builder;
                    }

                    if (builder.getProperty() !== undefined) {
                        obj = builder;
                    }
                }
            }
        }

        if (!obj) {
            // console.log("ActionFindPropStrict::ERROR", name, AVM2, this);
        }

        stack[stack.length] = obj;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetDescendAnts = function (stack, index)
    {
        console.log("ActionGetDescendAnts");
        var porp = this.names[index];
        var obj;
        stack[stack.length] = obj;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGetGlobalScope = function (stack)
    {
        var _this = this;
        var scopeStack = _this.scopeStack;
        stack[stack.length] = scopeStack[scopeStack.length - 1];
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetGlobalsLot = function (stack, index)
    {
        console.log("ActionGetGlobalsLot");
        var value;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetLex = function (stack, index)
    {
        var _this = this;
        var name = _this.names[index];
        stack[stack.length] = _this.getProperty(name);
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetLocal = function (stack, index)
    {
        var _this = this;
        var value = _this.args[index - 1];
        if (value === undefined) {
            value = _this.register[index];
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGetLocal0 = function (stack)
    {
        stack[stack.length] = this.register[0];
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGetLocal1 = function (stack)
    {
        var _this = this;
        var value = _this.args[0];
        if (value === undefined) {
            value = _this.register[1];
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGetLocal2 = function (stack)
    {
        var _this = this;
        var value = _this.args[1];
        if (value === undefined) {
            value = _this.register[2];
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGetLocal3 = function (stack)
    {
        var _this = this;
        var value = _this.args[2];
        if (value === undefined) {
            value = _this.register[3];
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetProperty = function (stack, index)
    {
        var _this = this;
        var prop = _this.names[index];
        if (prop === null) {
            prop = stack.pop();
        }
        var obj = stack.pop();

        var value;
        if (obj && prop) {
            if (obj instanceof DisplayObject) {
                if (prop in _this.methods) {
                    value = obj[prop];
                }
                if (!value && prop === "parent") {
                    value = obj;
                }
                if (!value) {
                    value = obj.getProperty(prop);
                }
            } else {
                value = obj[prop];
            }

            if (value === undefined) {
                value = _this.getProperty(prop);
            }
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetScopeObject = function (stack, index)
    {
        var activation = this.activation;
        if (!index) {
            stack[stack.length] = activation;
        } else {
            stack[stack.length] = (index in activation) ? activation : null;
        }
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetSlot = function (stack, index)
    {
        var obj = stack.pop();
        var name = obj[index];
        stack[stack.length] = this.activation[name];
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionGetSuper = function (stack, index)
    {
        var prop = this.prop;
        var obj = stack.pop();
        var value;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGreaterEquals = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = (value1 >= value2);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionGreaterThan = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = (value1 > value2);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionHasNext = function (stack)
    {
        var currentIndex = stack.pop();
        var obj = stack.pop();

        currentIndex++;
        var result = 0;
        if (obj) {
            var index = 0;
            for (var key in obj) {
                if (!obj.hasOwnProperty(key)) {
                    continue;
                }

                if (index === currentIndex) {
                    result = currentIndex;
                    break;
                }
                index++;
            }
        }

        stack[stack.length] = result;
    };

    /**
     * @param stack
     * @param objectReg
     * @param indexReg
     */
    ActionScript3.prototype.ActionHasNext2 = function (stack, objectReg, indexReg)
    {
        var _this = this;
        var obj = _this.register[objectReg];
        var currentIndex = _this.currentIndex;

        var value = false;
        var index = 0;
        if (obj) {
            for (var key in obj) {
                if (!obj.hasOwnProperty(key)) {
                    continue;
                }

                if (index === currentIndex) {
                    value = true;
                    currentIndex++;
                    break;
                }

                index++;
            }
        }

        if (!value) {
            currentIndex = 0;
        }

        _this.currentIndex = currentIndex;
        _this.register[indexReg] = index;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfFalse = function (stack, index)
    {
        var value = stack.pop();
        return (value === false) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfGe = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 < value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfGt = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 > value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfLe = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value2 < value1) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfLt = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 < value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfNge = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 < value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfNgt = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value2 < value1) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfNle = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value2 < value1) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfNlt = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 < value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfNe = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 == value2) ? 0 : index;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfStrictEq  = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 === value2) ? index : 0;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfStrictNe  = function (stack, index)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        return (value1 === value2) ? 0 : index;
    };

    /**
     * @param stack
     * @param index
     * @returns {number}
     */
    ActionScript3.prototype.ActionIfTrue = function (stack, index)
    {
        var value = stack.pop();
        return (value === true) ? index : 0;
    };


    /**
     * @param stack
     */
    ActionScript3.prototype.ActionIn = function (stack)
    {
        var obj = stack.pop();
        var name = stack.pop();
        stack[stack.length] = (name in obj);
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionIncLocal = function (stack, index)
    {
        this.register[index]+=1;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionIncLocalI = function (stack, index)
    {
        this.register[index]+=1;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionIncrement = function (stack)
    {
        var value = stack.pop();
        value++;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionIncrementI = function (stack)
    {
        var value = stack.pop();
        value++;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionInitProperty = function (stack, index)
    {
        var value = stack.pop();
        var prop = this.names[index];
        var obj = stack.pop();
        if (obj) {
            if (obj instanceof DisplayObject) {
                obj.setProperty(prop, value);
            } else {
                obj[prop] = value;
            }
        }
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionInstanceOf = function (stack)
    {
        var type = stack.pop();
        var value = stack.pop();
        stack[stack.length] = (value instanceof type);
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionIsType = function (stack, index)
    {
        var value = stack.pop();
        var type = this.name[index];
        stack[stack.length] = (value == type);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionIsTypeLate = function (stack)
    {
        var type = stack.pop();
        var value = stack.pop();
        stack[stack.length] = (value == type);
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionKill = function (stack, index)
    {
        delete this.register[index];
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionLabel = function (stack)
    {

    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionLessEquals = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] =  (value1 <= value2);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionLessThan = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] =  (value1 < value2);
    };

    /**
     * @param stack
     * @param offset
     * @param count
     * @param array
     */
    ActionScript3.prototype.ActionLookupSwitch = function (stack, offset, count, array)
    {
        var index = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionLShift = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 << value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionModulo = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 % value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionMultiply = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 * value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionMultiplyI = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 * value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNeGate = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = -value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNeGateI = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = -value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNewActivation = function (stack)
    {
        var _this = this;
        var trait = _this.body.trait;
        var length = trait.length;
        var activation = new Activation();
        for (var i = 0; i < length; i++) {
            var obj = trait[i];
            var kind = obj.kind;
            switch (kind) {
                case 0:
                    activation[i + 1] = _this.names[obj.name];
                    break;
            }
        }

        _this.activation = activation;
        stack[stack.length] = activation;
    };

    /**
     * @param stack
     * @param argCount
     */
    ActionScript3.prototype.ActionNewArray = function (stack, argCount)
    {
        var array = [];
        for (var i = argCount; i--;) {
            array[i] = stack.pop();
        }
        stack[stack.length] = array;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionNewCatch = function (stack, index)
    {
        var catchScope;
        stack[stack.length] = catchScope;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionNewClass = function (stack, index)
    {
        var basetype = stack.pop();
        var data = this.data;
        var classInfo = data.class[index];
        var id = classInfo.cinit;

        stack[stack.length] = basetype;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionNewFunction = function (stack, index)
    {
        stack[stack.length] = (function (self, id)
        {
            return function ()
            {
                var as3 = new ActionScript3(self.data, id, self.ns, self.stage);
                as3.caller = this;
                as3.parent = self;
                as3.args = arguments;
                return as3.execute();
            };
        })(this, index);
    };

    /**
     * @param stack
     * @param argCount
     */
    ActionScript3.prototype.ActionNewObject = function (stack, argCount)
    {
        var obj = {};
        for (var i = argCount; i--;) {
            var value = stack.pop();
            var prop = stack.pop();
            obj[prop] = value;
        }
        stack[stack.length] = obj;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNextName = function (stack)
    {
        var index = +stack.pop();
        var obj = stack.pop();

        var name;
        if (obj) {
            var count = 0;
            for (var prop in obj) {
                if (!obj.hasOwnProperty(prop)) {
                    continue;
                }

                if (count === index) {
                    name = prop;
                    break;
                }
                count++;
            }
        }
        stack[stack.length] = name;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNextValue = function (stack)
    {
        var index = stack.pop();
        var obj = stack.pop();
        var value;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNop = function (stack)
    {


    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionNot = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = (!value);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPop = function (stack)
    {
        stack.pop();
    };

    /**
     *
     */
    ActionScript3.prototype.ActionPopScope = function ()
    {
        this.scopeStack.pop();
    };

    /**
     * @param stack
     * @param value
     */
    ActionScript3.prototype.ActionPushByte = function (stack, value)
    {
        stack[stack.length] = value|0;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionPushDouble = function (stack, index)
    {
        var data = this.data;
        var double = data.double;
        var value = double[index];
        stack[stack.length] = +value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushFalse = function (stack)
    {
        stack[stack.length] = false;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionPushInt = function (stack, index)
    {
        var data = this.data;
        var integer = data.integer;
        var value = integer[index];
        stack[stack.length] = +value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionPushNameSpace = function (stack, index)
    {
        var data = this.data;
        var names = data.names;
        var value = names[index];
        stack[stack.length] = +value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushNan = function (stack)
    {
        stack[stack.length] = NaN;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushNull = function (stack)
    {
        stack[stack.length] = null;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushScope = function (stack)
    {
        var scope = stack.pop();
        if (scope) {
            var scopeStack = this.scopeStack;
            scopeStack[scopeStack.length] = scope;
        }
    };

    /**
     * @param stack
     * @param value
     */
    ActionScript3.prototype.ActionPushShort = function (stack, value)
    {
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionPushString = function (stack, index)
    {
        var data = this.data;
        var string = data.string;
        stack[stack.length] = ""+string[index];
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushTrue = function (stack)
    {
        stack[stack.length] = true;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionPushUInt = function (stack, index)
    {
        var data = this.data;
        var uinteger = data.uinteger;
        stack[stack.length] = uinteger[index];
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushUndefined = function (stack)
    {
        stack[stack.length] = undefined;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionPushWith = function (stack)
    {
        var obj = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionRShift = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 >> value2;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionSetLocal = function (stack, index)
    {
        this.register[index] = stack.pop();
    };


        /**
     * @param stack
     */
    ActionScript3.prototype.ActionSetLocal0 = function (stack)
    {
        this.register[0] = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSetLocal1 = function (stack)
    {
        this.register[1] = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSetLocal2 = function (stack)
    {
        this.register[2] = stack.pop();
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSetLocal3 = function (stack)
    {
        this.register[3] = stack.pop();
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionSetGlobalSlot = function (stack, index)
    {
        var value = stack.pop();
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionSetProperty = function (stack, index)
    {
        var _this = this;
        var value = stack.pop();
        var prop = _this.names[index];
        var obj = stack.pop();

        if (obj) {
            if (obj instanceof DisplayObject) {
                if (prop in _this.methods) {
                    obj[prop] = value;
                } else {
                    console.log("ActionSetProperty", prop);
                }
            } else if (prop in obj) {
                obj[prop] = value;
            } else {
                var builder = _this.getBuilder();
                var caller = _this.caller;
                if (caller instanceof MovieClip) {
                    builder = caller;
                }

                if (builder instanceof DisplayObject) {
                    if (prop in _this.methods) {
                        builder[prop] = value;
                    } else {
                        obj[prop] = value;
                    }
                }
            }
        }
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionSetSlot = function (stack, index)
    {
        var value = stack.pop();
        var obj = stack.pop();
        var name = obj[index];
        this.activation[name] = value;
    };

    /**
     * @param stack
     * @param index
     */
    ActionScript3.prototype.ActionSetSuper = function (stack, index)
    {
        var value = stack.pop();
        var prop = this.names[index];
        var obj = stack.pop();

    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionStrictEquals = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = (value1 === value2);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSubtract = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value1 - value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSubtractI = function (stack)
    {
        var value2 = +stack.pop();
        var value1 = +stack.pop();
        stack[stack.length] = value1 - value2;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionSwap = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value2;
        stack[stack.length] = value1;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionThrow = function (stack)
    {
        var value = stack.pop();
        console.log(value);
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionTypeof = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = typeof value;
    };

    /**
     * @param stack
     */
    ActionScript3.prototype.ActionURShift = function (stack)
    {
        var value2 = stack.pop();
        var value1 = stack.pop();
        stack[stack.length] = value2 >> value1;
    };

    /**
     * @param data
     * @param constantPool
     * @param register
     * @param initAction
     * @constructor
     */
    var ActionScript = function (data, constantPool, register, initAction)
    {
        var _this = this;
        _this.cache = [];
        _this.params = [];
        _this.constantPool = constantPool || [];
        _this.register = register || [];
        _this.variables = {};
        _this.initAction = (initAction) ? true : false;
        _this.scope = null;
        _this.parent = null;
        _this.arg = null;
        _this.version = 7;
        _this.superClass = null;
        if (data.length) {
            _this.initialize(data);
        }
        _this.initParam();
    };

    /**
     * reset
     */
    ActionScript.prototype.reset = function ()
    {
        var _this = this;
        _this.arg = null;
        _this.variables = {};
        _this.initParam();
    };

    /**
     * initParam
     */
    ActionScript.prototype.initParam = function ()
    {
        var _this = this;
        var register = _this.register;
        var length = register.length;
        var params = [];
        for (var i = 0; i < length; i++) {
            var obj = register[i];
            if (obj.name === null) {
                params[obj.register] = obj.value;
            } else {
                params[obj.register] = obj.name;
            }
        }
        _this.params = params;
    };

    /**
     * @param values
     */
    ActionScript.prototype.initVariable = function (values)
    {
        var _this = this;
        _this.arg = values;
        var register = _this.register;
        var length = register.length;
        var variables = _this.variables;
        var key = 0;
        for (var i = 0; i < length; i++) {
            var obj = register[i];
            if (obj.name === null) {
                continue;
            }
            variables[obj.name] = values[key++];
        }
        _this.variables = variables;
        _this.initParam();
    };

    /**
     * @returns {{}}
     */
    ActionScript.prototype.getSuperClass = function ()
    {
        var _this = this;
        var superClass = _this.superClass;
        if (!superClass) {
            var parent = _this.parent;
            if (parent) {
                superClass = parent.getSuperClass();
            }
        }
        return superClass;
    };

    /**
     * @param name
     * @param value
     */
    ActionScript.prototype.setVariable = function (name, value)
    {
        var _this = this;
        var finish = false;
        if (name in _this.variables) {
            _this.variables[name] = value;
            finish = true;
        }
        if (!finish) {
            var parent = _this.parent;
            if (parent) {
                finish = parent.setVariable(name, value);
            }
        }
        return finish;
    };

    /**
     * @param name
     * @returns {*}
     */
    ActionScript.prototype.getVariable = function (name)
    {
        var _this = this;
        var value, parent;
        switch (name) {
            case "this":
                value = _this.variables["this"];
                break;
            case "arguments":
                value = _this.arg;
                break;
            default:
                value = _this.variables[name];
                if (value === undefined) {
                    parent = _this.parent;
                    if (parent) {
                        value = parent.getVariable(name);
                    }
                }
                break;
        }
        return value;
    };

    /**
     * @param value
     * @returns {string}
     */
    ActionScript.prototype.valueToString = function (value)
    {
        if (typeof value !== "string") {
            value += "";
        }
        return value;
    };

    /**
     * @param str
     * @param mc
     * @returns {*}
     */
    ActionScript.prototype.stringToObject = function(str, mc)
    {
        var object = this.getVariable(str);
        if (object === undefined) {
            object = mc.getProperty(str);
        }
        return object;
    };

    /**
     * @param data
     */
    ActionScript.prototype.initialize = function (data)
    {
        var _this = this;
        var isEnd = false;
        var obj = {};
        var i = 0;
        var idx = 0;
        var cache = [];
        var indexes = [];
        var asData;
        var register;
        var values;
        var NumParams;
        var payloadLength;
        var withEndPoint = 0;
        var bitio = new BitIO();
        bitio.setData(data);

        var pBitio = new BitIO();
        var endPoint = data.length;

        _this.initParam();
        while (bitio.byte_offset < endPoint) {
            var startOffset = bitio.byte_offset;
            obj = {};

            if (withEndPoint && withEndPoint === bitio.byte_offset) {
                withEndPoint = 0;
                obj.actionCode = 0x94;
                obj.Size = 0;
                cache[cache.length] = obj;
                continue;
            }

            var actionCode = bitio.getUI8();
            obj.actionCode = actionCode;

            var payload = null;
            if (actionCode >= 0x80) {
                payloadLength = bitio.getUI16();
                payload = bitio.getData(payloadLength);
                pBitio.setData(payload);
                pBitio.setOffset(0, 0);
            }

            switch (actionCode) {
                // GotoFrame
                case 0x81:
                    obj.frame = (pBitio.getUI16()|0) + 1;
                    break;
                // WaitForFrame
                case 0x8A:
                    obj.frame = pBitio.getUI16();
                    obj.skipCount = pBitio.getUI8();
                    break;
                // SetTarget
                case 0x8B:
                    obj.targetName = pBitio.getDataUntil("\0");
                    break;
                // GoToLabel
                case 0x8C:
                    obj.label = pBitio.getDataUntil("\0");
                    break;
                case 0x83:
                    var len = payload.length - 1;
                    var urls = [[]];
                    idx = 0;
                    for (i = 0; i < len; i++) {
                        var str = _fromCharCode(payload[i]);
                        if (payload[i] === 0) {
                            idx++;
                            urls[idx] = [];
                            continue;
                        }
                        urls[idx] += str;
                    }

                    var urlString = urls[0];
                    if (typeof urlString === "string") {
                        var splitUrl = urlString.split("?");
                        if (2 in splitUrl) {
                            urlString = splitUrl[0];
                            urlString += "?" + splitUrl[1];
                            var paramLength = splitUrl.length;
                            for (i = 2; i < paramLength; i++) {
                                urlString += "&" + splitUrl[i];
                            }
                        }
                    }

                    obj.url = urlString;
                    obj.target = urls[1];
                    break;
                // Push
                case 0x96:
                    values = [];
                    while (pBitio.byte_offset < payloadLength) {
                        var type = pBitio.getUI8();
                        switch (type) {
                            case 0: // String
                                values[values.length] = String(pBitio.getDataUntil("\0"));
                                break;
                            case 1: // Float
                                values[values.length] = pBitio.getFloat32();
                                break;
                            case 2: // null
                                values[values.length] = null;
                                break;
                            case 3: // undefined
                                values[values.length] = undefined;
                                break;
                            case 4: // RegisterNumber
                                values[values.length] = {"key": pBitio.getUI8()};
                                break;
                            case 5: // Boolean
                                values[values.length] = (pBitio.getUI8()) ? true : false;
                                break;
                            case 6: // Double
                                values[values.length] = pBitio.getFloat64();
                                break;
                            case 7: // Integer
                                values[values.length] = pBitio.getUI32();
                                break;
                            case 8: // Constant8
                                values[values.length] = _this.constantPool[pBitio.getUI8()];
                                break;
                            case 9: // Constant16
                                values[values.length] = _this.constantPool[pBitio.getUI16()];
                                break;
                            default:
                                break;
                        }
                    }
                    obj.values = values;
                    break;
                // If
                case 0x9D:
                    obj.offset = bitio.byte_offset + bitio.toSI16LE(payload);
                    break;
                // Jump
                case 0x99:
                    obj.offset = bitio.byte_offset + bitio.toSI16LE(payload);
                    break;
                // GetURL2
                case 0x9A:
                    obj.LoadVariablesFlag = pBitio.getUIBits(1); // 0=none, 1=LoadVariables
                    obj.LoadTargetFlag = pBitio.getUIBits(1);// 0=web, 1=Sprite
                    pBitio.getUIBits(4); // Reserved
                    obj.SendVarsMethod = pBitio.getUIBits(2);// 0=NONE, 1=GET, 2=POST
                    break;
                // GoToFrame2
                case 0x9F:
                    pBitio.getUIBits(6); // Reserved
                    obj.SceneBiasFlag = pBitio.getUIBit();
                    obj.PlayFlag = pBitio.getUIBit();// 0=stop, 1=play
                    if (obj.SceneBiasFlag === 1) {
                        obj.SceneBias = pBitio.getUI16();
                    }
                    break;
                // WaitForFrame2
                case 0x8D:
                    obj.skipCount = pBitio.getUI8();
                    break;
                // ConstantPool
                case 0x88:
                    var count = pBitio.getUI16();
                    var constantPool = [];
                    if (count > 0) {
                        while (count--) {
                            constantPool[constantPool.length] = pBitio.getDataUntil("\0");
                        }
                    }
                    obj.constantPool = constantPool;
                    _this.constantPool = constantPool;
                    break;
                // ActionDefineFunction
                case 0x9b:
                    obj.FunctionName = pBitio.getDataUntil("\0");
                    NumParams = pBitio.getUI16();
                    register = [];
                    if (NumParams > 0) {
                        idx = 1;
                        while (NumParams--) {
                            register[register.length] = {
                                register: idx,
                                name: pBitio.getDataUntil("\0"),
                                value: null
                            };
                        }
                    }

                    asData = bitio.getData(pBitio.getUI16());
                    obj.ActionScript = new ActionScript(asData, _this.constantPool, register, _this.initAction);

                    break;
                // ActionWith
                case 0x94:
                    obj.Size = pBitio.getUI16();
                    withEndPoint = obj.Size + bitio.byte_offset;
                    break;
                // ActionStoreRegister
                case 0x87:
                    obj.RegisterNumber = pBitio.getUI8();
                    break;
                // SWF 7 ***********************************
                // ActionDefineFunction2
                case 0x8e:
                    register = [];
                    values = [];

                    obj.FunctionName = pBitio.getDataUntil("\0");
                    NumParams = pBitio.getUI16();
                    var RegisterCount = pBitio.getUI8();
                    obj.PreloadParentFlag = pBitio.getUIBits(1);
                    obj.PreloadRootFlag = pBitio.getUIBits(1);
                    obj.SuppressSuperFlag = pBitio.getUIBits(1);
                    obj.PreloadSuperFlag = pBitio.getUIBits(1);
                    obj.SuppressArgumentsFlag = pBitio.getUIBits(1);
                    obj.PreloadArgumentsFlag = pBitio.getUIBits(1);
                    obj.SuppressThisFlag = pBitio.getUIBits(1);
                    obj.PreloadThisFlag = pBitio.getUIBits(1);
                    pBitio.getUIBits(7); // Reserved
                    obj.PreloadGlobalFlag = pBitio.getUIBits(1);

                    if (obj.PreloadThisFlag) {
                        values[values.length] = "this";
                    }
                    if (obj.PreloadArgumentsFlag) {
                        values[values.length] = "arguments";
                    }
                    if (obj.PreloadSuperFlag) {
                        values[values.length] = "super";
                    }
                    if (obj.PreloadRootFlag) {
                        values[values.length] = "_root";
                    }
                    if (obj.PreloadParentFlag) {
                        values[values.length] = "_parent";
                    }
                    if (obj.PreloadGlobalFlag) {
                        values[values.length] = "_global";
                    }
                    for (idx = 1; idx < RegisterCount; idx++) {
                        var rIdx = idx - 1;
                        if (!(rIdx in values)) {
                            continue;
                        }
                        register[register.length] = {
                            register: idx,
                            name: null,
                            value: values[rIdx]
                        };
                    }

                    if (NumParams > 0) {
                        while (NumParams--) {
                            var Register = pBitio.getUI8();
                            var ParamName = pBitio.getDataUntil("\0");
                            register[register.length] = {
                                register: Register,
                                name: ParamName,
                                value: null
                            };
                        }
                    }

                    asData = bitio.getData(pBitio.getUI16());
                    obj.ActionScript = new ActionScript(asData, _this.constantPool, register, _this.initAction);
                    break;
                // ActionTry
                case 0x8f:
                    pBitio.getUIBits(5); // Reserved
                    var CatchInRegisterFlag = pBitio.getUIBits(1);
                    obj.FinallyBlockFlag = pBitio.getUIBits(1);
                    obj.CatchBlockFlag = pBitio.getUIBits(1);
                    var TrySize = pBitio.getUI16();
                    var CatchSize = pBitio.getUI16();
                    var FinallySize = pBitio.getUI16();

                    var CatchName;
                    if (!CatchInRegisterFlag) {
                        CatchName = pBitio.getDataUntil("\0");
                    } else {
                        CatchName = pBitio.getUI8();
                    }

                    i = 0;
                    var TryBody = [];
                    if (TrySize) {
                        while (TrySize) {
                            TryBody[TryBody.length] = bitio.getUI8();
                            TrySize--;
                        }
                    }

                    obj.try = (function (data)
                    {
                        var as = new ActionScript(data);
                        return function ()
                        {
                            as.reset();
                            as.variables["this"] = this;
                            return as.execute(this);
                        };
                    })(TryBody);

                    if (obj.CatchBlockFlag) {
                        var CatchBody = [];
                        if (CatchSize) {
                            while (CatchSize) {
                                CatchBody[CatchBody.length] = bitio.getUI8();
                                CatchSize--;
                            }
                        }

                        obj.catch = (function (data, catchName)
                        {
                            var as = new ActionScript(data);
                            return function ()
                            {
                                as.reset();
                                as.variables["this"] = this;
                                as.variables[catchName] = arguments[0];
                                return as.execute(this);
                            };
                        })(CatchBody, CatchName);
                    }

                    if (obj.FinallyBlockFlag) {
                        var FinallyBody = [];
                        if (FinallySize) {
                            while (FinallySize) {
                                FinallyBody[FinallyBody.length] = bitio.getUI8();
                                FinallySize--;
                            }
                        }

                        obj.finally = (function (data)
                        {
                            var as = new ActionScript(data);
                            return function ()
                            {
                                as.reset();
                                as.variables["this"] = this;
                                return as.execute(this);
                            };
                        })(FinallyBody);
                    }

                    break;
                case 0x00:
                    isEnd = true;
                    break;
            }

            indexes[startOffset] = cache.length;
            cache[cache.length] = obj;

            if (isEnd) {
                break;
            }
        }

        // If and Jump
        var length = cache.length;
        for (i = 0; i < length; i++) {
            obj = cache[i];
            var code = obj.actionCode;
            if (code === 0x9D || code === 0x99) {
                var index = indexes[obj.offset];
                if (index !== undefined) {
                    obj.offset = index - 1;
                } else {
                    obj.offset = cache.length - 1;
                }
            }
        }
        _this.cache = cache;
    };

    /**
     * @param value
     * @returns {*}
     */
    ActionScript.prototype.calc = function (value)
    {
        var calc;
        switch (typeof value) {
            case "boolean":
                calc = (value) ? 1 : 0;
                break;
            case "object":
                if (value === null) {
                    calc = 0;
                } else if (value instanceof Array) {
                    calc = value.length;
                } else if (value instanceof Object) {
                    calc = 1;
                }
                break;
            default:
                calc = +value;
                break;
        }

        if (_isNaN(calc)) {
            calc = 0;
        }

        return calc;
    };

    /**
     * @param value
     * @returns {*}
     */
    ActionScript.prototype.logicValue = function (value)
    {
        var calc;
        switch (typeof value) {
            case "boolean":
                calc = (value) ? 1 : 0;
                break;
            case "object":
                if (value === null) {
                    calc = 0;
                } else if (value instanceof Array) {
                    calc = value.length;
                } else if (value instanceof Object) {
                    calc = 1;
                }
                break;
            case "string":
                if (value === "") {
                    calc = 0;
                } else {
                    calc = 1;
                }
                break;
            case "function":
                calc = 1;
                break;
            default:
                calc = +value;
                if (_isNaN(calc)) {
                    calc = 0;
                }
                break;
        }
        return calc;
    };

    /**
     * @param stack
     * @returns {Number}
     */
    ActionScript.prototype.operationValue = function (stack)
    {
        var value = +stack.pop();
        if (_isNaN(value)) {
            value = 0;
        }
        return value;
    };

    /**
     * @param mc
     * @returns {*}
     */
    ActionScript.prototype.execute = function (mc)
    {
        var _this = this;
        var scope = _this.scope;
        var movieClip = (scope instanceof MovieClip) ? scope : mc;
        if (!movieClip.active) {
            return undefined;
        }
        var stage = movieClip.getStage();
        if (stage) {
            _this.version = stage.getVersion();
        }

        var stack = [];
        var cache = _this.cache;
        var cLength = cache.length;
        var cIdx = 0;
        while(cIdx < cLength) {
            if (!(cIdx in cache)) {
                cIdx++;
                continue;
            }

            var aScript = cache[cIdx];
            var actionCode = aScript.actionCode;
            if (actionCode === 0) {
                break;
            }

            switch (actionCode) {
                // ********************************************
                // SWF 3
                // ********************************************
                case 0x81:
                    _this.ActionGotoFrame(movieClip, aScript.frame);
                    break;
                case 0x04:
                    _this.ActionNextFrame(movieClip);
                    break;
                case 0x05:
                    _this.ActionPreviousFrame(movieClip);
                    break;
                case 0x06:
                    _this.ActionPlay(movieClip);
                    break;
                case 0x07:
                    _this.ActionStop(movieClip);
                    break;
                case 0x08: // ActionToggleQuality
                case 0x8A: // ActionWaitForFrame
                    break;
                case 0x09:
                    _this.ActionStopSounds(movieClip);
                    break;
                case 0x8B:
                    movieClip = _this.ActionSetTarget(movieClip, mc, aScript.targetName);
                    break;
                case 0x8C:
                    _this.ActionGoToLabel(movieClip, aScript.label);
                    break;
                case 0x83:
                    _this.ActionGetURL(movieClip, aScript.url, aScript.target);
                    break;

                // ********************************************
                // SWF 4
                // ********************************************
                case 0x0A: // ActionAdd
                    _this.ActionOperation(stack, 0);
                    break;
                case 0x0B: // ActionSubtract
                    _this.ActionOperation(stack, 1);
                    break;
                case 0x0C: // ActionMultiply
                    _this.ActionOperation(stack, 2);
                    break;
                case 0x0D: // ActionDivide
                    _this.ActionOperation(stack, 3);
                    break;
                case 0x0E:
                    _this.ActionEquals(stack);
                    break;
                case 0x0F:
                    _this.ActionLess(stack);
                    break;
                case 0x10:
                    _this.ActionAnd(stack);
                    break;
                case 0x11:
                    _this.ActionOr(stack);
                    break;
                case 0x12:
                    _this.ActionNot(stack);
                    break;
                case 0x13:
                    _this.ActionStringEquals(stack);
                    break;
                case 0x14: // ActionStringLength
                case 0x31: // ActionMBStringLength
                    _this.ActionStringLength(stack);
                    break;
                case 0x21:
                    _this.ActionStringAdd(stack);
                    break;
                case 0x15:// ActionStringExtract
                case 0x35:// ActionMBStringExtract
                    _this.ActionStringExtract(stack);
                    break;
                case 0x29:
                    _this.ActionStringLess(stack);
                    break;
                case 0x17: // ActionPop
                    stack.pop();
                    break;
                case 0x96:
                    _this.ActionPush(stack, movieClip, aScript.values);
                    break;
                case 0x33: // ActionAsciiToChar
                case 0x37: // ActionMBAsciiToChar
                    _this.ActionAsciiToChar(stack);
                    break;
                case 0x36: // ActionMBCharToAscii
                case 0x32: // ActionCharToAscii
                    _this.ActionCharToAscii(stack);
                    break;
                case 0x18:
                    _this.ActionToInteger(stack);
                    break;
                case 0x9E:
                    _this.ActionCall(stack, movieClip);
                    break;
                case 0x9D:
                    cIdx = _this.ActionIf(stack, aScript.offset, cIdx);
                    break;
                case 0x99: // ActionJump
                    cIdx = aScript.offset;
                    break;
                case 0x1C:
                    _this.ActionGetVariable(stack, movieClip);
                    break;
                case 0x1D:
                    _this.ActionSetVariable(stack, movieClip);
                    break;
                case 0x9A:
                    _this.ActionGetURL2(stack, aScript, movieClip);
                    break;
                case 0x22:
                    _this.ActionGetProperty(stack, movieClip);
                    break;
                case 0x9F:
                    _this.ActionGoToFrame2(stack, aScript, movieClip);
                    break;
                case 0x20:
                    movieClip = _this.ActionSetTarget2(stack, movieClip, mc);
                    break;
                case 0x23:
                    _this.ActionSetProperty(stack, movieClip);
                    break;
                case 0x27:
                    _this.ActionStartDrag(stack, movieClip);
                    break;
                case 0x8D: // ActionWaitForFrame2
                    stack.pop();
                    break;
                case 0x24:
                    _this.ActionCloneSprite(stack, movieClip);
                    break;
                case 0x25:
                    _this.ActionRemoveSprite(stack, movieClip);
                    break;
                case 0x28:
                    _this.ActionEndDrag(movieClip);
                    break;
                case 0x34:
                    _this.ActionGetTime(stack);
                    break;
                case 0x30:
                    _this.ActionRandomNumber(stack);
                    break;
                case 0x26:
                    _this.ActionTrace(stack);
                    break;
                case 0x00:
                    break;
                case 0x2D:
                    _this.ActionFsCommand2(stack, movieClip);
                    break;

                // ********************************************
                // SWF 5
                // ********************************************
                case 0x52:
                    _this.ActionCallMethod(stack, movieClip);
                    break;
                case 0x88: // ActionConstantPool
                    _this.constantPool = aScript.constantPool;
                    break;
                case 0x3d:
                    _this.ActionCallFunction(stack, movieClip);
                    break;
                case 0x9b:
                    _this.ActionDefineFunction(stack, aScript, movieClip);
                    break;
                case 0x3c:
                    _this.ActionDefineLocal(stack, movieClip);
                    break;
                case 0x41:
                    _this.ActionDefineLocal2(stack, movieClip);
                    break;
                case 0x3a:
                    _this.ActionDelete(stack, movieClip);
                    break;
                case 0x3b:
                    _this.ActionDelete2(stack, movieClip);
                    break;
                case 0x46:
                    _this.ActionEnumerate(stack, movieClip);
                    break;
                case 0x49:
                    _this.ActionEquals2(stack);
                    break;
                case 0x4e:
                    _this.ActionGetMember(stack, movieClip);
                    break;
                case 0x42:
                    _this.ActionInitArray(stack);
                    break;
                case 0x43:
                    _this.ActionInitObject(stack);
                    break;
                case 0x53:
                    _this.ActionNewMethod(stack, movieClip);
                    break;
                case 0x40:
                    _this.ActionNewObject(stack, movieClip);
                    break;
                case 0x4f:
                    _this.ActionSetMember(stack, movieClip);
                    break;
                case 0x45:
                    _this.ActionTargetPath(stack);
                    break;
                case 0x94:
                    movieClip = _this.ActionWith(stack, aScript.Size, mc);
                    break;
                case 0x4a:
                    _this.ActionToNumber(stack);
                    break;
                case 0x4b:
                    _this.ActionToString(stack);
                    break;
                case 0x44:
                    _this.ActionTypeOf(stack);
                    break;
                case 0x47:
                    _this.ActionAdd2(stack);
                    break;
                case 0x48:
                    _this.ActionLess2(stack);
                    break;
                case 0x3f:
                    _this.ActionModulo(stack);
                    break;
                case 0x60:
                    _this.ActionBitAnd(stack);
                    break;
                case 0x63:
                    _this.ActionBitLShift(stack);
                    break;
                case 0x61:
                    _this.ActionBitOr(stack);
                    break;
                case 0x64:
                    _this.ActionBitRShift(stack);
                    break;
                case 0x65:
                    _this.ActionBitURShift(stack);
                    break;
                case 0x62:
                    _this.ActionBitXor(stack);
                    break;
                case 0x51:
                    _this.ActionDecrement(stack);
                    break;
                case 0x50:
                    _this.ActionIncrement(stack);
                    break;
                case 0x4c:
                    _this.ActionPushDuplicate(stack);
                    break;
                case 0x3e: // ActionReturn
                    return stack.pop();
                case 0x4d:
                    _this.ActionStackSwap(stack);
                    break;
                case 0x87:
                    _this.ActionStoreRegister(stack, aScript.RegisterNumber);
                    break;

                // ********************************************
                // SWF 6
                // ********************************************
                case 0x54:
                    _this.ActionInstanceOf(stack);
                    break;
                case 0x55:
                    _this.ActionEnumerate(stack, movieClip);
                    break;
                case 0x66:
                    _this.ActionStrictEquals(stack);
                    break;
                case 0x67: // ActionGreater
                case 0x68: // ActionStringGreater
                    _this.ActionGreater(stack);
                    break;

                // ********************************************
                // SWF 7
                // ********************************************
                case 0x8e: // ActionDefineFunction2
                    _this.ActionDefineFunction(stack, aScript, movieClip);
                    break;
                case 0x69:
                    _this.ActionExtends(stack);
                    break;
                case 0x2b:
                    _this.ActionCastOp(stack);
                    break;
                case 0x2c:
                    _this.ActionImplementsOp(stack);
                    break;
                case 0x8f:
                    _this.ActionTry(aScript, movieClip);
                    break;
                case 0x2a:
                    _this.ActionThrow(stack);
                    break;

                default:
                    console.log("[ActionScript Error] Code: " + actionCode);
                    break;
            }
            cIdx++;
        }
    };

    /**
     * @type {{}}
     */
    ActionScript.prototype.methods = {
        gotoandstop: "gotoAndStop",
        gotoandplay: "gotoAndPlay",
        play: "play",
        stop: "stop",
        duplicatemovieclip: "duplicateMovieClip",
        getproperty: "getProperty",
        removemovieclip: "removeMovieClip",
        setproperty: "setProperty",
        startdrag: "startDrag",
        stopdrag: "stopDrag",
        targetpath: "targetPath",
        updateafterevent: "updateAfterEvent",
        nextframe: "nextFrame",
        nextscene: "nextScene",
        prevframe: "prevFrame",
        prevscene: "prevScene",
        stopallsounds: "stopAllSounds",
        setmask: "setMask",
        geturl: "getURL",
        loadmovie: "loadMovie",
        loadmovienum: "loadMovieNum",
        loadvariables: "loadVariables",
        loadvariablesnum: "loadVariablesNum",
        unloadmovie: "unloadMovie",
        unloadmovienum: "unloadMovieNum",
        swapdepths: "swapDepths",
        getinstanceatdepth: "getInstanceAtDepth",
        attachmovie: "attachMovie",
        attachaudio: "attachAudio",
        attachbitmap: "attachBitmap",
        getnexthighestdepth: "getNextHighestDepth",
        getbytesloaded: "getBytesLoaded",
        getbytestotal: "getBytesTotal",
        assetpropflags: "ASSetPropFlags",
        linestyle: "lineStyle",
        linegradientstyle: "lineGradientStyle",
        beginfill: "beginFill",
        begingradientfill: "beginGradientFill",
        beginbitmapfill: "beginBitmapFill",
        clear: "clear",
        moveto: "moveTo",
        lineto: "lineTo",
        curveto: "curveTo",
        endfill: "endFill",
        hittest: "hitTest",
        getdepth: "getDepth",
        createemptymovieclip: "createEmptyMovieClip",
        createtextfield: "createTextField",
        getbounds: "getBounds",
        getrect: "getRect",
        getswfversion: "getSWFVersion",
        gettextsnapshot: "getTextSnapshot",
        globaltolocal: "globalToLocal",
        localtoglobal: "localToGlobal"
    };

    /**
     * @param method
     * @returns {*}
     */
    ActionScript.prototype.checkMethod = function (method)
    {
        if (!method || typeof method !== "string") {
            return method;
        }
        var lowerMethod = method.toLowerCase();
        return this.methods[lowerMethod] || null;
    };

    /**
     * @param mc
     * @param frame
     */
    ActionScript.prototype.ActionGotoFrame = function (mc, frame)
    {
        if (mc instanceof MovieClip) {
            mc.stop();
            mc.setNextFrame(frame);
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionNextFrame = function (mc)
    {
        if (mc instanceof MovieClip) {
            mc.nextFrame();
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionPreviousFrame = function (mc)
    {
        if (mc instanceof MovieClip) {
            mc.prevFrame();
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionPlay = function (mc)
    {
        if (mc instanceof MovieClip) {
            mc.play();
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionStop = function (mc)
    {
        if (mc instanceof MovieClip) {
            mc.stop();
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionStopSounds = function (mc)
    {
        if (mc instanceof MovieClip) {
            mc.stopAllSounds();
        }
    };

    /**
     * @param movieClip
     * @param mc
     * @param target
     * @returns {*}
     */
    ActionScript.prototype.ActionSetTarget = function (movieClip, mc, target)
    {
        if (target !== "") {
            var targetMc = movieClip;
            if (!targetMc) {
                targetMc = mc;
            }
            return targetMc.getDisplayObject(target);
        } else {
            if (mc.active) {
                return mc;
            } else {
                return undefined;
            }
        }
    };

    /**
     * @param mc
     * @param label
     */
    ActionScript.prototype.ActionGoToLabel = function (mc, label)
    {
        if (mc instanceof MovieClip) {
            var frame = mc.getLabel(label);
            mc.stop();
            if (typeof frame === "number" && frame) {
                mc.setNextFrame(frame);
            }
        }
    };

    /**
     * @param mc
     * @param url
     * @param target
     */
    ActionScript.prototype.ActionGetURL = function (mc, url, target)
    {
        if (mc instanceof MovieClip) {
            mc.getURL(url, target);
        }
    };

    /**
     * @param stack
     * @param operation
     */
    ActionScript.prototype.ActionOperation = function (stack, operation)
    {
        var _this = this;
        var a = _this.operationValue(stack);
        var b = _this.operationValue(stack);
        var value;
        switch (operation) {
            case 0:
                value = b + a;
                break;
            case 1:
                value = b - a;
                break;
            case 2:
                value = b * a;
                break;
            case 3:
                value = b / a;
                break;
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionEquals = function (stack)
    {
        var _this = this;
        var a = _this.calc(stack.pop());
        var b = _this.calc(stack.pop());
        if (_this.version > 4) {
            stack[stack.length] = (a === b);
        } else {
            stack[stack.length] = (a === b) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionLess = function (stack)
    {
        var _this = this;
        var a = _this.calc(stack.pop());
        var b = _this.calc(stack.pop());
        if (_this.version > 4) {
            stack[stack.length] = (b < a);
        } else {
            stack[stack.length] = (b < a) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionAnd = function (stack)
    {
        var _this = this;
        var a = stack.pop();
        var b = stack.pop();
        if (_this.version > 4) {
            a = _this.logicValue(a);
            b = _this.logicValue(b);
            stack[stack.length] = (a !== 0 && b !== 0);
        } else {
            a = _this.calc(a);
            b = _this.calc(b);
            stack[stack.length] = (a !== 0 && b !== 0) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionOr = function (stack)
    {
        var _this = this;
        var a = stack.pop();
        var b = stack.pop();
        if (_this.version > 4) {
            a = _this.logicValue(a);
            b = _this.logicValue(b);
            stack[stack.length] = (a !== 0 || b !== 0);
        } else {
            a = _this.calc(a);
            b = _this.calc(b);
            stack[stack.length] = (a !== 0 || b !== 0) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionNot = function (stack)
    {
        var _this = this;
        var a = stack.pop();
        if (_this.version > 4) {
            a = _this.logicValue(a);
            stack[stack.length] = (a === 0);
        } else {
            a = _this.calc(a);
            stack[stack.length] = (a === 0) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStringEquals = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();

        if (a instanceof MovieClip) {
            a = a.getTarget();
        } else {
            a += "";
        }

        if (b instanceof MovieClip) {
            b = b.getTarget();
        } else {
            b += "";
        }

        if (this.version > 4) {
            stack[stack.length] = (b === a);
        } else {
            stack[stack.length] = (b === a) ? 1 : 0;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStringLength = function (stack)
    {
        var value = stack.pop();
        value = this.valueToString(value);
        var length = 0;
        var sLen = value.length;
        for (var i = 0; i < sLen; i++, length++) {
            var code = value.charCodeAt(i);
            if (code < 255) {
                continue;
            }
            length++;
        }
        stack[stack.length] = length;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStringAdd = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        if (a === null || a === undefined) {
            a = "";
        }
        if (b === null || b === undefined) {
            b = "";
        }
        stack[stack.length] = b + "" + a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStringExtract = function (stack)
    {
        var count = stack.pop();
        var index = stack.pop();
        var string = stack.pop();
        string = this.valueToString(string);
        index--;
        if (index < 0) {
            index = 0;
        }
        stack[stack.length] = (count < 0) ? string.substr(index) : string.substr(index, count);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStringLess = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        if (this.version > 4) {
            stack[stack.length] = (b < a);
        } else {
            stack[stack.length] = (b < a) ? 1 : 0;
        }
    };

    /**
     * @param stack
     * @param mc
     * @param values
     */
    ActionScript.prototype.ActionPush = function (stack, mc, values)
    {
        var _this = this;
        var length = values.length;
        var params = _this.params;
        for (var i = 0; i < length; i++) {
            var value = values[i];
            if (value instanceof Object) {
                var key = value.key;
                value = undefined;
                if (key in params) {
                    var name = params[key];
                    if (typeof name === "string") {
                        value = _this.getVariable(name);
                        if (value === undefined && !(name in _this.variables)) {
                            value = name;
                        }
                    } else {
                        value = name;
                    }
                }
            }
            stack[stack.length] = value;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionAsciiToChar = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = _fromCharCode(value);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionCharToAscii = function (stack)
    {
        var value = stack.pop();
        value = this.valueToString(value);
        stack[stack.length] = value.charCodeAt(0);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionToInteger = function (stack)
    {
        var value = stack.pop();
        stack[stack.length] = value|0;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionCall = function (stack, mc)
    {
        var value = stack.pop();
        if (mc) {
            value = this.valueToString(value);
            var splitData = value.split(":");
            var frame;
            var label = splitData[0];
            var targetMc = mc;
            if (splitData.length > 1) {
                targetMc = mc.getDisplayObject(splitData[0]);
                label = splitData[1];
            }
            if (targetMc instanceof MovieClip) {
                frame = (typeof label === "number") ? label : targetMc.getLabel(label);
                targetMc.executeActions(frame);
            }
        }
    };

    /**
     * @param stack
     * @param offset
     * @param index
     * @returns {*}
     */
    ActionScript.prototype.ActionIf = function (stack, offset, index)
    {
        var condition = stack.pop();
        switch (typeof condition) {
            case "boolean":
                break;
            case "string":
                if (!_isNaN(condition)) {
                    condition = +condition;
                }
                break;
        }
        if (condition) {
            return offset;
        }
        return index;
    };

    /**
     * @param stack
     * @param mc
     * @returns {undefined}
     */
    ActionScript.prototype.ActionGetVariable = function (stack, mc)
    {
        var _this = this;
        var name = stack.pop();
        var value;
        if (name instanceof MovieClip) {
            value = name;
        } else {
            value = _this.getNativeClass(name);
            if (value === undefined) {
                value = _this.getVariable(name);
                if (value === undefined && mc) {
                    value = mc.getProperty(name);
                }
            }
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionSetVariable = function (stack, mc)
    {
        var value = stack.pop();
        var name = stack.pop();
        if (!this.setVariable(name, value)) {
            mc.setProperty(name, value);
        }
    };

    /**
     * @param stack
     * @param aScript
     * @param mc
     */
    ActionScript.prototype.ActionGetURL2 = function (stack, aScript, mc)
    {
        var target = stack.pop();
        var value = stack.pop();
        var LoadVariablesFlag = aScript.LoadVariablesFlag; // 0=none, 1=LoadVariables
        var LoadTargetFlag = aScript.LoadTargetFlag; // 0=web, 1=Sprite
        var SendVarsMethod = aScript.SendVarsMethod; // 0=NONE, 1=GET, 2=POST
        var method = "GET";
        if (SendVarsMethod === 2) {
            method = "POST";
        }

        var url;
        if (mc instanceof MovieClip) {
            if (value) {
                value = this.valueToString(value);
                var urls = value.split("?");
                var uLen = urls.length;
                var query = "";
                if (uLen === 1) {
                    query = "?";
                }

                if (uLen > 2) {
                    url = urls[0] + "?";
                    url = url + urls[1];
                    for (var u = 2; u < uLen; u++) {
                        var params = urls[u];
                        url = url + "&" + params;
                    }
                } else {
                    url = value;
                }

                // local variables
                if (SendVarsMethod) {
                    var variables = mc.variables;
                    var queryString = "";
                    for (var key in variables) {
                        if (!variables.hasOwnProperty(key)) {
                            continue;
                        }
                        var val = variables[key];
                        if (val === null) {
                            val = "";
                        }
                        if (typeof val !== "string") {
                            var typeText = typeof val;
                            typeText = typeText.replace(/^[a-z]/g, function (str)
                            {
                                return str.toUpperCase();
                            });
                            val = "%5Btype+" + typeText + "%5D";
                        }
                        queryString += "&" + key + "=" + val;
                    }

                    if (query !== "" && queryString !== "") {
                        queryString = query + queryString.slice(1);
                    }
                    url += queryString;
                }

                if (LoadVariablesFlag) {
                    mc.loadVariables(url, target, method);
                } else if (LoadTargetFlag) {
                    if (target instanceof MovieClip) {
                        target.loadMovie(url, null, SendVarsMethod);
                    } else {
                        mc.loadMovie(url, target, SendVarsMethod);
                    }
                } else {
                    mc.getURL(url, target, method);
                }
            } else {
                mc.unloadMovie(target);
            }
        }
    };

    /**
     * @param stack
     * @param mc
     * @returns {*}
     */
    ActionScript.prototype.ActionGetProperty = function (stack, mc)
    {
        var index = stack.pop();
        var target = stack.pop();
        if (!_isNaN(index)) {
            index = _floor(index);
        }

        var _this = this;
        var value = _this.getVariable(index);
        if (value === undefined && mc) {
            var targetMc = mc;
            if (target) {
                if (typeof target !== "string") {
                    target += "";
                }
                targetMc = mc.getDisplayObject(target);
            }
            if (targetMc instanceof MovieClip) {
                value = targetMc.getProperty(index);
            }
        }
        stack[stack.length] = value;
    };

    /**
     * @param stack
     * @param aScript
     * @param mc
     */
    ActionScript.prototype.ActionGoToFrame2 = function (stack, aScript, mc)
    {
        var SceneBiasFlag = aScript.SceneBiasFlag;
        var PlayFlag = aScript.PlayFlag; // 0=stop, 1=play
        if (SceneBiasFlag === 1) {
            var SceneBias = aScript.SceneBias;
            console.log("SceneBias", SceneBias);
        }

        var frame = stack.pop();
        if (frame && mc) {
            if (_isNaN(frame)) {
                var splitData = frame.split(":");
                if (splitData.length > 1) {
                    var targetMc = mc.getDisplayObject(splitData[0]);
                    if (targetMc) {
                        frame = targetMc.getLabel(splitData[1]);
                    }
                } else {
                    frame = mc.getLabel(splitData[0]);
                }
            }

            if (typeof frame === "string") {
                frame |= 0;
            }

            if (typeof frame === "number" && frame > 0) {
                mc.setNextFrame(frame);
                if (PlayFlag) {
                    mc.play();
                } else {
                    mc.stop();
                }
            }
        }
    };

    /**
     * @param stack
     * @param movieClip
     * @param mc
     * @returns {*}
     */
    ActionScript.prototype.ActionSetTarget2 = function (stack, movieClip, mc)
    {
        var target = stack.pop();
        if (!movieClip) {
            movieClip = mc;
        }
        return movieClip.getDisplayObject(target);
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionSetProperty = function (stack, mc)
    {
        var value = stack.pop();
        var index = stack.pop();
        var target = stack.pop();
        if (!_isNaN(index)) {
            index = _floor(index);
        }

        if (mc) {
            var targetMc = mc;
            if (target !== undefined) {
                targetMc = mc.getDisplayObject(target);
            }
            if (targetMc instanceof MovieClip) {
                targetMc.setProperty(index, value);
            }
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionStartDrag = function (stack, mc)
    {
        var target = stack.pop();
        var lock = stack.pop();
        var constrain = stack.pop();
        var y2 = null;
        var x2 = null;
        var y1 = null;
        var x1 = null;
        if (constrain) {
            y2 = stack.pop();
            x2 = stack.pop();
            y1 = stack.pop();
            x1 = stack.pop();
        }

        var targetMc = mc;
        if (target instanceof MovieClip) {
            targetMc = target;
        }

        if (typeof target === "string" && target) {
            targetMc = mc.getDisplayObject(target);
        }

        if (targetMc instanceof MovieClip) {
            targetMc.startDrag(lock, x1, y1, x2, y2);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionCloneSprite = function (stack, mc)
    {
        var depth = +stack.pop();
        var target = stack.pop();
        var source = stack.pop();
        if (mc) {
            mc.duplicateMovieClip(target, source, depth);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionRemoveSprite = function (stack, mc)
    {
        var target = stack.pop();
        if (mc) {
            mc.removeMovieClip(target);
        }
    };

    /**
     * @param mc
     */
    ActionScript.prototype.ActionEndDrag = function (mc)
    {
        if (mc) {
            mc.stopDrag();
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionGetTime = function (stack)
    {
        var now = new Date();
        stack[stack.length] = now.getTime() - StartDate.getTime();
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionRandomNumber = function (stack)
    {
        var maximum = stack.pop();
        stack[stack.length] = _floor(_random() * maximum);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionTrace = function (stack)
    {
        var value = stack.pop();
        if (value instanceof DisplayObject && value.removeFlag) {
            value = "";
        }
        if (value && typeof value === "object") {
            if ("callee" in value) {
                value = Array.prototype.slice.call(value);
            }
            value = value.toString();
        }
        console.log("[trace] " + value);
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionFsCommand2 = function (stack, mc)
    {
        stack.pop(); // count
        var method = stack.pop();
        var now = new Date();
        switch (method.toLowerCase()) {
            case "getdateyear":
                stack[stack.length] = now.getFullYear();
                break;
            case "getdatemonth":
                stack[stack.length] = now.getMonth() + 1;
                break;
            case "getdateday":
                stack[stack.length] = now.getDate();
                break;
            case "getdateweekday":
                stack[stack.length] = now.getDay();
                break;
            case "gettimehours":
                stack[stack.length] = now.getHours();
                break;
            case "gettimeminutes":
                stack[stack.length] = now.getMinutes();
                break;
            case "gettimeseconds":
                stack[stack.length] = now.getSeconds();
                break;
            case "startvibrate":
                stack.pop();
                stack.pop();
                stack.pop();
                stack[stack.length] = -1;
                break;
            case "gettimezoneoffset":
                mc.setVariable(stack.pop(), now.toUTCString());
                mc.setVariable(stack.pop(), 0);
                break;
            case "getlocalelongdate":
                mc.setVariable(stack.pop(), now.toLocaleDateString());
                mc.setVariable(stack.pop(), 0);
                break;
            case "getlocaleshortdate":
                mc.setVariable(stack.pop(), now.toDateString());
                mc.setVariable(stack.pop(), 0);
                break;
            case "getlocaletime":
                mc.setVariable(stack.pop(), now.toLocaleTimeString());
                mc.setVariable(stack.pop(), 0);
                break;
            case "getnetworkname":
            case "getdevice":
            case "getdeviceid":
                mc.setVariable(stack.pop(), "");
                mc.setVariable(stack.pop(), -1);
                break;
            case "getlanguage":
                var language = _navigator.userLanguage ||
                    _navigator.language ||
                    _navigator.browserLanguage ||
                    "ja-JP";
                mc.setVariable(stack.pop(), language);
                mc.setVariable(stack.pop(), 0);
                break;
            case "setsoftkeys":
                stack.pop();
                stack.pop();
                stack[stack.length] = -1;
                break;
            case "fullscreen":
                stack.pop(); // bool
                stack[stack.length] = -1;
                break;
            case "setquality":
            case "getfreestagememory":
            case "gettotalstagememory":
                stack.pop();
                stack[stack.length] = -1;
                break;
            default:
                stack[stack.length] = -1;
                break;
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionCallMethod = function (stack, mc)
    {
        var _this = this;
        var method = stack.pop();
        var object = stack.pop();
        var count = +stack.pop();
        var params = [];
        if (count > 0) {
            while (count) {
                count--;
                var param = stack.pop();
                if (param && typeof param === "object" && "callee" in param) {
                    param = Array.prototype.slice.call(param);
                }
                params[params.length] = param;
            }
        }

        if (typeof object === "string" && object[method] === undefined) {
            var target = _this.stringToObject(object, mc);
            if (target) {
                object = target;
            }

            if (object === "super") {
                var caller = _this.variables["this"];
                var SuperClass = _this.getSuperClass();
                if (!method && SuperClass) {
                    var sc = new SuperClass();
                    switch (SuperClass) {
                        case MovieClip:
                            var loadStage = mc.getStage();
                            sc.setStage(loadStage);
                            sc.setParent(mc);
                            sc._extend = true;
                            break;
                    }

                    var proto = Object.getPrototypeOf(caller);
                    proto.constructor = SuperClass;
                    Object.setPrototypeOf(proto, sc);
                    Object.setPrototypeOf(caller, proto);
                } else {
                    object = caller;
                }
            }
        }

        var value;
        if (object && method) {
            var func;
            if (typeof object === "object") {
                var variables = object.variables;
                if (variables) {
                    func = variables[method];
                    if (!func && variables.registerClass) {
                        func = variables.registerClass[method];
                    }
                }
            }

            if (!func) {
                var originMethod = _this.checkMethod(method);
                if (originMethod) {
                    func = object[originMethod];
                }
            }

            if (!func) {
                func = object[method];
            }

            if (!func && object instanceof MovieClip) {
                func = object.getVariable(method);
            }

            if (!func && object instanceof Global) {
                func = window[method];
                if (func) {
                    params = _this.ActionNativeFunction(params, mc);
                    object = window;
                }
            }

            if (method === "call" || method === "apply") {
                func = object;
                object = params.shift();
                if (method === "apply") {
                    var args = params.shift();
                    params = [];
                    if (args) {
                        params = Array.prototype.slice.call(args);
                    }
                }
            }

            if (func && typeof func === "function") {
                switch (true) {
                    case object instanceof MovieClipLoader:
                        if (method === "loadClip" && typeof params[1] === "string") {
                            var targetStr = params[1];
                            params[1] = mc.getDisplayObject(targetStr);
                        }
                        break;
                }
                value = func.apply(object, params);
            }

            if (!func && object instanceof Object && typeof method === "string") {
                switch (method.toLowerCase()) {
                    case "registerclass":
                        value = false;
                        var _root = mc.getDisplayObject("_root");
                        var stage = _root.getStage();
                        var characterId = stage.exportAssets[params[0]];
                        if (characterId) {
                            stage.registerClass[characterId] = params[1];
                            value = true;
                        }
                        break;
                    case "addproperty":
                        _this.addProperty(object, params);
                        break;
                }
            }
        } else {
            if (!method && typeof object === "function") {
                value = object.apply(_this.variables["this"], params);
            }
        }

        stack[stack.length] = value;
    };

    /**
     * @param target
     * @param params
     * @returns {boolean}
     */
    ActionScript.prototype.addProperty = function (target, params)
    {
        var property = params[0];
        if (typeof property !== "string" || property === "") {
            return false;
        }

        var getter = params[1];
        if (!getter) {
            getter = function () {};
        }
        var setter = params[2];
        if (!setter) {
            setter = function () {};
        }

        if (typeof getter !== "function" || typeof setter !== "function") {
            return false;
        }

        Object.defineProperty(target, property,
        {
            get: getter,
            set: setter
        });

        return true;
    };

    /**
     * @param args
     * @param mc
     * @returns {Array}
     */
    ActionScript.prototype.ActionNativeFunction = function (args, mc)
    {
        var targetMc = mc;
        var params = args;
        if (args[0] instanceof MovieClip) {
            // setInterval, setTimeout
            targetMc = args.shift();
            if (args.length > 0) {
                var obj = args.shift();
                var as;
                if (typeof obj === "string") {
                    as = this.getVariable(obj);
                    if (typeof as !== "function") {
                        as = targetMc.getVariable(obj);
                    }
                }
                if (typeof as === "function") {
                    var time = args.shift();
                    var action = (function (script, mc, args)
                    {
                        return function ()
                        {
                            script.apply(mc, args);
                        };
                    })(as, targetMc, args);
                    params = [];
                    params[params.length] = action;
                    params[params.length] = time;
                } else {
                    console.log("DEBUG: ", params);
                    args.unshift(obj);
                    params = args;
                }
            }
        }
        return params;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionCallFunction = function (stack, mc)
    {
        var _this = this;
        var name = stack.pop();
        var count = +stack.pop();
        var params = [];
        if (count > 0) {
            while (count) {
                count--;
                var param = stack.pop();
                if (param && typeof param === "object" && "callee" in param) {
                    param = Array.prototype.slice.call(param);
                }
                params[params.length] = param;
            }
        }

        if (mc) {
            var caller = mc;
            var func;
            var method = _this.checkMethod(name);
            if (method) {
                func = mc[method];
            } else {
                func = mc.variables[name];
                if (!func) {
                    var registerClass = mc.variables.registerClass;
                    if (registerClass && typeof registerClass === "object") {
                        func = registerClass[name];
                    }

                    if (!func) {
                        if (window[name]) {
                            caller = window;
                            params = _this.ActionNativeFunction(params, mc);
                            func = window[name];
                        } else {
                            func = mc.getVariable(name);
                        }
                    }
                }
            }
            stack[stack.length] = (func) ? func.apply(caller, params) : undefined;
        }
    };

    /**
     * @param stack
     * @param aScript
     * @param mc
     */
    ActionScript.prototype.ActionDefineFunction = function (stack, aScript, mc)
    {
        var action = mc.createActionScript2(aScript.ActionScript, this);
        var name = aScript.FunctionName;
        if (name !== "") {
            mc.setVariable(name, action);
        } else {
            stack[stack.length] = action;
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionDefineLocal = function (stack, mc)
    {
        var _this = this;
        var value = stack.pop();
        var name = stack.pop();
        if (_this.parent) {
            _this.variables[name] = value;
        } else {
            mc.setVariable(name, value);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionDefineLocal2 = function (stack, mc)
    {
        var _this = this;
        var name = stack.pop();
        if (_this.parent) {
            _this.variables[name] = undefined;
        } else {
            mc.setVariable(name, undefined);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionDelete = function (stack, mc)
    {
        var name = stack.pop();
        var object = stack.pop();

        if (typeof object === "string") {
            var target = this.stringToObject(object, mc);
            if (target) {
                object = target;
            }
        }

        if (object instanceof MovieClip) {
            object.setVariable(name, undefined);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionDelete2 = function (stack, mc)
    {
        var name = stack.pop();
        if (mc) {
            mc.setVariable(name, undefined);
        }
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionEnumerate = function (stack, mc)
    {
        var object = stack.pop();
        stack[stack.length] = null;

        if (typeof object === "string") {
            object = this.stringToObject(object, mc);
        }

        if (object instanceof Object) {
            var name;
            switch (true) {
                case object instanceof DisplayObject:
                    var container = object.getTags();
                    var stage = object.getStage();
                    for (name in container) {
                        if (!container.hasOwnProperty(name)) {
                            continue;
                        }
                        var id = container[name];
                        var instance = stage.getInstance(id);
                        var prop = "instance" + id;
                        if (instance.getName()) {
                            prop = instance.getName();
                        }
                        stack[stack.length] = prop;
                    }
                    var variables = object.variables;
                    for (name in variables) {
                        if (!variables.hasOwnProperty(name)) {
                            continue;
                        }
                        stack[stack.length] = name;
                    }
                    break;
                default:
                    for (name in object) {
                        if (!object.hasOwnProperty(name)) {
                            continue;
                        }
                        stack[stack.length] = name;
                    }
                    break;
            }
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionEquals2 = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        var A = a;
        var B = b;
        if (a instanceof MovieClip) {
            A = a.getTarget();
        }
        if (b instanceof MovieClip) {
            B = b.getTarget();
        }
        stack[stack.length] = (B == A);
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionGetMember = function (stack, mc)
    {
        var _this = this;
        var property;
        var name = stack.pop();
        var object = stack.pop();
        if (typeof object === "string") {
            var target = _this.stringToObject(object, mc);
            if (target ) {
                object = target;
            }
        }

        if (object) {
            switch (true) {
                default:
                    property = object[name];
                    break;
                case object instanceof DisplayObject:
                case object instanceof Global:
                    if (!object._extend) {
                        property = object.getProperty(name, false);
                        if (property === undefined &&
                            typeof name === "string" &&
                            name.substr(0, 8) === "instance"
                        ) {
                            var stage = object.getStage();
                            var id = name.split("instance")[1];
                            property = stage.getInstance(id);
                        }

                        if (property === undefined && _this.checkMethod(name)) {
                            property = object[name];
                        }

                    } else {
                        property = object[name];
                    }
                    break;
                case object instanceof Element && name === "childNodes":
                    var childNodes = object[name];
                    var length = childNodes.length;
                    property = [];
                    if (length) {
                        for (var i = 0; i < length; i++) {
                            var node = childNodes[i];
                            if (node.nodeType !== 1) {
                                continue;
                            }
                            property[property.length] = node;
                        }
                    }
                    break;
                case object instanceof window.NamedNodeMap:
                    var item = object.getNamedItem(name);
                    property = item.value;
                    break;
            }
        }
        stack[stack.length] = property;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionInitArray = function (stack)
    {
        var number = stack.pop();
        var array = [];
        if (number > 0) {
            while (number--) {
                array[array.length] = stack.pop();
            }
        }
        stack[stack.length] = array;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionInitObject = function (stack)
    {
        var number = stack.pop();
        var object = {};
        if (number > 0) {
            while (number--) {
                var value = stack.pop();
                var property = stack.pop();
                object[property] = value;
            }
        }
        stack[stack.length] = object;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionNewMethod = function (stack, mc)
    {
        var method = stack.pop();
        var object = stack.pop();
        var number = stack.pop();
        var params = [];
        if (number > 0) {
            while (number--) {
                var param = stack.pop();
                if (param && typeof param === "object" && "callee" in param) {
                    param = Array.prototype.slice.call(param);
                }
                params[params.length] = param;
            }
        }

        var constructor;
        if (method === "") {
            constructor = object.apply(object, params);
        }
        if (!constructor && method in object) {
            constructor = this.CreateNewActionScript(object[method], mc, params);
        }
        if (!constructor && method in window) {
            if (method === "CSSStyleDeclaration") {
                constructor = undefined;
            } else {
                constructor = this.CreateNewActionScript(window[method], mc, params);
            }
        }
        stack[stack.length] = constructor;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionNewObject = function (stack, mc)
    {
        var object = stack.pop();
        var numArgs = +stack.pop();
        var params = [];
        if (numArgs > 0) {
            while (numArgs) {
                numArgs--;
                var param = stack.pop();
                if (param && typeof param === "object" && "callee" in param) {
                    param = Array.prototype.slice.call(param);
                }
                params[params.length] = param;
            }
        }

        var obj = {};
        if (object in window) {
            params.unshift(window[object]);
            obj = new (Function.prototype.bind.apply(window[object], params))();
        } else {
            switch (object) {
                case "Object":
                    obj = {};
                    break;
                case "MovieClip":
                    obj = new MovieClip();
                    var stage = mc.getStage();
                    obj.setStage(stage);
                    obj.setParent(mc);
                    break;
                case "Sound":
                    obj = new Sound(mc);
                    obj.movieClip = mc;
                    break;
                case "XML":
                    obj = new Xml();
                    break;
                case "LoadVars":
                    obj = new LoadVars();
                    break;
                case "Color":
                    obj = new Color(params[0]);
                    break;
                case "TextFormat":
                    obj = new TextFormat();
                    break;
                case "MovieClipLoader":
                    obj = new MovieClipLoader();
                    break;
                default:
                    if (mc) {
                        var _this = this;
                        var func = _this.getVariable(object) || mc.getVariable(object);
                        obj = _this.CreateNewActionScript(func, mc, params);
                    }
                    break;
            }
        }
        stack[stack.length] = obj;
    };

    /**
     * @param name
     * @returns {*}
     */
    ActionScript.prototype.getNativeClass = function (name)
    {
        var value;
        switch (name) {
            case "MovieClip":
                value = MovieClip;
                break;
            case "Sprite":
                value = Sprite;
                break;
            case "SimpleButton":
                value = SimpleButton;
                break;
            case "TextField":
                value = TextField;
                break;
            case "Shape":
                value = Shape;
                break;
            case "Sound":
                value = Sound;
                break;
            case "XML":
                value = Xml;
                break;
            case "LoadVars":
                value = LoadVars;
                break;
            case "Color":
                value = Color;
                break;
            case "TextFormat":
                value = TextFormat;
                break;
            case "MovieClipLoader":
                value = MovieClipLoader;
                break;
        }
        return value;
    };

    /**
     * @param Constr
     * @param mc
     * @param params
     * @returns {*}
     */
    ActionScript.prototype.CreateNewActionScript = function (Constr, mc, params)
    {
        if (Constr) {
            params.unshift(Constr);
            return new (Function.prototype.bind.apply(Constr, params))();
        }
        return undefined;
    };

    /**
     * @param stack
     * @param mc
     */
    ActionScript.prototype.ActionSetMember = function (stack, mc)
    {
        var value = stack.pop();
        var name = stack.pop();
        var object = stack.pop();
        if (object) {
            if (typeof object === "string") {
                var target = this.stringToObject(object, mc);
                if (target) {
                    object = target;
                }
            }

            if (typeof object === "object" || typeof object === "function") {
                switch (true) {
                    default:
                    case object === MovieClip.prototype:
                    case object === TextField.prototype:
                    case object === SimpleButton.prototype:
                    case object === Sprite.prototype:
                    case object === Shape.prototype:
                        object[name] = value;
                        break;
                    case object instanceof DisplayObject:
                    case object instanceof Global:
                        if (!object._extend) {
                            object.setProperty(name, value, false);
                        } else {
                            object[name] = value;
                        }
                        break;
                }
            }
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionTargetPath = function (stack)
    {
        console.log("ActionTargetPath");
        var object = stack.pop();
        var path = null;
        if (object instanceof MovieClip) {
            path = object.getName();
            if (path !== null) {
                while (true) {
                    var parent = object.getParent();
                    if (parent === null) {
                        path = "/" + path;
                        break;
                    }

                    var name = parent.getName();
                    if (name === null) {
                        path = null;
                        break;
                    }

                    path = name + "/" + path;
                }
            }
        }
        stack[stack.length] = path;
    };

    /**
     * @param stack
     * @param size
     * @param mc
     * @returns {*}
     */
    ActionScript.prototype.ActionWith = function (stack, size, mc)
    {
        var object = mc;
        if (size) {
            object = stack.pop();
        }
        return object;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionToNumber = function (stack)
    {
        var object = +stack.pop();
        stack[stack.length] = object;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionToString = function (stack)
    {
        var object = stack.pop();
        stack[stack.length] = this.valueToString(object);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionTypeOf = function (stack)
    {
        var object = stack.pop();
        var str = "";
        switch (true) {
            case object instanceof MovieClip:
                str = "movieclip";
                break;
            default:
                str = typeof object;
                break;
        }
        stack[stack.length] = str;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionAdd2 = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b + a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionLess2 = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = (b < a);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionModulo = function (stack)
    {
        var y = stack.pop();
        var x = stack.pop();
        stack[stack.length] = x % y;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitAnd = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b & a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitLShift = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b << a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitOr = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b | a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitRShift = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b >> a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitURShift = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = b >> a;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionBitXor = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = a ^ b;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionDecrement = function (stack)
    {
        var value = +stack.pop();
        value--;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionIncrement = function (stack)
    {
        var value = +stack.pop();
        value++;
        stack[stack.length] = value;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionPushDuplicate = function (stack)
    {
        var length = stack.length;
        stack[length] = stack[length - 1];
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStackSwap = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = a;
        stack[stack.length] = b;
    };

    /**
     * @param stack
     * @param number
     */
    ActionScript.prototype.ActionStoreRegister = function (stack, number)
    {
        this.params[number] = stack[stack.length - 1];
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionInstanceOf = function (stack)
    {
        var constr = stack.pop();
        var object = stack.pop();
        stack[stack.length] = (object instanceof constr);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionStrictEquals = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = (b === a);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionGreater = function (stack)
    {
        var a = stack.pop();
        var b = stack.pop();
        stack[stack.length] = (b > a);
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionExtends = function (stack)
    {
        var SuperClass = stack.pop();
        var SubClass = stack.pop();
        if (SuperClass && SubClass) {
            this.superClass = SuperClass;
        }
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionCastOp = function (stack)
    {
        var object = stack.pop();
        var func = stack.pop();
        stack[stack.length] = (typeof func === "function" &&
            object instanceof func.prototype.constructor) ? object : null;
    };

    /**
     * @param stack
     */
    ActionScript.prototype.ActionImplementsOp = function (stack)
    {
        console.log("ActionImplementsOp");
        var func = stack.pop();
        console.log(func);
        var count = stack.pop();
        var params = [];
        if (count > 0) {
            while (count--) {
                params[params.length] = stack.pop();
            }
        }
        stack[stack.length] = null;
    };

    /**
     * @param script
     * @param mc
     */
    ActionScript.prototype.ActionTry = function (script, mc)
    {

        try {
            script.try.apply(mc);
        } catch (e) {
            if (script.CatchBlockFlag) {
                script.catch.apply(mc,[e]);
            }
        } finally {
            if (script.FinallyBlockFlag) {
                script.finally.apply(mc);
            }
        }
    };

    /**
     * ActionThrow
     */
    ActionScript.prototype.ActionThrow = function (stack)
    {
        var value = stack.pop();
        throw value.message;
    };


    /**
     *
     * @constructor
     */
    var BitmapFilter = function () {};

    /**
     * @param color
     * @param data
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    BitmapFilter.prototype.generateColorTransform = function (color, data)
    {
        return {
            R: _max(0, _min((color.R * data[0]) + data[4], 255))|0,
            G: _max(0, _min((color.G * data[1]) + data[5], 255))|0,
            B: _max(0, _min((color.B * data[2]) + data[6], 255))|0,
            A: _max(0, _min((color.A * 255 * data[3]) + data[7], 255)) / 255
        };
    };

    /**
     * @param int
     * @param alpha
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    BitmapFilter.prototype.intToRGBA = function (int, alpha)
    {
        alpha = alpha || 100;
        return {
            R: (int & 0xff0000) >> 16,
            G: (int & 0x00ff00) >> 8,
            B: (int & 0x0000ff),
            A: (alpha / 100)
        };
    };

    /**
     * @param inner
     * @param knockout
     * @param hideObject
     * @returns {*}
     */
    BitmapFilter.prototype.filterOperation = function (inner, knockout, hideObject)
    {
        var operation = "source-over";
        if (knockout === true) {
            if (inner) {
                operation = "source-in";
            } else {
                operation = "source-out";
            }
        } else {
            if (hideObject === true) {
                if (inner) {
                    operation = "source-in";
                } else {
                    operation = "copy";
                }
            } else {
                if (inner) {
                    operation = "source-atop";
                } else {
                    operation = "destination-over";
                }
            }
        }
        return operation;
    };

    /**
     * @param ctx
     * @param color
     * @param inner
     * @param strength
     * @returns {*}
     */
    BitmapFilter.prototype.coatOfColor = function (ctx, color, inner, strength)
    {
        var canvas = ctx.canvas;
        var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);

        var i = 0;
        var pxData = imgData.data;
        var R = color.R;
        var G = color.G;
        var B = color.B;
        var length = pxData.length;
        for (; i < length; i += 4) {
            var aKey = i + 3;
            var alpha = pxData[aKey];
            if (!inner) {
                if (alpha !== 0) {
                    pxData[i] = R|0;
                    pxData[i + 1] = G|0;
                    pxData[i + 2] = B|0;
                    pxData[aKey] = alpha|0;
                }
            } else {
                if (alpha !== 255) {
                    pxData[i] = R|0;
                    pxData[i + 1] = G|0;
                    pxData[i + 2] = B|0;
                    pxData[aKey] = 255 - (alpha|0);
                }
            }
        }

        ctx.putImageData(imgData, 0, 0);
        if (strength > 0) {
            for (i = 1; i < strength; i++) {
                ctx.drawImage(ctx.canvas, 0, 0);
            }
        }
        return ctx;
    };

    /**
     * clone
     */
    BitmapFilter.prototype.clone = function ()
    {
        var _this = this;
        var args = [];
        for (var prop in _this) {
            if (!_this.hasOwnProperty(prop)) {
                continue;
            }
            args[args.length] = _this[prop];
        }

        var type = _this.filterId;
        var filter = _this;
        switch (type) {
            case 0: // DropShadowFilter
                filter = new (Function.prototype.bind.apply(DropShadowFilter, args))();
                break;
            case 1: // BlurFilter
                filter = new (Function.prototype.bind.apply(BlurFilter, args))();
                break;
            case 2: // GlowFilter
                filter = new (Function.prototype.bind.apply(GlowFilter, args))();
                break;
            case 3: // BevelFilter
                filter = new (Function.prototype.bind.apply(BevelFilter, args))();
                break;
            case 4: // GradientGlowFilter
                filter = new (Function.prototype.bind.apply(GradientGlowFilter, args))();
                break;
            case 5: // ConvolutionFilter
                filter = new (Function.prototype.bind.apply(ConvolutionFilter, args))();
                break;
            case 6: // ColorMatrixFilter
                filter = new (Function.prototype.bind.apply(ColorMatrixFilter, args))();
                break;
            case 7: // GradientBevelFilter
                filter = new (Function.prototype.bind.apply(GradientBevelFilter, args))();
                break;
        }
        return filter;
    };

    /**
     * @param rgb
     * @returns {Number}
     */
    BitmapFilter.prototype.toColorInt = function (rgb)
    {
        if (typeof rgb === "string") {
            var canvas = cacheStore.getCanvas();
            canvas.width = 1;
            canvas.height = 1;
            var ctx = canvas.getContext("2d");
            ctx.fillStyle = rgb;
            rgb = "0x" + ctx.fillStyle.substr(1);
            cacheStore.destroy(ctx);
        }
        return rgb;
    };

    /**
     * @constructor
     */
    var BlurFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 1;
        _this.blurX = 4;
        _this.blurY = 4;
        _this.quality = 1;

        var arg = arguments;
        var blurX = arg[0]|0;
        if (!_isNaN(blurX) && 0 <= blurX && 255 >= blurX) {
            _this.blurX = blurX;
        }

        var blurY = arg[1]|0;
        if (!_isNaN(blurY) && 0 <= blurY && 255 >= blurY) {
            _this.blurY = blurY;
        }

        var quality = arg[2]|0;
        if (!_isNaN(quality) && 1 <= quality && 15 >= quality) {
            _this.quality = quality;
        }
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    BlurFilter.prototype = Object.create(BitmapFilter.prototype);
    BlurFilter.prototype.constructor = BlurFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    BlurFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        var cacheCanvas = cache.canvas;
        var canvas = cacheStore.getCanvas();
        canvas.width = cacheCanvas.width;
        canvas.height = cacheCanvas.height;
        var ctx = canvas.getContext("2d");
        ctx.drawImage(cacheCanvas, 0, 0);
        ctx._offsetX = cache._offsetX;
        ctx._offsetY = cache._offsetY;
        return this.executeFilter(ctx, stage);
    };

    /**
     * @param ctx
     * @param stage
     * @returns {*}
     */
    BlurFilter.prototype.executeFilter = function (ctx, stage)
    {
        var _this = this;

        var _blurX = _this.blurX;
        var _blurY = _this.blurY;
        if (_blurX === 0 && _blurY === 0) {
            return ctx;
        }

        if (_blurX === 0) {
            _blurX = 4;
        }

        if (_blurY === 0) {
            _blurY = 4;
        }

        var _quality = _this.quality;
        var scale = stage.getScale();

        var STEP = [0.5, 1.05, 1.35, 1.55, 1.75, 1.9, 2, 2.1, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5];
        var stepNo = STEP[_quality - 1];
        var blurX = _ceil(_blurX * stepNo * scale * devicePixelRatio);
        var blurY = _ceil(_blurY * stepNo * scale * devicePixelRatio);

        var canvas = ctx.canvas;
        var width = _ceil(canvas.width + (blurX * 2) + 1);
        var height = _ceil(canvas.height + (blurY * 2) + 1);

        var blurCanvas = cacheStore.getCanvas();
        blurCanvas.width = width;
        blurCanvas.height = height;
        var blurCtx = blurCanvas.getContext("2d");
        var offsetX = blurX;
        var offsetY = blurY;

        blurCtx._offsetX = blurX + ctx._offsetX;
        blurCtx._offsetY = blurY + ctx._offsetY;
        blurCtx.drawImage(canvas, offsetX, offsetY);

        var imgData = blurCtx.getImageData(0, 0, width, height);
        var px = imgData.data;

        var radiusX = (offsetX) >> 1;
        var radiusY = (offsetY) >> 1;

        var MUL = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1];

        var SHG = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9];

        var mtx = MUL[radiusX];
        var stx = SHG[radiusX];
        var mty = MUL[radiusY];
        var sty = SHG[radiusY];

        var x = 0;
        var y = 0;
        var p = 0;
        var yp = 0;
        var yi = 0;
        var yw = 0;
        var r = 0;
        var g = 0;
        var b = 0;
        var a = 0;
        var pr = 0;
        var pg = 0;
        var pb = 0;
        var pa = 0;

        var divx = radiusX + radiusX + 1;
        var divy = radiusY + radiusY + 1;
        var w = imgData.width;
        var h = imgData.height;

        var w1 = w - 1;
        var h1 = h - 1;
        var rxp1 = radiusX + 1;
        var ryp1 = radiusY + 1;

        var ssx = {r: 0, b: 0, g: 0, a: 0};
        var sx = ssx;
        for (var i = 1; i < divx; i++) {
            sx = sx.n = {r: 0, b: 0, g: 0, a: 0};
        }
        sx.n = ssx;

        var ssy = {r: 0, b: 0, g: 0, a: 0};
        var sy = ssy;
        for (i = 1; i < divy; i++) {
            sy = sy.n = {r: 0, b: 0, g: 0, a: 0};
        }
        sy.n = ssy;

        var si = null;
        while (_quality-- > 0) {

            yw = yi = 0;
            var ms = mtx | 0;
            var ss = stx | 0;
            for (y = h; --y > -1;) {
                pr = px[yi];
                pg = px[yi + 1];
                pb = px[yi + 2];
                pa = px[yi + 3];
                r = rxp1 * pr;
                g = rxp1 * pg;
                b = rxp1 * pb;
                a = rxp1 * pa;

                sx = ssx;

                for (i = rxp1; --i > -1;) {
                    sx.r = pr;
                    sx.g = pg;
                    sx.b = pb;
                    sx.a = pa;
                    sx = sx.n;
                }

                for (i = 1; i < rxp1; i++) {
                    p = (yi + ((w1 < i ? w1 : i) << 2)) | 0;
                    r += (sx.r = px[p]);
                    g += (sx.g = px[p + 1]);
                    b += (sx.b = px[p + 2]);
                    a += (sx.a = px[p + 3]);
                    sx = sx.n;
                }

                si = ssx;
                for (x = 0; x < w; x++) {
                    px[yi++] = (r * ms) >>> ss;
                    px[yi++] = (g * ms) >>> ss;
                    px[yi++] = (b * ms) >>> ss;
                    px[yi++] = (a * ms) >>> ss;

                    p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2);

                    r -= si.r - (si.r = px[p]);
                    g -= si.g - (si.g = px[p + 1]);
                    b -= si.b - (si.b = px[p + 2]);
                    a -= si.a - (si.a = px[p + 3]);

                    si = si.n;

                }
                yw += w;
            }

            ms = mty;
            ss = sty;
            for (x = 0; x < w; x++) {
                yi = (x << 2) | 0;

                r = (ryp1 * (pr = px[yi])) | 0;
                g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0;
                b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0;
                a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0;

                sy = ssy;
                for (i = 0; i < ryp1; i++) {
                    sy.r = pr;
                    sy.g = pg;
                    sy.b = pb;
                    sy.a = pa;
                    sy = sy.n;
                }

                yp = w;

                for (i = 1; i <= radiusY; i++) {
                    yi = (yp + x) << 2;

                    r += (sy.r = px[yi]);
                    g += (sy.g = px[yi + 1]);
                    b += (sy.b = px[yi + 2]);
                    a += (sy.a = px[yi + 3]);

                    sy = sy.n;
                    if (i < h1) {
                        yp += w;
                    }
                }

                yi = x;
                si = ssy;
                if (_quality > 0) {
                    for (y = 0; y < h; y++) {
                        p = yi << 2;
                        px[p + 3] = pa = (a * ms) >>> ss;
                        if (pa > 0) {
                            px[p] = ((r * ms) >>> ss );
                            px[p + 1] = ((g * ms) >>> ss);
                            px[p + 2] = ((b * ms) >>> ss);
                        } else {
                            px[p] = px[p + 1] = px[p + 2] = 0;
                        }

                        p = (x + (((p = y + ryp1) < h1 ? p : h1) * w)) << 2;

                        r -= si.r - (si.r = px[p]);
                        g -= si.g - (si.g = px[p + 1]);
                        b -= si.b - (si.b = px[p + 2]);
                        a -= si.a - (si.a = px[p + 3]);

                        si = si.n;

                        yi += w;
                    }
                } else {
                    for (y = 0; y < h; y++) {
                        p = yi << 2;
                        px[p + 3] = pa = (a * ms) >>> ss;
                        if (pa > 0) {
                            pa = 255 / pa;
                            px[p] = ((r * ms) >>> ss) * pa;
                            px[p + 1] = ((g * ms) >>> ss) * pa;
                            px[p + 2] = ((b * ms) >>> ss) * pa;
                        } else {
                            px[p] = px[p + 1] = px[p + 2] = 0;
                        }

                        p = (x + (((p = y + ryp1) < h1 ? p : h1) * w)) << 2;

                        r -= si.r - (si.r = px[p]);
                        g -= si.g - (si.g = px[p + 1]);
                        b -= si.b - (si.b = px[p + 2]);
                        a -= si.a - (si.a = px[p + 3]);

                        si = si.n;

                        yi += w;
                    }
                }
            }
        }

        blurCtx.putImageData(imgData, 0, 0);
        cacheStore.destroy(ctx);

        return blurCtx;
    };

    /**
     * @constructor
     */
    var DropShadowFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 0;
        _this.distance = 4;
        _this.angle = 45;
        _this.color = 0;
        _this.alpha = 1;
        _this.blurX = 4;
        _this.blurY = 4;
        _this.strength = 1;
        _this.quality = 1;
        _this.inner = false;
        _this.knockout = false;
        _this.hideObject = false;

        var arg = arguments;
        var distance = arg[0]|0;
        if (!_isNaN(distance)) {
            _this.distance = distance;
        }

        var angle = +arg[1];
        if (!_isNaN(angle) && 0 <= angle && 360 >= angle) {
            _this.angle = angle;
        }

        var color = _this.toColorInt(arg[2]);
        if (!_isNaN(color)) {
            _this.color = color;
        }

        var alpha = +arg[3];
        if (!_isNaN(alpha) && 0 <= alpha && 1 >= alpha) {
            _this.alpha = alpha;
        }

        var blurX = arg[4]|0;
        if (!_isNaN(blurX) && 0 <= blurX && 255 >= blurX) {
            _this.blurX = blurX;
        }

        var blurY = arg[5]|0;
        if (!_isNaN(blurY) && 0 <= blurY && 255 >= blurY) {
            _this.blurY = blurY;
        }

        var strength = +arg[6];
        if (!_isNaN(strength) && 0 <= strength && 255 >= strength) {
            _this.strength = strength;
        }

        var quality = arg[7]|0;
        if (!_isNaN(quality) && 1 <= quality && 15 >= quality) {
            _this.quality = quality;
        }

        var inner = arg[8];
        if (typeof inner === "boolean") {
            _this.inner = inner;
        }

        var knockout = arg[9];
        if (typeof knockout === "boolean") {
            _this.knockout = knockout;
        }

        var hideObject = arg[10];
        if (typeof hideObject === "boolean") {
            _this.hideObject = hideObject;
        }
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    DropShadowFilter.prototype = Object.create(BitmapFilter.prototype);
    DropShadowFilter.prototype.constructor = DropShadowFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     */
    DropShadowFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        var _this = this;
        var strength = _this.strength;
        if (strength === 0) {
            return cache;
        }

        var quality = _this.quality;
        var inner = _this.inner;
        var r = _this.angle * _PI / 180;
        var blurX = _this.blurX;
        var blurY = _this.blurY;

        // blur
        var blurFilter = new BlurFilter(blurX, blurY, quality);
        var ctx = blurFilter.render(cache, matrix, colorTransform, stage);

        // dropShadow
        var intColor = _this.toColorInt(_this.color);
        var filterColor = _this.intToRGBA(intColor);
        var color = _this.generateColorTransform(filterColor, colorTransform);
        ctx = _this.coatOfColor(ctx, color, inner, strength);

        // synthesis
        var cacheOffsetX = cache._offsetX;
        var cacheOffsetY = cache._offsetY;
        var _offsetX = ctx._offsetX;
        var _offsetY = ctx._offsetY;

        var canvas = ctx.canvas;
        var synCanvas = cacheStore.getCanvas();
        var width = canvas.width + cacheOffsetX;
        var height = canvas.height + cacheOffsetY;
        var ox = 0;
        var oy = 0;
        var dx = 0;
        var dy = 0;

        var distance = _this.distance;
        var scale = stage.getScale();
        var x = _ceil(_cos(r) * distance * scale);
        var y = _ceil(_sin(r) * distance * scale);

        if (x !== 0) {
            width += _abs(x);
            if (x < 0) {
                ox -= x;
            } else {
                dx = x;
            }
        }

        if (y !== 0) {
            height += _abs(y);
            if (y < 0) {
                oy -= y;
            } else {
                dy = y;
            }
        }

        synCanvas.width = width;
        synCanvas.height = height;
        var synCtx = synCanvas.getContext("2d");
        synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
        synCtx.globalAlpha = _this.alpha;
        if (strength < 1) {
            synCtx.globalAlpha *= strength;
        }

        var knockout = _this.knockout;
        var hideObject = _this.hideObject;
        synCtx.globalCompositeOperation = _this.filterOperation(inner, knockout, hideObject);
        synCtx.drawImage(canvas, cacheOffsetX + dx, cacheOffsetY + dy);

        synCtx._offsetX = cacheOffsetX + _offsetX;
        synCtx._offsetY = cacheOffsetY + _offsetY;

        cacheStore.destroy(ctx);

        return synCtx;
    };

    /**
     * @constructor
     */
    var GlowFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 2;
        _this.color = 0xFF0000;
        _this.alpha = 1;
        _this.blurX = 6;
        _this.blurY = 6;
        _this.strength = 2;
        _this.quality = 1;
        _this.inner = false;
        _this.knockout = false;

        var arg = arguments;
        var color = _this.toColorInt(arg[0]);
        if (!_isNaN(color)) {
            _this.color = color;
        }

        var alpha = +arg[1];
        if (!_isNaN(alpha) && 0 <= alpha && 1 >= alpha) {
            _this.alpha = alpha;
        }

        var blurX = arg[2]|0;
        if (!_isNaN(blurX) && 0 <= blurX && 255 >= blurX) {
            _this.blurX = blurX;
        }

        var blurY = arg[3]|0;
        if (!_isNaN(blurY) && 0 <= blurY && 255 >= blurY) {
            _this.blurY = blurY;
        }

        var strength = +arg[4];
        if (!_isNaN(strength) && 0 <= strength && 255 >= strength) {
            _this.strength = strength;
        }

        var quality = arg[5]|0;
        if (!_isNaN(quality) && 1 <= quality && 15 >= quality) {
            _this.quality = quality;
        }

        var inner = arg[6];
        if (typeof inner === "boolean") {
            _this.inner = inner;
        }

        var knockout = arg[7];
        if (typeof knockout === "boolean") {
            _this.knockout = knockout;
        }
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    GlowFilter.prototype = Object.create(BitmapFilter.prototype);
    GlowFilter.prototype.constructor = GlowFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    GlowFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        var _this = this;
        var strength = _this.strength;
        if (strength === 0) {
            return cache;
        }

        var inner = _this.inner;
        var blurX = _this.blurX;
        var blurY = _this.blurY;

        var blurFilter = new BlurFilter(blurX, blurY, _this.quality);
        var ctx = blurFilter.render(cache, matrix, colorTransform, stage);

        var width = ctx.canvas.width + cache._offsetX;
        var height = ctx.canvas.height + cache._offsetY;

        var intColor = _this.toColorInt(_this.color);
        var filterColor = _this.intToRGBA(intColor);
        var color = _this.generateColorTransform(filterColor, colorTransform);
        ctx = _this.coatOfColor(ctx, color, inner, strength);

        var synCanvas = cacheStore.getCanvas();
        synCanvas.width = width;
        synCanvas.height = height;
        var synCtx = synCanvas.getContext("2d");

        synCtx.drawImage(cache.canvas, ctx._offsetX, ctx._offsetY);
        synCtx.globalAlpha = _this.alpha;
        if (strength < 1) {
            synCtx.globalAlpha *= strength;
        }

        var operation = "source-over";
        if (_this.knockout) {
            if (inner) {
                operation = "source-in";
            } else {
                operation = "source-out";
            }
        } else {
            if (inner) {
                operation = "source-atop";
            } else {
                operation = "destination-over";
            }
        }

        synCtx.globalCompositeOperation = operation;
        synCtx.drawImage(ctx.canvas, cache._offsetX, cache._offsetY);
        synCtx._offsetX = cache._offsetX + ctx._offsetX;
        synCtx._offsetY = cache._offsetY + ctx._offsetY;

        cacheStore.destroy(ctx);

        return synCtx;
    };

    /**
     * @constructor
     */
    var BevelFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 3;
        _this.distance = 4;
        _this.angle = 45;
        _this.highlightColor = 0xffffff;
        _this.highlightAlpha = 1;
        _this.shadowColor = 0x000000;
        _this.shadowAlpha = 1;
        _this.blurX = 4;
        _this.blurY = 4;
        _this.strength = 1;
        _this.quality = 1;
        _this.type = "inner";
        _this.knockout = false;

        var arg = arguments;

        var distance = arg[0]|0;
        if (!_isNaN(distance)) {
            _this.distance = distance;
        }

        var angle = +arg[1];
        if (!_isNaN(angle) && 0 <= angle && 360 >= angle) {
            _this.angle = angle;
        }

        var highlightColor = _this.toColorInt(arg[2]);
        if (!_isNaN(highlightColor)) {
            _this.highlightColor = highlightColor;
        }

        var highlightAlpha = +arg[3];
        if (!_isNaN(highlightAlpha) && 0 <= highlightAlpha && 1 >= highlightAlpha) {
            _this.highlightAlpha = highlightAlpha;
        }

        var shadowColor = _this.toColorInt(arg[4]);
        if (!_isNaN(shadowColor)) {
            _this.shadowColor = shadowColor;
        }

        var shadowAlpha = +arg[5];
        if (!_isNaN(shadowAlpha) && 0 <= shadowAlpha && 1 >= shadowAlpha) {
            _this.shadowAlpha = shadowAlpha;
        }

        var blurX = arg[6]|0;
        if (!_isNaN(blurX) && 0 <= blurX && 255 >= blurX) {
            _this.blurX = blurX;
        }

        var blurY = arg[7]|0;
        if (!_isNaN(blurY) && 0 <= blurY && 255 >= blurY) {
            _this.blurY = blurY;
        }

        var strength = +arg[8];
        if (!_isNaN(strength) && 0 <= strength && 255 >= strength) {
            _this.strength = strength;
        }

        var quality = arg[9]|0;
        if (!_isNaN(quality) && 1 <= quality && 15 >= quality) {
            _this.quality = quality;
        }

        var type = arg[10];
        if (typeof type === "string") {
            _this.type = type;
        }

        var knockout = arg[11];
        if (typeof knockout === "boolean") {
            _this.knockout = knockout;
        }
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    BevelFilter.prototype = Object.create(BitmapFilter.prototype);
    BevelFilter.prototype.constructor = BevelFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    BevelFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        var _this = this;
        var distance = _this.distance;
        var angle = _this.angle;
        var shadowColor = _this.shadowColor;
        var shadowAlpha = _this.shadowAlpha;
        var highlightColor = _this.highlightColor;
        var highlightAlpha = _this.highlightAlpha;
        var blurX = _this.blurX;
        var blurY = _this.blurY;
        var strength = _this.strength;
        var quality = _this.quality;
        var knockout = _this.knockout;
        var r = angle * _PI / 180;
        var filterColor, color;
        var type = _this.type;

        // blur
        var blurFilter = new BlurFilter(blurX, blurY, quality);
        var ctx = blurFilter.render(cache, matrix, colorTransform, stage);
        var canvas = ctx.canvas;
        var _offsetX = ctx._offsetX;
        var _offsetY = ctx._offsetY;

        // shadow
        var shadowCanvas = cacheStore.getCanvas();
        shadowCanvas.width = canvas.width;
        shadowCanvas.height = canvas.height;
        var shadowCtx = shadowCanvas.getContext("2d");
        shadowCtx.drawImage(canvas, 0, 0);
        var intShadowColor = _this.toColorInt(shadowColor);
        filterColor = _this.intToRGBA(intShadowColor);
        color = _this.generateColorTransform(filterColor, colorTransform);
        shadowCtx = _this.coatOfColor(shadowCtx, color, false, strength);

        // shadow
        var highlightCanvas = cacheStore.getCanvas();
        highlightCanvas.width = canvas.width;
        highlightCanvas.height = canvas.height;
        var highlightCtx = highlightCanvas.getContext("2d");
        highlightCtx.drawImage(canvas, 0, 0);
        var intHighlightColor = _this.toColorInt(highlightColor);
        filterColor = _this.intToRGBA(intHighlightColor);
        color = _this.generateColorTransform(filterColor, colorTransform);
        highlightCtx = _this.coatOfColor(highlightCtx, color, false, strength);

        var isInner = (type === "inner" || type === "full");
        var isOuter = (type === "outer" || type === "full");

        var cacheOffsetX = cache._offsetX;
        var cacheOffsetY = cache._offsetY;
        var synCanvas = cacheStore.getCanvas();
        var width = canvas.width + cacheOffsetX;
        var height = canvas.height + cacheOffsetY;
        var ox = 0;
        var oy = 0;

        var scale = stage.getScale();
        var x = _ceil(_cos(r) * distance * scale);
        var y = _ceil(_sin(r) * distance * scale);

        if (x !== 0) {
            width += _abs(x);
            if (x < 0) {
                ox -= x;
            }
        }

        if (y !== 0) {
            height += _abs(y);
            if (y < 0) {
                oy -= y;
            }
        }

        synCanvas.width = width;
        synCanvas.height = height;
        var synCtx = synCanvas.getContext("2d");
        if (!knockout) {
            synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
        }
        if (strength < 1) {
            synCtx.globalAlpha *= strength;
        }
        synCtx._offsetX = cacheOffsetX + _offsetX;
        synCtx._offsetY = cacheOffsetY + _offsetY;

        var xorCanvas = cacheStore.getCanvas();
        xorCanvas.width = width + _offsetX;
        xorCanvas.height = height + _offsetY;
        var xorCtx = xorCanvas.getContext("2d");

        xorCtx.globalCompositeOperation = "xor";
        xorCtx.globalAlpha = highlightAlpha;
        xorCtx.drawImage(highlightCtx.canvas, -x + ox, -y + oy);
        xorCtx.globalAlpha = shadowAlpha;
        xorCtx.drawImage(shadowCtx.canvas, x, y);

        var operation;
        if (isInner && isOuter) {
            operation = "source-over";
        } else if (isInner) {
            synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
            operation = _this.filterOperation(true, knockout);
        } else if (isOuter) {
            operation = "destination-over";
        }

        synCtx.globalCompositeOperation = operation;
        synCtx.drawImage(xorCtx.canvas, 0, 0);
        if (!isInner && isOuter && knockout) {
            synCtx.globalCompositeOperation = "destination-out";
            synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
        }

        cacheStore.destroy(ctx);
        cacheStore.destroy(highlightCtx);
        cacheStore.destroy(shadowCtx);
        cacheStore.destroy(xorCtx);

        return synCtx;
    };

    /**
     * @constructor
     */
    var GradientGlowFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 4;
        _this.distance = 4;
        _this.angle = 45;
        _this.colors = null;
        _this.alphas = null;
        _this.ratios = null;
        _this.blurX = 4;
        _this.blurY = 4;
        _this.strength = 1;
        _this.quality = 1;
        _this.type = "inner";
        _this.knockout = false;

        var arg = arguments;

        var distance = arg[0]|0;
        if (!_isNaN(distance)) {
            _this.distance = distance;
        }

        var angle = +arg[1];
        if (!_isNaN(angle) && 0 <= angle && 360 >= angle) {
            _this.angle = angle;
        }

        _this.colors = arg[2];
        _this.alphas = arg[3];
        _this.ratios = arg[4];

        var blurX = arg[5]|0;
        if (!_isNaN(blurX) && 0 <= blurX && 255 >= blurX) {
            _this.blurX = blurX;
        }

        var blurY = arg[6]|0;
        if (!_isNaN(blurY) && 0 <= blurY && 255 >= blurY) {
            _this.blurY = blurY;
        }

        var strength = +arg[7];
        if (!_isNaN(strength) && 0 <= strength && 255 >= strength) {
            _this.strength = strength;
        }

        var quality = arg[8]|0;
        if (!_isNaN(quality) && 1 <= quality && 15 >= quality) {
            _this.quality = quality;
        }

        var type = arg[9];
        if (typeof type === "string") {
            _this.type = type;
        }

        var knockout = arg[10];
        if (typeof knockout === "boolean") {
            _this.knockout = knockout;
        }
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    GradientGlowFilter.prototype = Object.create(BitmapFilter.prototype);
    GradientGlowFilter.prototype.constructor = GradientGlowFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    GradientGlowFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        var _this = this;
        var strength = _this.strength;
        if (strength === 0) {
            return cache;
        }

        var type = _this.type;
        var blurX = _this.blurX;
        var blurY = _this.blurY;
        var isInner = (type === "inner" || type === "full");
        var isOuter = (type === "outer" || type === "full");
        var knockout = _this.knockout;
        var angle = _this.angle;
        var r = angle * _PI / 180;

        var blurFilter = new BlurFilter(blurX, blurY, _this.quality);
        var ctx = blurFilter.render(cache, matrix, colorTransform, stage);

        // synthesis
        var cacheOffsetX = cache._offsetX;
        var cacheOffsetY = cache._offsetY;
        var _offsetX = ctx._offsetX;
        var _offsetY = ctx._offsetY;

        var canvas = ctx.canvas;
        var synCanvas = cacheStore.getCanvas();
        var width = canvas.width + cacheOffsetX;
        var height = canvas.height + cacheOffsetY;
        var ox = 0;
        var oy = 0;
        var dx = 0;
        var dy = 0;

        var distance = _this.distance;
        var scale = stage.getScale();
        var x = _ceil(_cos(r) * distance * scale);
        var y = _ceil(_sin(r) * distance * scale);

        if (x !== 0) {
            width += _abs(x);
            if (x < 0) {
                ox -= x;
            } else {
                dx = x;
            }
        }

        if (y !== 0) {
            height += _abs(y);
            if (y < 0) {
                oy -= y;
            } else {
                dy = y;
            }
        }

        synCanvas.width = width;
        synCanvas.height = height;
        var synCtx = synCanvas.getContext("2d");
        if (!knockout) {
            synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
        }

        if (strength < 1) {
            synCtx.globalAlpha *= strength;
        }

        var operation;
        if (isInner && isOuter) {
            operation = "source-over";
        } else {
            if (knockout) {
                synCtx.drawImage(cache.canvas, _offsetX + ox, _offsetY + oy);
            }
            operation = _this.filterOperation(isInner, knockout);
        }

        synCtx.globalCompositeOperation = operation;
        synCtx.drawImage(canvas, cacheOffsetX + dx, cacheOffsetY + dy);

        synCtx._offsetX = cacheOffsetX + _offsetX;
        synCtx._offsetY = cacheOffsetY + _offsetY;

        cacheStore.destroy(ctx);

        return synCtx;
    };

    /**
     * @constructor
     */
    var ConvolutionFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 5;
    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    ConvolutionFilter.prototype = Object.create(BitmapFilter.prototype);
    ConvolutionFilter.prototype.constructor = ConvolutionFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    ConvolutionFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        return cache;
    };

    /**
     * @constructor
     */
    var ColorMatrixFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 6;


    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    ColorMatrixFilter.prototype = Object.create(BitmapFilter.prototype);
    ColorMatrixFilter.prototype.constructor = ColorMatrixFilter;

    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    ColorMatrixFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        return cache;
    };

    /**
     * @constructor
     */
    var GradientBevelFilter = function ()
    {
        var _this = this;
        BitmapFilter.call(_this);

        _this.filterId = 7;


    };

    /**
     * extends
     * @type {BitmapFilter}
     */
    GradientBevelFilter.prototype = Object.create(BitmapFilter.prototype);
    GradientBevelFilter.prototype.constructor = GradientBevelFilter;


    /**
     * @param cache
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    GradientBevelFilter.prototype.render = function (cache, matrix, colorTransform, stage)
    {
        return cache;
    };

    /**
     * @constructor
     */
    var Graphics = function ()
    {
        this.clear();
    };

    /**
     * @type {number}
     */
    Graphics.prototype.MOVE_TO = 0;

    /**
     * @type {number}
     */
    Graphics.prototype.CURVE_TO = 1;

    /**
     * @type {number}
     */
    Graphics.prototype.LINE_TO = 2;

    /**
     * @type {number}
     */
    Graphics.prototype.CUBIC = 3;

    /**
     * @type {number}
     */
    Graphics.prototype.ARC = 4;

    /**
     * @type {number}
     */
    Graphics.prototype.FILL_STYLE = 5;

    /**
     * @type {number}
     */
    Graphics.prototype.STROKE_STYLE = 6;

    /**
     * @type {number}
     */
    Graphics.prototype.FILL = 7;

    /**
     * @type {number}
     */
    Graphics.prototype.STROKE = 8;

    /**
     * @type {number}
     */
    Graphics.prototype.LINE_WIDTH = 9;

    /**
     * @type {number}
     */
    Graphics.prototype.LINE_CAP = 10;

    /**
     * @type {number}
     */
    Graphics.prototype.LINE_JOIN = 11;

    /**
     * @type {number}
     */
    Graphics.prototype.MITER_LIMIT = 12;

    /**
     * @type {number}
     */
    Graphics.prototype.BEGIN_PATH = 13;

    /**
     * @param a
     * @param b
     * @returns []
     */
    Graphics.prototype.multiplicationMatrix = function(a, b)
    {
        return [
            a[0] * b[0] + a[2] * b[1],
            a[1] * b[0] + a[3] * b[1],
            a[0] * b[2] + a[2] * b[3],
            a[1] * b[2] + a[3] * b[3],
            a[0] * b[4] + a[2] * b[5] + a[4],
            a[1] * b[4] + a[3] * b[5] + a[5]
        ];
    };

    /**
     * @param color
     * @param data
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    Graphics.prototype.generateColorTransform = function (color, data)
    {
        return {
            R: _max(0, _min((color.R * data[0]) + data[4], 255))|0,
            G: _max(0, _min((color.G * data[1]) + data[5], 255))|0,
            B: _max(0, _min((color.B * data[2]) + data[6], 255))|0,
            A: _max(0, _min((color.A * 255 * data[3]) + data[7], 255)) / 255
        };
    };

    /**
     * @param int
     * @param alpha
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    Graphics.prototype.intToRGBA = function (int, alpha)
    {
        alpha = alpha || 100;
        return {
            R: (int & 0xff0000) >> 16,
            G: (int & 0x00ff00) >> 8,
            B: (int & 0x0000ff),
            A: (alpha / 100)
        };
    };

    /**
     * @returns {Graphics}
     */
    Graphics.prototype.clear = function ()
    {
        var _this = this;
        var no = _Number.MAX_VALUE;
        _this.bounds = {xMin: no, xMax: -no, yMin: no, yMax: -no};
        _this.maxWidth = 0;
        _this.cmd = null;
        _this.isDraw = false;
        _this.isFillDraw = false;
        _this.isLineDraw = false;
        _this.cacheKey = "";
        _this.recodes = [];
        _this.lineRecodes = [];
        return _this;
    };

    /**
     * @returns {string}
     */
    Graphics.prototype.getCacheKey = function ()
    {
        return this.cacheKey;
    };

    /**
     * @returns {string}
     */
    Graphics.prototype.addCacheKey = function ()
    {
        var args = arguments;
        var cacheKey = "";
        var length = args.length;
        if (length) {
            for (var i = 0; i < length; i++) {
                var value = args[i];
                cacheKey += "_" + value;
            }
        }
        this.cacheKey += cacheKey;
    };

    /**
     * @returns {*}
     */
    Graphics.prototype.getBounds = function ()
    {
        return this.bounds;
    };

    /**
     * @param x
     * @param y
     */
    Graphics.prototype.setBounds = function (x, y)
    {
        var bounds = this.bounds;
        bounds.xMin = _min(bounds.xMin, x);
        bounds.xMax = _max(bounds.xMax, x);
        bounds.yMin = _min(bounds.yMin, y);
        bounds.yMax = _max(bounds.yMax, y);
    };

    /**
     * @param str
     * @returns {string}
     */
    Graphics.prototype.colorStringToInt = function(str)
    {
        var canvas = cacheStore.getCanvas();
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = str;
        var color = "0x" + ctx.fillStyle.substr(1);
        cacheStore.destroy(ctx);
        return color;
    };

    /**
     * @param rgb
     * @param alpha
     * @returns {Graphics}
     */
    Graphics.prototype.beginFill = function (rgb, alpha)
    {
        var _this = this;
        if (typeof rgb === "string") {
            rgb = _this.colorStringToInt(rgb);
        }

        rgb |= 0;
        alpha = +alpha;
        if (_isNaN(alpha)) {
            alpha = 100;
        } else {
            alpha *= 100;
        }

        var color = _this.intToRGBA(rgb, alpha);
        var recodes = _this.recodes;
        if (!_this.isFillDraw) {
            recodes[recodes.length] = [_this.BEGIN_PATH];
        }
        recodes[recodes.length] = [_this.FILL_STYLE, color.R, color.G, color.B, color.A];

        _this.addCacheKey(rgb, alpha);
        _this.isFillDraw = true;
        _this.isDraw = true;
        return _this;
    };

    /**
     * @param width
     * @param rgb
     * @param alpha
     * @param pixelHinting
     * @param noScale
     * @param capsStyle
     * @param jointStyle
     * @param miterLimit
     * @returns {Graphics}
     */
    Graphics.prototype.lineStyle = function (width, rgb, alpha, pixelHinting, noScale, capsStyle, jointStyle, miterLimit)
    {
        var _this = this;
        var lineRecodes = _this.lineRecodes;

        width = +width;
        if (!_isNaN(width)) {
            if (rgb === undefined) {
                rgb = 0;
            }

            if (typeof rgb === "string") {
                rgb = _this.colorStringToInt(rgb);
            }

            if (!capsStyle) {
                capsStyle = "round";
            }
            if (!jointStyle) {
                jointStyle = "round";
            }

            rgb |= 0;
            alpha = +alpha;
            if (_isNaN(alpha)) {
                alpha = 100;
            } else {
                alpha *= 100;
            }

            var color = _this.intToRGBA(rgb, alpha);
            if (width < 0.5) {
                width += 0.2;
            }

            width *= 20;
            _this.maxWidth = _max(_this.maxWidth, width);

            if (_this.isLineDraw) {
                lineRecodes[lineRecodes.length] = [_this.STROKE];
            }
            lineRecodes[lineRecodes.length] = [_this.BEGIN_PATH];
            lineRecodes[lineRecodes.length] = [_this.STROKE_STYLE, color.R, color.G, color.B, color.A];
            lineRecodes[lineRecodes.length] = [_this.LINE_WIDTH, width];
            lineRecodes[lineRecodes.length] = [_this.LINE_CAP, capsStyle];
            lineRecodes[lineRecodes.length] = [_this.LINE_JOIN, jointStyle];
            _this.addCacheKey(rgb, alpha);
            _this.isLineDraw = true;
            _this.isDraw = true;
        } else if (_this.isLineDraw) {
            _this.isLineDraw = false;
            lineRecodes[lineRecodes.length] = [_this.STROKE];
            var length = lineRecodes.length;
            var recodes = _this.recodes;
            for (var i = 0; i < length; i++) {
                recodes[recodes.length] = lineRecodes[i];
            }
            _this.lineRecodes = [];
        }
        return _this;
    };

    /**
     * @param x
     * @param y
     * @returns {Graphics}
     */
    Graphics.prototype.moveTo = function (x, y)
    {
        var _this = this;
        var recodes = _this.recodes;
        x *= 20;
        y *= 20;

        if (_this.isFillDraw) {
            recodes[recodes.length] = [_this.MOVE_TO, x, y];
        }

        if (_this.isLineDraw) {
            var lineRecodes = _this.lineRecodes;
            lineRecodes[lineRecodes.length] = [_this.MOVE_TO, x, y];
        }

        if (_this.isFillDraw || _this.isLineDraw) {
            _this.setBounds(x, y);
            _this.addCacheKey(x, y);
        }
        return _this;
    };

    /**
     * @param x
     * @param y
     * @returns {Graphics}
     */
    Graphics.prototype.lineTo = function (x, y)
    {
        var _this = this;
        var recodes = _this.recodes;
        x *= 20;
        y *= 20;

        if (_this.isFillDraw) {
            recodes[recodes.length] = [_this.LINE_TO, x, y];
        }

        if (_this.isLineDraw) {
            var lineRecodes = _this.lineRecodes;
            lineRecodes[lineRecodes.length] = [_this.LINE_TO, x, y];
        }

        if (_this.isFillDraw || _this.isLineDraw) {
            _this.setBounds(x, y);
            _this.addCacheKey(x, y);
        }
        return _this;
    };

    /**
     * @param cx
     * @param cy
     * @param dx
     * @param dy
     * @returns {Graphics}
     */
    Graphics.prototype.curveTo = function (cx, cy, dx, dy)
    {
        var _this = this;
        var recodes = _this.recodes;
        cx *= 20;
        cy *= 20;
        dx *= 20;
        dy *= 20;

        if (_this.isFillDraw) {
            recodes[recodes.length] = [_this.CURVE_TO, cx, cy, dx, dy];
        }

        if (_this.isLineDraw) {
            var lineRecodes = _this.lineRecodes;
            lineRecodes[lineRecodes.length] = [_this.CURVE_TO, cx, cy, dx, dy];
        }

        if (_this.isFillDraw || _this.isLineDraw) {
            _this.setBounds(cx, cy);
            _this.setBounds(dx, dy);
            _this.addCacheKey(cx, cy, dx, dy);
        }
        return _this;
    };

    /**
     * @param cp1x
     * @param cp1y
     * @param cp2x
     * @param cp2y
     * @param x
     * @param y
     * @returns {Graphics}
     */
    Graphics.prototype.cubicCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y)
    {
        var _this = this;
        var recodes = _this.recodes;
        cp1x *= 20;
        cp1y *= 20;
        cp2x *= 20;
        cp2y *= 20;
        x *= 20;
        y *= 20;

        if (_this.isFillDraw) {
            recodes[recodes.length] = [_this.CUBIC, cp1x, cp1y, cp2x, cp2y, x, y];
        }

        if (_this.isLineDraw) {
            var lineRecodes = _this.lineRecodes;
            lineRecodes[lineRecodes.length] = [_this.CUBIC, cp1x, cp1y, cp2x, cp2y, x, y];
        }

        if (_this.isFillDraw || _this.isLineDraw) {
            _this.setBounds(x, y);
            _this.setBounds(cp1x, cp1y);
            _this.setBounds(cp2x, cp2y);
            _this.addCacheKey(cp1x, cp1y, cp2x, cp2y, x, y);
        }
        return _this;
    };

    /**
     * @param x
     * @param y
     * @param radius
     * @returns {Graphics}
     */
    Graphics.prototype.drawCircle = function (x, y, radius)
    {
        var _this = this;
        var recodes = _this.recodes;
        x *= 20;
        y *= 20;
        radius *= 20;

        if (_this.isFillDraw) {
            recodes[recodes.length] = [_this.ARC, x, y, radius];
        }

        if (_this.isLineDraw) {
            var lineRecodes = _this.lineRecodes;
            lineRecodes[lineRecodes.length] = [_this.ARC, x, y, radius];
        }

        if (_this.isFillDraw || _this.isLineDraw) {
            _this.setBounds(x - radius, y - radius);
            _this.setBounds(x + radius, y + radius);
            _this.addCacheKey(x, y, radius);
        }
        return _this;
    };

    /**
     * @param x
     * @param y
     * @param width
     * @param height
     * @returns {Graphics}
     */
    Graphics.prototype.drawEllipse = function (x, y, width, height)
    {
        var _this = this;
        var hw = width / 2;
        var hh = height / 2;
        var x0 = x + hw;
        var x1 = x + width;
        var y0 = y + hh;
        var y1 = y + height;
        var cw = 4 / 3 * (_SQRT2 - 1) * hw;
        var ch = 4 / 3 * (_SQRT2 - 1) * hh;
        _this.moveTo(x0, y);
        _this.cubicCurveTo(x0 + cw, y, x1, y0 - ch, x1, y0);
        _this.cubicCurveTo(x1, y0 + ch, x0 + cw, y1, x0, y1);
        _this.cubicCurveTo(x0 - cw, y1, x, y0 + ch, x, y0);
        _this.cubicCurveTo(x, y0 - ch, x0 - cw, y, x0, y);
        return _this;
    };

    /**
     * @param x
     * @param y
     * @param width
     * @param height
     * @returns {Graphics}
     */
    Graphics.prototype.drawRect = function (x, y, width, height)
    {
        var _this = this;
        _this.moveTo(x, y);
        _this.lineTo(x + width, y);
        _this.lineTo(x + width, y + height);
        _this.lineTo(x, y + height);
        _this.lineTo(x, y);
        return _this;
    };

    /**
     * @param x
     * @param y
     * @param width
     * @param height
     * @param ellipseWidth
     * @param ellipseHeight
     * @returns {Graphics}
     */
    Graphics.prototype.drawRoundRect = function (x, y, width, height, ellipseWidth, ellipseHeight)
    {
        var _this = this;
        var hew = ellipseWidth / 2;
        var heh = ellipseHeight / 2;
        var cw = 4 / 3 * (_SQRT2 - 1) * hew;
        var ch = 4 / 3 * (_SQRT2 - 1) * heh;

        var dx0 = x + hew;
        var dx1 = x + width;
        var dx2 = dx1 - hew;

        var dy0 = y + heh;
        var dy1 = y + height;
        var dy2 = dy1 - heh;

        _this.moveTo(dx0, y);
        _this.lineTo(dx2, y);
        _this.cubicCurveTo(dx2 + cw, y, dx1, dy0 - ch, dx1, dy0);
        _this.lineTo(dx1, dy2);
        _this.cubicCurveTo(dx1, dy2 + ch, dx2 + cw, dy1, dx2, dy1);
        _this.lineTo(dx0, dy1);
        _this.cubicCurveTo(dx0 - cw, dy1, x, dy2 + ch, x, dy2);
        _this.lineTo(x, dy0);
        _this.cubicCurveTo(x, dy0 - ch, dx0 - cw, y, dx0, y);

        return _this;
    };

    /**
     * @param vertices
     * @param indices
     * @param uvtData
     * @param culling
     * @returns {Graphics}
     */
    Graphics.prototype.drawTriangles = function (vertices, indices, uvtData, culling)
    {
        var _this = this;
        var length = vertices.length;
        if (length && length % 3 === 0) {
            var i = 0;
            var count = 0;
            if (indices) {
                length = indices.length;
                if (length && length % 3 === 0) {
                    for (i = 0; i < length; i++) {
                        var idx = indices[i];
                        if (count === 0) {
                            _this.moveTo(vertices[idx], vertices[idx + 1]);
                        } else {
                            _this.lineTo(vertices[idx], vertices[idx + 1]);
                        }
                        count++;
                        if (count % 3 === 0) {
                            count = 0;
                        }
                    }
                }
            } else {
                for (i = 0; i < length; i++) {
                    if (count === 0) {
                        _this.moveTo(vertices[i++], vertices[i]);
                    } else {
                        _this.lineTo(vertices[i++], vertices[i]);
                    }
                    count++;
                    if (count % 3 === 0) {
                        count = 0;
                    }
                }
            }
        }
        return _this;
    };

    /**
     * @returns {Graphics}
     */
    Graphics.prototype.endFill = function ()
    {
        var _this = this;
        if (_this.isFillDraw) {
            var recodes = _this.recodes;
            recodes[recodes.length] = [_this.FILL];
        }
        _this.isFillDraw = false;
        return _this;
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {*}
     */
    Graphics.prototype.render = function (ctx, matrix, colorTransform, stage)
    {
        var _this = this;
        var cacheKey = "";
        var alpha = colorTransform[3] + (colorTransform[7] / 255);
        if (!alpha) {
            return cacheKey;
        }

        var rMatrix = _this.multiplicationMatrix(stage.getMatrix(), matrix);
        var xScale = _sqrt(rMatrix[0] * rMatrix[0] + rMatrix[1] * rMatrix[1]);
        var yScale = _sqrt(rMatrix[2] * rMatrix[2] + rMatrix[3] * rMatrix[3]);
        xScale = _pow(_SQRT2, _ceil(_log(xScale) / _LN2_2 - _LOG1P));
        yScale = _pow(_SQRT2, _ceil(_log(yScale) / _LN2_2 - _LOG1P));

        var maxWidth = _this.maxWidth;
        var halfWidth = maxWidth / 2;
        var bounds = _this.getBounds();
        var xMax = bounds.xMax;
        var xMin = bounds.xMin;
        var yMax = bounds.yMax;
        var yMin = bounds.yMin;

        var W = _abs(_ceil((xMax - xMin + maxWidth) * xScale));
        var H = _abs(_ceil((yMax - yMin + maxWidth) * yScale));
        if (W <= 0 || H <= 0) {
            return cacheKey;
        }

        var cache;
        var canvas;
        var isClipDepth = stage.clipMc || _this.isClipDepth;
        if (!isClipDepth) {
            cacheKey = cacheStore.generateKey("Graphics", 0, [xScale, yScale], colorTransform);
            cacheKey += _this.getCacheKey();
            cache = cacheStore.getCache(cacheKey);
            if (!cache && stage.getWidth() > W && stage.getHeight() > H && cacheStore.size > (W * H)) {
                canvas = cacheStore.getCanvas();
                canvas.width = W;
                canvas.height = H;
                cache = canvas.getContext("2d");
                var cMatrix = [xScale, 0, 0, yScale, (-xMin + halfWidth) * xScale, (-yMin + halfWidth) * yScale];
                cache.setTransform(cMatrix[0], cMatrix[1], cMatrix[2], cMatrix[3], cMatrix[4], cMatrix[5]);
                cache = _this.executeRender(cache, _min(xScale, yScale), colorTransform, false);
                cacheStore.setCache(cacheKey, cache);
            }
        }

        if (cache) {
            canvas = cache.canvas;
            var sMatrix = [1 / xScale, 0, 0, 1 / yScale, xMin - halfWidth, yMin - halfWidth];
            var m2 = _this.multiplicationMatrix(rMatrix, sMatrix);
            ctx.setTransform(m2[0],m2[1],m2[2],m2[3],m2[4],m2[5]);
            if (isAndroid4x && !isChrome) {
                ctx.fillStyle = stage.context.createPattern(cache.canvas, "no-repeat");
                ctx.fillRect(0, 0, W, H);
            } else {
                ctx.drawImage(canvas, 0, 0, W, H);
            }
        } else {
            ctx.setTransform(rMatrix[0],rMatrix[1],rMatrix[2],rMatrix[3],rMatrix[4],rMatrix[5]);
            _this.executeRender(ctx, _min(rMatrix[0], rMatrix[3]), colorTransform, isClipDepth);
        }

        return cacheKey + "_" + rMatrix[4] + "_" + rMatrix[5];
    };

    /**
     * @param ctx
     * @param minScale
     * @param colorTransform
     * @param isClip
     */
    Graphics.prototype.executeRender = function (ctx, minScale, colorTransform, isClip)
    {
        var _this = this;
        var recodes = _this.recodes;
        var length = recodes.length;
        var lineRecodes = _this.lineRecodes;

        if (length || lineRecodes) {
            var cmd = _this.cmd;
            if (!cmd) {
                var lLen = lineRecodes.length;
                if (lLen) {
                    for (var i = 0; i < lLen; i++) {
                        recodes[recodes.length] = lineRecodes[i];
                    }
                    _this.lineRecodes = [];
                }
                cmd = vtc.buildCommand(recodes);
                _this.cmd = cmd;
            }

            ctx.beginPath();
            cmd(ctx, colorTransform, isClip);
            if (isClip) {
                ctx.clip();
            } else {
                if (_this.isFillDraw) {
                    ctx.fill();
                }
                if (_this.isLineDraw) {
                    ctx.stroke();
                }
            }
        }

        var resetCss = "rgba(0,0,0,1)";
        ctx.strokeStyle = resetCss;
        ctx.fillStyle = resetCss;
        ctx.globalAlpha = 1;

        return ctx;
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    Graphics.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;

        var cmd = _this.cmd;
        if (!cmd) {
            var recodes = _this.recodes;
            cmd = vtc.buildCommand(recodes);
            _this.cmd = cmd;
        }

        var rMatrix = _this.multiplicationMatrix(stage.getMatrix(), matrix);
        ctx.setTransform(rMatrix[0],rMatrix[1],rMatrix[2],rMatrix[3],rMatrix[4],rMatrix[5]);

        ctx.beginPath();
        cmd(ctx, [1,1,1,1,0,0,0,0], true);

        var hit = ctx.isPointInPath(x, y);
        if (hit) {
            return hit;
        }

        if ("isPointInStroke" in ctx) {
            hit = ctx.isPointInStroke(x, y);
            if (hit) {
                return hit;
            }
        }

        return hit;
    };

    /**
     * @constructor
     */
    var SoundTransform = function ()
    {
        var _this = this;
        _this._leftToLeft = 0;
        _this._leftToRight = 1;
        _this._pan = 0;
        _this._rightToLeft = 0;
        _this._rightToRight = 1;
        _this._volume = 1;
    };

    /**
     * properties
     */
    Object.defineProperties(SoundTransform.prototype,
    {
        leftToLeft: {
            get: function () {
                return this.getLeftToLeft();
            },
            set: function (leftToLeft) {
                this.setLeftToLeft(leftToLeft);
            }
        },
        leftToRight: {
            get: function () {
                return this.getLeftToRight();
            },
            set: function (leftToRight) {
                this.setLeftToRight(leftToRight);
            }
        },
        pan: {
            get: function () {
                return this.getPan();
            },
            set: function (pan) {
                this.setPan(pan);
            }
        },
        rightToLeft: {
            get: function () {
                return this.getRightToLeft();
            },
            set: function (rightToLeft) {
                this.setRightToLeft(rightToLeft);
            }
        },
        rightToRight: {
            get: function () {
                return this.getRightToRight();
            },
            set: function (rightToRight) {
                this.setRightToRight(rightToRight);
            }
        },
        volume: {
            get: function () {
                return this.getVolume();
            },
            set: function (volume) {
                this.setVolume(volume);
            }
        }
    });

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getLeftToLeft = function ()
    {
        return this._leftToLeft;
    };

    /**
     * @param leftToLeft
     */
    SoundTransform.prototype.setLeftToLeft = function (leftToLeft)
    {
        this._leftToLeft = leftToLeft | 0;
    };

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getLeftToRight = function ()
    {
        return this._leftToRight;
    };

    /**
     * @param leftToRight
     */
    SoundTransform.prototype.setLeftToRight = function (leftToRight)
    {
        this._leftToRight = leftToRight | 0;
    };

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getPan = function ()
    {
        return this._pan;
    };

    /**
     * @param pan
     */
    SoundTransform.prototype.setPan = function (pan)
    {
        this._pan = pan | 0;
    };

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getRightToLeft = function ()
    {
        return this._rightToLeft;
    };

    /**
     * @param rightToLeft
     */
    SoundTransform.prototype.setRightToLeft = function (rightToLeft)
    {
        this._rightToLeft = rightToLeft | 0;
    };

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getRightToRight = function ()
    {
        return this._rightToRight;
    };

    /**
     * @param rightToRight
     */
    SoundTransform.prototype.setRightToRight = function (rightToRight)
    {
        this._rightToRight = rightToRight | 0;
    };

    /**
     * @returns {number}
     */
    SoundTransform.prototype.getVolume = function ()
    {
        return this._volume;
    };

    /**
     * @param volume
     */
    SoundTransform.prototype.setVolume = function (volume)
    {
        this._volume = volume | 0;
    };

    /**
     * @param vol
     * @param panning
     */
    SoundTransform.prototype.SoundTransform = function (vol, panning)
    {
        var _this = this;
        _this.volume = vol | 0;
        _this.pan = panning | 0;
    };

    /**
     * @constructor
     */
    var Swf2jsEvent = function ()
    {
        var _this = this;
        _this.target = {};
        _this.bubbles = true;
        _this.cancelable = true;
        _this.currentTarget = {};
        _this.eventPhase = 0;
    };

    /**
     * @type {string}
     */
    Swf2jsEvent.prototype.ACTIVATE = "activate";
    Swf2jsEvent.prototype.CLICK = "press";
    Swf2jsEvent.prototype.CONTEXT_MENU = "contextMenu";
    Swf2jsEvent.prototype.DOUBLE_CLICK = "doubleClick";
    Swf2jsEvent.prototype.MIDDLE_CLICK = "middleClick";
    Swf2jsEvent.prototype.MIDDLE_MOUSE_DOWN = "middleMouseDown";
    Swf2jsEvent.prototype.MIDDLE_MOUSE_UP = "middleMouseUp";
    Swf2jsEvent.prototype.MOUSE_DOWN = "mouseDown";
    Swf2jsEvent.prototype.MOUSE_MOVE = "mouseMove";
    Swf2jsEvent.prototype.MOUSE_OUT = "rollOut"; // mouseOut
    Swf2jsEvent.prototype.MOUSE_OVER = "rollOver"; // mouseOver
    Swf2jsEvent.prototype.MOUSE_UP = "mouseUp";
    Swf2jsEvent.prototype.MOUSE_WHEEL = "mouseWheel";
    Swf2jsEvent.prototype.RIGHT_CLICK = "rightClick";
    Swf2jsEvent.prototype.RIGHT_MOUSE_DOWN = "rightMouseDown";
    Swf2jsEvent.prototype.RIGHT_MOUSE_UP = "rightMouseUp";
    Swf2jsEvent.prototype.ROLL_OUT = "rollOut";
    Swf2jsEvent.prototype.ROLL_OVER = "rollOver";

    /**
     * @param type
     * @param target
     * @constructor
     */
    var ClipEvent = function (type)
    {
        var _this = this;
        _this.type = type;
        _this.target = null;
        Swf2jsEvent.call(this);
    };

    /**
     * extends
     * @type {EventDispatcher}
     */
    ClipEvent.prototype = Object.create(Swf2jsEvent.prototype);
    ClipEvent.prototype.constructor = ClipEvent;

    var clipEvent = new ClipEvent();

    /**
     * @constructor
     */
    var EventDispatcher = function ()
    {
        var _this = this;
        _this.events = {};
        _this.isLoad = false;
        _this.active = false;
    };

    /**
     * properties
     */
    Object.defineProperties(EventDispatcher.prototype,
    {
        onEnterFrame: {
            get: function () {
                return this.getOnEvent("onEnterFrame");
            },
            set: function (onEnterFrame) {
                this.setOnEvent("onEnterFrame", onEnterFrame);
            }
        },
        onPress: {
            get: function () {
                return this.getOnEvent("onPress");
            },
            set: function (onPress) {
                this.setOnEvent("onPress", onPress);
            }
        },
        onRelease: {
            get: function () {
                return this.getOnEvent("onRelease");
            },
            set: function (onRelease) {
                this.setOnEvent("onRelease", onRelease);
            }
        },
        onReleaseOutside: {
            get: function () {
                return this.getOnEvent("onReleaseOutside");
            },
            set: function (onReleaseOutside) {
                this.setOnEvent("onReleaseOutside", onReleaseOutside);
            }
        },
        onRollOver: {
            get: function () {
                return this.getOnEvent("onRollOver");
            },
            set: function (onRollOver) {
                this.setOnEvent("onRollOver", onRollOver);
            }
        },
        onRollOut: {
            get: function () {
                return this.getOnEvent("onRollOut");
            },
            set: function (onRollOut) {
                this.setOnEvent("onRollOut", onRollOut);
            }
        },
        onData: {
            get: function () {
                return this.getOnEvent("onData");
            },
            set: function (onData) {
                this.setOnEvent("onData", onData);
            }
        },
        onMouseDown: {
            get: function () {
                return this.getOnEvent("onMouseDown");
            },
            set: function (onMouseDown) {
                this.setOnEvent("onMouseDown", onMouseDown);
            }
        },
        onMouseUp: {
            get: function () {
                return this.getOnEvent("onMouseUp");
            },
            set: function (onMouseUp) {
                this.setOnEvent("onMouseUp", onMouseUp);
            }
        },
        onMouseMove: {
            get: function () {
                return this.getOnEvent("onMouseMove");
            },
            set: function (onMouseMove) {
                this.setOnEvent("onMouseMove", onMouseMove);
            }
        },
        onDragOut: {
            get: function () {
                return this.getOnEvent("onDragOut");
            },
            set: function (onDragOut) {
                this.setOnEvent("onDragOut", onDragOut);
            }
        },
        onDragOver: {
            get: function () {
                return this.getOnEvent("onDragOver");
            },
            set: function (onDragOver) {
                this.setOnEvent("onDragOver", onDragOver);
            }
        },
        onKeyDown: {
            get: function () {
                return this.getOnEvent("onKeyDown");
            },
            set: function (onKeyDown) {
                this.setOnEvent("onKeyDown", onKeyDown);
            }
        },
        onKeyUp: {
            get: function () {
                return this.getOnEvent("onKeyUp");
            },
            set: function (onKeyUp) {
                this.setOnEvent("onKeyUp", onKeyUp);
            }
        },
        onLoad: {
            get: function () {
                return this.getOnEvent("onLoad");
            },
            set: function (onLoad) {
                this.setOnEvent("onLoad", onLoad);
            }
        },
        onUnLoad: {
            get: function () {
                return this.getOnEvent("onUnLoad");
            },
            set: function (onUnLoad) {
                this.setOnEvent("onUnLoad", onUnLoad);
            }
        }
    });

    /**
     * @param type
     * @returns {*}
     */
    EventDispatcher.prototype.getOnEvent = function (type)
    {
        return this.variables[type];
    };

    /**
     * @param type
     * @param as
     */
    EventDispatcher.prototype.setOnEvent = function (type, as)
    {
        this.variables[type] = as;
    };

    /**
     * @param type
     * @param listener
     * @param useCapture
     * @param priority
     * @param useWeakReference
     */
    EventDispatcher.prototype.addEventListener = function (type, listener, useCapture, priority, useWeakReference)
    {
        var events = this.events;
        if (!(type in events)) {
            events[type] = [];
        }
        var event = events[type];
        event[event.length] = listener;
    };

    /**
     * @param event
     * @param stage
     */
    EventDispatcher.prototype.dispatchEvent = function (event, stage)
    {
        var _this = this;
        var type = event.type;
        if (_this.hasEventListener(type)) {
            var events = _this.events[type];
            var cEvent = new ClipEvent();
            cEvent.type = type;
            cEvent.target = this;
            var args = [cEvent];
            _this.setActionQueue(events, stage, args);
        }
    };

    /**
     * @param type
     * @returns {boolean}
     */
    EventDispatcher.prototype.hasEventListener = function (type)
    {
        return (type in this.events);
    };

    /**
     * @param type
     * @param listener
     * @param useCapture
     */
    EventDispatcher.prototype.removeEventListener = function (type, listener, useCapture)
    {
        var _this = this;
        if (_this.hasEventListener(type)) {
            var events = _this.events;
            var listeners = events[type];
            var length = listeners.length;
            for (var i = 0; i < length; i++) {
                if (listeners[i] !== listener) {
                    continue;
                }
                listeners.slice(i, 0);
                break;
            }
        }
    };

    /**
     * @param type
     */
    EventDispatcher.prototype.willTrigger = function (type)
    {
        return this.hasEventListener(type);
    };

    /**
     * @param as
     * @param stage
     * @param args
     */
    EventDispatcher.prototype.setActionQueue = function (as, stage, args)
    {
        var actions = stage.actions;
        actions[actions.length] = {as: as, mc: this, args: args};
    };

    /**
     * @constructor
     */
    var AccessibilityProperties = function () {};

    /**
     * @constructor
     */
    var DisplayObject = function ()
    {
        var _this = this;
        EventDispatcher.call(_this);
        _this.initialize();
    };

    /**
     * extends
     * @type {EventDispatcher}
     */
    DisplayObject.prototype = Object.create(EventDispatcher.prototype);
    DisplayObject.prototype.constructor = DisplayObject;

    /**
     * properties
     */
    Object.defineProperties(DisplayObject.prototype,
    {
        accessibilityProperties: {
            value: new AccessibilityProperties()
        },
        alpha: {
            get: function () {
                return this.getAlpha() / 100;
            },
            set: function (alpha) {
                this.setAlpha(alpha * 100);
            }
        },
        _alpha: {
            get: function () {
                return this.getAlpha();
            },
            set: function (alpha) {
                this.setAlpha(alpha);
            }
        },
        name: {
            get: function () {
                return this.getName();
            },
            set: function (name) {
                this.setName(name);
            }
        },
        _name: {
            get: function () {
                return this.getName();
            },
            set: function (name) {
                this.setName(name);
            }
        },
        blendMode: {
            get: function () {
                return this.getBlendMode();
            },
            set: function (blendMode) {
                this.setBlendMode(blendMode);
            }
        },
        filters: {
            get: function () {
                return this.getFilters();
            },
            set: function (filters) {
                this.setFilters(filters);
            }
        },
        _visible: {
            get: function () {
                return this.getVisible();
            },
            set: function (visible) {
                this.setVisible(visible);
            }
        },
        visible: {
            get: function () {
                return this.getVisible();
            },
            set: function (visible) {
                this.setVisible(visible);
            }
        },
        _rotation: {
            get: function () {
                return this.getRotation();
            },
            set: function (rotation) {
                this.setRotation(rotation);
            }
        },
        rotation: {
            get: function () {
                return this.getRotation();
            },
            set: function (rotation) {
                this.setRotation(rotation);
            }
        },
        _height: {
            get: function () {
                return this.getHeight();
            },
            set: function (height) {
                this.setHeight(height);
            }
        },
        height: {
            get: function () {
                return this.getHeight();
            },
            set: function (height) {
                this.setHeight(height);
            }
        },
        _width: {
            get: function () {
                return this.getWidth();
            },
            set: function (width) {
                this.setWidth(width);
            }
        },
        width: {
            get: function () {
                return this.getWidth();
            },
            set: function (width) {
                this.setWidth(width);
            }
        },
        _x: {
            get: function () {
                return this.getX();
            },
            set: function (x) {
                this.setX(x);
            }
        },
        x: {
            get: function () {
                return this.getX();
            },
            set: function (x) {
                this.setX(x);
            }
        },
        _y: {
            get: function () {
                return this.getY();
            },
            set: function (y) {
                this.setY(y);
            }
        },
        y: {
            get: function () {
                return this.getY();
            },
            set: function (y) {
                this.setY(y);
            }
        },
        _xscale: {
            get: function () {
                return this.getXScale();
            },
            set: function (xscale) {
                this.setXScale(xscale);
            }
        },
        scaleX: {
            get: function () {
                return this.getXScale();
            },
            set: function (xscale) {
                this.setXScale(xscale);
            }
        },
        _yscale: {
            get: function () {
                return this.getYScale();
            },
            set: function (yscale) {
                this.setYScale(yscale);
            }
        },
        scaleY: {
            get: function () {
                return this.getYScale();
            },
            set: function (yscale) {
                this.setYScale(yscale);
            }
        },
        _xmouse: {
            get: function () {
                return this.getXMouse();
            },
            set: function () {
            }
        },
        mouseX: {
            get: function () {
                return this.getXMouse();
            },
            set: function () {
            }
        },
        _ymouse: {
            get: function () {
                return this.getYMouse();
            },
            set: function () {
            }
        },
        mouseY: {
            get: function () {
                return this.getYMouse();
            },
            set: function () {
            }
        },
        mask: {
            get: function () {
                return this.getMask();
            },
            set: function (obj) {
                this.setMask(obj);
            }
        },
        enabled: {
            get: function () {
                return this.getEnabled();
            },
            set: function (enabled) {
                this.setEnabled(enabled);
            }
        },
        _parent: {
            get: function () {
                return this.getParent();
            },
            set: function (parent) {
                this.setParent(parent);
            }
        },
        parent: {
            get: function () {
                return this.getParent();
            },
            set: function (parent) {
                this.setParent(parent);
            }
        }
    });

    /**
     * initialize
     */
    DisplayObject.prototype.initialize = function ()
    {
        var _this = this;

        // common
        _this.instanceId = instanceId++;
        _this.characterId = 0;
        _this.tagType = 0;
        _this.ratio = 0;
        _this.isMask = false;
        _this.clipDepth = 0;
        _this.isClipDepth = false;
        _this.stageId = null;
        _this.loadStageId = null;
        _this.variables = {};
        _this.buttonStatus = "up";
        _this.removeFlag = false;
        _this.parentId = null;

        // properties
        _this.__visible = true;
        _this.__name = null;
        _this._url = null;
        _this._highquality = 1;
        _this._focusrect = 1;
        _this._soundbuftime = null;
        _this._totalframes = 1;
        _this._level = 0;
        _this._depth = null;
        _this._framesloaded = 0;
        _this._target = "";
        _this._lockroot = undefined;
        _this._enabled = true;
        _this._blendMode = null;
        _this._filters = null;
        _this._filterCacheKey = null;
        _this._parentPlace = null;
        _this._mask = null;
        _this._matrix = null;
        _this._colorTransform = null;
        _this._extend = false;
        _this._viewRotation = null;
        _this._viewXScale = null;
        _this._viewYScale = null;

        // avm2
        _this.avm2 = null;
    };

    // filters
    DisplayObject.prototype.flash = {
        filters: {
            DropShadowFilter: DropShadowFilter,
            BlurFilter: BlurFilter,
            GlowFilter: GlowFilter,
            BevelFilter: BevelFilter,
            GradientGlowFilter: GradientGlowFilter,
            ConvolutionFilter: ConvolutionFilter,
            ColorMatrixFilter: ColorMatrixFilter,
            GradientBevelFilter: GradientBevelFilter,
            BitmapFilter: BitmapFilter
        }
    };

    /**
     * @returns {string}
     */
    DisplayObject.prototype.toString = function ()
    {
        var target = this.getTarget();
        var str = "_level0";
        var array = target.split("/");
        str += array.join(".");
        return str;
    };

    DisplayObject.prototype.invert = function (matrix)
    {
        const newMatrix = [];
        let a, b, c, d ,tx, ty, det;

        a  = matrix[0];
        b  = matrix[1];
        c  = matrix[2];
        d  = matrix[3];
        tx = matrix[4] / 20;
        ty = matrix[5] / 20;

        switch (true) {

            case (b === 0 && c === 0):

                newMatrix[0]  = 1 / a;
                newMatrix[1]  = 0;
                newMatrix[2]  = 0;
                newMatrix[3]  = 1 / d;
                newMatrix[4] = -newMatrix[0] * tx;
                newMatrix[5] = -newMatrix[3] * ty;

                break;

            default:

                det = a * d - b * c;

                switch (true) {

                    case det === 0:
                        this.identity();
                        break;

                    default:

                        const rdet = 1 / det;

                        newMatrix[0]  = d  * rdet;
                        newMatrix[1]  = -b * rdet;
                        newMatrix[2]  = -c * rdet;
                        newMatrix[3]  = a  * rdet;
                        newMatrix[4] = -(newMatrix[0] * tx + newMatrix[2] * ty);
                        newMatrix[5] = -(newMatrix[1] * tx + newMatrix[3] * ty);
                        break;

                }

                break;

        }

        return newMatrix;
    };

    DisplayObject.prototype.globalToLocal = function (point)
    {
        let matrix = this.getMatrix();

        let parent = this._parent;
        while (parent) {

            matrix = this.multiplicationMatrix(
                parent.getMatrix(),
                matrix
            );

            parent = parent._parent;
        }

        matrix = this.invert(matrix);

        const x = point.x * matrix[0] + point.y * matrix[2] + matrix[4];
        const y = point.x * matrix[1] + point.y * matrix[3] + matrix[5];

        point.x = x;
        point.y = y;
    };

    DisplayObject.prototype.localToGlobal = function (point)
    {
        let matrix = this.getMatrix();

        let parent = this._parent;
        while (parent) {

            matrix = this.multiplicationMatrix(
                parent.getMatrix(),
                matrix
            );

            parent = parent._parent;
        }

        matrix[4] /= 20;
        matrix[5] /= 20;

        const x = point.x * matrix[0] + point.y * matrix[2] + matrix[4];
        const y = point.x * matrix[1] + point.y * matrix[3] + matrix[5];

        point.x = x;
        point.y = y;
    };

    /**
     * @param a
     * @param b
     * @returns []
     */
    DisplayObject.prototype.multiplicationMatrix = function(a, b)
    {
        return [
            a[0] * b[0] + a[2] * b[1],
            a[1] * b[0] + a[3] * b[1],
            a[0] * b[2] + a[2] * b[3],
            a[1] * b[2] + a[3] * b[3],
            a[0] * b[4] + a[2] * b[5] + a[4],
            a[1] * b[4] + a[3] * b[5] + a[5]
        ];
    };

    /**
     * @param a
     * @param b
     * @returns []
     */
    DisplayObject.prototype.multiplicationColor = function(a, b)
    {
        return [
            a[0] * b[0], a[1] * b[1],
            a[2] * b[2], a[3] * b[3],
            a[0] * b[4] + a[4], a[1] * b[5] + a[5],
            a[2] * b[6] + a[6], a[3] * b[7] + a[7]
        ];
    };

    /**
     * @param bounds
     * @param matrix
     * @param object
     * @returns {{xMin: Number, xMax: number, yMin: Number, yMax: number}}
     */
    DisplayObject.prototype.boundsMatrix = function (bounds, matrix, object)
    {
        var no = _Number.MAX_VALUE;
        var xMax = -no;
        var yMax = -no;
        var xMin = no;
        var yMin = no;

        if (object) {
            xMin = object.xMin;
            xMax = object.xMax;
            yMin = object.yMin;
            yMax = object.yMax;
        }

        var x0 = bounds.xMax * matrix[0] + bounds.yMax * matrix[2] + matrix[4];
        var x1 = bounds.xMax * matrix[0] + bounds.yMin * matrix[2] + matrix[4];
        var x2 = bounds.xMin * matrix[0] + bounds.yMax * matrix[2] + matrix[4];
        var x3 = bounds.xMin * matrix[0] + bounds.yMin * matrix[2] + matrix[4];
        var y0 = bounds.xMax * matrix[1] + bounds.yMax * matrix[3] + matrix[5];
        var y1 = bounds.xMax * matrix[1] + bounds.yMin * matrix[3] + matrix[5];
        var y2 = bounds.xMin * matrix[1] + bounds.yMax * matrix[3] + matrix[5];
        var y3 = bounds.xMin * matrix[1] + bounds.yMin * matrix[3] + matrix[5];

        xMax = _max(_max(_max(_max(xMax, x0), x1), x2), x3);
        xMin = _min(_min(_min(_min(xMin, x0), x1), x2), x3);
        yMax = _max(_max(_max(_max(yMax, y0), y1), y2), y3);
        yMin = _min(_min(_min(_min(yMin, y0), y1), y2), y3);

        return {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax};
    };

    /**
     * @param color
     * @param data
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    DisplayObject.prototype.generateColorTransform = function (color, data)
    {
        return {
            R: _max(0, _min((color.R * data[0]) + data[4], 255))|0,
            G: _max(0, _min((color.G * data[1]) + data[5], 255))|0,
            B: _max(0, _min((color.B * data[2]) + data[6], 255))|0,
            A: _max(0, _min((color.A * 255 * data[3]) + data[7], 255)) / 255
        };
    };

    /**
     * @param src
     * @returns {Array}
     */
    DisplayObject.prototype.cloneArray = function(src)
    {
        var arr = [];
        var length = src.length;
        for (var i = 0; i < length; i++) {
            arr[i] = src[i];
        }
        return arr;
    };

    /**
     * @param blendMode
     * @returns {String}
     */
    DisplayObject.prototype.getBlendName = function (blendMode)
    {
        var mode = null;
        switch (blendMode) {
            case 1:
            case "normal":
                mode = "normal";
                break;
            case 2:
            case "layer":
                mode = "layer";
                break;
            case 3:
            case "multiply":
                mode = "multiply";
                break;
            case 4:
            case "screen":
                mode = "screen";
                break;
            case 5:
            case "lighten":
                mode = "lighten";
                break;
            case 6:
            case "darken":
                mode = "darken";
                break;
            case 7:
            case "difference":
                mode = "difference";
                break;
            case 8:
            case "add":
                mode = "add";
                break;
            case 9:
            case "subtract":
                mode = "subtract";
                break;
            case 10:
            case "invert":
                mode = "invert";
                break;
            case 11:
            case "alpha":
                mode = "alpha";
                break;
            case 12:
            case "erase":
                mode = "erase";
                break;
            case 13:
            case "overlay":
                mode = "overlay";
                break;
            case 14:
            case "hardlight":
                mode = "hardlight";
                break;
        }
        return mode;
    };

    /**
     * @param stage
     */
    DisplayObject.prototype.setStage = function (stage)
    {
        var _this = this;
        _this.stageId = stage.getId();
        if (_this instanceof SimpleButton) {
            var upState = _this.getSprite("up");
            upState.setStage(stage);
            var downState = _this.getSprite("down");
            downState.setStage(stage);
            var hitState = _this.getSprite("hit");
            hitState.setStage(stage);
            var overState = _this.getSprite("over");
            overState.setStage(stage);
        }
        stage.setInstance(_this);
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getStage = function ()
    {
        var _this = this;
        var stage = _this.getLoadStage();
        if (!stage) {
            stage = _this.getParentStage();
        }
        return stage;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getParentStage = function ()
    {
        var stageId = this.stageId;
        if (stageId !== null) {
            if (stageId in stages) {
                return stages[stageId];
            } else {
                return loadStages[stageId];
            }
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getLoadStage = function ()
    {
        var loadStageId = this.loadStageId;
        if (loadStageId !== null) {
            if (loadStageId in stages) {
                return stages[loadStageId];
            } else {
                return loadStages[loadStageId];
            }
        }
    };

    /**
     * @param stage
     */
    DisplayObject.prototype.setLoadStage = function (stage)
    {
        var _this = this;
        _this.loadStageId = null;
        if (stage !== null) {
            stage.setInstance(_this);
            _this.loadStageId = stage.getId();
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getCharacterId = function ()
    {
        return this.characterId;
    };

    /**
     * @param characterId
     */
    DisplayObject.prototype.setCharacterId = function (characterId)
    {
        this.characterId = characterId;
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getTagType = function ()
    {
        return this.tagType;
    };

    /**
     * @param tagType
     */
    DisplayObject.prototype.setTagType = function (tagType)
    {
        this.tagType = tagType;
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getRatio = function ()
    {
        return this.ratio;
    };

    /**
     * @param ratio
     */
    DisplayObject.prototype.setRatio = function (ratio)
    {
        this.ratio = ratio;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getParent = function ()
    {
        var _this = this;
        var stage = _this.getLoadStage();
        var parent;
        var pId = _this.parentId;
        if (stage) {
            parent = stage.getInstance(pId);
        }
        if (!parent) {
            stage = _this.getParentStage();
            parent = stage.getInstance(pId);
        }
        return parent;
    };

    /**
     * @param parent
     */
    DisplayObject.prototype.setParent = function (parent)
    {
        var _this = this;
        if (parent instanceof DisplayObjectContainer) {
            parent.setInstance(_this);
        }
        _this.parentId = parent.instanceId;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getParentSprite = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        return stage.getInstance(_this._sprite);
    };

    /**
     * @param sprite
     */
    DisplayObject.prototype.setParentSprite = function (sprite)
    {
        this._sprite = sprite.instanceId;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getButtonStatus = function ()
    {
        return this.buttonStatus;
    };

    /**
     * @param status
     */
    DisplayObject.prototype.setButtonStatus = function (status)
    {
        this.buttonStatus = status;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getMask = function ()
    {
        return this._mask;
    };

    /**
     * @param obj
     */
    DisplayObject.prototype.setMask = function (obj)
    {
        var _this = this;
        var maskMc = _this._mask;
        if (maskMc) {
            maskMc.isMask = false;
        }
        obj.isMask = true;
        _this._mask = obj;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getEnabled = function ()
    {
        return this._enabled;
    };

    /**
     * @param enabled
     */
    DisplayObject.prototype.setEnabled = function (enabled)
    {
        this._enabled = enabled;
    };

    /**
     * @returns {boolean}
     */
    DisplayObject.prototype.getButtonMode = function ()
    {
        return this._buttonMode;
    };

    /**
     * @param buttonMode
     */
    DisplayObject.prototype.setButtonMode = function (buttonMode)
    {
        this._buttonMode = buttonMode;
    };

    /**
     * @returns {string}
     */
    DisplayObject.prototype.getTarget = function ()
    {
        return this._target;
    };

    /**
     * @param target
     */
    DisplayObject.prototype.setTarget = function (target)
    {
        this._target = target;
    };

    /**
     * @param path
     * @returns {{scope: DisplayObject, target: *}}
     */
    DisplayObject.prototype.splitPath = function (path)
    {
        var _this = this;
        var scope = _this;
        var target = path;
        var split;
        var targetPath = "";
        if (typeof path === "string") {
            if (path.indexOf("::") !== -1) {
                scope = _this;
                target = path;
            } else if (path.indexOf(":") !== -1) {
                split = path.split(":");
                targetPath = split[0];
                target = split[1];
            } else if (path.indexOf(".") !== -1) {
                split = path.split(".");
                target = split.pop();
                targetPath += split.join(".");
            }

            if (targetPath !== "") {
                var mc = _this.getDisplayObject(targetPath);
                if (mc) {
                    scope = mc;
                }
            }
        }

        return {
            "scope": scope,
            "target": target
        };
    };

    /**
     * @param name
     * @param parse
     * @returns {undefined}
     */
    DisplayObject.prototype.getProperty = function (name, parse)
    {
        var _this = this;
        var target = name;
        if (parse !== false) {
            var obj = _this.splitPath(name);
            _this = obj.scope;
            target = obj.target;
        }

        if (_this.removeFlag) {
            return undefined;
        }

        var value;
        var prop = (typeof target === "string") ? target.toLowerCase() : target;
        switch (prop) {
            case 0:
            case "_x":
                value = _this.getX();
                break;
            case 1:
            case "_y":
                value = _this.getY();
                break;
            case 2:
            case "_xscale":
                value = _this.getXScale();
                break;
            case 3:
            case "_yscale":
                value = _this.getYScale();
                break;
            case 4:
            case "_currentframe":
                if (_this instanceof MovieClip) {
                    value = _this.getCurrentFrame();
                }
                break;
            case 5:
            case "_totalframes":
                if (_this instanceof MovieClip) {
                    value = _this.getTotalFrames();
                }
                break;
            case 6:
            case "_alpha":
                value = _this.getAlpha();
                break;
            case 7:
            case "_visible":
                value = _this.getVisible();
                break;
            case 8:
            case "_width":
                value = _this.getWidth();
                break;
            case 9:
            case "_height":
                value = _this.getHeight();
                break;
            case 10:
            case "_rotation":
                value = _this.getRotation();
                break;
            case 11:
            case "_target":
                value = _this.getTarget();
                break;
            case 12:
            case "_framesloaded":
                value = _this._framesloaded;
                break;
            case 13:
            case "_name":
                value = _this.getName();
                break;
            case 14:
            case "_droptarget":
                if (_this instanceof MovieClip) {
                    value = _this.getDropTarget();
                }
                break;
            case 15:
            case "_url":
                value = _this._url;
                break;
            case 16:
            case "_highquality":
                value = _this._highquality;
                break;
            case 17:
            case "_focusrect":
                value = _this._focusrect;
                break;
            case 18:
            case "_soundbuftime":
                value = _this._soundbuftime;
                break;
            case 19:
            case "_quality":
                value = _this._quality;
                break;
            case 20:
            case "_xmouse":
                value = _this.getXMouse();
                break;
            case 21:
            case "_ymouse":
                value = _this.getYMouse();
                break;
            case "text":
            case "htmltext":
                if (_this instanceof TextField) {
                    var variable = _this.getVariable("variable");
                    if (variable) {
                        var mc = _this.getParent();
                        value = mc.getProperty(variable);
                    } else {
                        value = _this.getVariable("text");
                    }
                } else {
                    value = _this.getVariable(target);
                }
                break;
            case "$version":
                value = "swf2js 8,0,0";
                break;
            case "enabled":
                value = _this.getEnabled();
                break;
            case "blendmode":
                value = _this.getBlendMode();
                break;
            case "sharedobject":
                value = new SharedObject();
                break;
            case "key":
                value = keyClass;
                break;
            case "mouse":
                var _root = _this.getDisplayObject("_root");
                var rootStage = _root.getStage();
                value = rootStage.mouse;
                break;
            default:
                value = _this.getVariable(target, parse);
                if (value === undefined && target !== name) {
                    value = _this.getGlobalVariable(name);
                }
                break;
        }

        return value;
    };

    /**
     * @param name
     * @param value
     * @param parse
     */
    DisplayObject.prototype.setProperty = function (name, value, parse)
    {
        var _this = this;
        var target = name;
        if (parse !== false) {
            var obj = _this.splitPath(name);
            _this = obj.scope;
            target = obj.target;
        }

        var prop = (typeof target === "string") ? target.toLowerCase() : target;
        switch (prop) {
            case 0:
            case "_x":
                _this.setX(value);
                break;
            case 1:
            case "_y":
                _this.setY(value);
                break;
            case 2:
            case "_xscale":
                _this.setXScale(value);
                break;
            case 3:
            case "_yscale":
                _this.setYScale(value);
                break;
            case 4:
            case "_currentframe":
            case 5:
            case "_totalframes":
            case 15:
            case "_url":
            case 20:
            case "_xmouse":
            case 21:
            case "_ymouse":
            case 11:
            case "_target":
            case 12:
            case "_framesloaded":
            case 14:
            case "_droptarget":
                // readonly
                break;
            case 6:
            case "_alpha":
                _this.setAlpha(value);
                break;
            case 7:
            case "_visible":
                _this.setVisible(value);
                break;
            case 8:
            case "_width":
                _this.setWidth(value);
                break;
            case 9:
            case "_height":
                _this.setHeight(value);
                break;
            case 10:
            case "_rotation":
                _this.setRotation(value);
                break;
            case 13:
            case "_name":
                _this.setName(value);
                break;
            case 16:
            case "_highquality":
                _this._highquality = value;
                break;
            case 17:
            case "_focusrect":
                _this._focusrect = value;
                break;
            case 18:
            case "_soundbuftime":
                _this._soundbuftime = value;
                break;
            case 19:
            case "_quality":
                _this._quality = value;
                break;
            case "text":
            case "htmltext":
                if (_this instanceof TextField) {
                    var variable = _this.getVariable("variable");
                    if (variable) {
                        var mc = _this.getParent();
                        mc.setProperty(variable, value);
                    } else {
                        _this.setVariable("text", value);
                    }
                    var input = _this.input;
                    if (input) {
                        input.value = value;
                    }
                } else {
                    _this.setVariable(target, value);
                }
                break;
            case "blendmode":
                _this.setBlendMode(value);
                break;
            case "enabled":
                _this.setEnabled(value);
                break;
            case "filters":
                _this.setFilters(value);
                break;
            default:
                _this.setVariable(target, value);
                break;
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getDepth = function ()
    {
        var _this = this;
        var _depth = _this._depth;
        var depth = (_depth !== null) ? _depth : _this.getLevel();
        return depth - 16384;
    };

    /**
     * @param depth
     * @param swapDepth
     * @param swapMc
     */
    DisplayObject.prototype.setDepth = function (depth, swapDepth, swapMc)
    {
        var _this = this;
        var parent = _this.getParent();
        var _depth = _this._depth;
        var level = (_depth !== null) ? _depth : _this.getLevel();
        var totalFrame = parent.getTotalFrames() + 1;

        if (!swapMc) {
            _this._depth = depth;
        } else {
            _this._depth = swapDepth;
            swapMc._depth = depth;
        }

        var container = parent.container;
        var instanceId = _this.instanceId;
        for (var frame = 1; frame < totalFrame; frame++) {
            if (!(frame in container)) {
                container[frame] = [];
            }

            var tags = container[frame];
            if (swapMc) {
                if (level in tags && tags[level] === instanceId) {
                    tags[depth] = swapMc.instanceId;
                }

                if (swapDepth in tags && tags[swapDepth] === swapMc.instanceId) {
                    tags[swapDepth] = instanceId;
                }
            } else {
                if (!(level in tags) || level in tags && tags[level] === instanceId) {
                    delete tags[level];
                    tags[depth] = instanceId;
                }
            }

            container[frame] = tags;
        }
        _this.setController(false, false, false, false);
        if (swapMc) {
            swapMc.setController(false, false, false, false);
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getX = function ()
    {
        var matrix = this.getMatrix();
        return (matrix) ? matrix[4] / 20 : undefined;
    };

    /**
     * @param x
     */
    DisplayObject.prototype.setX = function (x)
    {
        x = +x;
        if (!_isNaN(x)) {
            var _this = this;
            var _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            matrix[4] = x * 20;
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getY = function ()
    {
        var matrix = this.getMatrix();
        return (matrix) ? matrix[5] / 20 : undefined;
    };

    /**
     * @param y
     */
    DisplayObject.prototype.setY = function (y)
    {
        y = +y;
        if (!_isNaN(y)) {
            var _this = this;
            var _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            matrix[5] = y * 20;
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getXScale = function ()
    {
        if (this._viewXScale !== null) {
            return this._viewXScale;
        }

        var matrix = this.getMatrix();
        var xScale = _sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]) * 100;
        if (0 > matrix[0]) {
            xScale *= -1;
        }
        return xScale;
    };

    /**
     * @param xscale
     */
    DisplayObject.prototype.setXScale = function (xscale)
    {
        xscale = +xscale;
        if (!_isNaN(xscale) && this._viewXScale !== xscale) {
            var _this = this;
            var _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            var radianX = _atan2(matrix[1], matrix[0]);
            if (radianX === -_PI) {
                radianX = 0;
            }
            this._viewXScale = xscale;
            xscale /= 100;
            matrix[0] = xscale * _cos(radianX);
            matrix[1] = xscale * _sin(radianX);
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getYScale = function ()
    {
        if (this._viewYScale !== null) {
            return this._viewYScale;
        }

        var matrix = this.getMatrix();
        var yScale = _sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]) * 100;
        if (0 > matrix[3]) {
            yScale *= -1;
        }
        return yScale;
    };

    /**
     * @param yscale
     */
    DisplayObject.prototype.setYScale = function (yscale)
    {
        yscale = +yscale;
        if (!_isNaN(yscale) && this._viewYScale !== yscale) {
            var _this = this;
            var _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            var radianY = _atan2(-matrix[2], matrix[3]);
            if (radianY === -_PI) {
                radianY = 0;
            }
            this._viewYScale = yscale;
            yscale /= 100;
            matrix[2] = -yscale * _sin(radianY);
            matrix[3] = yscale * _cos(radianY);
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getAlpha = function ()
    {
        var colorTransform = this.getColorTransform();
        var alpha = colorTransform[3] + (colorTransform[7] / 255);
        return alpha * 100;
    };

    /**
     * @param alpha
     */
    DisplayObject.prototype.setAlpha = function (alpha)
    {
        alpha = +alpha;
        if (!_isNaN(alpha)) {
            var _this = this;
            var _colorTransform = _this.getColorTransform();
            var colorTransform = _this.cloneArray(_colorTransform);
            colorTransform[3] = alpha / 100;
            colorTransform[7] = 0;
            _this.setColorTransform(colorTransform);
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getVisible = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        var version = stage.getVersion();
        if (version > 4) {
            return _this.__visible;
        }
        return (_this.__visible) ? 1 : 0;
    };

    /**
     * @param visible
     */
    DisplayObject.prototype.setVisible = function (visible)
    {
        var _this = this;
        if (typeof visible === "boolean") {
            _this.__visible = visible;
        } else {
            visible = +visible;
            if (!_isNaN(visible)) {
                _this.__visible = (visible) ? true : false;
            }
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getLevel = function ()
    {
        return this._level;
    };

    /**
     * @param level
     */
    DisplayObject.prototype.setLevel = function (level)
    {
        this._level = level;
    };

    /**
     * @returns {null}
     */
    DisplayObject.prototype.getName = function ()
    {
        return this.__name;
    };

    /**
     * @param name
     */
    DisplayObject.prototype.setName = function (name)
    {
        this.__name = name;
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getRotation = function ()
    {
        if (this._viewRotation !== null) {
            return this._viewRotation * 180 / _PI;
        }

        var matrix = this.getMatrix();
        var rotation = _atan2(matrix[1], matrix[0]) * 180 / _PI;
        switch (rotation) {
            case -90.00000000000001:
                rotation = -90;
                break;
            case 90.00000000000001:
                rotation = 90;
                break;
        }
        return rotation;
    };

    /**
     * @param rotation
     */
    DisplayObject.prototype.setRotation = function (rotation)
    {
        rotation = +rotation;
        if (!_isNaN(rotation)) {
            var _this = this;
            var _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            var radianX = _atan2(matrix[1], matrix[0]);
            var radianY = _atan2(-matrix[2], matrix[3]);
            var ScaleX = _sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
            var ScaleY = _sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]);
            rotation *= _PI / 180;
            radianY += rotation - radianX;
            radianX = rotation;
            matrix[0] = ScaleX * _cos(radianX);
            matrix[1] = ScaleX * _sin(radianX);
            matrix[2] = -ScaleY * _sin(radianY);
            matrix[3] = ScaleY * _cos(radianY);

            _this.setMatrix(matrix);
            this._viewRotation = rotation;
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getWidth = function ()
    {
        var _this = this;
        var matrix = _this.getMatrix();
        var bounds = _this.getBounds(matrix);
        return _abs(bounds.xMax - bounds.xMin);
    };

    /**
     * @param width
     */
    DisplayObject.prototype.setWidth = function (width)
    {
        width = +width;
        if (!_isNaN(width)) {
            var _this = this;
            var _matrix = _this.getOriginMatrix();
            var bounds = _this.getBounds(_matrix);
            var _width = _abs(bounds.xMax - bounds.xMin);
            var xScale = width * _matrix[0] / _width;
            if (_isNaN(xScale)) {
                xScale = 0;
            }
            _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            matrix[0] = xScale;
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {number}
     */
    DisplayObject.prototype.getHeight = function ()
    {
        var _this = this;
        var matrix = _this.getMatrix();
        var bounds = _this.getBounds(matrix);
        return _abs(bounds.yMax - bounds.yMin);
    };

    /**
     * @param height
     */
    DisplayObject.prototype.setHeight = function (height)
    {
        height = +height;
        if (!_isNaN(height)) {
            var _this = this;
            var _matrix = _this.getOriginMatrix();
            var bounds = _this.getBounds(_matrix);
            var _height = _abs(bounds.yMax - bounds.yMin);
            var yScale = height * _matrix[3] / _height;
            if (_isNaN(yScale)) {
                yScale = 0;
            }
            _matrix = _this.getMatrix();
            var matrix = _this.cloneArray(_matrix);
            matrix[3] = yScale;
            _this.setMatrix(matrix);
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getXMouse = function ()
    {
        if (!_event) {
            return null;
        }
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var div = _document.getElementById(stage.getName());
        var bounds = div.getBoundingClientRect();
        var x = window.pageXOffset + bounds.left;
        var touchX = 0;
        if (isTouch) {
            var changedTouche = _event.changedTouches[0];
            touchX = changedTouche.pageX;
        } else {
            touchX = _event.pageX;
        }

        var mc = _this;
        var matrix = _this.getMatrix();
        while (true) {
            var parent = mc.getParent();
            if (!parent) {
                break;
            }
            matrix = _this.multiplicationMatrix(parent.getMatrix(), matrix);
            mc = parent;
        }

        var scale = stage.getScale();
        touchX -= x;
        touchX /= scale;
        touchX -= matrix[4] / 20;
        return touchX;
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getYMouse = function ()
    {
        if (!_event) {
            return null;
        }
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var div = _document.getElementById(stage.getName());
        var bounds = div.getBoundingClientRect();
        var y = window.pageYOffset + bounds.top;
        var touchY = 0;
        if (isTouch) {
            var changedTouche = _event.changedTouches[0];
            touchY = changedTouche.pageY;
        } else {
            touchY = _event.pageY;
        }

        var mc = _this;
        var matrix = _this.getMatrix();
        while (true) {
            var parent = mc.getParent();
            if (!parent) {
                break;
            }
            matrix = _this.multiplicationMatrix(parent.getMatrix(), matrix);
            mc = parent;
        }

        var scale = stage.getScale();
        touchY -= y;
        touchY /= scale;
        touchY -= matrix[5] / 20;
        return touchY;
    };

    /**
     * @param name
     * @param parse
     * @returns {*}
     */
    DisplayObject.prototype.getVariable = function (name, parse)
    {
        var _this = this;
        if (name === undefined) {
            return name;
        }

        var variables = _this.variables;
        if (!variables) {
            return undefined;
        }

        if (name in variables) {
            return variables[name];
        }

        var stage = _this.getStage();
        var version = stage.getVersion();
        if (version < 7) {
            for (var key in variables) {
                if (!variables.hasOwnProperty(key)) {
                    continue;
                }
                if (key.toLowerCase() === name.toLowerCase()) {
                    return variables[key];
                }
            }
        }

        var value;
        if (version > 4) {
            var registerClass = variables.registerClass;
            if (registerClass &&
                typeof registerClass === "object" &&
                name in registerClass
            ) {
                return registerClass[name];
            }

            if (_this instanceof MovieClip) {
                value = _this.getDisplayObject(name, parse);
                if (value) {
                    return value;
                }
            }

            // avm2
            var cId = _this.getCharacterId();
            var symbol = stage.symbols[cId];
            if (symbol) {
                var symbols = symbol.split(".");
                var classMethod = symbols.pop();
                var sLen = symbols.length;
                var classObj = stage.avm2;
                for (var sIdx = 0; sIdx < sLen; sIdx++) {
                    classObj = classObj[symbols[sIdx]];
                }

                var AVM2 = classObj[classMethod];
                value = AVM2[name];
                if (value) {
                    return value;
                }
            }

            var _global = stage.getGlobal();
            value = _global.getVariable(name);
            if (value) {
                return value;
            }
            if (_this instanceof MovieClip && name === "flash") {
                return _this.flash;
            }
            if (name in window) {
                return window[name];
            }
        }
        return undefined;
    };

    /**
     * @param name
     * @param value
     */
    DisplayObject.prototype.setVariable = function (name, value)
    {
        var _this = this;
        var variables = _this.variables;
        var stage = _this.getStage();
        if (typeof name !== "string") {
            name += "";
        }

        if (stage.getVersion() < 7) {
            for (var key in variables) {
                if (!variables.hasOwnProperty(key)) {
                    continue;
                }
                if (key.toLowerCase() !== name.toLowerCase()) {
                    continue;
                }
                _this.variables[key] = value;
                return 0;
            }
        }
        _this.variables[name] = value;
    };

    /**
     * @param path
     * @returns {*}
     */
    DisplayObject.prototype.getGlobalVariable = function (path)
    {
        var _this = this;
        var stage = _this.getStage();
        var version = stage.getVersion();
        if (version < 5) {
            return undefined;
        }

        var splitData = null;
        if (path.indexOf(".") !== -1) {
            splitData = path.split(".");
        }

        var value;
        if (splitData) {
            var _global = stage.getGlobal();
            var variables = _global.variables;
            var length = splitData.length;
            for (var i = 0; i < length; i++) {
                var name = splitData[i];
                if (version < 7) {
                    for (var key in variables) {
                        if (!variables.hasOwnProperty(key)) {
                            continue;
                        }
                        if (key.toLowerCase() === name.toLowerCase()) {
                            value = variables[key];
                            break;
                        }
                    }
                } else {
                    value = variables[name];
                }

                if (!value) {
                    break;
                }
                variables = value;
            }
        }

        return value;
    };

    /**
     * @param path
     * @param parse
     * @returns {*}
     */
    DisplayObject.prototype.getDisplayObject = function (path, parse)
    {
        var _this = this;
        var mc = _this;
        var _root = mc;
        var tags, tag, stage, parent;

        if (!_this._lockroot) {
            while (true) {
                parent = _root.getParent();
                if (!parent) {
                    break;
                }
                _root = parent;
            }
        } else {
            stage = _this.getStage();
            _root = stage.getParent();
        }

        if (typeof path !== "string") {
            path += "";
        }
        if (path === "_root") {
            return _root;
        }
        if (path === "this") {
            return this;
        }
        stage = _root.getStage();
        if (path === "_global") {
            return stage.getGlobal();
        }

        parent = mc.getParent();
        if (path === "_parent") {
            return (parent !== null) ? parent : undefined;
        }

        var len = 1;
        var splitData = [path];
        if (parse !== false) {
            if (path.indexOf("/") !== -1) {
                splitData = path.split("/");
                len = splitData.length;
                if (splitData[0] === "") {
                    mc = _root;
                }
            } else if (path.indexOf(".") !== -1) {
                splitData = path.split(".");
                len = splitData.length;
                if (splitData[0] === "_root") {
                    mc = _root;
                }
            } else if (path.substr(0, 6) === "_level") {
                var level = path.substr(6);
                level = +level;
                if (level === 0) {
                    return _root;
                }
                if (!parent) {
                    parent = stage.getParent();
                }
                tags = parent.getTags();
                if (level in tags) {
                    var tId = tags[level];
                    tag = stage.getInstance(tId);
                    if (tag instanceof MovieClip) {
                        return tag;
                    }
                }
                return undefined;
            }
        }

        var version = stage.getVersion();
        for (var i = 0; i < len; i++) {
            var name = splitData[i];
            if (name === "") {
                continue;
            }
            if (name === "_root") {
                mc = _root;
                continue;
            }
            if (name === "this") {
                mc = _this;
                continue;
            }
            if (name === "_parent") {
                parent = mc.getParent();
                if (!parent) {
                    return undefined;
                }
                mc = parent;
                continue;
            }
            if (name === "..") {
                mc = mc.getParent();

                if (!mc) {
                    return undefined;
                }
                continue;
            }

            tags = mc.getTags();
            if (tags === undefined) {
                return undefined;
            }

            var tagLength = tags.length;
            var setTarget = false;
            if (tagLength > 0) {
                for (var idx in tags) {
                    if (!tags.hasOwnProperty(idx)) {
                        continue;
                    }

                    var instanceId = tags[idx];
                    var loadStage = mc.getStage();
                    tag = loadStage.getInstance(instanceId);
                    if (!tag || tag.removeFlag) {
                        continue;
                    }

                    var tagName = tag.getName();
                    if (!tagName) {
                        continue;
                    }

                    if (version < 7) {
                        if (tagName.toLowerCase() === name.toLowerCase()) {
                            mc = tag;
                            setTarget = true;
                            break;
                        }
                    } else {
                        if (tagName === name) {
                            mc = tag;
                            setTarget = true;
                            break;
                        }
                    }
                }
            }

            if (!setTarget) {
                return undefined;
            }
        }
        return mc;
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     */
    DisplayObject.prototype.preRender = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;
        _this.isLoad = true;

        var cacheKey = "";
        var preCtx = ctx;
        var preMatrix = matrix;

        var isFilter = false;
        var isBlend = false;
        var cache, rMatrix, xScale, yScale, xMin, yMin, xMax, yMax;

        // mask
        var maskObj = _this.getMask();
        if (maskObj) {
            _this.renderMask(ctx, stage);
        }

        // filter
        if (visible && !stage.clipMc) {
            var filters = _this.getFilters();
            if (filters !== null && filters.length) {
                isFilter = true;
            }

            // blend
            var blendMode = _this.getBlendMode();
            if (blendMode !== null && blendMode !== "normal") {
                isBlend = true;
            }
        }

        // filter or blend
        if (isFilter || isBlend) {
            rMatrix = _this.multiplicationMatrix(stage.getMatrix(), matrix);

            var bounds;
            var twips = 1;
            if (_this instanceof Shape || _this instanceof StaticText) {
                bounds = _this.getBounds();
                xScale = _sqrt(rMatrix[0] * rMatrix[0] + rMatrix[1] * rMatrix[1]);
                yScale = _sqrt(rMatrix[2] * rMatrix[2] + rMatrix[3] * rMatrix[3]);
            } else {
                twips = 20;
                bounds = _this.getBounds(matrix);
                xScale = stage.getScale() * _devicePixelRatio;
                yScale = stage.getScale() * _devicePixelRatio;
            }

            xMin = bounds.xMin;
            yMin = bounds.yMin;
            xMax = bounds.xMax;
            yMax = bounds.yMax;

            var width = _abs(_ceil((xMax - xMin) * xScale));
            var height = _abs(_ceil((yMax - yMin) * yScale));

            var canvas = cacheStore.getCanvas();
            canvas.width = width;
            canvas.height = height;
            cache = canvas.getContext("2d");
            cache._offsetX = 0;
            cache._offsetY = 0;

            var m2 = [1, 0, 0, 1, -xMin * twips, -yMin * twips];
            var m3 = [matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]];
            if (_this instanceof Shape) {
                m3[4] = 0;
                m3[5] = 0;
            }
            preCtx = cache;
            preMatrix = _this.multiplicationMatrix(m2, m3);
        }

        // graphics
        if (visible) {
            cacheKey += _this.renderGraphics(preCtx, preMatrix, colorTransform, stage);
        }

        return {
            preCtx: preCtx,
            preMatrix: preMatrix,
            isFilter: isFilter,
            isBlend: isBlend,
            rMatrix: rMatrix,
            cacheKey: cacheKey,
            xMin: xMin * xScale,
            yMin: yMin * yScale
        };
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param obj
     */
    DisplayObject.prototype.postRender = function(ctx, matrix, colorTransform, stage, obj)
    {
        var _this = this;
        var cache = obj.preCtx;
        var isFilter = obj.isFilter;
        var cacheKey = obj.cacheKey;
        if (isFilter && cacheKey !== "") {
            cache = _this.renderFilter(cache, matrix, colorTransform, stage, cacheKey);
        }

        var xMin = obj.xMin;
        var yMin = obj.yMin;
        if (_this instanceof Shape) {
            xMin += obj.rMatrix[4];
            yMin += obj.rMatrix[5];
        }
        if (cache) {
            xMin -= cache._offsetX;
            yMin -= cache._offsetY;
        }

        _this.renderBlend(ctx, cache, xMin, yMin, isFilter);
    };


        /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @returns {string}
     */
    DisplayObject.prototype.renderGraphics = function (ctx, matrix, colorTransform, stage)
    {
        var graphics = this.graphics;
        var cacheKey = "";
        if (graphics && graphics.isDraw) {
            cacheKey = graphics.render(ctx, matrix, colorTransform, stage);
        }
        return cacheKey;
    };

    /**
     * @param ctx
     * @param stage
     */
    DisplayObject.prototype.renderMask = function (ctx, stage)
    {
        var _this = this;
        var maskObj = _this.getMask();
        if (maskObj) {
            ctx.save();
            ctx.beginPath();
            stage.clipMc = true;

            var mc = maskObj;
            var matrix = [1,0,0,1,0,0];
            var _multiplicationMatrix = _this.multiplicationMatrix;
            while (true) {
                var parent = mc.getParent();
                if (!parent.getParent()) {
                    break;
                }
                matrix = _multiplicationMatrix(parent.getMatrix(), matrix);
                mc = parent;
            }
            maskObj.render(ctx, matrix, [1,1,1,1,0,0,0,0], stage, true);
            ctx.clip();
            stage.clipMc = false;
        }
    };

    /**
     * @param filters
     * @returns {string}
     */
    DisplayObject.prototype.getFilterKey = function (filters)
    {
        var keys = [];
        var length = filters.length;
        for (var i = 0; i < length; i++) {
            var filter = filters[i];
            for (var prop in filter) {
                if (!filter.hasOwnProperty(prop)) {
                    continue;
                }
                keys[keys.length] = filter[prop];
            }
        }
        return keys.join("_");
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param cacheKey
     * @returns {*}
     */
    DisplayObject.prototype.renderFilter = function (ctx, matrix, colorTransform, stage, cacheKey)
    {
        var _this = this;
        var filters = _this.getFilters();
        if (stage.clipMc || !filters || !filters.length) {
            return ctx;
        }

        cacheKey += "_" + _this.getFilterKey(filters);
        var cacheStoreKey = "Filter_" + _this.instanceId;

        var cache;
        if (_this._filterCacheKey === cacheKey) {
            cache = cacheStore.getCache(cacheStoreKey);
        }

        if (!cache) {
            var fLength = filters.length;
            for (var i = 0; i < fLength; i++) {
                var filter = filters[i];
                cache = filter.render(ctx, matrix, colorTransform, stage);
            }
            _this._filterCacheKey = cacheKey;
            cacheStore.setCache(cacheStoreKey, cache);
        }

        cacheStore.destroy(ctx);

        return cache;
    };

    /**
     * @param ctx
     * @param cache
     * @param xMin
     * @param yMin
     * @param isFilter
     */
    DisplayObject.prototype.renderBlend = function (ctx, cache, xMin, yMin, isFilter)
    {
        var _this = this;
        var mode = _this.getBlendMode();
        var operation = "source-over";
        var canvas = cache.canvas;
        var width = canvas.width;
        var height = canvas.height;
        if (!width || !height) {
            return ;
        }

        cache.setTransform(1, 0, 0, 1, 0, 0);

        switch (mode) {
            case "multiply":
                operation = "multiply";
                break;
            case "screen":
                operation = "screen";
                break;
            case "lighten":
                operation = "lighten";
                break;
            case "darken":
                operation = "darken";
                break;
            case "difference":
                operation = "difference";
                break;
            case "add":
                operation = "lighter";
                break;
            case "subtract":
                cache.globalCompositeOperation = "difference";
                cache.fillStyle = "rgb(255,255,255)";
                cache.fillRect(0, 0, width, height);
                cache.globalCompositeOperation = "darken";
                cache.fillStyle = "rgb(255,255,255)";
                cache.fillRect(0, 0, width, height);
                operation = "color-burn";
                break;
            case "invert":
                cache.globalCompositeOperation = "difference";
                cache.fillStyle = "rgb(255,255,255)";
                cache.fillRect(0, 0, width, height);
                cache.globalCompositeOperation = "lighter";
                cache.fillStyle = "rgb(255,255,255)";
                cache.fillRect(0, 0, width, height);
                operation = "difference";
                break;
            case "alpha":
                operation = "source-over";
                break;
            case "erase":
                operation = "destination-out";
                break;
            case "overlay":
                operation = "overlay";
                break;
            case "hardlight":
                operation = "hard-light";
                break;
        }

        ctx.globalAlpha = 1;
        ctx.globalCompositeOperation = operation;
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.drawImage(canvas, xMin, yMin, canvas.width, canvas.height);
        ctx.globalCompositeOperation = "source-over";
        if (!isFilter) {
            cacheStore.destroy(cache);
        }
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getOriginMatrix = function ()
    {
        var _this = this;
        var controller = _this.getController();
        return controller.getMatrix();
    };

    /**
     * @returns []
     */
    DisplayObject.prototype.getMatrix = function ()
    {
        return this._matrix || this.getOriginMatrix();
    };

    /**
     * @param matrix
     */
    DisplayObject.prototype.setMatrix = function (matrix)
    {
        var _this = this;
        _this._matrix = matrix;
        _this.setController(true, false, false, false);
    };

    /**
     * @returns {*}
     */
    DisplayObject.prototype.getOriginColorTransform = function ()
    {
        var _this = this;
        var controller = _this.getController();
        return controller.getColorTransform();
    };

    /**
     * @returns []
     */
    DisplayObject.prototype.getColorTransform = function ()
    {
        return this._colorTransform || this.getOriginColorTransform();
    };

    /**
     * @param colorTransform
     */
    DisplayObject.prototype.setColorTransform = function (colorTransform)
    {
        var _this = this;
        _this._colorTransform = colorTransform;
        _this.setController(false, true, false, false);
    };

    /**
     * @returns {string}
     */
    DisplayObject.prototype.getOriginBlendMode = function ()
    {
        var _this = this;
        var controller = _this.getController();
        return controller.getBlendMode();
    };

    /**
     * @returns {string}
     */
    DisplayObject.prototype.getBlendMode = function ()
    {
        return this._blendMode || this.getOriginBlendMode();
    };

    /**
     * @param blendMode
     */
    DisplayObject.prototype.setBlendMode = function (blendMode)
    {
        var _this = this;
        var mode = _this.getBlendName(blendMode);
        if (mode !== null) {
            _this._blendMode = mode;
            _this.setController(false, false, false, true);
        }
    };

    /**
     * @returns {Array}
     */
    DisplayObject.prototype.getOriginFilters = function ()
    {
        var _this = this;
        var controller = _this.getController();
        return controller.getFilters();
    };

    /**
     * @returns {Array}
     */
    DisplayObject.prototype.getFilters = function ()
    {
        return this._filters || this.getOriginFilters();
    };

    /**
     * @param filters
     */
    DisplayObject.prototype.setFilters = function (filters)
    {
        var _this = this;
        _this._filterCacheKey = null;
        _this._filters = filters;
        _this.setController(false, false, true, false);
    };

    /**
     * @param isMatrix
     * @param isColorTransform
     * @param isFilters
     * @param isBlend
     */
    DisplayObject.prototype.setController = function (isMatrix, isColorTransform, isFilters, isBlend)
    {
        var _this = this;

        if (!isMatrix) {
            var _matrix = _this._matrix;
            if (_matrix === null) {
                _matrix = _this.getMatrix();
                _this._matrix = _this.cloneArray(_matrix);
            }
        }

        if (!isColorTransform) {
            var _colorTransform = _this._colorTransform;
            if (_colorTransform === null) {
                _colorTransform = _this.getColorTransform();
                _this._colorTransform = _this.cloneArray(_colorTransform);
            }
        }

        if (!isFilters) {
            var _filters = _this._filters;
            if (_filters === null) {
                _filters = _this.getFilters();
                if (_filters === null) {
                    _filters = [];
                }
                _this._filters = _filters;
            }
        }

        if (!isBlend) {
            var _blendMode = _this._blendMode;
            if (_blendMode === null) {
                _blendMode = _this.getBlendMode();
                _this._blendMode = _blendMode;
            }
        }
    };

    /**
     * @returns {PlaceObject}
     */
    DisplayObject.prototype.getController = function ()
    {
        var _this = this;
        var frame = 0;
        var depth = _this.getLevel();
        var stage = _this.getParentStage();
        if (!stage) {
            return new PlaceObject();
        }

        var parent = _this.getParentSprite();
        if (!parent) {
            parent = _this.getParent();
        }
        if (!parent) {
            return new PlaceObject();
        }

        if (parent instanceof MovieClip) {
            frame = parent.getCurrentFrame();
        }
        var placeObject = stage.getPlaceObject(parent.instanceId, depth, frame);
        if (!placeObject) {
            stage = _this.getLoadStage();
            if (stage) {
                placeObject = stage.getPlaceObject(parent.instanceId, depth, frame);
            }
        }

        return placeObject || new PlaceObject();
    };

    /**
     * reset
     */
    DisplayObject.prototype.reset = function ()
    {
        var _this = this;
        _this.active = false;
        _this.isMask = false;
        _this._matrix = null;
        _this._colorTransform = null;
        _this._filters = null;
        _this._blendMode = null;
        _this._depth = null;
        _this.setVisible(true);
        _this.setEnabled(true);
        _this.setButtonStatus("up");

        if (_this instanceof TextField) {
            var input = _this.input;
            if (_this.inputActive) {
                _this.inputActive = false;
                input.onchange = null;
                var stage = _this.getStage();
                var div = _document.getElementById(stage.getName());
                if (div) {
                    var el = _document.getElementById(_this.getTagName());
                    if (el) {
                        try {
                            div.removeChild(el);
                        } catch (e) {

                        }
                    }
                }
            }
            _this.variables.text = _this.initialText;
        }
    };

    /**
     * trace
     */
    DisplayObject.prototype.trace = function ()
    {
        var params = ["[trace]"];
        var length = arguments.length;
        for (var i = 0; i < length; i++) {
            params[params.length] = arguments[i];
        }
        console.log.apply(window, params);
    };

    /**
     * @constructor
     */
    var InteractiveObject = function ()
    {
        var _this = this;
        _this._mouseEnabled = true;
        DisplayObject.call(_this);
    };

    /**
     * extends
     * @type {DisplayObject}
     */
    InteractiveObject.prototype = Object.create(DisplayObject.prototype);
    InteractiveObject.prototype.constructor = InteractiveObject;

    /**
     * properties
     */
    Object.defineProperties(DisplayObject.prototype,
    {
        mouseEnabled: {
            get: function () {
                return this.getMouseEnabled();
            },
            set: function (mouseEnabled) {
                this.setMouseEnabled(mouseEnabled);
            }
        }
    });

    /**
     * @returns {boolean}
     */
    InteractiveObject.prototype.getMouseEnabled = function ()
    {
        return this._mouseEnabled;
    };

    /**
     * @param mouseEnabled
     */
    InteractiveObject.prototype.setMouseEnabled = function (mouseEnabled)
    {
        this._mouseEnabled = mouseEnabled;
    };

    /**
     * @constructor
     */
    var TextSnapshot = function ()
    {
        this.charCount = 0;
    };

    /**
     * @param beginIndex
     * @param textToFind
     * @param caseSensitive
     */
    TextSnapshot.prototype.findText = function (beginIndex, textToFind, caseSensitive)
    {

    };

    /**
     * @param beginIndex
     * @param endIndex
     */
    TextSnapshot.prototype.getSelected = function (beginIndex, endIndex)
    {

    };

    /**
     * @param includeLineEndings
     */
    TextSnapshot.prototype.getSelectedText = function (includeLineEndings)
    {

    };

    TextSnapshot.prototype.getText = function (beginIndex, endIndex, includeLineEndings)
    {

    };

    /**
     * @param beginIndex
     * @param endIndex
     */
    TextSnapshot.prototype.getTextRunInfo = function (beginIndex, endIndex)
    {

    };

    /**
     * @param x
     * @param y
     * @param maxDistance
     */
    TextSnapshot.prototype.hitTestTextNearPos = function (x, y, maxDistance)
    {

    };

    /**
     * @param hexColor
     */
    TextSnapshot.prototype.setSelectColor = function (hexColor)
    {

    };

    /**
     * @param beginIndex
     * @param endIndex
     * @param select
     */
    TextSnapshot.prototype.setSelected = function (beginIndex, endIndex, select)
    {

    };

    /**
     * @constructor
     */
    var DisplayObjectContainer = function ()
    {
        var _this = this;
        InteractiveObject.call(_this);

        _this._mouseChildren = true;
        _this._tabChildren = true;
        _this._textSnapshot = new TextSnapshot();
        _this._numChildren = 0;
        _this.soundId = null;
        _this.soundInfo = null;
        _this.container = [];
        if (_this instanceof MovieClip) {
            var totalFrames = _this.getTotalFrames() + 1;
            var frame = 1;
            while (frame < totalFrames) {
                _this.container[frame++] = [];
            }
        }
        _this.instances = [];
        _this.isSwap = false;
    };

    /**
     * extends
     * @type {InteractiveObject}
     */
    DisplayObjectContainer.prototype = Object.create(InteractiveObject.prototype);
    DisplayObjectContainer.prototype.constructor = DisplayObjectContainer;

    /**
     * properties
     */
    Object.defineProperties(DisplayObjectContainer.prototype,
    {
        mouseChildren: {
            get: function () {
                return this.getMouseChildren();
            },
            set: function (mouseChildren) {
                this.setMouseChildren(mouseChildren);
            }
        },
        textSnapshot: {
            get: function () {
                return this.getTextSnapshot();
            },
            set: function () {
            }
        },
        numChildren: {
            get: function () {
                return this.getNumChildren();
            },
            set: function () {
            }
        },
        tabChildren: {
            get: function () {
                return this.getTabChildren();
            },
            set: function (tabChildren) {
                this.setTabChildren(tabChildren);
            }
        }
    });

    /**
     * @returns {boolean}
     */
    DisplayObjectContainer.prototype.getMouseChildren = function ()
    {
        return this._mouseChildren;
    };

    /**
     * @param mouseChildren
     */
    DisplayObjectContainer.prototype.setMouseChildren = function (mouseChildren)
    {
        this._mouseChildren = mouseChildren;
    };

    /**
     * @returns {TextSnapshot}
     */
    DisplayObjectContainer.prototype.getTextSnapshot = function ()
    {
        return this._textSnapshot;
    };

    /**
     * @returns {number}
     */
    DisplayObjectContainer.prototype.getNumChildren = function ()
    {
        return this._numChildren;
    };

    /**
     * @returns {boolean}
     */
    DisplayObjectContainer.prototype.getTabChildren = function ()
    {
        return this._tabChildren;
    };

    /**
     * @param tabChildren
     */
    DisplayObjectContainer.prototype.setTabChildren = function (tabChildren)
    {
        this._tabChildren = tabChildren;
    };

    /**
     * @returns {Array}
     */
    DisplayObjectContainer.prototype.getContainer = function ()
    {
        return this.container;
    };

    /**
     * @returns {Array}
     */
    DisplayObjectContainer.prototype.getInstances = function ()
    {
        return this.instances;
    };

    /**
     * @param instance
     */
    DisplayObjectContainer.prototype.setInstance = function (instance)
    {
        var instances = this.instances;
        var instanceId = instance.instanceId;
        if (!(instanceId in instances)) {
            instances[instanceId] = 1;
        }
    };

    /**
     * @param instance
     */
    DisplayObjectContainer.prototype.deleteInstance = function (instance)
    {
        delete this.instances[instance.instanceId];
    };

    /**
     * @param child
     * @param depth
     * @returns {DisplayObject}
     */
    DisplayObjectContainer.prototype.addChild = function (child, depth)
    {
        if (child instanceof DisplayObject) {
            var _this = this;

            if (depth === undefined) {
                depth = _this._numChildren;
            }

            var stage = _this.getStage();
            child.setParent(_this);
            child.setStage(stage);
            child.setLevel(depth);

            var container = _this.getContainer();
            var frame = 1;
            var placeObject = new PlaceObject();
            var instanceId = _this.instanceId;
            if (_this instanceof MovieClip) {
                var totalFrames = _this.getTotalFrames() + 1;
                for (; frame < totalFrames; frame++) {
                    if (!(frame in container)) {
                        container[frame] = [];
                    }
                    stage.setPlaceObject(placeObject, instanceId, depth, frame);
                    container[frame][depth] = child.instanceId;
                }
            } else {
                stage.setPlaceObject(placeObject, instanceId, depth, frame);
                container[depth] = child.instanceId;
            }

            _this._numChildren++;
        }
        return child;
    };

    /**
     * @param child
     * @param depth
     * @returns {DisplayObject}
     */
    DisplayObjectContainer.prototype.addChildAt = function (child, depth)
    {
        return this.addChild(child, depth);
    };

    /**
     *
     * @param depth
     * @returns {DisplayObject}
     */
    DisplayObjectContainer.prototype.getChildAt = function (depth)
    {
        var _this = this;
        var container = _this.getContainer();
        var children = container;

        if (16384 > depth) {
            depth += 16384;
        }

        if (_this instanceof MovieClip) {
            var frame = _this.getCurrentFrame();
            children = container[frame];
        }
        return children[depth];
    };

    /**
     * @param name
     * @return {DisplayObject}
     */
    DisplayObjectContainer.prototype.getChildByName = function (name)
    {
        var _this = this;
        var container = _this.getContainer();
        var children = container;
        if (_this instanceof MovieClip) {
            var frame = _this.getCurrentFrame();
            children = container[frame];
        }

        var obj;
        for (var depth in children) {
            if (!children.hasOwnProperty(depth)) {
                continue;
            }

            var child = children[depth];
            if (child.getName() !== name) {
                continue;
            }
            obj = child;
            break;
        }
        return obj;
    };

    /**
     * @param child
     * @returns {number}
     */
    DisplayObjectContainer.prototype.getChildIndex = function (child)
    {
        var index;
        if (child instanceof DisplayObject) {
            index = child.getLevel() - 16384;
        }
        return index;
    };

    /**
     * @param child
     * @return {DisplayObject}
     */
    DisplayObjectContainer.prototype.removeChild = function (child)
    {
        var _this = this;
        var container = _this.getContainer();
        var depth, obj;
        if (_this instanceof MovieClip) {
            var totalFrames = _this.getTotalFrames() + 1;
            for (var frame = 1; frame < totalFrames; frame++) {
                if (!(frame in container)) {
                    continue;
                }
                var children = container[frame];
                for (depth in children) {
                    if (!children.hasOwnProperty(depth)) {
                        continue;
                    }
                    var instanceId = children[depth];
                    if (instanceId !== child.instanceId) {
                        continue;
                    }
                    delete container[frame][depth];
                    break;
                }
            }
        } else {
            for (depth in container) {
                if (!container.hasOwnProperty(depth)) {
                    continue;
                }
                obj = container[depth];
                if (obj.instanceId !== child.instanceId) {
                    continue;
                }
                delete container[depth];
                break;
            }
        }

        if (child) {
            _this.deleteInstance(child);
            _this._numChildren--;
        }

        return child;
    };

    /**
     * @param depth
     * @returns {*}
     */
    DisplayObjectContainer.prototype.removeChildAt = function (depth)
    {
        var _this = this;
        var container = _this.getContainer();
        var children = container;

        if (16384 > depth) {
            depth += 16384;
        }

        var child;
        if (_this instanceof MovieClip) {
            var totalFrames = _this.getTotalFrames();
            for (var frame = 1; frame < totalFrames; frame++) {
                if (!(frame in container)) {
                    continue;
                }

                children = container[frame];
                if (!(depth in children)) {
                    continue;
                }

                child = children[depth];
                delete container[frame][depth];
            }
        } else {
            child = children[depth];
            delete children[depth];
        }

        if (child) {
            _this._numChildren--;
        }

        return child;
    };

    /**
     * @param depth
     * @param obj
     */
    DisplayObjectContainer.prototype.addTag = function (depth, obj)
    {
        var _this = this;
        _this.container[depth] = obj.instanceId;
        _this._numChildren++;
    };

    /**
     * startSound
     */
    DisplayObjectContainer.prototype.startSound = function ()
    {
        var _this = this;
        var soundId = _this.soundId;
        if (soundId) {
            var stage = _this.getStage();
            var sound = stage.sounds[soundId];
            if (sound) {
                var audio = _document.createElement("audio");
                audio.onload = function ()
                {
                    this.load();
                    this.preload = "auto";
                    this.autoplay = false;
                    this.loop = false;
                };
                audio.src = sound.base64;
                startSound(audio, _this.soundInfo);
            }
        }
    };

    /**
     * reset
     */
    DisplayObjectContainer.prototype.reset = function ()
    {
        var _this = this;
        var container = _this.container;
        var length = container.length;
        if (length) {
            var stage = _this.getStage();
            for (var depth in container) {
                if (!container.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = container[depth];
                var obj = stage.getInstance(instanceId);
                obj.reset();
            }
        }

        _this.isMask = false;
        _this._depth = null;
        _this._matrix = null;
        _this._colorTransform = null;
        _this._filters = null;
        _this._blendMode = null;
        _this.mouseEnabled = true;
    };

    /**
     * @param matrix
     * @param stage
     * @param visible
     * @param mask
     */
    DisplayObjectContainer.prototype.setHitRange = function (matrix, stage, visible, mask)
    {
        var _this = this;
        var isVisible = _min(_this.getVisible(), visible);
        if (_this.getEnabled() && isVisible) {
            var buttonHits = stage.buttonHits;
            var variables = _this.variables;
            var events = _this.events;
            if (events.press !== undefined ||
                events.release !== undefined ||
                events.releaseOutside !== undefined ||
                events.rollOver !== undefined ||
                events.rollOut !== undefined ||
                events.dragOver !== undefined ||
                events.dragOut !== undefined ||
                variables.onPress !== undefined ||
                variables.onRelease !== undefined ||
                variables.onRollOver !== undefined ||
                variables.onReleaseOutside !== undefined ||
                variables.onRollOut !== undefined ||
                variables.onDragOver !== undefined ||
                variables.onDragOut !== undefined
            ) {
                var rMatrix = _this.multiplicationMatrix(matrix, _this.getMatrix());
                var bounds = _this.getBounds(rMatrix);
                buttonHits[buttonHits.length] = {
                    xMax: bounds.xMax,
                    xMin: bounds.xMin,
                    yMax: bounds.yMax,
                    yMin: bounds.yMin,
                    parent: _this,
                    matrix: _this.cloneArray(matrix)
                };
            }
        }
    };

    /**
     *
     * @param name
     * @param depth
     * @returns {MovieClip}
     */
    DisplayObjectContainer.prototype.createMovieClip = function (name, depth)
    {
        var movieClip = new MovieClip();
        movieClip = this.addChild(movieClip, depth);
        if (name) {
            movieClip.setName(name);
        }
        return movieClip;
    };

    /**
     * @param name
     * @param depth
     * @returns {Sprite}
     */
    DisplayObjectContainer.prototype.createSprite = function (name, depth)
    {
        var sprite = new Sprite();
        sprite = this.addChild(sprite, depth);
        if (name) {
            sprite.setName(name);
        }
        return sprite;
    };

    /**
     * @param name
     * @param depth
     * @returns {SimpleButton}
     */
    DisplayObjectContainer.prototype.createButton = function (name, depth)
    {
        var button = new SimpleButton();
        button = this.addChild(button, depth);
        if (name) {
            button.setName(name);
        }
        return button;
    };

    /**
     * @param name
     * @param width
     * @param height
     * @param depth
     * @returns {TextField}
     */
    DisplayObjectContainer.prototype.createText = function (name, width, height, depth)
    {
        var textField = new TextField(name, depth, width, height);
        textField = this.addChild(textField, depth);
        textField.setInitParams();
        if (name) {
            textField.setName(name);
        }
        textField.size = 12;
        return textField;
    };

    /**
     * @returns {Shape}
     */
    DisplayObjectContainer.prototype.createShape = function (depth)
    {
        var shape = new Shape();
        this.addChild(shape, depth);
        return shape;
    };

    /**
     * @constructor
     */
    var Sprite = function ()
    {
        var _this = this;
        DisplayObjectContainer.call(_this);

        _this.touchPointID = 0;
        _this._buttonMode = false;
        _this._useHandCursor = false;
        _this._dropTarget = null;
        _this._hitArea = null;
        _this._graphics = new Graphics();
        _this._soundTransform = new SoundTransform();
    };

    /**
     * extends
     * @type {DisplayObjectContainer}
     */
    Sprite.prototype = Object.create(DisplayObjectContainer.prototype);
    Sprite.prototype.constructor = Sprite;

    /**
     * properties
     */
    Object.defineProperties(Sprite.prototype,
    {
        graphics: {
            get: function () {
                return this.getGraphics();
            },
            set: function () {
            }
        },
        hitArea: {
            get: function () {
                return this.getHitArea();
            },
            set: function (sprite) {
                this.setHitArea(sprite);
            }
        },
        buttonMode: {
            get: function () {
                return this.getButtonMode();
            },
            set: function (buttonMode) {
                this.setButtonMode(buttonMode);
            }
        },
        soundTransform: {
            get: function () {
                return this._soundTransform;
            },
            set: function () {
            }
        },
        useHandCursor: {
            get: function () {
                return this.getUseHandCursor();
            },
            set: function (useHandCursor) {
                this.setUseHandCursor(useHandCursor);
            }
        },
        dropTarget: {
            get: function () {
                return this.getDropTarget();
            },
            set: function () {
                this.setDropTarget();
            }
        }
    });

    /**
     * @returns {Graphics}
     */
    Sprite.prototype.getGraphics = function ()
    {
        return this._graphics;
    };

    /**
     * @returns {DisplayObject}
     */
    Sprite.prototype.getHitArea = function ()
    {
        return this._hitArea;
    };

    /**
     * @param displayObject
     */
    Sprite.prototype.setHitArea = function (displayObject)
    {
        this._hitArea = displayObject;
    };

    /**
     * @returns {boolean}
     */
    Sprite.prototype.getUseHandCursor = function ()
    {
        return this._useHandCursor;
    };

    /**
     * @param useHandCursor
     */
    Sprite.prototype.setUseHandCursor = function (useHandCursor)
    {
        this._useHandCursor = useHandCursor;
    };

    /**
     * startTouchDrag
     */
    Sprite.prototype.startTouchDrag = function (touchPointID, lock, bounds)
    {
        this.startDrag(lock);
    };

    /**
     * @param touchPointID
     */
    Sprite.prototype.stopTouchDrag = function (touchPointID)
    {
        this.stopDrag();
    };

    /**
     * startDrag
     */
    Sprite.prototype.startDrag = function ()
    {
        var args = arguments;
        var lock = args[0];
        var left = args[1];
        var top = args[2];
        var right = args[3];
        var bottom = args[4];

        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var startX = 0;
        var startY = 0;
        if (!lock) {
            startX = _this.getXMouse();
            startY = _this.getYMouse();
        }

        stage.dragMc = _this;
        stage.dragRules = {
            startX: startX,
            startY: startY,
            left: left,
            top: top,
            right: right,
            bottom: bottom
        };

        _this.setDropTarget();
    };

    /**
     * stopDrag
     */
    Sprite.prototype.stopDrag = function ()
    {
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        stage.dragMc = null;
        stage.dragRules = null;
        _this.setDropTarget();
    };

    /**
     * executeDrag
     */
    Sprite.prototype.executeDrag = function ()
    {
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var dragRules = stage.dragRules;
        var startX = dragRules.startX;
        var startY = dragRules.startY;
        var left = dragRules.left;
        var top = dragRules.top;
        var right = dragRules.right;
        var bottom = dragRules.bottom;
        var x = _this.getX();
        var y = _this.getY();
        var xmouse = _this.getXMouse();
        var ymouse = _this.getYMouse();

        xmouse -= startX;
        ymouse -= startY;

        var moveX = x + xmouse;
        var moveY = y + ymouse;

        if (left === null || left === undefined) {
            _this.setX(moveX);
            _this.setY(moveY);
        } else {
            left = +left;
            top = +top;
            right = +right;
            bottom = +bottom;

            // x
            if (right < moveX) {
                _this.setX(right);
            } else if (moveX < left) {
                _this.setX(left);
            } else {
                _this.setX(moveX);
            }

            // y
            if (bottom < moveY) {
                _this.setY(bottom);
            } else if (moveY < top) {
                _this.setY(top);
            } else {
                _this.setY(moveY);
            }
        }
    };

    /**
     *
     * @returns {null|*}
     */
    Sprite.prototype.getDropTarget = function ()
    {
        return this._droptarget;
    };

    /**
     * setDropTarget
     */
    Sprite.prototype.setDropTarget = function ()
    {
        var _this = this;
        _this._droptarget = null;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var parent = _this.getParent();
        if (!parent) {
            parent = stage.getParent();
        }

        var x = _root.getXMouse();
        var y = _root.getYMouse();

        var tags = parent.getTags();
        for (var depth in tags) {
            if (!tags.hasOwnProperty(depth)) {
                continue;
            }

            var id = tags[depth];
            if (id === _this.instanceId) {
                continue;
            }

            var instance = stage.getInstance(id);
            if (!(instance instanceof MovieClip)) {
                continue;
            }

            var hit = instance.hitTest(x, y);
            if (hit) {
                _this._droptarget = instance;
                break;
            }
        }
    };

    /**
     * @returns {Array}
     */
    Sprite.prototype.getTags = function ()
    {
        return this.getContainer();
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     */
    Sprite.prototype.render = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;
        if (_this.removeFlag) {
            return "";
        }

        _this.isLoad = true;
        stage.doneTags.unshift(_this);

        // sound
        if (_this instanceof MovieClip && !_this.soundStopFlag) {
            var sounds = _this.getSounds();
            if (sounds !== undefined) {
                var sLen = sounds.length;
                for (var idx = 0; idx < sLen; idx++) {
                    if (!(idx in sounds)) {
                        continue;
                    }
                    var sound = sounds[idx];
                    _this.startSound(sound);
                }
            }
        }

        // matrix & colorTransform
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var rMatrix = _multiplicationMatrix(matrix, _this.getMatrix());
        var _multiplicationColor = _this.multiplicationColor;
        var rColorTransform = _multiplicationColor(colorTransform, _this.getColorTransform());
        var isVisible = _min(_this.getVisible(), visible);

        // pre render
        var obj = _this.preRender(ctx, rMatrix, rColorTransform, stage, visible);
        var cacheKey = obj.cacheKey;
        var preCtx = obj.preCtx;
        var preMatrix = obj.preMatrix;

        // render
        var clips = [];
        var container = _this.getTags();
        var length = container.length;
        var maskObj = _this.getMask();

        if (length) {
            var myStage = _this.getStage();
            for (var depth in container) {
                if (!container.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = container[depth];
                var instance = myStage.getInstance(instanceId);
                if (!instance) {
                    continue;
                }

                // mask end
                var cLen = clips.length;
                for (var cIdx = 0; cIdx < cLen; cIdx++) {
                    var cDepth = clips[cIdx];
                    if (depth > cDepth) {
                        clips.splice(cIdx, 1);
                        ctx.restore();
                        break;
                    }
                }

                // mask start
                if (instance.isClipDepth) {
                    ctx.save();
                    ctx.beginPath();
                    clips[clips.length] = instance.clipDepth;
                    if (instance instanceof MovieClip) {
                        stage.isClipDepth = true;
                    }
                }

                if (isVisible) {
                    switch (true) {
                        case instance instanceof TextField:
                        case instance instanceof DisplayObjectContainer:
                            instance.setHitRange(rMatrix, stage, visible, cLen);
                            break;
                        case instance instanceof SimpleButton:
                            if (!instance.clipDepth) {
                                instance.setHitRange(rMatrix, stage, visible, cLen);
                            }
                            break;
                        default:
                            break;
                    }
                }

                // mask
                if (instance.isMask) {
                    continue;
                }

                if (instance.isClipDepth) {
                    if (preMatrix[0] === 0) {
                        preMatrix[0] = 0.00000000000001;
                    }
                    if (preMatrix[3] === 0) {
                        preMatrix[3] = 0.00000000000001;
                    }
                }

                cacheKey += instance.render(preCtx, preMatrix, rColorTransform, stage, isVisible);
                if (stage.isClipDepth) {
                    preCtx.clip();
                    stage.isClipDepth = false;
                }
            }
        }

        if (clips.length || maskObj) {
            ctx.restore();
        }

        // post render
        if (obj.isFilter || obj.isBlend) {
            obj.cacheKey = cacheKey;
            _this.postRender(ctx, rMatrix, rColorTransform, stage, obj);
        }

        return cacheKey;
    };

    /**
     * initFrame
     */
    Sprite.prototype.initFrame = function () {};

    /**
     * @param stage
     * @param clipEvent
     */
    Sprite.prototype.putFrame = function (stage, clipEvent)
    {
        var _this = this;
        _this.active = true;
        _this.dispatchEvent(clipEvent, stage);
    };

    /**
     * @param stage
     */
    Sprite.prototype.addActions = function (stage)
    {
        var _this = this;
        var myStage = _this.getStage();
        var tags = _this.getTags();
        var length = tags.length;
        if (length) {
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }
                var instanceId = tags[depth];
                var instance = myStage.getInstance(instanceId);
                if (!instance) {
                    continue;
                }
                instance.addActions(stage);
            }
        }
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    Sprite.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;
        var loadStage = _this.getStage();
        var tags = _this.getTags();
        var length = tags.length;
        var hit = false;
        var rMatrix = _this.multiplicationMatrix(matrix, _this.getMatrix());

        if (length) {
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = tags[depth];
                var obj = loadStage.getInstance(instanceId);
                hit = obj.renderHitTest(ctx, rMatrix, stage, x, y);
                if (hit) {
                    return hit;
                }
            }
        }

        var graphics = _this.graphics;
        if (graphics.isDraw) {
            return graphics.renderHitTest(ctx, rMatrix, stage, x, y);
        }

        return hit;
    };

    /**
     * @param mc
     * @returns {{xMin: *, xMax: number, yMin: *, yMax: number}}
     */
    Sprite.prototype.getRect = function (mc)
    {
        var _this = this;
        if (!mc) {
            mc = _this;
        }
        var bounds = mc.getBounds(mc.getOriginMatrix());
        var graphics = _this.graphics;
        var twips = 20;
        var maxWidth = graphics.maxWidth / twips;
        var halfWidth = maxWidth / 2;
        var xMin = bounds.xMin + halfWidth;
        var xMax = bounds.xMax - halfWidth;
        var yMin = bounds.yMin + halfWidth;
        var yMax = bounds.yMax - halfWidth;
        return {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax};
    };

    /**
     * @param matrix
     * @returns {{}}
     */
    Sprite.prototype.getBounds = function (matrix)
    {
        if (matrix instanceof MovieClip) {
            return matrix.getBounds(matrix.getOriginMatrix());
        }

        var _this = this;
        var tags = _this.getTags();
        var xMax = 0;
        var yMax = 0;
        var xMin = 0;
        var yMin = 0;
        var graphics = _this.graphics;
        var isDraw = graphics.isDraw;
        if (isDraw) {
            var maxWidth = graphics.maxWidth;
            var halfWidth = maxWidth / 2;
            var gBounds = _this.boundsMatrix(graphics.bounds, matrix);
            var twips = (matrix) ? 20 : 1;
            xMin = (gBounds.xMin - halfWidth) / twips;
            xMax = (gBounds.xMax + halfWidth) / twips;
            yMin = (gBounds.yMin - halfWidth) / twips;
            yMax = (gBounds.yMax + halfWidth) / twips;
        }

        var length = tags.length;
        var stage = _this.getStage();
        if (length) {
            if (!isDraw) {
                var no = _Number.MAX_VALUE;
                xMax = -no;
                yMax = -no;
                xMin = no;
                yMin = no;
            }

            var _multiplicationMatrix = _this.multiplicationMatrix;
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }
                var instanceId = tags[depth];
                var tag = stage.getInstance(instanceId);
                if (!tag || tag.isClipDepth) {
                    continue;
                }

                var matrix2 = (matrix) ? _multiplicationMatrix(matrix, tag.getMatrix()) : tag.getMatrix();
                var bounds = tag.getBounds(matrix2);
                if (!bounds) {
                    continue;
                }

                xMin = _min(xMin, bounds.xMin);
                xMax = _max(xMax, bounds.xMax);
                yMin = _min(yMin, bounds.yMin);
                yMax = _max(yMax, bounds.yMax);
            }
        }

        return {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax};
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {*}
     */
    Sprite.prototype.hitCheck = function (ctx, matrix, stage, x, y)
    {
        var _this = this;
        if (!_this.getEnabled() ||
            !_this.getVisible() ||
            !_this.getMouseEnabled()
        ) {
            return false;
        }

        var hitObj;
        var hit = false;
        var tags = _this.getTags();
        var length = tags.length;
        var matrix2 = _this.multiplicationMatrix(matrix, _this.getMatrix());
        if (length) {
            var loadStage = _this.getStage();
            tags.reverse();
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var tagId = tags[depth];
                var instance = loadStage.getInstance(tagId);
                if (instance instanceof Shape ||
                    instance instanceof StaticText ||
                    instance instanceof TextField
                ) {
                    hit = instance.renderHitTest(ctx, matrix2, stage, x, y);
                } else {
                    hit = instance.hitCheck(ctx, matrix2, stage, x, y);
                }

                if (hit) {
                    hitObj = hit;
                    if (typeof hit !== "object") {
                        var events = _this.events;
                        if (events.press !== undefined ||
                            events.release !== undefined ||
                            events.releaseOutside !== undefined ||
                            events.rollOver !== undefined ||
                            events.rollOut !== undefined ||
                            events.dragOver !== undefined ||
                            events.dragOut !== undefined
                        ) {
                            stage.isHit = hit;
                            hitObj = {parent : _this};
                        }
                    }

                    tags.reverse();
                    return hitObj;
                }
            }
            tags.reverse();
        }

        var graphics = _this.graphics;
        if (graphics.isDraw) {
            hit = graphics.renderHitTest(ctx, matrix2, stage, x, y);
            if (hit) {
                hitObj = {parent : _this};
            }
        }

        return hitObj;
    };

    /**
     * @constructor
     */
    var Shape = function ()
    {
        var _this = this;
        DisplayObject.call(_this);

        _this.data = null;
        _this._graphics = new Graphics();

        var no = _Number.MAX_VALUE;
        _this.setBounds({xMin: no, xMax: -no, yMin: no, yMax: -no});
    };

    /**
     * extends
     * @type {DisplayObject}
     */
    Shape.prototype = Object.create(DisplayObject.prototype);
    Shape.prototype.constructor = Shape;

    /**
     * properties
     */
    Object.defineProperties(Shape.prototype,
    {
        graphics: {
            get: function () {
                return this.getGraphics();
            },
            set: function () {
            }
        }
    });

    /**
     * dummy
     */
    Shape.prototype.addActions = function () {};
    Shape.prototype.initFrame = function () {};

    /**
     * @param stage
     * @param clipEvent
     */
    Shape.prototype.putFrame = function (stage, clipEvent)
    {
        var _this = this;
        _this.active = true;
        _this.dispatchEvent(clipEvent, stage);
    };

    /**
     * @returns {Graphics}
     */
    Shape.prototype.getGraphics = function ()
    {
        return this._graphics;
    };

    /**
     * @returns []
     */
    Shape.prototype.getData = function ()
    {
        return this.data;
    };

    /**
     * @param data
     */
    Shape.prototype.setData = function (data)
    {
        this.data = data;
    };

    /**
     * @returns {{}}
     */
    Shape.prototype.getBounds = function (matrix)
    {
        var _this = this;
        var bounds, gBounds;
        var graphics = _this.graphics;
        var isDraw = graphics.isDraw;

        if (matrix) {
            bounds = _this.boundsMatrix(_this.bounds, matrix);
            if (isDraw) {
                gBounds = _this.boundsMatrix(graphics.getBounds(), matrix);
                bounds.xMin = _min(gBounds.xMin, bounds.xMin);
                bounds.xMax = _max(gBounds.xMax, bounds.xMax);
                bounds.yMin = _min(gBounds.yMin, bounds.yMin);
                bounds.yMax = _max(gBounds.yMax, bounds.yMax);
            }

            for (var name in bounds) {
                if (!bounds.hasOwnProperty(name)) {
                    continue;
                }
                bounds[name] /= 20;
            }
            return bounds;
        } else {
            bounds = _this.bounds;
            if (isDraw) {
                gBounds = graphics.getBounds();
                bounds.xMin = _min(gBounds.xMin, bounds.xMin);
                bounds.xMax = _max(gBounds.xMax, bounds.xMax);
                bounds.yMin = _min(gBounds.yMin, bounds.yMin);
                bounds.yMax = _max(gBounds.yMax, bounds.yMax);
            }
            return this.bounds;
        }
    };

    /**
     * @param bounds
     */
    Shape.prototype.setBounds = function (bounds)
    {
        this.bounds = bounds;
    };

    /**
     * @returns {boolean}
     */
    Shape.prototype.isMorphing = function ()
    {
        var tagType = this.getTagType();
        return (tagType === 46 || tagType === 84);
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     * @returns {*}
     */
    Shape.prototype.render = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;
        stage.doneTags.unshift(_this);

        // colorTransform
        var _multiplicationColor = _this.multiplicationColor;
        var rColorTransform = _multiplicationColor(colorTransform, _this.getColorTransform());
        var isVisible = _min(_this.getVisible(), visible);
        var alpha = rColorTransform[3] + (rColorTransform[7] / 255);
        var stageClip = stage.clipMc || stage.isClipDepth;
        if (!stageClip && (!alpha || !isVisible)) {
            return "";
        }

        // matrix
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());

        // pre render
        var obj = _this.preRender(ctx, m2, rColorTransform, stage, isVisible);
        var cacheKey = obj.cacheKey;
        var cache = null;

        // render
        var m3 = _multiplicationMatrix(stage.getMatrix(), obj.preMatrix);
        var isClipDepth = _this.isClipDepth || stageClip;
        if (isClipDepth) {
            if (m3[0]===0) {
                m3[0] = 0.00000000000001;
            }
            if (m3[3]===0) {
                m3[3] = 0.00000000000001;
            }
            ctx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);
            _this.executeRender(ctx, _min(m3[0], m3[3]), rColorTransform, isClipDepth, stage);
        } else {
            var xScale = _sqrt(m3[0] * m3[0] + m3[1] * m3[1]);
            var yScale = _sqrt(m3[2] * m3[2] + m3[3] * m3[3]);
            xScale = _pow(_SQRT2, _ceil(_log(xScale) / _LN2_2 - _LOG1P));
            yScale = _pow(_SQRT2, _ceil(_log(yScale) / _LN2_2 - _LOG1P));

            var bounds = _this.getBounds();
            var xMax = bounds.xMax;
            var xMin = bounds.xMin;
            var yMax = bounds.yMax;
            var yMin = bounds.yMin;

            var W = _abs(_ceil((xMax - xMin) * xScale));
            var H = _abs(_ceil((yMax - yMin) * yScale));
            if (W <= 0 || H <= 0) {
                return cacheKey;
            }

            var canvas;
            var loadStage = _this.getStage();
            var cacheId = _this.getCharacterId() + "_" + loadStage.getId();
            if (_this.isMorphing()) {
                cacheId += "_" + _this.getRatio();
            }

            cacheKey = cacheStore.generateKey("Shape", cacheId, [xScale, yScale], rColorTransform);
            cache = cacheStore.getCache(cacheKey);
            if (!cache &&
                stage.getWidth() > W &&
                stage.getHeight() > H &&
                cacheStore.size > (W * H)
            ) {
                canvas = cacheStore.getCanvas();
                canvas.width = W;
                canvas.height = H;
                cache = canvas.getContext("2d");
                var cMatrix = [xScale, 0, 0, yScale, -xMin * xScale, -yMin * yScale];
                cache.setTransform(cMatrix[0],cMatrix[1],cMatrix[2],cMatrix[3],cMatrix[4],cMatrix[5]);
                cache = _this.executeRender(
                    cache, _min(xScale, yScale), rColorTransform, isClipDepth, stage
                );
                cacheStore.setCache(cacheKey, cache);
            }

            var preCtx = obj.preCtx;
            if (cache) {
                canvas = cache.canvas;
                var sMatrix = [1 / xScale, 0, 0, 1 / yScale, xMin, yMin];
                var m4 = _multiplicationMatrix(m3, sMatrix);
                preCtx.setTransform(m4[0],m4[1],m4[2],m4[3],m4[4],m4[5]);
                if (isAndroid4x && !isChrome) {
                    preCtx.fillStyle = stage.context.createPattern(cache.canvas, "no-repeat");
                    preCtx.fillRect(0, 0, W, H);
                } else {
                    preCtx.drawImage(canvas, 0, 0, W, H);
                }
            } else {
                preCtx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);
                _this.executeRender(preCtx, _min(m3[0], m3[3]), rColorTransform, isClipDepth, stage);
            }
        }

        // post render
        cacheKey += "_" + m3[4] + "_" + m3[5];
        if (obj.isFilter || obj.isBlend) {
            obj.cacheKey = cacheKey;
            _this.postRender(ctx, matrix, rColorTransform, stage, obj);
        }

        return cacheKey;
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    Shape.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());

        var graphics = _this.graphics;
        if (graphics.isDraw) {
            return graphics.renderHitTest(ctx, m2, stage, x, y);
        }

        if (!_this.getData()) {
            return false;
        }

        var m3 = _multiplicationMatrix(stage.getMatrix(), m2);
        ctx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);

        var minScale = _min(m3[0], m3[3]);
        var shapes = _this.getData();
        var length = shapes.length;
        var hit = false;
        for (var idx = 0; idx < length; idx++) {
            var data = shapes[idx];
            var obj = data.obj;
            var isStroke = (obj.Width !== undefined);

            ctx.beginPath();
            var cmd = data.cmd;
            cmd(ctx);

            if (isStroke) {
                ctx.lineWidth = _max(obj.Width, 1 / minScale);
                ctx.lineCap = "round";
                ctx.lineJoin = "round";
            }

            hit = ctx.isPointInPath(x, y);
            if (hit) {
                return hit;
            }

            if ("isPointInStroke" in ctx) {
                hit = ctx.isPointInStroke(x, y);
                if (hit) {
                    return hit;
                }
            }
        }

        return hit;
    };

    /**
     * @param ctx
     * @param minScale
     * @param colorTransform
     * @param isClipDepth
     * @param stage
     * @returns {*}
     */
    Shape.prototype.executeRender = function (ctx, minScale, colorTransform, isClipDepth, stage)
    {
        var _this = this;
        var shapes = _this.getData();
        if (!shapes) {
            return ctx;
        }

        var stageClip = stage.clipMc || stage.isClipDepth;
        var length = shapes.length;
        var color;
        var css;
        var canvas;
        for (var idx = 0; idx < length; idx++) {
            var data = shapes[idx];
            var obj = data.obj;
            var styleObj = (!obj.HasFillFlag) ? obj : obj.FillType;
            var cmd = data.cmd;
            var isStroke = (obj.Width !== undefined);

            if (isClipDepth) {
                if (isStroke) {
                    continue;
                }
                cmd(ctx);
                continue;
            }

            ctx.beginPath();
            cmd(ctx);

            var styleType = styleObj.fillStyleType;
            switch (styleType) {
                case 0x00:
                    color = styleObj.Color;
                    color = _this.generateColorTransform(color, colorTransform);
                    css = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
                    if (isStroke) {
                        ctx.strokeStyle = css;
                        ctx.lineWidth = _max(obj.Width, 1 / minScale);
                        ctx.lineCap = "round";
                        ctx.lineJoin = "round";
                        ctx.stroke();
                    } else {
                        ctx.fillStyle = css;
                        ctx.fill();
                    }

                    break;

                // gradient
                case 0x10:
                case 0x12:
                case 0x13:
                    var m = styleObj.gradientMatrix;
                    var type = styleObj.fillStyleType;
                    if (type !== 16) {
                        ctx.save();
                        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
                        css = ctx.createRadialGradient(0, 0, 0, 0, 0, 16384);
                    } else {
                        var xy = _this.linearGradientXY(m);
                        css = ctx.createLinearGradient(xy[0], xy[1], xy[2], xy[3]);
                    }

                    var records = styleObj.gradient.GradientRecords;
                    var rLength = records.length;
                    for (var rIdx = 0; rIdx < rLength; rIdx++) {
                        var record = records[rIdx];
                        color = record.Color;
                        color = _this.generateColorTransform(color, colorTransform);
                        var rgba = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
                        css.addColorStop(record.Ratio, rgba);
                    }

                    if (isStroke) {
                        ctx.strokeStyle = css;
                        ctx.lineWidth = _max(obj.Width, 1 / minScale);
                        ctx.lineCap = "round";
                        ctx.lineJoin = "round";
                        ctx.stroke();
                    } else {
                        ctx.fillStyle = css;
                        ctx.fill();
                    }

                    if (type !== 16) {
                        ctx.restore();
                    }

                    break;

                // bitmap
                case 0x40:
                case 0x41:
                case 0x42:
                case 0x43:
                    var width;
                    var height;
                    var loadStage = _this.getStage();
                    var bitmapId = styleObj.bitmapId;
                    var bMatrix = styleObj.bitmapMatrix;
                    var repeat = (styleType === 0x40 || styleType === 0x42) ? "repeat" : "no-repeat";
                    var bitmapCacheKey = cacheStore.generateKey(
                        "Bitmap",
                        bitmapId + "_" + loadStage.getId() + "_" + repeat,
                        undefined,
                        colorTransform
                    );

                    var image = cacheStore.getCache(bitmapCacheKey);
                    if (image === undefined) {
                        image = loadStage.getCharacter(bitmapId);
                        if (!image) {
                            break;
                        }

                        if (colorTransform[0] !== 1 ||
                            colorTransform[1] !== 1 ||
                            colorTransform[2] !== 1 ||
                            colorTransform[4] ||
                            colorTransform[5] ||
                            colorTransform[6]
                        ) {
                            var imgCanvas = image.canvas;
                            width = imgCanvas.width;
                            height = imgCanvas.height;
                            if (width > 0 && height > 0) {
                                canvas = cacheStore.getCanvas();
                                canvas.width = width;
                                canvas.height = height;
                                var imageContext = canvas.getContext("2d");
                                imageContext.drawImage(image.canvas, 0, 0, width, height);
                                image = _this.generateImageTransform.call(_this, imageContext, colorTransform);
                                cacheStore.setCache(bitmapCacheKey, image);
                            }
                        } else {
                            ctx.globalAlpha = _max(0, _min((255 * colorTransform[3]) + colorTransform[7], 255)) / 255;
                        }
                    }

                    if (image) {
                        ctx.save();
                        canvas = image.canvas;
                        width = canvas.width;
                        height = canvas.height;
                        if (width > 0 && height > 0) {
                            if (styleType === 0x41 || styleType === 0x43) {
                                ctx.clip();
                                ctx.transform(bMatrix[0], bMatrix[1], bMatrix[2], bMatrix[3], bMatrix[4], bMatrix[5]);
                                ctx.drawImage(canvas, 0, 0, width, height);
                            } else {
                                ctx.fillStyle = stage.context.createPattern(canvas, repeat);
                                ctx.transform(bMatrix[0], bMatrix[1], bMatrix[2], bMatrix[3], bMatrix[4], bMatrix[5]);
                                ctx.fill();
                            }
                        }
                        ctx.restore();
                    }

                    break;
            }
        }

        if (isClipDepth && !stageClip) {
            ctx.clip();

            if (isAndroid && isChrome) {
                if (!canvas) {
                    canvas = ctx.canvas;
                }

                var cWidth = canvas.width;
                var cHeight = canvas.height;

                var tmpCanvas = tmpContext.canvas;
                canvas = ctx.canvas;
                tmpCanvas.width = cWidth;
                tmpCanvas.height = cHeight;
                tmpContext.drawImage(canvas, 0, 0);

                ctx.save();
                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.beginPath();
                ctx.clearRect(0, 0, cWidth + 1, cHeight + 1);
                ctx.drawImage(tmpCanvas, 0, 0);
                ctx.restore();

                tmpContext.setTransform(1,0,0,1,0,0);
                tmpContext.clearRect(0, 0, cWidth + 1, cHeight + 1);
            }
        }

        var resetCss = "rgba(0,0,0,1)";
        ctx.strokeStyle = resetCss;
        ctx.fillStyle = resetCss;
        ctx.globalAlpha = 1;

        return ctx;
    };

    /**
     * @param ctx
     * @param color
     * @returns {*}
     */
    Shape.prototype.generateImageTransform = function (ctx, color)
    {
        var canvas = ctx.canvas;
        var width = canvas.width;
        var height = canvas.height;
        var imgData = ctx.getImageData(0, 0, width, height);
        var pxData = imgData.data;
        var idx = 0;
        var RedMultiTerm = color[0];
        var GreenMultiTerm = color[1];
        var BlueMultiTerm = color[2];
        var AlphaMultiTerm = color[3];
        var RedAddTerm = color[4];
        var GreenAddTerm = color[5];
        var BlueAddTerm = color[6];
        var AlphaAddTerm = color[7];
        var length = width * height;
        if (length > 0) {
            while (length--) {
                var R = pxData[idx++];
                var G = pxData[idx++];
                var B = pxData[idx++];
                var A = pxData[idx++];
                pxData[idx - 4] = _max(0, _min((R * RedMultiTerm) + RedAddTerm, 255))|0;
                pxData[idx - 3] = _max(0, _min((G * GreenMultiTerm) + GreenAddTerm, 255))|0;
                pxData[idx - 2] = _max(0, _min((B * BlueMultiTerm) + BlueAddTerm, 255))|0;
                pxData[idx - 1] = _max(0, _min((A * AlphaMultiTerm) + AlphaAddTerm, 255));
            }
        }
        ctx.putImageData(imgData, 0, 0);
        return ctx;
    };

    /**
     * @param m
     * @returns {*[]}
     */
    Shape.prototype.linearGradientXY = function (m)
    {
        var x0 = -16384 * m[0] - 16384 * m[2] + m[4];
        var x1 =  16384 * m[0] - 16384 * m[2] + m[4];
        var x2 = -16384 * m[0] + 16384 * m[2] + m[4];
        var y0 = -16384 * m[1] - 16384 * m[3] + m[5];
        var y1 =  16384 * m[1] - 16384 * m[3] + m[5];
        var y2 = -16384 * m[1] + 16384 * m[3] + m[5];
        var vx2 = x2 - x0;
        var vy2 = y2 - y0;
        var r1 = _sqrt(vx2 * vx2 + vy2 * vy2);
        vx2 /= r1;
        vy2 /= r1;
        var r2 = (x1 - x0) * vx2 + (y1 - y0) * vy2;
        return [x0 + r2 * vx2, y0 + r2 * vy2, x1, y1];
    };

    /**
     * @constructor
     */
    var TextRecord = function ()
    {
        var _this = this;
        _this.color = null;
        _this.matrix = null;
    };

    /**
     * @returns {*}
     */
    TextRecord.prototype.getColor = function ()
    {
        return this.color;
    };

    /**
     * @param color
     */
    TextRecord.prototype.setColor = function (color)
    {
        this.color = color;
    };

    /**
     * @returns {*}
     */
    TextRecord.prototype.getMatrix = function ()
    {
        return this.matrix;
    };

    /**
     * @param matrix
     */
    TextRecord.prototype.setMatrix = function (matrix)
    {
        this.matrix = matrix;
    };

    /**
     * @returns {Array}
     */
    TextRecord.prototype.getData = function ()
    {
        return this.data;
    };

    /**
     * @param data
     */
    TextRecord.prototype.setData = function (data)
    {
        this.data = data;
    };

    /**
     * @constructor
     */
    var StaticText = function ()
    {
        var _this = this;
        DisplayObject.call(_this);

        _this.data = null;
        _this.records = [];
    };

    /**
     * extends
     * @type {DisplayObject}
     */
    StaticText.prototype = Object.create(DisplayObject.prototype);
    StaticText.prototype.constructor = StaticText;

    /**
     * dummy
     */
    StaticText.prototype.initFrame = function () {};
    StaticText.prototype.addActions = function () {};

    /**
     * @returns {{}}
     */
    StaticText.prototype.getBounds = function (matrix)
    {
        var _this = this;
        if (matrix) {
            var bounds = _this.boundsMatrix(_this.bounds, matrix);
            for (var name in bounds) {
                if (!bounds.hasOwnProperty(name)) {
                    continue;
                }
                bounds[name] /= 20;
            }
            return bounds;
        } else {
            return _this.bounds;
        }
    };

    /**
     * @param bounds
     */
    StaticText.prototype.setBounds = function (bounds)
    {
        this.bounds = bounds;
    };

    /**
     * @returns {Array|*}
     */
    StaticText.prototype.getRecords = function ()
    {
        return this.records;
    };

    /**
     * @param record
     */
    StaticText.prototype.addRecord = function (record)
    {
        var records = this.getRecords();
        records[records.length] = record;
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     * @return {*}
     */
    StaticText.prototype.render = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;

        // colorTransform
        var _multiplicationColor = _this.multiplicationColor;
        var rColorTransform = _multiplicationColor(colorTransform, _this.getColorTransform());
        var isVisible = _min(_this.getVisible(), visible);
        var alpha = rColorTransform[3] + (rColorTransform[7] / 255);
        var stageClip = stage.clipMc || stage.isClipDepth;
        if (!stageClip && (!alpha || !isVisible)) {
            return 0;
        }

        // matrix
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());

        // pre render
        var obj = _this.preRender(ctx, m2, rColorTransform, stage, visible);
        var m3 = _multiplicationMatrix(stage.getMatrix(), obj.preMatrix);
        var xScale = _sqrt(m3[0] * m3[0] + m3[1] * m3[1]);
        var yScale = _sqrt(m3[2] * m3[2] + m3[3] * m3[3]);
        xScale = _pow(_SQRT2, _ceil(_log(xScale) / _LN2_2 - _LOG1P));
        yScale = _pow(_SQRT2, _ceil(_log(yScale) / _LN2_2 - _LOG1P));

        // render
        var bounds = _this.getBounds();
        var xMax = bounds.xMax;
        var xMin = bounds.xMin;
        var yMax = bounds.yMax;
        var yMin = bounds.yMin;
        var W = _abs(_ceil((xMax - xMin) * xScale));
        var H = _abs(_ceil((yMax - yMin) * yScale));
        var isClipDepth = _this.isClipDepth || stageClip;
        if (W > 0 && H > 0) {
            var cacheId = _this.getCharacterId() + "_" + _this.getStage().getId();
            var cacheKey = cacheStore.generateKey("Text", cacheId, [xScale, yScale], rColorTransform);
            var cache = cacheStore.getCache(cacheKey);
            var canvas;
            if (!cache && !isClipDepth) {
                if (stage.getWidth() > W && stage.getHeight() > H && cacheStore.size > W * H) {
                    canvas = cacheStore.getCanvas();
                    canvas.width = W;
                    canvas.height = H;
                    cache = canvas.getContext("2d");
                    var cMatrix = [xScale, 0, 0, yScale, -xMin * xScale, -yMin * yScale];
                    cache.setTransform(cMatrix[0],cMatrix[1],cMatrix[2],cMatrix[3],cMatrix[4],cMatrix[5]);
                    cache = _this.executeRender(cache, cMatrix, rColorTransform, false, false);
                    cacheStore.setCache(cacheKey, cache);
                }
            }
            if (cache) {
                canvas = cache.canvas;
                var m4 = _multiplicationMatrix(m3, [1 / xScale, 0, 0, 1 / yScale, xMin, yMin]);
                ctx.setTransform(m4[0],m4[1],m4[2],m4[3],m4[4],m4[5]);
                if (isAndroid4x && !isChrome) {
                    ctx.fillStyle = stage.context.createPattern(cache.canvas, "no-repeat");
                    ctx.fillRect(0, 0, W, H);
                } else {
                    ctx.drawImage(canvas, 0, 0, W, H);
                }
            } else {
                ctx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);
                _this.executeRender(ctx, m3, rColorTransform, isClipDepth, stageClip);
            }

            cacheKey += "_" + m3[4] + "_" + m3[5];
            if (obj.isFilter || obj.isBlend) {
                obj.cacheKey = cacheKey;
                _this.postRender(ctx, matrix, rColorTransform, stage, obj);
            }

            return cacheKey;
        }

        return null;
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param isClipDepth
     * @param stageClip
     * @returns {*}
     */
    StaticText.prototype.executeRender = function (ctx, matrix, colorTransform, isClipDepth, stageClip)
    {
        var _this = this;
        var records = _this.getRecords();
        var length = records.length;
        if (!length) {
            return ctx;
        }

        var _multiplicationMatrix = _this.multiplicationMatrix;
        var _generateColorTransform = _this.generateColorTransform;
        for (var i = 0; i < length; i++) {
            var record = records[i];
            var shapes = record.getData();
            var shapeLength = shapes.length;
            if (!shapeLength) {
                continue;
            }

            var m2 = _multiplicationMatrix(matrix, record.getMatrix());
            ctx.setTransform(m2[0],m2[1],m2[2],m2[3],m2[4],m2[5]);
            var color = record.getColor();
            color = _generateColorTransform(color, colorTransform);
            ctx.fillStyle = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
            for (var idx = 0; idx < shapeLength; idx++) {
                var styleObj = shapes[idx];
                var cmd = styleObj.cmd;
                if (!isClipDepth) {
                    ctx.beginPath();
                    cmd(ctx);
                    ctx.fill();
                } else {
                    cmd(ctx);
                }
            }
        }

        if (isClipDepth && !stageClip) {
            ctx.clip();
        }

        ctx.globalAlpha = 1;
        return ctx;
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    StaticText.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;
        var records = _this.getRecords();
        var length = records.length;
        if (!length) {
            return false;
        }

        var hit = false;
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());
        var m3 = _multiplicationMatrix(stage.getMatrix(), m2);
        for (var i = 0; i < length; i++) {
            var record = records[i];
            var shapes = record.getData();
            var shapeLength = shapes.length;
            if (!shapeLength) {
                continue;
            }

            var m4 = _multiplicationMatrix(m3, record.getMatrix());
            ctx.setTransform(m4[0],m4[1],m4[2],m4[3],m4[4],m4[5]);
            for (var idx = 0; idx < shapeLength; idx++) {
                var styleObj = shapes[idx];
                var cmd = styleObj.cmd;
                ctx.beginPath();
                cmd(ctx);

                hit = ctx.isPointInPath(x, y);
                if (hit) {
                    return hit;
                }
            }
        }

        return hit;
    };

    /**
     * @constructor
     */
    var TextFormat = function ()
    {
        var _this = this;
        _this.align = "left";
        _this.font = "'HiraKakuProN-W3', 'sans-serif'";
        _this.size = 8;
        _this.color = {R: 0, G: 0, B: 0, A: 1};
        _this.bold = 0;
        _this.italic = 0;
        _this.underline = 0;
        _this.bullet = 0;
        _this.kerning = 0;
        _this.blockIndent = 0;
        _this.indent = 0;
        _this.leading = 80;
        _this.leftMargin = 0;
        _this.rightMargin = 0;
        _this.letterSpacing = 0;
        _this.tabStops = [];
        _this.url = null;
        _this.target = null;
    };

    /**
     * @param name
     * @param depth
     * @param width
     * @param height
     * @constructor
     */
    var TextField = function (name, depth, width, height)
    {
        var _this = this;
        InteractiveObject.call(_this);

        if (name) {
            _this.setName(name);
        }

        if (depth) {
            _this.setLevel(depth);
        }

        if (!width) {
            width = 0;
        }
        width *= 20;

        if (!height) {
            height = 0;
        }
        height *= 20;

        _this.fontId = 0;
        _this.bounds = {xMin: 0, xMax: width, yMin: 0, yMax: height};
        _this.input = null;
        _this.inputActive = false;
        _this.span = null;
    };

    /**
     * extends
     * @type {InteractiveObject}
     */
    TextField.prototype = Object.create(InteractiveObject.prototype);
    TextField.prototype.constructor = TextField;

    /**
     * properties
     */
    Object.defineProperties(TextField.prototype,
    {
        text: {
            get: function () {
                return this.variables.text;
            },
            set: function (text) {
                this.variables.text = text;
            }
        },
        htmlText: {
            get: function () {
                return this.variables.text;
            },
            set: function (text) {
                this.variables.text = text;
            }
        },
        size: {
            get: function () {
                return this.variables.size;
            },
            set: function (size) {
                this.variables.size = size;
            }
        },
        font: {
            get: function () {
                return this.variables.font;
            },
            set: function (font) {
                this.variables.font = font;
            }
        },
        type: {
            get: function () {
                return this.variables.type;
            },
            set: function (type) {
                this.variables.type = type;
                if (type === "input") {
                    this.setInputElement();
                }
            }
        },
        multiline: {
            get: function () {
                return this.variables.multiline;
            },
            set: function (multiline) {
                this.variables.multiline = multiline;
                if (multiline) {
                    this.wordWrap = multiline;
                }
                if (this.type === "input") {
                    this.setInputElement();
                }
            }
        },
        wordWrap: {
            get: function () {
                return this.variables.wordWrap;
            },
            set: function (wordWrap) {
                this.variables.wordWrap = wordWrap;
                if (this.type === "input") {
                    this.setInputElement();
                }
            }
        },
        border: {
            get: function () {
                return this.variables.border;
            },
            set: function (border) {
                this.variables.border = border;
            }
        },
        borderColor: {
            get: function () {
                return this.variables.borderColor;
            },
            set: function (color) {
                if (typeof color === "string") {
                    color = this.colorStringToInt(color);
                }
                color = this.intToRGBA(color);
                this.variables.borderColor = color;
            }
        },
        background: {
            get: function () {
                return this.variables.background;
            },
            set: function (background) {
                this.variables.background = background;
            }
        },
        backgroundColor: {
            get: function () {
                return this.variables.backgroundColor;
            },
            set: function (color) {
                if (typeof color === "string") {
                    color = this.colorStringToInt(color);
                }
                color = this.intToRGBA(color);
                this.variables.backgroundColor = color;
            }
        },
        textColor: {
            get: function () {
                return this.variables.textColor;
            },
            set: function (color) {
                if (typeof color === "string") {
                    color = this.colorStringToInt(color);
                }
                color = this.intToRGBA(color);
                this.variables.textColor = color;
            }
        },
        align: {
            get: function () {
                return this.variables.align;
            },
            set: function (align) {
                this.variables.align = align;
            }
        },
        autoSize: {
            get: function () {
                return this.variables.autoSize;
            },
            set: function (autoSize) {
                this.variables.autoSize = autoSize;
            }
        },
        onChanged: {
            get: function () {
                return this.variables.onChanged;
            },
            set: function (onChanged) {
                this.variables.onChanged = onChanged;
            }
        }
    });

    /**
     * @param int
     * @param alpha
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    TextField.prototype.intToRGBA = function (int, alpha)
    {
        alpha = alpha || 100;
        return {
            R: (int & 0xff0000) >> 16,
            G: (int & 0x00ff00) >> 8,
            B: (int & 0x0000ff),
            A: (alpha / 100)
        };
    };

    /**
     * @param str
     * @returns {string}
     */
    TextField.prototype.colorStringToInt = function (str)
    {
        var canvas = cacheStore.getCanvas();
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = str;
        var color = "0x" + ctx.fillStyle.substr(1);
        cacheStore.destroy(ctx);
        return color;
    };

    /**
     * setInitParams
     */
    TextField.prototype.setInitParams = function ()
    {
        var _this = this;
        var obj = {};
        obj.antiAliasType = null;
        obj.autoSize = "none";
        obj.background = 0;
        obj.backgroundColor = {R: 255, G: 255, B: 255, A: 1};
        obj.border = 0;
        obj.borderColor = {R: 0, G: 0, B: 0, A: 1};
        obj.condenseWhite = 0;
        obj.html = 0;
        obj.password = 0;
        obj.embedFonts = 0;
        obj.gridFitType = "none";
        obj.maxChars = null;
        obj.mouseWheelEnabled = 0;
        obj.multiline = 0;
        obj.selectable = 0;
        obj.sharpness = 0;
        obj.textColor = 0;
        obj.thickness = 0;
        obj.type = "dynamic";
        obj.wordWrap = 0;
        obj.text = "";
        for (var key in obj) {
            if (!obj.hasOwnProperty(key)) {
                continue;
            }
            _this.setProperty(key, obj[key]);
        }
        _this.setTextFormat(new TextFormat());
    };

    /**
     * @returns {string}
     */
    TextField.prototype.getTagName = function ()
    {
        return "__swf2js_input_element_" + this.instanceId;
    };

    /**
     * @param format
     */
    TextField.prototype.setTextFormat = function (format)
    {
        var _this = this;
        for (var name in format) {
            if (!format.hasOwnProperty(name)) {
                continue;
            }
            _this.setProperty(name, format[name]);
        }
    };

    /**
     * @returns {*}
     */
    TextField.prototype.getBounds = function (matrix)
    {
        var _this = this;
        if (matrix) {
            var bounds = _this.boundsMatrix(_this.bounds, matrix);
            for (var name in bounds) {
                if (!bounds.hasOwnProperty(name)) {
                    continue;
                }
                bounds[name] /= 20;
            }
            return bounds;
        } else {
            return _this.bounds;
        }
    };

    /**
     * @param bounds
     */
    TextField.prototype.setBounds = function (bounds)
    {
        this.bounds = bounds;
    };

    /**
     * InputElemen
     */
    TextField.prototype.setInputElement = function ()
    {
        var _this = this;
        var variables = _this.variables;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getParentStage();
        var element = _document.createElement("textarea");
        var multiline = variables.multiline;
        var align = variables.align;
        var text = _this.initialText;
        if (!text) {
            text = variables.text;
        }

        element.onkeypress = null;
        if (!multiline) {
            element.onkeypress = function (e)
            {
                if (e.keyCode === 13) {
                    return false;
                }
            };
        }

        element.style.position = "absolute";
        element.style.webkitBorderRadius = "0px";
        element.style.padding = "1px";
        element.style.margin = "0px";
        element.style.webkitAppearance = "none";
        element.style.resize = "none";
        element.style.border = "none";
        element.style.overflow = "hidden";
        element.style.backgroundColor = "transparent";
        element.style.zIndex = 0x7fffffff;
        element.style.textAlign = align;

        element.value = text;
        if (typeof text !== "string") {
            var str = "";
            var length = text.length;
            for (var i = 0; i < length; i++) {
                var txt = text[i];
                str += txt.innerText;
                if ((i + 1) !== length) {
                    str += "\n";
                }
            }
            element.value = str;
        }

        element.id = _this.getTagName();
        var onBlur = function (stage, textField, el)
        {
            return function ()
            {
                textField.setProperty("text", el.value);
                textField.inputActive = false;
                var div = _document.getElementById(stage.getName());
                if (div) {
                    var element = _document.getElementById(textField.getTagName());
                    if (element) {
                        try {
                            div.removeChild(element);
                        } catch (e) {}
                    }
                }
            };
        };

        element.onblur = onBlur(stage, _this, element);
        _this.input = element;
    };

    /**
     * @param matrix
     * @param stage
     * @param visible
     * @param mask
     */
    TextField.prototype.setHitRange = function (matrix, stage, visible, mask)
    {
        var _this = this;
        var type = _this.variables.type;
        var isVisible = _min(_this.getVisible(), visible);
        if (type === "input" && isVisible) {
            var buttonHits = stage.buttonHits;
            var m2 = _this.multiplicationMatrix(matrix, _this.getMatrix());
            var bounds = _this.getBounds(m2);
            buttonHits[buttonHits.length] = {
                xMax: bounds.xMax,
                xMin: bounds.xMin,
                yMax: bounds.yMax,
                yMin: bounds.yMin,
                parent: _this
            };
        }
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     */
    TextField.prototype.render = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;
        stage.doneTags.unshift(_this);

        // colorTransform
        var _multiplicationColor = _this.multiplicationColor;
        var rColorTransform = _multiplicationColor(colorTransform, _this.getColorTransform());
        var isVisible = _min(_this.getVisible(), visible);
        var stageClip = stage.clipMc || stage.isClipDepth;
        var alpha = rColorTransform[3] + (rColorTransform[7] / 255);
        if (!stageClip && (!alpha || !isVisible)) {
            return 0;
        }

        // matrix
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());

        // pre render
        var obj = _this.preRender(ctx, m2, rColorTransform, stage, visible);
        var preCtx = obj.preCtx;
        var preMatrix = obj.preMatrix;
        var m3 = _multiplicationMatrix(stage.getMatrix(), preMatrix);
        preCtx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);

        var textCacheKey = ["TextField"];
        var variables = _this.variables;
        var text = variables.text;
        var variable = variables.variable;
        if (variable) {
            var parent = _this.getParent();
            text = parent.getProperty(variable);
            if (text === undefined) {
                text = variables.text;
            }
        }

        if (typeof text === "number") {
            text += "";
        }

        var html = variables.html;
        if (html && typeof text === "string") {
            if (text.indexOf("<sbr />") !== -1) {
                text = text.replace(new RegExp("<sbr />", "gi"), "\n");
            }
            if (text.indexOf("<b>") !== -1) {
                text = text.replace(new RegExp("<b>", "gi"), "");
                text = text.replace(new RegExp("</b>", "gi"), "");
            }

            var span = _document.createElement("span");
            span.innerHTML = text;

            var tags = span.getElementsByTagName("p");
            var domLength = tags.length;
            if (domLength) {
                var tagData = [];
                for (var d = 0; d < domLength; d++) {
                    tagData[d] = tags[d];
                }
                text = tagData;
            } else {
                text = span.innerText;
            }
        }
        preCtx.textBaseline = "top";
        if (text === undefined) {
            text = "";
        }

        var bounds = _this.getBounds();
        var xMax = bounds.xMax;
        var xMin = bounds.xMin;
        var yMax = bounds.yMax;
        var yMin = bounds.yMin;
        var W = _abs(_ceil(xMax - xMin));
        var H = _abs(_ceil(yMax - yMin));

        // auto size
        var scale = stage.getScale();
        var autoSize = variables.autoSize;
        var wordWrap = variables.wordWrap;
        var splitData = (typeof text === "string") ? text.split("\n") : text;
        var length = splitData.length;
        var i, txtObj, measureText;
        var txtTotalWidth = 0;
        var txtTotalHeight = 0;
        var isAutoSize = false;
        var autoMode = (typeof autoSize === "string") ? autoSize.toLowerCase() : autoSize;
        switch (autoMode) {
            default:
            case "none":
            case false:
            case 0:
                txtTotalWidth = W;
                txtTotalHeight = H;
                break;
            case true:
            case 1:
            case "left":
            case "center":
            case "right":
                isAutoSize = true;
                break;
        }

        var fontData = _this.getStage().getCharacter(_this.fontId);
        if (isAutoSize) {
            if (variables.embedFonts) {
                var CodeTable = fontData.CodeTable;
                var FontAdvanceTable = fontData.FontAdvanceTable;
                var fontScale = _this.fontScale;
                txtTotalWidth = 0;
                txtTotalHeight = (fontData.FontAscent * fontScale) + variables.leading;
                for (i = 0; i < length; i++) {
                    txtObj = splitData[i];
                    if (typeof txtObj !== "string") {
                        var firstChild = txtObj.firstChild;
                        txtTotalWidth = _this.getDomWidth(firstChild, CodeTable, FontAdvanceTable);
                    } else {
                        var txtLength = txtObj.length;
                        for (var idx = 0; idx < txtLength; idx++) {
                            var index = CodeTable.indexOf(txtObj[idx].charCodeAt(0));
                            if (index === -1) {
                                continue;
                            }
                            txtTotalWidth += (FontAdvanceTable[index] * fontScale);
                        }
                    }
                }
            } else {
                var addH = (variables.size * 20) + variables.leading;
                txtTotalHeight = (bounds.yMin + 80);
                if (wordWrap) {
                    txtTotalWidth = W;
                    for (i = 0; i < length; i++) {
                        txtObj = splitData[i];
                        if (typeof txtObj === "string") {
                            measureText = preCtx.measureText(txtObj);
                            var checkW = _ceil(measureText.width * 20);
                            if (checkW > W) {
                                txtTotalHeight += _ceil(_ceil(checkW / W) * addH);
                            }
                        }
                    }
                } else {
                    for (i = 0; i < length; i++) {
                        txtObj = splitData[i];
                        if (typeof txtObj === "string") {
                            measureText = preCtx.measureText(txtObj);
                            txtTotalWidth = _max(txtTotalWidth, _ceil(measureText.width * 20));
                            txtTotalHeight += addH;
                        }
                    }
                }
                txtTotalWidth += 80;
            }
        }

        var offsetX = 40;
        switch (autoMode) {
            case "center":
                offsetX = _ceil((_max(txtTotalWidth, W) - _min(txtTotalWidth, W)) / 2);
                break;
            case "right":
                offsetX = _ceil(_max(txtTotalWidth, W) - _min(txtTotalWidth, W));
                break;
        }

        W = txtTotalWidth;
        H = txtTotalHeight;

        if (W > 0 && H > 0) {
            var isClipDepth = _this.isClipDepth || stageClip;
            var color;
            var rx = xMin;
            var ry = yMin;
            var m = _this._matrix;
            if (m) {
                rx = -xMin;
                ry = -yMin;
                var m4 = _multiplicationMatrix(preMatrix, [1, 0, 0, 1, xMin, yMin]);
                m3 = _multiplicationMatrix(stage.getMatrix(), m4);
                preCtx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);
            }

            // border
            var border = variables.border;
            if (border && !isClipDepth) {
                preCtx.beginPath();
                preCtx.rect(rx - offsetX, ry, W, H);
                color = _this.generateColorTransform(variables.borderColor, rColorTransform);
                textCacheKey[textCacheKey.length] = color;
                preCtx.strokeStyle = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
                preCtx.lineWidth = _min(20, 1 / _min(m3[0], m3[3]));
                preCtx.globalAlpha = 1;
                preCtx.fillStyle = "rgba(0,0,0,0)";
                if (variables.background) {
                    color = _this.generateColorTransform(variables.backgroundColor, rColorTransform);
                    textCacheKey[textCacheKey.length] = color;
                    preCtx.fillStyle = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
                }
                preCtx.fill();
                preCtx.stroke();
            }

            var textColor = variables.textColor;
            var objRGBA = textColor;
            if (typeof  textColor === "number") {
                objRGBA = _this.intToRGBA(textColor, 100);
            }

            color = _this.generateColorTransform(objRGBA, rColorTransform);
            var fillStyle = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
            textCacheKey[textCacheKey.length] = fillStyle;
            preCtx.fillStyle = fillStyle;

            // font type
            var fontType = "";
            if (variables.italic) {
                fontType += "italic ";
            }
            if (variables.bold) {
                fontType += "bold ";
            }

            var fontStyle = fontType + variables.size + "px " + variables.font;
            textCacheKey[textCacheKey.length] = fontStyle;
            preCtx.font = fontStyle;

            if (_this.input !== null) {
                var input = _this.input;
                var fontSize = _ceil(variables.size * scale * _min(preMatrix[0], preMatrix[3]));
                input.style.font = fontType + fontSize + "px " + variables.font;
                input.style.color = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
                var as = variables.onChanged;
                if (as && !input.onchange) {
                    var onChanged = function (stage, origin, clip, el)
                    {
                        return function ()
                        {
                            if (clip.active) {
                                clip.setProperty("text", el.value);
                                origin.apply(clip, arguments);
                                stage.executeAction();
                            }
                        };
                    };
                    input.onchange = onChanged(stage, as, _this, input);
                }
            }

            if (text && !isClipDepth) {
                preCtx.save();
                preCtx.beginPath();
                preCtx.rect(rx - offsetX, ry, W, (H-40));
                preCtx.clip();

                if (_this.inputActive === false) {
                    if (variables.embedFonts) {
                        _this.renderOutLine(preCtx, fontData, splitData, m3, rx - offsetX, W, fillStyle);
                    } else {
                        _this.renderText(preCtx, splitData, m3, fontType, fillStyle);
                    }
                }

                preCtx.restore();
                preCtx.globalAlpha = 1;
            }

            textCacheKey[textCacheKey.length] = text;
            var cacheKey = cacheStore.generateKey(
                textCacheKey.join("_"), _this.getCharacterId(), m3, rColorTransform);
            obj.cacheKey = cacheKey;
            if (obj.isFilter || obj.isBlend) {
                _this.postRender(ctx, matrix, rColorTransform, stage, obj);
            }
            return cacheKey;
        }

        return null;
    };

    /**
     * @param ctx
     * @param fontData
     * @param splitData
     * @param matrix
     * @param offset
     * @param width
     * @param fillStyle
     */
    TextField.prototype.renderOutLine = function (ctx, fontData, splitData, matrix, offset, width, fillStyle)
    {
        var _this = this;
        var variables = _this.variables;
        var fontScale = _this.fontScale;
        var leading = (fontData.FontAscent + fontData.FontDescent) * fontScale;
        var rightMargin = variables.rightMargin * fontScale;
        var leftMargin = variables.leftMargin * fontScale;
        var indent = variables.indent * fontScale;
        var align = variables.align;
        var txt = "";
        var CodeTable = fontData.CodeTable;
        var GlyphShapeTable = fontData.GlyphShapeTable;
        var FontAdvanceTable = fontData.FontAdvanceTable;
        var YOffset = (fontData.FontAscent * fontScale);
        var cacheYOffset = YOffset;
        var wordWrap = variables.wordWrap;
        var multiline = variables.multiline;
        var bounds = _this.getBounds();
        var areaWidth = (_ceil((bounds.xMax) - (bounds.xMin)) - leftMargin - rightMargin);
        var idx;
        var index;
        var length = splitData.length;
        var _multiplicationMatrix = _this.multiplicationMatrix;
        for (var i = 0; i < length; i++) {
            var XOffset = offset;
            var textWidth = 0;
            var txtLength = 0;
            var obj = splitData[i];
            var firstChild;
            if (typeof obj !== "string") {
                firstChild = obj.firstChild;
                if (!firstChild) {
                    continue;
                }
                textWidth = _this.getDomWidth(firstChild, CodeTable, FontAdvanceTable);
                txt = obj.innerText;
                align = variables.align;
                if (obj.align) {
                    align = obj.align;
                }
            } else {
                txt = obj;
                txtLength = txt.length;
                for (idx = 0; idx < txtLength; idx++) {
                    index = CodeTable.indexOf(txt[idx].charCodeAt(0));
                    if (index === -1) {
                        continue;
                    }
                    textWidth += (FontAdvanceTable[index] * fontScale);
                }
            }

            if (align === "right") {
                XOffset += width - rightMargin - textWidth - 40;
            } else if (align === "center") {
                XOffset += indent + leftMargin + 40 + ((width - indent - leftMargin - rightMargin - textWidth) / 2);
            } else {
                XOffset += indent + leftMargin + 40;
            }

            var cacheXOffset = XOffset;
            var wordWidth = 0;
            if (typeof obj !== "string") {
                var gridData = {
                    XOffset: XOffset,
                    YOffset: YOffset,
                    cacheXOffset: cacheXOffset,
                    cacheYOffset: cacheYOffset,
                    wordWidth: wordWidth,
                    addXOffset: 0,
                    size: firstChild.size,
                    areaWidth: areaWidth,
                    matrix: matrix
                };

                _this.renderDomOutLine(
                    ctx, firstChild, gridData, fillStyle,
                    CodeTable, FontAdvanceTable, GlyphShapeTable
                );
            } else {
                for (idx = 0; idx < txtLength; idx++) {
                    index = CodeTable.indexOf(txt[idx].charCodeAt(0));
                    if (index === -1) {
                        continue;
                    }

                    var addXOffset = FontAdvanceTable[index] * fontScale;
                    if (wordWrap && multiline) {
                        if (wordWidth + addXOffset > areaWidth) {
                            XOffset = cacheXOffset;
                            YOffset += cacheYOffset;
                            wordWidth = 0;
                        }
                    }

                    var m2 = _multiplicationMatrix(matrix, [fontScale, 0, 0, fontScale, XOffset, YOffset]);
                    ctx.setTransform(m2[0],m2[1],m2[2],m2[3],m2[4],m2[5]);
                    _this.renderGlyph(GlyphShapeTable[index], ctx);
                    XOffset += addXOffset;
                    wordWidth += addXOffset;
                }
            }

            YOffset += leading;
        }
    };

    /**
     * @param ctx
     * @param child
     * @param gridData
     * @param fillStyle
     * @param CodeTable
     * @param FontAdvanceTable
     * @param GlyphShapeTable
     */
    TextField.prototype.renderDomOutLine = function (
        ctx, child, gridData, fillStyle,
        CodeTable, FontAdvanceTable, GlyphShapeTable
    ) {
        var _this = this;
        var variables = _this.variables;
        var wordWrap = variables.wordWrap;
        var multiline = variables.multiline;
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var stage = _this.getStage();
        var fonts = stage.fonts;
        var face = child.face;
        var fontData = fonts[face];
        var codeTable = CodeTable;
        var faTable = FontAdvanceTable;
        var shapeTable = GlyphShapeTable;
        var color = fillStyle;
        if (fontData) {
            codeTable = fontData.CodeTable;
            faTable = fontData.FontAdvanceTable;
            shapeTable = fontData.GlyphShapeTable;
        }

        if (child.color) {
            color = child.color;
        }

        if (child.size) {
            gridData.size = child.size;
        }

        var childNodes = child.childNodes;
        var length = childNodes.length;
        for (var i = 0; i < length; i++) {
            var node = childNodes[i];
            if (node instanceof HTMLFontElement) {
                _this.renderDomOutLine(
                    ctx, node, gridData, color,
                    codeTable, faTable, shapeTable
                );
            } else {
                var size = gridData.size;
                var fontScale = size / 1024;
                var sTable;
                var values = node.nodeValue;
                if (!values) {
                    continue;
                }
                var vLength = values.length;
                for (var idx = 0; idx < vLength; idx++) {
                    var txt = values[idx];
                    var index = codeTable.indexOf(txt.charCodeAt(0));
                    if (index === -1) {
                        index = CodeTable.indexOf(txt.charCodeAt(0));
                        if (index === -1) {
                            continue;
                        }
                        color = fillStyle;
                        gridData.addXOffset = FontAdvanceTable[index] * fontScale;
                        sTable = GlyphShapeTable;
                    } else  {
                        gridData.addXOffset = faTable[index] * fontScale;
                        sTable = shapeTable;
                    }

                    if (wordWrap && multiline) {
                        if (gridData.wordWidth + gridData.addXOffset > gridData.areaWidth) {
                            gridData.XOffset = gridData.cacheXOffset;
                            gridData.YOffset += gridData.cacheYOffset;
                            gridData.wordWidth = 0;
                        }
                    }

                    var m2 = [fontScale, 0, 0, fontScale, gridData.XOffset, gridData.YOffset];
                    var m3 = _multiplicationMatrix(gridData.matrix, m2);
                    ctx.setTransform(m3[0], m3[1], m3[2], m3[3], m3[4], m3[5]);
                    ctx.fillStyle = color;
                    _this.renderGlyph(sTable[index], ctx);
                    gridData.XOffset += gridData.addXOffset;
                    gridData.wordWidth += gridData.addXOffset;
                }
            }
        }
    };

    /**
     * @param child
     * @param CodeTable
     * @param FontAdvanceTable
     * @returns {number}
     */
    TextField.prototype.getDomWidth = function (child, CodeTable, FontAdvanceTable)
    {
        var _this = this;
        var fontScale = _this.fontScale;
        var stage = _this.getStage();
        var fonts = stage.fonts;
        var width = 0;
        var face = child.face;
        var fontData = fonts[face];
        var codeTable = CodeTable;
        var faTable = FontAdvanceTable;
        if (fontData) {
            codeTable = fontData.CodeTable;
            faTable = fontData.FontAdvanceTable;
        }

        var childNodes = child.childNodes;
        var length = childNodes.length;
        for (var i = 0; i < length; i++) {
            var node = childNodes[i];
            if (node instanceof HTMLFontElement) {
                width += _this.getDomWidth(node, codeTable, faTable);
            } else {
                var values = node.nodeValue;
                if (!values) {
                    continue;
                }
                var vLength = values.length;
                for (var idx = 0; idx < vLength; idx++) {
                    var txt = values[idx];
                    var index = codeTable.indexOf(txt.charCodeAt(0));
                    if (index === -1) {
                        index = CodeTable.indexOf(txt.charCodeAt(0));
                        if (index === -1) {
                            continue;
                        }
                        width += (FontAdvanceTable[index] * fontScale);
                    } else  {
                        width += (faTable[index] * fontScale);
                    }
                }
            }
        }
        return width;
    };

    /**
     * @param records
     * @param ctx
     */
    TextField.prototype.renderGlyph = function (records, ctx)
    {
        if (!records.data) {
            records.data = vtc.convert(records);
        }

        var shapes = records.data;
        var shapeLength = shapes.length;
        for (var idx = 0; idx < shapeLength; idx++) {
            var styleObj = shapes[idx];
            var cmd = styleObj.cmd;
            ctx.beginPath();
            cmd(ctx);
            ctx.fill();
        }
    };

    /**
     * @param ctx
     * @param splitData
     * @param matrix
     * @param fontType
     * @param fillStyle
     */
    TextField.prototype.renderText = function (ctx, splitData, matrix, fontType, fillStyle, _x)
    {
        var _this = this;
        var variables = _this.variables;
        var wordWrap = variables.wordWrap;
        var multiline = variables.multiline;
        var leading = variables.leading / 20;
        var rightMargin = variables.rightMargin / 20;
        var leftMargin = variables.leftMargin / 20;
        var indent = variables.indent / 20;
        var align = variables.align;
        var bounds = _this.getBounds();
        var xMax = bounds.xMax / 20;
        var xMin = bounds.xMin / 20;
        var width = _ceil(xMax - xMin);

        var m2 = [matrix[0] * 20, matrix[1] * 20, matrix[2] * 20, matrix[3] * 20, matrix[4], matrix[5]];
        var xScale = _sqrt(m2[0] * m2[0] + m2[1] * m2[1]);
        var yScale = _sqrt(m2[2] * m2[2] + m2[3] * m2[3]);
        var scale = _max(xScale, yScale);
        ctx.setTransform(scale,m2[1],m2[2],scale,m2[4],m2[5]);

        var dx = xMin;
        var dy = (bounds.yMin / 20) + 2;
        if (align === "right") {
            ctx.textAlign = "end";
            dx += width - rightMargin - 2;
        } else if (align === "center") {
            ctx.textAlign = "center";
            dx += leftMargin + indent + ((width - leftMargin - indent - rightMargin) / 2);
        } else {
            dx += 2 + leftMargin + indent;
        }

        bounds = _this.getBounds(m2);
        var areaWidth = (bounds.xMax - bounds.xMin) - ((leftMargin - rightMargin) * xScale);
        areaWidth /= scale;

        var size = variables.size;
        var length = splitData.length;
        for (var i = 0; i < length; i++) {
            var txt = "";
            var obj = splitData[i];
            if (typeof obj !== "string") {
                txt = obj.innerText;
            } else {
                txt = obj;
            }

            if (txt === "") {
                dy += leading + size;
                continue;
            }

            var measureText = ctx.measureText(txt);
            var txtTotalWidth = measureText.width;
            if (typeof obj === "string") {
                if (wordWrap || multiline) {
                    if (txtTotalWidth > areaWidth) {
                        var txtLength = txt.length;
                        var joinTxt = "";
                        var joinWidth = 2 * scale;
                        for (var t = 0; t < txtLength; t++) {
                            var txtOne = txt[t];
                            var textOne = ctx.measureText(txtOne);
                            joinWidth += textOne.width;
                            joinTxt += txtOne;
                            var nextOne = txt[t+1];
                            if (nextOne) {
                                textOne = ctx.measureText(nextOne);
                                joinWidth += textOne.width;
                            }
                            if (joinWidth > areaWidth || (t + 1) === txtLength) {
                                ctx.fillText(joinTxt, dx, dy, _ceil(joinWidth));
                                joinWidth = 2 * scale;
                                joinTxt = "";
                                dy += leading + size;
                            } else if (nextOne) {
                                joinWidth -= textOne.width;
                            }
                        }
                    } else {
                        ctx.fillText(txt, dx, dy, txtTotalWidth);
                        dy += leading + size;
                    }
                } else {
                    ctx.fillText(txt, dx, dy, txtTotalWidth);
                    dy += leading + size;
                }
            } else {
                var firstChild = obj.firstChild;
                var gridData = {
                    startDx: dx,
                    dx: dx,
                    cloneDy: dy,
                    dy: dy,
                    color: fillStyle,
                    fontType: fontType,
                    fillStyle: fillStyle,
                    size: size,
                    scale: scale,
                    originSize: size,
                    txtTotalWidth: txtTotalWidth,
                    areaWidth: areaWidth,
                    joinWidth: 0,
                    joinTxt: "",
                    offset: 0,
                    offsetArray: []
                };

                if (gridData.offsetArray.length === 0) {
                    _this.offsetDomText(ctx, firstChild, gridData);
                }

                // reset
                gridData.dx = dx;
                gridData.dy = dy;
                gridData.cloneDy = dy;
                gridData.size = size;
                gridData.joinWidth = 0;
                gridData.joinTxt = "";
                gridData.offset = 0;
                if (gridData.offsetArray.length > 0) {
                    var offsetY = gridData.offsetArray[0];
                    if (offsetY) {
                        gridData.dy += offsetY;
                        gridData.cloneDy = gridData.dy;
                    }
                }

                _this.renderDomText(ctx, firstChild, gridData);

                dy = gridData.dy;
            }
        }
    };

    /**
     * @param ctx
     * @param child
     * @param gridData
     */
    TextField.prototype.offsetDomText = function(ctx, child, gridData)
    {
        var _this = this;
        var variables = _this.variables;
        var wordWrap = variables.wordWrap;
        var multiline = variables.multiline;
        var leading = variables.leading / 20;
        if (child.face) {
            gridData.face = child.face;
        }

        if (child.size) {
            var size = child.size|0;
            var changeSize = gridData.originSize - size;
            if (changeSize) {
                gridData.dy += changeSize;
                if (changeSize > 0) {
                    gridData.dy -= 4;
                } else {
                    var offsetArray = gridData.offsetArray;
                    var offset = gridData.offset;
                    var offsetSize = offsetArray[offset];
                    if (offsetSize) {
                        offsetArray[offset] = _max(offsetSize, ~changeSize);
                    } else {
                        offsetArray[offset] = ~changeSize;
                    }
                    gridData.dy += 6;
                }
            }
            gridData.size = size;
        }

        var childNodes = child.childNodes;
        var length = childNodes.length;
        for (var i = 0; i < length; i++) {
            var node = childNodes[i];
            if (node instanceof HTMLFontElement) {
                _this.offsetDomText(ctx, node, gridData);
            } else {
                var txt = node.nodeValue;
                if (wordWrap && multiline) {
                    if (gridData.txtTotalWidth > gridData.areaWidth) {
                        var txtLength = txt.length;
                        for (var t = 0; t < txtLength; t++) {
                            var textOne = ctx.measureText(txt[t]);
                            gridData.joinWidth += textOne.width;
                            gridData.joinTxt += txt[t];
                            var isOver = (gridData.joinWidth > gridData.areaWidth);
                            if (isOver || (t + 1) === txtLength) {
                                if ((gridData.dx + textOne.width) > gridData.areaWidth) {
                                    gridData.dx = gridData.startDx;
                                    gridData.dy += leading + gridData.size;
                                    gridData.cloneDy = gridData.dy;
                                    gridData.joinWidth = 2 * gridData.scale;
                                    isOver = false;
                                    gridData.offset++;
                                }

                                gridData.joinTxt = "";
                                if (isOver) {
                                    gridData.dx = gridData.startDx;
                                    gridData.joinWidth = 22 * gridData.scale;
                                    gridData.dy += leading + gridData.size;
                                    gridData.cloneDy = gridData.dy;
                                    gridData.offset++;
                                }
                            }
                        }
                    } else {
                        gridData.dy += leading + gridData.size;
                        gridData.cloneDy = gridData.dy;
                        gridData.offset++;
                    }
                } else {
                    gridData.dy += leading + gridData.size;
                    gridData.cloneDy = gridData.dy;
                    gridData.offset++;
                }

                var mText = ctx.measureText(txt);
                gridData.dx += mText.width;
                gridData.size = gridData.originSize;
                gridData.dy = gridData.cloneDy;
            }
        }
    };

    /**
     * @param ctx
     * @param child
     * @param gridData
     */
    TextField.prototype.renderDomText = function(ctx, child, gridData)
    {
        var _this = this;
        var variables = _this.variables;
        var wordWrap = variables.wordWrap;
        var multiline = variables.multiline;
        var leading = variables.leading / 20;

        if (child.face) {
            gridData.face = child.face;
        }

        if (child.color) {
            gridData.color = child.color;
        }

        if (child.size) {
            var size = child.size|0;
            var changeSize = gridData.originSize - size;
            if (changeSize) {
                gridData.dy += changeSize;
                if (changeSize > 0) {
                    gridData.dy -= 4;
                } else {
                    gridData.dy += 8;
                }
            }
            gridData.size = size;
        }

        var offsetY;
        var childNodes = child.childNodes;
        var length = childNodes.length;
        for (var i = 0; i < length; i++) {
            var node = childNodes[i];
            if (node instanceof HTMLFontElement) {
                _this.renderDomText(ctx, node, gridData);
            } else {
                ctx.fillStyle = gridData.color;
                ctx.font = gridData.fontType + gridData.size + "px " + gridData.face;

                var text = node.nodeValue;
                var splits = text.split("\n");
                var sLen= splits.length;
                for (var idx = 0; idx < sLen; idx++) {
                    gridData.dx = gridData.startDx;
                    var txt = splits[idx];

                    if (wordWrap && multiline) {
                        if (gridData.txtTotalWidth > gridData.areaWidth) {
                            var txtLength = txt.length;
                            for (var t = 0; t < txtLength; t++) {
                                var textOne = ctx.measureText(txt[t]);
                                gridData.joinWidth += textOne.width;
                                gridData.joinTxt += txt[t];
                                var isOver = (gridData.joinWidth > gridData.areaWidth);
                                if (isOver || (t + 1) === txtLength) {
                                    if ((gridData.dx + textOne.width) > gridData.areaWidth) {
                                        isOver = 0;
                                        gridData.joinWidth = gridData.size;
                                        gridData.dx = gridData.startDx;
                                        gridData.offset++;
                                        gridData.dy += leading + gridData.size;
                                        if (gridData.offsetArray.length > 0) {
                                            offsetY = gridData.offsetArray[gridData.offset];
                                            if (offsetY) {
                                                gridData.dy += offsetY;
                                            }
                                        }
                                        gridData.cloneDy = gridData.dy;
                                    }

                                    ctx.fillText(gridData.joinTxt, gridData.dx, gridData.dy, _ceil(gridData.joinWidth));
                                    gridData.joinTxt = "";
                                    if (isOver) {
                                        gridData.dx = gridData.startDx;
                                        gridData.joinWidth = gridData.size;
                                        gridData.offset++;
                                        gridData.dy += leading + gridData.size;
                                        if (gridData.offsetArray.length > 0) {
                                            offsetY = gridData.offsetArray[gridData.offset];
                                            if (offsetY) {
                                                gridData.dy += offsetY;
                                            }
                                        }
                                        gridData.cloneDy = gridData.dy;
                                    }
                                }
                            }
                        } else {
                            ctx.fillText(txt, gridData.dx, gridData.dy, _ceil(gridData.txtTotalWidth));
                            gridData.offset++;
                            gridData.dy += leading + gridData.size;
                            if (gridData.offsetArray.length > 0) {
                                offsetY = gridData.offsetArray[gridData.offset];
                                if (offsetY) {
                                    gridData.dy += offsetY;
                                }
                            }
                            gridData.cloneDy = gridData.dy;
                        }
                    } else {
                        ctx.fillText(txt, gridData.dx, gridData.dy, _ceil(gridData.txtTotalWidth));
                        gridData.offset++;
                        gridData.dy += leading + gridData.size;
                        if (gridData.offsetArray.length > 0) {
                            offsetY = gridData.offsetArray[gridData.offset];
                            if (offsetY) {
                                gridData.dy += offsetY;
                            }
                        }
                        gridData.cloneDy = gridData.dy;
                    }

                    var mText = ctx.measureText(txt);
                    gridData.dx += mText.width;
                    gridData.color = gridData.fillStyle;
                    gridData.size = gridData.originSize;
                    gridData.dy = gridData.cloneDy;
                }
            }
        }
    };

    /**
     * @param stage
     * @param clipEvent
     */
    TextField.prototype.putFrame = function (stage, clipEvent)
    {
        var _this = this;
        _this.active = true;
        if (_this.inputActive === false) {
            _this.dispatchEvent(clipEvent, stage);
        }
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    TextField.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;
        var bounds = _this.getBounds();
        var xMax = bounds.xMax;
        var xMin = bounds.xMin;
        var yMax = bounds.yMax;
        var yMin = bounds.yMin;
        var width = _ceil(xMax - xMin);
        var height = _ceil(yMax - yMin);

        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());
        var m3 = _multiplicationMatrix(stage.getMatrix(), m2);
        ctx.setTransform(m3[0],m3[1],m3[2],m3[3],m3[4],m3[5]);

        var m = _this._matrix;
        if (m) {
            xMin = -xMin;
            yMin = -yMin;
            var m4 = _multiplicationMatrix(m2, [1, 0, 0, 1, xMin, yMin]);
            var m5 = _multiplicationMatrix(stage.getMatrix(), m4);
            ctx.setTransform(m5[0],m5[1],m5[2],m5[3],m5[4],m5[5]);
        }

        ctx.beginPath();
        ctx.rect(xMin, yMin, width, height);
        return ctx.isPointInPath(x, y);
    };

    // dummy
    TextField.prototype.initFrame = function () {};
    TextField.prototype.addActions = function () {};
    TextField.prototype.getTags = function () { return undefined; };

    /**
     * @constructor
     */
    var SimpleButton = function ()
    {
        var _this = this;
        InteractiveObject.call(_this);

        _this.actions = [];
        _this._downState = new Sprite();
        _this._hitState = new Sprite();
        _this._overState = new Sprite();
        _this._upState = new Sprite();
    };

    /**
     * extends
     * @type {InteractiveObject}
     */
    SimpleButton.prototype = Object.create(InteractiveObject.prototype);
    SimpleButton.prototype.constructor = SimpleButton;

    /**
     * properties
     */
    Object.defineProperties(SimpleButton.prototype,
    {
        downState: {
            get: function () {
                return this.getSprite("down");
            },
            set: function (sprite) {
                this.setSprite("down", sprite);
            }
        },
        hitState: {
            get: function () {
                return this.getSprite("hit");
            },
            set: function (sprite) {
                this.setSprite("hit", sprite);
            }
        },
        overState: {
            get: function () {
                return this.getSprite("over");
            },
            set: function (sprite) {
                this.setSprite("over", sprite);
            }
        },
        upState: {
            get: function () {
                return this.getSprite("up");
            },
            set: function (sprite) {
                this.setSprite("up", sprite);
            }
        }
    });

    /**
     *
     * @returns {Array|ActionScript|*|actions}
     */
    SimpleButton.prototype.getActions = function ()
    {
        return this.actions;
    };

    /**
     * @param actions
     */
    SimpleButton.prototype.setActions = function (actions)
    {
        this.actions = actions;
    };

    /**
     * @param status
     */
    SimpleButton.prototype.setButtonStatus = function (status)
    {
        var _this = this;
        if (_this.getButtonStatus() !== status) {
            _this.buttonReset(status);
        }
        _this.buttonStatus = status;
    };

    /**
     * @param status
     * @returns {*}
     */
    SimpleButton.prototype.getSprite = function (status)
    {
        var _this = this;
        if (!status) {
            status = _this.buttonStatus;
        }
        status += "State";
        return _this["_" + status];
    };

    /**
     * @param status
     * @param sprite
     */
    SimpleButton.prototype.setSprite = function (status, sprite)
    {
        var _this = this;
        var stage = _this.getStage();

        var level = 0;
        switch (status) {
            case "down":
                level = 1;
                break;
            case "hit":
                level = 2;
                break;
            case "over":
                level = 3;
                break;
            case "up":
                level = 4;
                break;
        }

        stage.setPlaceObject(new PlaceObject(), _this.instanceId, level, 0);
        sprite.setParent(_this);
        sprite.setLevel(level);
        sprite.setStage(stage);
        var container = sprite.getContainer();
        for (var depth in container) {
            if (!container.hasOwnProperty(depth)) {
                continue;
            }

            var instanceId = container[depth];
            var obj = stage.getInstance(instanceId);
            obj.setParentSprite(sprite);
        }

        status += "State";
        _this["_" + status] = sprite;
    };

    /**
     * @param matrix
     * @param status
     * @returns {{xMin: number, xMax: number, yMin: number, yMax: number}}
     */
    SimpleButton.prototype.getBounds = function (matrix, status)
    {
        var _this = this;
        var xMax = 0;
        var yMax = 0;
        var xMin = 0;
        var yMin = 0;

        var sprite = _this.getSprite(status);
        var tags = sprite.getContainer();
        var length = tags.length;
        if (length) {
            var stage = _this.getStage();
            var no = _Number.MAX_VALUE;
            xMax = -no;
            yMax = -no;
            xMin = no;
            yMin = no;

            var _multiplicationMatrix = _this.multiplicationMatrix;
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = tags[depth];
                var tag = stage.getInstance(instanceId);
                if (!tag || tag.isClipDepth) {
                    continue;
                }

                var matrix2 = (matrix) ? _multiplicationMatrix(matrix, tag.getMatrix()) : tag.getMatrix();
                var bounds = tag.getBounds(matrix2, status);
                if (!bounds) {
                    continue;
                }
                xMin = _min(xMin, bounds.xMin);
                xMax = _max(xMax, bounds.xMax);
                yMin = _min(yMin, bounds.yMin);
                yMax = _max(yMax, bounds.yMax);
            }
        }
        return {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax};
    };

    /**
     * @param status
     */
    SimpleButton.prototype.buttonReset = function (status)
    {
        var _this = this;
        var sprite = _this.getSprite();
        var container = sprite.getContainer();
        var nextSprite = _this.getSprite(status);
        var nextContainer = nextSprite.getContainer();
        var stage = _this.getStage();
        for (var depth in container) {
            if (!container.hasOwnProperty(depth)) {
                continue;
            }
            var instanceId = container[depth];
            if (depth in nextContainer && instanceId === nextContainer[depth]) {
                continue;
            }
            var instance = stage.getInstance(instanceId);
            if (!instance) {
                continue;
            }
            instance.reset();
        }
    };

    /**
     * @param matrix
     * @param stage
     * @param visible
     * @param mask
     */
    SimpleButton.prototype.setHitRange = function (matrix, stage, visible, mask)
    {
        var _this = this;
        var isVisible = _min(_this.getVisible(), visible);
        if (_this.getEnabled() && isVisible) {
            var buttonHits = stage.buttonHits;

            // enter
            if (isTouch) {
                var actions = _this.getActions();
                var aLen = actions.length;
                if (aLen) {
                    for (var idx = 0; idx < aLen; idx++) {
                        var cond = actions[idx];
                        if (cond.CondKeyPress === 13) {
                            buttonHits[buttonHits.length] = {
                                button: _this,
                                xMin: 0,
                                xMax: stage.getWidth(),
                                yMin: 0,
                                yMax: stage.getHeight(),
                                CondKeyPress: cond.CondKeyPress,
                                parent: _this.getParent()
                            };
                        }
                    }
                }
            }

            var status = "hit";
            var hitTest = _this.getSprite(status);
            var hitTags = hitTest.getContainer();
            if (!hitTags.length) {
                status = "up";
                hitTest = _this.getSprite(status);
                hitTags = hitTest.getContainer();
            }

            if (hitTags.length) {
                var m2 = _this.multiplicationMatrix(matrix, _this.getMatrix());
                var bounds = _this.getBounds(m2, status);
                if (bounds) {
                    buttonHits[buttonHits.length] = {
                        button: _this,
                        xMin: bounds.xMin,
                        xMax: bounds.xMax,
                        yMin: bounds.yMin,
                        yMax: bounds.yMax,
                        CondKeyPress: 0,
                        parent: _this.getParent(),
                        matrix: _this.cloneArray(matrix)
                    };
                }
            }
        }
    };

    /**
     * @param ctx
     * @param matrix
     * @param colorTransform
     * @param stage
     * @param visible
     */
    SimpleButton.prototype.render = function (ctx, matrix, colorTransform, stage, visible)
    {
        var _this = this;

        // colorTransform
        var _multiplicationColor = _this.multiplicationColor;
        var rColorTransform = _multiplicationColor(colorTransform, _this.getColorTransform());

        // matrix
        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());

        // pre render
        var isVisible = _min(_this.getVisible(), visible);
        var obj = _this.preRender(ctx, m2, rColorTransform, stage, isVisible);

        // render
        var sprite = _this.getSprite();
        var rMatrix = _multiplicationMatrix(obj.preMatrix, sprite.getMatrix());
        var rColorTransform2 = _multiplicationColor(rColorTransform, sprite.getColorTransform());
        isVisible = _min(sprite.getVisible(), visible);
        var cacheKey = obj.cacheKey;
        cacheKey += sprite.render(obj.preCtx, rMatrix, rColorTransform2, stage, isVisible);

        // post render
        if (obj.isFilter || obj.isBlend) {
            obj.cacheKey = cacheKey;
            _this.postRender(ctx, matrix, colorTransform, stage, obj);
        }
        return cacheKey;
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {boolean}
     */
    SimpleButton.prototype.renderHitTest = function (ctx, matrix, stage, x, y)
    {
        var _this = this;

        var sprite = _this.getSprite("hit");
        var tags = sprite.getContainer();
        var length = tags.length;
        if (!length) {
            return false;
        }

        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());
        var m3 = _multiplicationMatrix(m2, sprite.getMatrix());

        if (length) {
            var loadStage = _this.getStage();
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = tags[depth];
                var tag = loadStage.getInstance(instanceId);
                if (!tag) {
                    continue;
                }

                var hit = tag.renderHitTest(ctx, m3, stage, x, y);
                if (hit) {
                    return hit;
                }
            }
        }

        return false;
    };

    /**
     * @param ctx
     * @param matrix
     * @param stage
     * @param x
     * @param y
     * @returns {*}
     */
    SimpleButton.prototype.hitCheck = function (ctx, matrix, stage, x, y)
    {
        var _this = this;

        var sprite = _this.getSprite("hit");
        var tags = sprite.getContainer();
        var length = tags.length;
        if (!length) {
            return false;
        }

        var _multiplicationMatrix = _this.multiplicationMatrix;
        var m2 = _multiplicationMatrix(matrix, _this.getMatrix());
        var m3 = _multiplicationMatrix(m2, sprite.getMatrix());

        var hitObj = false;
        var hit = false;
        if (length) {
            var loadStage = _this.getStage();
            tags.reverse();
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var tagId = tags[depth];
                var instance = loadStage.getInstance(tagId);
                if (instance instanceof Shape ||
                    instance instanceof StaticText ||
                    instance instanceof TextField
                ) {
                    hit = instance.renderHitTest(ctx, m3, stage, x, y);
                } else {
                    hit = instance.hitCheck(ctx, m3, stage, x, y);
                }

                if (hit) {
                    hitObj = hit;
                    if (typeof hit !== "object") {
                        var events = _this.events;
                        if (events.press !== undefined ||
                            events.release !== undefined ||
                            events.releaseOutside !== undefined ||
                            events.rollOver !== undefined ||
                            events.rollOut !== undefined ||
                            events.dragOver !== undefined ||
                            events.dragOut !== undefined
                        ) {
                            stage.isHit = hit;
                            hitObj = {
                                parent : _this.getParent(),
                                button : _this
                            };
                        }
                    }

                    tags.reverse();
                    return hitObj;
                }
            }
            tags.reverse();
        }

        return false;
    };

    /**
     * @see MovieClip.addActions
     */
    SimpleButton.prototype.addActions = function (stage)
    {
        var _this = this;
        var sprite = _this.getSprite();
        var tags = sprite.getContainer();
        var length = tags.length;
        if (length) {
            var myStage = _this.getStage();
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }
                var instanceId = tags[depth];
                var tag = myStage.getInstance(instanceId);
                if (!tag) {
                    continue;
                }
                tag.addActions(stage);
            }
        }
    };

    /**
     * Dummy
     * @returns {undefined}
     */
    SimpleButton.prototype.getTags = function () { return undefined; };
    SimpleButton.prototype.initFrame = function () {};

    /**
     * @constructor
     */
    var MovieClip = function ()
    {
        var _this = this;
        Sprite.call(_this);

        _this._currentframe = 1;
        _this.removeTags = [];
        _this.actions = [];
        _this.labels = [];

        // flag
        _this.stopFlag = false;
        _this.isAction = true;

        // clip
        _this.isClipDepth = false;
        _this.clipDepth = 0;

        // sound
        _this.sounds = [];
        _this.soundStopFlag = false;
    };

    /**
     * extends
     * @type {Sprite}
     */
    MovieClip.prototype = Object.create(Sprite.prototype);
    MovieClip.prototype.constructor = MovieClip;

    /**
     * @param name
     * @param stage
     */
    MovieClip.prototype.dispatchOnEvent = function (name, stage)
    {
        var _this = this;
        var as = _this.variables[name];
        if (as) {
            _this.setActionQueue(as, stage);
        }
    };

    /**
     * @param name
     * @param depth
     * @returns {MovieClip}
     */
    MovieClip.prototype.createEmptyMovieClip = function (name, depth)
    {
        var _this = this;
        var stage = _this.getStage();

        if (name === undefined) {
            return undefined;
        }

        var mc = _this.getDisplayObject(name);
        if (!mc) {
            mc = new MovieClip();
        }

        depth += 16384;

        mc.setName(name);
        mc.setLevel(depth);
        mc.setParent(_this);
        mc.setStage(stage);

        var container = _this.getContainer();
        var totalFrames = _this.getTotalFrames() + 1;
        var placeObject = new PlaceObject();
        var instanceId = _this.instanceId;
        for (var frame = 1; frame < totalFrames; frame++) {
            if (!(frame in container)) {
                container[frame] = [];
            }
            container[frame][depth] = mc.instanceId;
            stage.setPlaceObject(placeObject, instanceId, depth, frame);
        }
        return mc;
    };

    /**
     * @param name
     * @param depth
     * @param x
     * @param y
     * @param width
     * @param height
     * @returns {TextField}
     */
    MovieClip.prototype.createTextField = function (name, depth, x, y, width, height)
    {
        if (16384 > depth) {
            depth += 16384;
        }
        var _this = this;
        var textField = new TextField(name, depth, width, height);
        textField.setX(x);
        textField.setY(y);
        textField.setParent(_this);
        textField.setStage(_this.getStage());
        textField.setInitParams();
        var container = _this.getContainer();
        for (var frame in container) {
            if (!container.hasOwnProperty(frame)) {
                continue;
            }
            container[frame][depth] = textField.instanceId;
        }
        return textField;
    };

    /**
     * @param r
     * @param g
     * @param b
     */
    MovieClip.prototype.setBackgroundColor = function (r, g, b)
    {
        var _this = this;
        var stage = _this.getStage();
        stage.setBackgroundColor(r, g, b);
    };

    /**
     * play
     */
    MovieClip.prototype.play = function ()
    {
        this.stopFlag = false;
    };

    /**
     * stop
     */
    MovieClip.prototype.stop = function ()
    {
        this.stopFlag = true;
    };

    /**
     * @param frame
     */
    MovieClip.prototype.gotoAndPlay = function (frame)
    {
        var _this = this;
        if (!_isNaN(frame)) {
            frame = +frame;
        } else if (typeof frame === "string") {
            frame = _this.getLabel(frame);
        }

        if (typeof frame === "number" && frame > 0) {
            _this.setNextFrame(frame);
            _this.play();
        }
    };

    /**
     * @param frame
     */
    MovieClip.prototype.gotoAndStop = function (frame)
    {
        var _this = this;
        if (typeof frame === "string") {
            frame = _this.getLabel(frame);
        }
        frame |= 0;
        if (typeof frame === "number" && frame > _this.getTotalFrames()) {
            frame = _this.getTotalFrames();
            _this.isAction = false;
        }
        if (frame > 0) {
            _this.setNextFrame(frame);
            _this.stop();
        }
    };

    /**
     * stopAllSounds
     */
    MovieClip.prototype.stopAllSounds = function ()
    {
        var stage = this.getStage();
        var loadSounds = stage.loadSounds;
        var sLen = loadSounds.length;
        var stopSound = function () {
            this.removeEventListener("pause", stopSound);
            this.currentTime = 0;
            this.loop = false;
        };

        if (sLen > 0) {
            while (sLen--) {
                if (!(sLen in loadSounds)) {
                    continue;
                }
                var audio = loadSounds[sLen];
                audio.addEventListener("pause", stopSound);
                audio.pause();
            }
        }
        stage.loadSounds = [];
    };

    /**
     * @param url
     * @param target
     * @param SendVarsMethod
     * @returns {number}
     */
    MovieClip.prototype.loadMovie = function (url, target, SendVarsMethod)
    {
        var _this = this;
        var stage = _this.getStage();
        var targetMc = null;

        if (!target) {
            target = _this.getName();
            targetMc = _this;
        }

        if (!targetMc) {
            if (typeof target === "string") {
                var _level = target.substr(0, 6);
                if (_level === "_level") {
                    target = +target.substr(6);
                }
            }
            if (typeof target === "number") {
                var parent = stage.getParent();
                if (!parent) {
                    parent = stage.getParent();
                }
                var tags = parent.getTags();
                targetMc = tags[target];
            } else {
                targetMc = _this.getDisplayObject(target);
            }
        }

        if (targetMc) {
            _this.unloadMovie(targetMc);

            var xmlHttpRequest = new XMLHttpRequest();
            var targetUrl = url;
            var body = null;
            if (SendVarsMethod === 2) {
                var urls = url.split("?");
                if (urls[1] !== undefined) {
                    body = urls[1];
                }
                targetUrl = urls[0];
                xmlHttpRequest.open("POST", targetUrl, true);
                xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            } else {
                xmlHttpRequest.open("GET", targetUrl, true);
            }

            if (isXHR2) {
                xmlHttpRequest.responseType = "arraybuffer";
            } else {
                xmlHttpRequest.overrideMimeType("text/plain; charset=x-user-defined");
            }

            xmlHttpRequest.onreadystatechange = function ()
            {
                var readyState = xmlHttpRequest.readyState;
                var status = xmlHttpRequest.status;
                if (readyState === 4) {
                    switch (status) {
                        case 200:
                        case 304:
                            var _root = _this.getDisplayObject("_root");
                            var rootStage = _root.getStage();
                            var data = isXHR2 ? xmlHttpRequest.response : xmlHttpRequest.responseText;
                            var loadStage = new Stage();
                            loadStages[loadStage.getId()] = loadStage;
                            targetMc._url = url;
                            targetMc.reset();
                            loadStage.setParent(targetMc);
                            targetMc.setLoadStage(loadStage);
                            loadStage.parse(data, targetUrl);
                            loadStage.stop();

                            if (target === 0 || (typeof target !== "number" && !targetMc.getParent())) {
                                stage.stop();
                                loadStage.setId(stage.getId());
                                loadStage.setName(stage.getName());
                                loadStage.backgroundColor = stage.backgroundColor;
                                loadStage.initCanvas();
                                loadStage.loadStatus = 2;
                                loadStage.loadEvent();
                                delete loadStages[loadStage.getId()];
                                stages[stage.getId()] = loadStage;
                                stage = null;
                            }

                            var onData = targetMc.variables.onData;
                            if (typeof onData === "function") {
                                loadStage.executeEventAction(onData, targetMc);
                            }

                            clipEvent.type = "data";
                            targetMc.dispatchEvent(clipEvent, rootStage);

                            targetMc.addActions(rootStage);
                            break;
                    }
                }
            };
            xmlHttpRequest.send(body);
        }
    };

    /**
     * @param target
     * @returns {number}
     */
    MovieClip.prototype.unloadMovie = function (target)
    {
        var _this = this;
        var targetMc = null;
        if (target instanceof MovieClip) {
            targetMc = target;
        } else {
            targetMc = _this.getDisplayObject(target);
            if (!targetMc) {
                return 0;
            }
        }

        // delete
        targetMc.reset();
        targetMc.setLoadStage(null);
        targetMc.setStage(_this.getStage());
        targetMc.container = [];
        targetMc.actions = [];
        targetMc.instances = [];
        targetMc.labels = [];
        targetMc.sounds = [];
        targetMc.removeTags = [];
        targetMc._totalframes = 1;
        targetMc._url = null;
        targetMc._lockroot = undefined;

        var loadStage = targetMc.getStage();
        delete loadStages[loadStage.getId()];
    };

    /**
     * @param url
     * @param target
     * @param method
     * @returns {*}
     */
    MovieClip.prototype.getURL = function (url, target, method)
    {
        var _this = this;
        if (typeof url === "string") {
            var cmd = url.substr(0, 9);
            if (cmd === "FSCommand") {
                var values = url.split(":");
                cmd = values.pop();
                var str = arguments[1];
                if (str === undefined) {
                    str = "";
                }

                var stage = _this.getStage();
                var FSCommand = stage.abc.flash.system.fscommand;
                return FSCommand.apply(stage, [cmd, str]);
            }
        }

        if (target && typeof target === "string") {
            switch (target.toLowerCase()) {
                case "_self":
                case "_blank":
                case "_parent":
                case "_top":
                    break;
                case "post":
                    target = "_self";
                    method = "GET";
                    break;
                case "get":
                    target = "_self";
                    method = "GET";
                    break;
                default:
                    if (!method) {
                        method = "GET";
                    }
                    _this.loadMovie(url, target, method);
                    return 0;
            }
        }

        // form
        if (method === "POST") {
            var form = _document.createElement("form");
            form.action = url;
            form.method = method;
            if (target) {
                form.target = target;
            }

            var urls = url.split("?");
            if (urls.length > 1) {
                var pears = urls[1].split("&");
                var pLen = pears.length;
                var _encodeURI = encodeURI;
                for (var pIdx = 0; pIdx < pLen; pIdx++) {
                    var pear = pears[pIdx].split("=");
                    var input = _document.createElement("input");
                    input.type = "hidden";
                    input.name = pear[0];
                    input.value = _encodeURI(pear[1] || "");
                    form.appendChild(input);
                }
            }
            _document.body.appendChild(form);
            form.submit();
        } else {
            var a = _document.createElement("a");
            a.href = url;
            a.target = target;
            a.click();
        }
    };

    /**
     * @param url
     * @param target
     * @param method
     */
    MovieClip.prototype.loadVariables = function (url, target, method)
    {
        var _this = this;
        var targetMc = _this;
        if (target) {
            targetMc = _this.getDisplayObject(target);
        }

        if (targetMc) {
            var xmlHttpRequest = new XMLHttpRequest();
            var body = null;
            if (method === "POST") {
                var urls = url.split("?");
                if (urls[1] !== undefined) {
                    body = urls[1];
                }
                xmlHttpRequest.open(method, urls[0], true);
                xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            } else {
                xmlHttpRequest.open("GET", url, true);
            }

            xmlHttpRequest.onreadystatechange = function ()
            {
                var readyState = xmlHttpRequest.readyState;
                if (readyState === 4) {
                    var status = xmlHttpRequest.status;
                    switch (status) {
                        case 200:
                        case 304:
                            var responseText = decodeURIComponent(xmlHttpRequest.responseText);
                            var pairs = responseText.split("&");
                            var length = pairs.length;
                            for (var idx = 0; idx < length; idx++) {
                                var pair = pairs[idx];
                                var values = pair.split("=");
                                targetMc.setVariable(values[0], values[1]);
                            }

                            var _root = _this.getDisplayObject();
                            var rootStage = _root.getStage();
                            var stage = _this.getStage();
                            var onData = targetMc.variables.onData;
                            if (typeof onData === "function") {
                                stage.executeEventAction(onData, targetMc);
                            }

                            clipEvent.type = "data";
                            targetMc.dispatchEvent(clipEvent, rootStage);

                            break;
                    }
                }
            };
            xmlHttpRequest.send(body);
        }
    };

    /**
     * @returns {boolean}
     */
    MovieClip.prototype.hitTest = function ()
    {
        var _this = this;
        var targetMc = arguments[0];
        var x = 0;
        var y = 0;
        var bool = false;
        if (!(targetMc instanceof MovieClip)) {
            x = arguments[0];
            y = arguments[1];
            bool = arguments[2];
            if (!x || !y) {
                return false;
            }
        }

        var bounds = _this.getHitBounds();
        var xMax = bounds.xMax;
        var xMin = bounds.xMin;
        var yMax = bounds.yMax;
        var yMin = bounds.yMin;

        if (targetMc instanceof MovieClip) {
            var targetBounds = targetMc.getHitBounds();
            var txMax = targetBounds.xMax;
            var txMin = targetBounds.xMin;
            var tyMax = targetBounds.yMax;
            var tyMin = targetBounds.yMin;
            return (txMax > xMin && tyMax > yMin && xMax > txMin && yMax > tyMin);
        } else {
            if (x >= xMin && x <= xMax && y >= yMin && y <= yMax) {
                if (bool) {
                    var matrix = [1,0,0,1,0,0];
                    var mc = _this;
                    var _multiplicationMatrix = _this.multiplicationMatrix;
                    while (true) {
                        var parent = mc.getParent();
                        if (!parent.getParent()) {
                            break;
                        }
                        matrix = _multiplicationMatrix(parent.getMatrix(), matrix);
                        mc = parent;
                    }
                    var _root = _this.getDisplayObject("_root");
                    var stage = _root.getStage();
                    var ctx = stage.hitContext;
                    var scale = stage.getScale();
                    x *= scale;
                    y *= scale;
                    y *= _devicePixelRatio;
                    x *= _devicePixelRatio;

                    return _this.renderHitTest(ctx, matrix, stage, x, y);
                } else {
                    return true;
                }
            }
            return false;
        }
    };

    /**
     * @returns {{xMin: *, xMax: *, yMin: *, yMax: *}}
     * @returns {*}
     */
    MovieClip.prototype.getHitBounds = function ()
    {
        var _this = this;
        var mc = _this;
        var matrix = _this.getMatrix();
        var _multiplicationMatrix = _this.multiplicationMatrix;
        while (true) {
            var parent = mc.getParent();
            if (!parent.getParent()) {
                break;
            }
            matrix = _multiplicationMatrix(parent.getMatrix(), matrix);
            mc = parent;
        }
        return _this.getBounds(matrix);
    };

    /**
     * @param depth
     * @returns {*}
     */
    MovieClip.prototype.getInstanceAtDepth = function (depth)
    {
        var _this = this;
        var parent = _this.getParent();
        if (!parent) {
            parent = _this.getDisplayObject("_root");
        }
        var tags = parent.getTags();
        depth += 16384;
        return tags[depth];
    };

    /**
     * swapDepths
     */
    MovieClip.prototype.swapDepths = function ()
    {
        var _this = this;
        var mc = arguments[0];
        var depth = 0;
        var parent = _this.getParent();
        if (parent) {
            var tags = parent.getTags();
            if (mc instanceof MovieClip) {
                if (parent === mc.getParent()) {
                    depth = _this.getDepth() + 16384;
                    var swapDepth = mc.getDepth() + 16384;
                    _this.setDepth(depth, swapDepth, mc);
                }
            } else {
                depth = arguments[0];
                if (_isNaN(depth)) {
                    depth = parent.getNextHighestDepth();
                }
                if (16384 > depth) {
                    depth += 16384;
                }
                if (depth in tags) {
                    var id = tags[depth];
                    if (id !== _this.instanceId) {
                        var stage = _this.getStage();
                        var instance = stage.getInstance(id);
                        _this.swapDepths(instance);
                    }
                } else {
                    _this.setDepth(depth, null, null);
                }
            }
        }
    };

    /**
     * @param id
     * @param name
     * @param depth
     * @param object
     * @returns {*}
     */
    MovieClip.prototype.attachMovie = function (id, name, depth, object)
    {
        var movieClip = null;
        var _this = this;
        if (_isNaN(depth)) {
            depth = _this.getNextHighestDepth();
        }
        if (depth < 16384) {
            depth += 16384;
        }

        var mc = _this.getDisplayObject(name);
        if (mc) {
            mc.removeMovieClip();
        }

        var stage = _this.getStage();
        var exportAssets = stage.exportAssets;
        if (id in exportAssets) {
            var characterId = exportAssets[id];
            var tag = stage.getCharacter(characterId);
            if (tag) {
                movieClip = new MovieClip();
                movieClip.setStage(stage);
                movieClip.setParent(_this);
                movieClip.setCharacterId(characterId);
                movieClip.setLevel(depth);
                movieClip.setName(name);
                movieClip.setTarget(_this.getTarget() + "/" + name);

                // init action
                var initAction = stage.initActions[characterId];
                if (typeof initAction === "function") {
                    movieClip.active = true;
                    initAction.apply(movieClip);
                    movieClip.reset();
                }

                // registerClass
                var RegClass = stage.registerClass[characterId];
                if (RegClass) {
                    movieClip.variables.registerClass = new RegClass();
                }

                var swfTag = new SwfTag(stage, null);
                swfTag.build(tag, movieClip);

                var placeObject = new PlaceObject();
                var instanceId = _this.instanceId;
                var totalFrame = _this.getTotalFrames() + 1;
                var container = _this.getContainer();
                for (var frame = 1; frame < totalFrame; frame++) {
                    if (!(frame in container)) {
                        container[frame] = [];
                    }
                    container[frame][depth] = movieClip.instanceId;
                    stage.setPlaceObject(placeObject, instanceId, depth, frame);
                }

                if (object) {
                    for (var prop in object) {
                        if (!object.hasOwnProperty(prop)) {
                            continue;
                        }
                        movieClip.setProperty(prop, object[prop]);
                    }
                }

                var _root = _this.getDisplayObject("_root");
                var rootStage = _root.getStage();
                movieClip.addActions(rootStage);
            }
        }
        return movieClip;
    };

    /**
     * @returns {number}
     */
    MovieClip.prototype.getNextHighestDepth = function ()
    {
        var depth = 0;
        var _this = this;
        var container = _this.getContainer();
        for (var idx in container) {
            if (!container.hasOwnProperty(idx)) {
                continue;
            }
            var children = container[idx];
            depth = _max(depth, children.length);
        }
        if (16384 > depth) {
            depth = 0;
        }
        return depth;
    };

    /**
     * @returns {*}
     */
    MovieClip.prototype.getBytesLoaded = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        var bitio = stage.bitio;
        return (!bitio) ? stage.fileSize : bitio.byte_offset;
    };

    /**
     * @returns {number|*|fileLength}
     */
    MovieClip.prototype.getBytesTotal = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        return stage.fileSize;
    };

    /**
     * updateAfterEvent
     */
    MovieClip.prototype.updateAfterEvent = function ()
    {
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        stage.touchRender();
    };

    /**
     * @returns {*}
     */
    MovieClip.prototype.duplicateMovieClip = function ()
    {
        var _this = this;
        var _root = _this.getDisplayObject("_root");
        var stage = _root.getStage();
        var target = arguments[0];
        var name = arguments[1];
        var depth = arguments[2];

        var targetMc = _this.getDisplayObject(name);
        var parent;
        var object;
        if (!targetMc && stage.getVersion() > 4) {
            target = arguments[0];
            depth = arguments[1];
            if (_isNaN(depth)) {
                parent = _this.getParent();
                if (!parent) {
                    parent = stage.getParent();
                }
                depth = parent.getNextHighestDepth();
            }
            object = arguments[2];
            targetMc = _this;
        }

        if (16384 > depth) {
            depth += 16384;
        }

        var cloneMc;
        if (targetMc !== undefined && targetMc.getCharacterId() !== 0) {
            stage = targetMc.getStage();
            parent = targetMc.getParent();
            if (!parent) {
                parent = stage.getParent();
            }

            var char = stage.getCharacter(targetMc.characterId);
            var swftag = new SwfTag(stage);
            if (char instanceof Array) {
                cloneMc = new MovieClip();
                cloneMc.setStage(stage);
                cloneMc.setParent(parent);
                cloneMc.setLevel(depth);
                cloneMc.setTotalFrames(targetMc.getTotalFrames());
                cloneMc.setCharacterId(targetMc.characterId);
                swftag.build(char, cloneMc);
            } else {
                var tag = {
                    CharacterId: targetMc.characterId,
                    Ratio: 0,
                    Depth: depth
                };
                cloneMc = swftag.buildObject(tag, parent);
            }

            cloneMc.setName(target);
            if (targetMc._matrix) {
                cloneMc._blendMode = targetMc._blendMode;
                cloneMc._filters = targetMc._filters;
                cloneMc._matrix = _this.cloneArray(targetMc._matrix);
                cloneMc._colorTransform = _this.cloneArray(targetMc._colorTransform);
            }

            var totalFrame = parent.getTotalFrames() + 1;
            var container = parent.getContainer();
            var instanceId = parent.instanceId;
            var placeObjects = stage.placeObjects[instanceId];
            var level = targetMc.getLevel();
            for (var frame = 1; frame < totalFrame; frame++) {
                if (!(frame in container)) {
                    container[frame] = [];
                }
                container[frame][depth] = cloneMc.instanceId;

                if (frame in placeObjects) {
                    var placeObject = placeObjects[frame][level];
                    if (placeObject) {
                        if (!(frame in placeObjects)) {
                            placeObjects[frame] = [];
                        }
                        placeObjects[frame][depth] = placeObject.clone();
                    }
                }
            }

            if (object) {
                for (var prop in object) {
                    if (!object.hasOwnProperty(prop)) {
                        continue;
                    }
                    cloneMc.setProperty(prop, object[prop]);
                }
            }

            cloneMc.addActions(stage);
        }

        return cloneMc;
    };

    /**
     * @param name
     */
    MovieClip.prototype.removeMovieClip = function (name)
    {
        var _this = this;
        var targetMc = _this;
        if (typeof name === "string") {
            var target = _this.getDisplayObject(name);
            if (target) {
                targetMc = target;
            }
        }

        var depth = targetMc.getDepth() + 16384;
        var level = targetMc.getLevel();
        if (targetMc instanceof MovieClip && depth >= 16384) {
            targetMc.reset();
            targetMc.removeFlag = true;
            var parent = targetMc.getParent();
            var container = parent.getContainer();
            var instanceId = targetMc.instanceId;
            var tagId;
            for (var frame = parent.getTotalFrames() + 1; --frame;) {
                if (!(frame in container)) {
                    continue;
                }

                var tags = container[frame];
                if (depth in tags) {
                    tagId = tags[depth];
                    if (tagId === instanceId) {
                        delete container[frame][depth];
                    }
                }

                if (depth !== level && 16384 > level) {
                    if (!(level in tags)) {
                        tags[level] = instanceId;
                    }
                }
            }
        }
    };

    /**
     * initFrame
     */
    MovieClip.prototype.initFrame = function ()
    {
        var _this = this;
        _this.active = true;

        var stage = _this.getStage();
        var tags = _this.getTags();
        var length = tags.length;
        if (length) {
            tags.reverse();
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }

                var instanceId = tags[depth];
                var instance = stage.getInstance(instanceId);
                if (!instance) {
                    continue;
                }
                instance.initFrame();
            }
            tags.reverse();
        }

        var initAction = stage.initActions[_this.getCharacterId()];
        if (typeof initAction === "function") {
            initAction.apply(_this);
        }
    };

    /**
     * @param stage
     * @param clipEvent
     */
    MovieClip.prototype.putFrame = function (stage, clipEvent)
    {
        var _this = this;
        var myStage = _this.getStage();
        var prevTags;
        var stopFlag = _this.stopFlag;
        if (!stopFlag && _this.active) {
            var frame = _this.getCurrentFrame();
            var totalFrames = _this.getTotalFrames();
            if (totalFrames > 1) {
                if (_this.isLoad) {
                    prevTags = _this.getTags();
                    frame++;
                }
                if (frame > totalFrames) {
                    frame = 1;
                    _this.resetCheck();
                }

                _this.setCurrentFrame(frame);
                _this.remove(stage);
                _this.isAction = true;
                _this.soundStopFlag = false;
            }
        }

        if (_this.removeFlag) {
            return 0;
        }

        _this.active = true;
        if (prevTags !== undefined) {
            if (_this.isSwap) {
                _this.resetSwap();
            }

            var tags = _this.getTags();
            var length = tags.length;
            if (length && tags.toString() !== prevTags.toString()) {
                for (var depth in tags) {
                    if (!tags.hasOwnProperty(depth)) {
                        continue;
                    }

                    var instanceId = tags[depth];
                    if (depth in prevTags && instanceId === prevTags[depth]) {
                        continue;
                    }

                    var instance = myStage.getInstance(instanceId);
                    if (instance && instance instanceof MovieClip) {
                        stage.newTags.unshift(instance);
                    }
                }
            }
        }

        if (_this.isLoad) {
            clipEvent.type = "enterFrame";
            _this.dispatchEvent(clipEvent, stage);
            _this.dispatchOnEvent("onEnterFrame", stage);
            _this.addTouchEvent(stage);
            if (_this.isAction) {
                _this.isAction = false;
                var as = _this.getActions(_this.getCurrentFrame());
                if (as) {
                    _this.setActionQueue(as, stage);
                }
            }
        } else {
            // init action
            var initAction = myStage.initActions[_this.getCharacterId()];
            if (typeof initAction === "function") {
                initAction.apply(_this);
            }
        }
    };

    /**
     * nextFrame
     */
    MovieClip.prototype.nextFrame = function ()
    {
        var _this = this;
        var frame = _this.getCurrentFrame();
        frame++;
        _this.setNextFrame(frame);
        _this.stop();
    };

    /**
     * prevFrame
     */
    MovieClip.prototype.prevFrame = function ()
    {
        var _this = this;
        var frame = _this.getCurrentFrame();
        frame--;
        _this.setNextFrame(frame);
        _this.stop();
    };

    /**
     * @returns {number}
     */
    MovieClip.prototype.getCurrentFrame = function ()
    {
        return this._currentframe;
    };

    /**
     * @param frame
     */
    MovieClip.prototype.setCurrentFrame = function (frame)
    {
        this._currentframe = frame;
    };

    /**
     * @param frame
     */
    MovieClip.prototype.setNextFrame = function (frame)
    {
        var _this = this;
        if (frame > 0 && _this.getCurrentFrame() !== frame) {
            _this.isAction = true;

            if (frame > _this.getTotalFrames()) {
                frame = _this.getTotalFrames();
                _this.isAction = false;
            }

            var maxFrame = _max(frame, _this.getCurrentFrame()) + 1;
            var minFrame = _min(frame, _this.getCurrentFrame());

            var stage = _this.getStage();
            var tags = _this.getTags();
            var checked = [];
            var nextTags = _this.getTags(frame);
            var tag, tagId, depth, nextTag, nextTagId;
            var length = _max(tags.length, nextTags.length);
            if (length) {
                for (depth = 0; depth < length; depth++) {
                    if (!(depth in tags) && !(depth in nextTags)) {
                        continue;
                    }

                    tagId = tags[depth];
                    nextTagId = nextTags[depth];
                    if (!tagId && !nextTagId) {
                        continue;
                    }

                    tag = stage.getInstance(tagId);
                    nextTag = stage.getInstance(nextTagId);
                    if (tagId && nextTagId) {
                        if (tagId === nextTagId) {
                            checked[tagId] = true;
                            continue;
                        }

                        tag.reset();
                        nextTag.reset();
                        checked[tagId] = true;
                        checked[nextTagId] = true;
                    } else if (tag) {
                        tag.reset();
                        checked[tagId] = true;
                    } else if (nextTag) {
                        nextTag.reset();
                        checked[nextTagId] = true;
                    }
                }
            }

            if (checked.length) {
                for (var chkFrame = minFrame; chkFrame < maxFrame; chkFrame++) {
                    var container = _this.getTags(chkFrame);
                    if (!container.length) {
                        continue;
                    }

                    for (depth in container) {
                        if (!container.hasOwnProperty(depth)) {
                            continue;
                        }
                        tagId = container[depth];
                        if (tagId in checked) {
                            continue;
                        }

                        checked[tagId] = true;
                        tag = stage.getInstance(tagId);
                        tag.reset();
                    }
                }
            }

            _this.setCurrentFrame(frame);
            _this.soundStopFlag = false;

            var _root = _this.getDisplayObject("_root");
            var rootStage = _root.getStage();
            _this.addActions(rootStage);
        }
    };

    /**
     * @returns {number}
     */
    MovieClip.prototype.getTotalFrames = function ()
    {
        return this._totalframes;
    };

    /**
     * @param frame
     */
    MovieClip.prototype.setTotalFrames = function (frame)
    {
        this._totalframes = frame;
        this._framesloaded = frame;
    };

    /**
     * addLabel
     * @param frame
     * @param name
     */
    MovieClip.prototype.addLabel = function (frame, name)
    {
        if (typeof name !== "string") {
            name += "";
        }
        this.labels[name.toLowerCase()] = frame|0;
    };

    /**
     * @param name
     * @returns {*}
     */
    MovieClip.prototype.getLabel = function (name)
    {
        if (typeof name !== "string") {
            name += "";
        }
        return this.labels[name.toLowerCase()];
    };

    /**
     * @param frame
     * @param obj
     */
    MovieClip.prototype.addSound = function (frame, obj)
    {
        var _this = this;
        if (!(frame in _this.sounds)) {
            _this.sounds[frame] = [];
        }
        _this.sounds[frame].push(obj);
    };

    /**
     * @returns {*}
     */
    MovieClip.prototype.getSounds = function ()
    {
        var _this = this;
        return _this.sounds[_this.getCurrentFrame()];
    };

    /**
     * @param sound
     */
    MovieClip.prototype.startSound = function (sound)
    {
        var _this = this;
        var stage = _this.getStage();
        var soundId = sound.SoundId;
        var tag = stage.getCharacter(soundId);
        if (!tag) {
            return 0;
        }

        var soundInfo = tag.SoundInfo;
        startSound(sound.Audio, soundInfo);
        _this.soundStopFlag = true;
    };

    /**
     * @param frame
     * @returns {*}
     */
    MovieClip.prototype.getTags = function (frame)
    {
        var _this = this;
        var key = frame || _this.getCurrentFrame();
        return _this.container[key] || [];
    };

    /**
     * @param frame
     * @param tags
     */
    MovieClip.prototype.setRemoveTag = function (frame, tags)
    {
        var rTags = this.removeTags;
        rTags[frame] = [];
        var length = tags.length;
        for (var i = 0; i < length; i++) {
            var tag = tags[i];
            rTags[frame][tag.Depth] = 1;
        }
    };

    /**
     * @param frame
     * @returns {*}
     */
    MovieClip.prototype.getRemoveTags = function (frame)
    {
        return this.removeTags[frame];
    };

    /**
     * @param stage
     */
    MovieClip.prototype.remove = function (stage)
    {
        var _this = this;
        var removeTags = _this.getRemoveTags(_this.getCurrentFrame());
        if (removeTags) {
            var myStage = _this.getStage();
            var frame = _this.getCurrentFrame() - 1;
            var tags = _this.getTags(frame);
            for (var idx in tags) {
                if (!tags.hasOwnProperty(idx)) {
                    continue;
                }

                var instanceId = tags[idx];
                var tag = myStage.getInstance(instanceId);
                if (!tag) {
                    continue;
                }

                if (tag instanceof MovieClip) {
                    var depth = tag.getDepth() + 16384;
                    if (!(depth in removeTags)) {
                        continue;
                    }

                    clipEvent.type = "unload";
                    _this.dispatchEvent(clipEvent, stage);
                    tag.reset();
                } else {
                    if (!(idx in removeTags)) {
                        continue;
                    }
                    tag.reset();
                }
            }
        }
    };

    /**
     * resetCheck
     */
    MovieClip.prototype.resetCheck = function ()
    {
        var _this = this;
        var instances = _this.getInstances();
        var stage = _this.getStage();
        for (var id in instances) {
            if (!instances.hasOwnProperty(id)) {
                continue;
            }

            var instance = stage.getInstance(id);
            if (!instance || (!instance.getRatio() && !instance.removeFlag)) {
                continue;
            }
            instance.reset();
        }
    };

    /**
     * resetSwap
     */
    MovieClip.prototype.resetSwap = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        var currentTags = _this.getTags();
        var totalFrames = _this.getTotalFrames() + 1;
        for (var frame = 1; frame < totalFrames; frame++) {
            var tags = _this.getTags(frame);
            var length = tags.length;
            if (length) {
                var resetTags = [];
                for (var depth in tags) {
                    if (!tags.hasOwnProperty(depth)) {
                        continue;
                    }

                    depth |= 0;
                    var tagId = tags[depth];
                    var instance = stage.getInstance(tagId);
                    if (!instance) {
                        delete tags[depth];
                        continue;
                    }

                    if (instance.active) {
                        continue;
                    }

                    if (instance.getLevel() !== depth) {
                        if (!(instance.getLevel() in currentTags)) {
                            instance._depth = null;
                            resetTags[instance.getLevel()] = tagId;
                        }
                        delete tags[depth];
                    }
                }

                length = resetTags.length;
                if (length) {
                    for (var level in resetTags) {
                        if (!resetTags.hasOwnProperty(level)) {
                            continue;
                        }
                        tags[level] = resetTags[level];
                    }
                }
            }
        }
        _this.isSwap = false;
    };

    /**
     * reset
     */
    MovieClip.prototype.reset = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        var instances = _this.getInstances();
        for (var id in instances) {
            if (!instances.hasOwnProperty(id)) {
                continue;
            }
            var instance = stage.getInstance(id);
            if (instance instanceof MovieClip && instance.getDepth() >= 0) {
                instance.removeMovieClip();
                if (instance.getDepth() < 0) {
                    instance.removeFlag = false;
                }
            } else {
                instance.reset();
            }
        }

        var parent = _this.getParent();
        if (parent && _this.getLevel() !== _this.getDepth()+16384) {
            parent.isSwap = true;
        }

        _this.play();
        _this.setCurrentFrame(1);
        _this.clear();
        _this.initParams();
        _this.variables = {};
    };

    /**
     * init
     */
    MovieClip.prototype.initParams = function ()
    {
        var _this = this;
        _this.active = false;
        _this.removeFlag = false;
        _this.isLoad = false;
        _this.isMask = false;
        _this.isAction = true;
        _this.soundStopFlag = false;
        _this._droptarget = null;
        _this._depth = null;
        _this._mask = null;
        _this._matrix = null;
        _this._colorTransform = null;
        _this._filters = null;
        _this._blendMode = null;
        _this.buttonStatus = "up";
        _this.mouseEnabled = true;
        _this.setVisible(true);
        _this.setEnabled(true);
    };

    /**
     * @param stage
     */
    MovieClip.prototype.addTouchEvent = function (stage)
    {
        var _this = this;
        var events = _this.events;
        var moveEventHits = stage.moveEventHits;
        var downEventHits = stage.downEventHits;
        var upEventHits = stage.upEventHits;
        var keyDownEventHits = stage.keyDownEventHits;
        for (var name in events) {
            if (!events.hasOwnProperty(name)) {
                continue;
            }
            var as = events[name];
            switch (name) {
                case "mouseDown":
                    downEventHits[downEventHits.length] = {as: as, mc: _this};
                    break;
                case "mouseMove":
                    moveEventHits[moveEventHits.length] = {as: as, mc: _this};
                    break;
                case "mouseUp":
                    upEventHits[upEventHits.length] = {as: as, mc: _this};
                    break;
                case "keyDown":
                    if (isTouch) {
                        downEventHits[downEventHits.length] = {
                            as: as,
                            mc: _this
                        };
                    } else {
                        keyDownEventHits[keyDownEventHits.length] = {
                            as: as,
                            mc: _this
                        };
                    }
                    break;
                case "keyUp":
                    upEventHits[upEventHits.length] = {as: as, mc: _this};
                    break;
            }
        }

        var variables = _this.variables;
        var onMouseDown = variables.onMouseDown;
        if (onMouseDown) {
            downEventHits[downEventHits.length] = {mc: _this};
        }
        var onMouseMove = variables.onMouseMove;
        if (onMouseMove) {
            moveEventHits[moveEventHits.length] = {mc: _this};
        }
        var onMouseUp = variables.onMouseUp;
        if (onMouseUp) {
            upEventHits[upEventHits.length] = {mc: _this};
        }
    };

    /**
     * @param script
     * @returns {*}
     */
    MovieClip.prototype.createActionScript = function (script)
    {
        return (function (clip, origin)
        {
            var as = new ActionScript([], origin.constantPool, origin.register, origin.initAction);
            as.cache = origin.cache;
            as.scope = clip;
            return function ()
            {
                as.reset();
                as.variables["this"] = this;
                return as.execute(clip);
            };
        })(this, script);
    };

    /**
     * @param script
     * @param parent
     */
    MovieClip.prototype.createActionScript2 = function (script, parent)
    {
        return (function (clip, origin, chain)
        {
            return function ()
            {
                var as = new ActionScript([], origin.constantPool, origin.register, origin.initAction);
                as.parentId = origin.id; // todo
                as.cache = origin.cache;
                as.scope = clip;
                as.parent = (chain) ? chain : null;
                if (as.register.length) {
                    as.initVariable(arguments);
                }
                as.variables["this"] = this;
                return as.execute(clip);
            };
        })(this, script, parent);
    };

    /**
     * addFrameScript
     */
    MovieClip.prototype.addFrameScript = function ()
    {
        var _this = this;
        var args = arguments;
        var length = args.length;
        for (var i = 0; i < length; i++) {
            var frame = args[i++];
            var script = args[i];
            if (typeof frame === "string") {
                frame = _this.getLabel(frame);
            } else {
                frame += 1;
            }

            frame = frame|0;
            if (frame > 0 && _this.getTotalFrames() >= frame) {
                var actions = _this.actions;
                if (!(frame in actions)) {
                    actions[frame] = [];
                }

                if (script === null) {
                    actions[frame] = [];
                } else {
                    var aLen = actions[frame].length;
                    actions[frame][aLen] = script;
                }
            }
        }
    };

    /**
     * @param stage
     */
    MovieClip.prototype.addActions = function (stage)
    {
        var _this = this;
        _this.active = true;
        var myStage = _this.getStage();

        if (_this.isAction) {
            _this.isAction = false;
            if (!_this.isLoad) {
                // as3
                _this.buildAVM2();

                // registerClass
                var RegClass = myStage.registerClass[_this.getCharacterId()];
                if (typeof RegClass === "function") {
                    _this.variables.registerClass = new RegClass();
                }

                // clipEvent
                clipEvent.type = "initialize";
                _this.dispatchEvent(clipEvent, stage);
                clipEvent.type = "construct";
                _this.dispatchEvent(clipEvent, stage);
                clipEvent.type = "load";
                _this.dispatchEvent(clipEvent, stage);

                var onLoad = _this.variables.onLoad;
                if (typeof onLoad === "function") {
                    _this.setActionQueue(onLoad, stage);
                }
                _this.addTouchEvent(stage);
            }

            var action = _this.getActions(_this.getCurrentFrame());
            if (action) {
                _this.setActionQueue(action, stage);
            }
        }

        var tags = _this.getTags();
        var length = tags.length;
        if (length) {
            for (var depth in tags) {
                if (!tags.hasOwnProperty(depth)) {
                    continue;
                }
                var instanceId = tags[depth];
                var instance = myStage.getInstance(instanceId);
                if (!instance) {
                    continue;
                }
                instance.addActions(stage);
            }
        }
    };

    /**
     * @param frame
     * @returns {*}
     */
    MovieClip.prototype.getActions = function (frame)
    {
        return this.actions[frame];
    };

    /**
     * @param frame
     * @param actionScript
     */
    MovieClip.prototype.setActions = function (frame, actionScript)
    {
        var _this = this;
        var actions = _this.actions;
        if (!(frame in actions)) {
            actions[frame] = [];
        }
        var length = actions[frame].length;
        actions[frame][length] = _this.createActionScript(actionScript);
    };

    /**
     * @param frame
     * @param action
     */
    MovieClip.prototype.overWriteAction = function (frame, action)
    {
        var _this = this;
        if (typeof frame === "string") {
            frame = _this.getLabel(frame);
        }
        frame = frame|0;
        if (frame > 0 && _this.getTotalFrames() >= frame) {
            _this.actions[frame] = [action];
        }
    };

    /**
     * @param frame
     * @param action
     */
    MovieClip.prototype.addAction = function (frame, action)
    {
        var _this = this;
        if (typeof frame === "string") {
            frame = _this.getLabel(frame);
        }
        frame = frame|0;
        if (frame > 0 && _this.getTotalFrames() >= frame) {
            var actions = _this.actions;
            if (!(frame in actions)) {
                actions[frame] = [];
            }
            var length = actions[frame].length;
            actions[frame][length] = action;
        }
    };

    /**
     * @param frame
     */
    MovieClip.prototype.executeActions = function (frame)
    {
        var _this = this;
        var actions = _this.getActions(frame);
        if (actions !== undefined) {
            var length = actions.length;
            for (var i = 0; i < length; i++) {
                actions[i].apply(_this);
            }
        }
    };

    /**
     * ASSetPropFlags
     */
    MovieClip.prototype.ASSetPropFlags = function ()
    {
        // object, properties, n, allowFalse
    };

    /**
     * @param rgb
     * @param alpha
     */
    MovieClip.prototype.beginFill = function (rgb, alpha)
    {
        var graphics = this.getGraphics();
        graphics.beginFill(rgb, alpha);
    };

    /**
     * @param width
     * @param rgb
     * @param alpha
     * @param pixelHinting
     * @param noScale
     * @param capsStyle
     * @param jointStyle
     * @param miterLimit
     */
    MovieClip.prototype.lineStyle = function (width, rgb, alpha, pixelHinting, noScale, capsStyle, jointStyle, miterLimit)
    {
        var graphics = this.getGraphics();
        graphics.lineStyle(width, rgb, alpha, pixelHinting, noScale, capsStyle, jointStyle, miterLimit);
    };

    /**
     * @param dx
     * @param dy
     */
    MovieClip.prototype.moveTo = function (dx, dy)
    {
        var graphics = this.getGraphics();
        graphics.moveTo(dx, dy);
    };

    /**
     * @param dx
     * @param dy
     */
    MovieClip.prototype.lineTo = function (dx, dy)
    {
        var graphics = this.getGraphics();
        graphics.lineTo(dx, dy);
    };

    /**
     * @param cx
     * @param cy
     * @param dx
     * @param dy
     */
    MovieClip.prototype.curveTo = function (cx, cy, dx, dy)
    {
        var graphics = this.getGraphics();
        graphics.curveTo(cx, cy, dx, dy);
    };

    /**
     * clear
     */
    MovieClip.prototype.clear = function ()
    {
        var graphics = this.getGraphics();
        graphics.clear();
    };

    /**
     * endFill
     */
    MovieClip.prototype.endFill = function ()
    {
        var graphics = this.getGraphics();
        graphics.endFill();
    };

    /**
     * buildAVM2
     */
    MovieClip.prototype.buildAVM2 = function ()
    {
        var _this = this;
        var stage = _this.getStage();
        var symbol = stage.symbols[_this.getCharacterId()];
        if (symbol) {
            var symbols = symbol.split(".");
            var classMethod = symbols.pop();
            var length = symbols.length;
            var classObj = stage.avm2;
            var abcObj = stage.abc;
            for (var i = 0; i < length; i++) {
                classObj = classObj[symbols[i]];
                abcObj = abcObj[symbols[i]];
            }

            // build abc
            var DoABC = abcObj[classMethod];
            var ABCObj = new DoABC(_this);
            classObj[classMethod] = ABCObj;
            _this.avm2 = ABCObj;
            // AVM2 init
            var AVM2 = ABCObj[classMethod];
            if (typeof AVM2 === "function") {
                _this.actions = [];
                AVM2.apply(_this);
            }
        }
    };

    /**
     * @constructor
     */
    var MovieClipLoader = function ()
    {
        var _this = this;
        _this.events = {
            onLoadStart: undefined,
            onLoadProgress: undefined,
            onLoadComplete: undefined,
            onLoadInit: undefined,
            onLoadError: undefined
        };
    };

    /**
     * @param url
     * @param target
     * @returns {boolean}
     */
    MovieClipLoader.prototype.loadClip = function (url, target)
    {
        if (!url || !target) {
            return false;
        }

        var _this = this;
        var events = _this.events;

        var xmlHttpRequest = new XMLHttpRequest();
        xmlHttpRequest.open("GET", url, true);

        if (isXHR2) {
            xmlHttpRequest.responseType = "arraybuffer";
        } else {
            xmlHttpRequest.overrideMimeType("text/plain; charset=x-user-defined");
        }

        var onLoadProgress = events.onLoadProgress;
        if (!onLoadProgress) {
            onLoadProgress = _this.onLoadProgress;
        }
        if (typeof onLoadProgress === "function") {
            xmlHttpRequest.onprogress = function (e) {
                onLoadProgress.apply(_this, [target, e.loaded, e.total]);
            };
        }

        var onLoadComplete = events.onLoadComplete;
        if (!onLoadComplete) {
            onLoadComplete = _this.onLoadComplete;
        }
        if (typeof onLoadComplete === "function") {
            xmlHttpRequest.onloadend = function (e) {
                var eventStatus = e.currentTarget.status;
                if (eventStatus === 200) {
                    onLoadComplete.apply(_this, [target, eventStatus]);
                }
            };
        }

        xmlHttpRequest.onreadystatechange = function ()
        {
            var readyState = xmlHttpRequest.readyState;
            if (readyState === 4) {
                var status = xmlHttpRequest.status;

                var onLoadStart = events.onLoadStart;
                if (!onLoadStart) {
                    onLoadStart = _this.onLoadStart;
                }
                if (typeof onLoadStart === "function") {
                    xmlHttpRequest.onloadstart = function ()
                    {
                        onLoadStart.apply(_this, [target]);
                    };
                }

                switch (status) {
                    case 200:
                    case 304:
                        var _root = target.getDisplayObject("_root");
                        var rootStage = _root.getStage();
                        var data = isXHR2 ? xmlHttpRequest.response : xmlHttpRequest.responseText;

                        var loadStage = new Stage();
                        loadStages[loadStage.getId()] = loadStage;
                        target._url = url;
                        target.reset();
                        target.setLoadStage(loadStage);

                        loadStage.setParent(target);
                        loadStage.parse(data, url);
                        loadStage.stop();

                        // onLoadInit
                        var onLoadInit = events.onLoadInit;
                        if (!onLoadInit) {
                            onLoadInit = _this.onLoadInit;
                        }
                        if (typeof onLoadInit === "function") {
                            var queue = (function (as, loader, mc) {
                                return function () {
                                    return as.apply(loader, [mc]);
                                };
                            })(onLoadInit, _this, target);
                            target.events.load = [queue];
                        }

                        target.addActions(rootStage);

                        break;
                    default:
                        var onLoadError = events.onLoadError;
                        if (!onLoadError) {
                            onLoadError = _this.onLoadError;
                        }
                        if (typeof onLoadError === "function") {
                            onLoadError.apply(_this, [target, "error", status]);
                        }
                        break;
                }
            }
        };
        xmlHttpRequest.send(null);

        return true;
    };

    /**
     * @param listener
     * @returns {boolean}
     */
    MovieClipLoader.prototype.addListener = function (listener)
    {
        var _this = this;
        if (listener && typeof listener === "object") {
            var events = ["onLoadStart", "onLoadProgress", "onLoadComplete", "onLoadInit", "onLoadError"];
            var variables = listener.variables;
            for (var i = 0; i < 5; i++) {
                var event = events[i];
                if (typeof listener[event] === "function") {
                    _this.events[event] = listener[event];
                } else if (variables && typeof variables[event] === "function") {
                    _this.events[event] = variables[event];
                }
            }
        }
        return true;
    };

    /**
     * @param listener
     * @returns {boolean}
     */
    MovieClipLoader.prototype.removeListener = function (listener)
    {
        var _this = this;
        if (listener && typeof listener === "object") {
            var events = ["onLoadStart", "onLoadProgress", "onLoadComplete", "onLoadInit", "onLoadError"];
            for (var i = 0; i < 5; i++) {
                var event = events[i];
                var variables = listener.variables;
                if (typeof listener[event] === "function" ||
                    (variables && typeof variables[event] === "function")
                ) {
                    _this.events[event] = undefined;
                }
            }
        }
        return true;
    };

    /**
     * @param target
     * @returns {{bytesLoaded: number, bytesTotal: number}}
     */
    MovieClipLoader.prototype.getProgress = function (target)
    {
        return {
            bytesLoaded: 0,
            bytesTotal: 0
        };
    };

    /**
     * @constructor
     */
    var LoadVars = function ()
    {
        var _this = this;
        _this.xmlHttpRequest = new XMLHttpRequest();
        _this.variables = {};
        _this.target = _this;
        _this.events = {
            onData: undefined,
            onLoad: undefined
        };
    };

    /**
     * properties
     */
    Object.defineProperties(LoadVars.prototype,
    {
        onData: {
            get: function () {
                return this.getProperty("onData");
            },
            set: function (onData) {
                this.setProperty("onData", onData);
            }
        },
        onLoad: {
            get: function () {
                return this.getProperty("onLoad");
            },
            set: function (onLoad) {
                this.setProperty("onLoad", onLoad);
            }
        }
    });

    /**
     * @param name
     * @returns {*}
     */
    LoadVars.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    LoadVars.prototype.setProperty = function (name, value)
    {
        this.variables[String(name)] = value;
    };

    /**
     * @param url
     * @returns {boolean}
     */
    LoadVars.prototype.load = function (url)
    {
        var _this = this;
        var xmlHttpRequest = _this.xmlHttpRequest;
        xmlHttpRequest.open("GET", url, true);
        xmlHttpRequest.onreadystatechange = function ()
        {
            var readyState = xmlHttpRequest.readyState;
            if (readyState === 4) {
                var src = decodeURIComponent(xmlHttpRequest.responseText);
                _this.decode(src);
                var onData = _this.onData;
                if (typeof onData === "function") {
                    onData.apply(src, [src]);
                }

                var onLoad;
                var status = xmlHttpRequest.status;
                switch (status) {
                    case 200:
                    case 304:
                        onLoad = _this.onLoad;
                        if (typeof onLoad === "function") {
                            onLoad.apply(src, [true]);
                        }
                        return true;
                    default:
                        onLoad = _this.onLoad;
                        if (typeof onLoad === "function") {
                            onLoad.apply(src, [false]);
                        }
                        return false;
                }
            }
        };
        xmlHttpRequest.send(null);
    };

    /**
     * @param url
     * @param target
     * @param method
     * @returns {boolean}
     */
    LoadVars.prototype.send = function (url, target, method)
    {
        var _this = this;
        var xmlHttpRequest = _this.xmlHttpRequest;
        var sendMethod = method ? method.toUpperCase() : "GET";
        xmlHttpRequest.open(sendMethod, url, true);
        if (sendMethod === "POST") {
            xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        }
        if (target instanceof LoadVars) {
            _this.target = target;
        }
        xmlHttpRequest.send(_this.toString());
        return true;
    };

    /**
     * @param url
     * @param target
     * @param method
     * @returns {boolean}
     */
    LoadVars.prototype.sendAndLoad = function (url, target, method)
    {
        var _this = this;
        _this.send(url, target, method);
        return _this.load(url);
    };

    /**
     * @param header
     * @param headerValue
     */
    LoadVars.prototype.addRequestHeader = function (header, headerValue)
    {
        var xmlHttpRequest = this.xmlHttpRequest;
        if (header instanceof Array) {
            var length = header.length;
            for (var i = 0; i < length;) {
                xmlHttpRequest.setRequestHeader(header[i++], headerValue[i++]);
            }
        } else {
            xmlHttpRequest.setRequestHeader(header, headerValue);
        }
    };

    /**
     * @param queryString
     */
    LoadVars.prototype.decode = function (queryString)
    {
        var variables = this.variables;
        var array = queryString.split("&");
        var length = array.length;
        for (var i = 0; i < length; i++) {
            var values = array[i];
            var splitData = values.split("=");
            if (splitData.length < 1) {
                continue;
            }
            variables[String(splitData[0])] = splitData[1];
        }
    };

    /**
     * @returns {number}
     */
    LoadVars.prototype.getBytesLoaded = function ()
    {
        return 1;
    };

    /**
     * @returns {number}
     */
    LoadVars.prototype.getBytesTotal = function ()
    {
        return 1;
    };

    /**
     * @returns {string}
     */
    LoadVars.prototype.toString = function ()
    {
        var variables = this.variables;
        var array = [];
        for (var prop in variables) {
            if (!variables.hasOwnProperty(prop)) {
                continue;
            }
            array[array.length] = prop + "=" + variables[prop];
        }
        return array.join("&");
    };

    /**
     * @constructor
     */
    var Xml = function ()
    {
        var _this = this;
        _this.ignoreWhite = false;
        _this.loaded = false;
        _this.status = 0;
        _this.variables = {};
    };

    /**
     * properties
     */
    Object.defineProperties(Xml.prototype,
    {
        onData: {
            get: function () {
                return this.getProperty("onData");
            },
            set: function (onData) {
                this.setProperty("onData", onData);
            }
        },
        onLoad: {
            get: function () {
                return this.getProperty("onLoad");
            },
            set: function (onLoad) {
                this.setProperty("onLoad", onLoad);
            }
        }
    });

    /**
     * @param name
     * @returns {*}
     */
    Xml.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    Xml.prototype.setProperty = function (name, value)
    {
        this.variables[String(name)] = value;
    };


    /**
     * @param url
     */
    Xml.prototype.load = function (url)
    {
        var _this = this;
        url = "" + url;
        var xmlHttpRequest = new XMLHttpRequest();
        xmlHttpRequest.open("GET", url, true);
        xmlHttpRequest.onreadystatechange = function ()
        {
            var readyState = xmlHttpRequest.readyState;
            if (readyState === 4) {
                var src = xmlHttpRequest.responseXML;
                var onData = _this.onData;
                if (typeof onData === "function") {
                    onData.apply(src, [src]);
                }

                var onLoad;
                var status = xmlHttpRequest.status;
                switch (status) {
                    case 200:
                    case 304:
                        onLoad = _this.onLoad;
                        if (typeof onLoad === "function") {
                            onLoad.apply(src, [true]);
                        }
                        return true;
                    default:
                        onLoad = _this.onLoad;
                        if (typeof onLoad === "function") {
                            onLoad.apply(src, [false]);
                        }
                        return false;
                }
            }
        };
        xmlHttpRequest.send(null);
    };

    /**
     * @param url
     * @param target
     * @param method
     */
    Xml.prototype.send = function (url, target, method)
    {
        var sendMethod = method ? method.toUpperCase() : "GET";
        if (target) {
            console.log(target);
        }
        var xmlHttpRequest = new XMLHttpRequest();
        xmlHttpRequest.open(sendMethod, url, true);
        xmlHttpRequest.send(null);
        return true;
    };

    /**
     * @param url
     * @param resultXML
     */
    Xml.prototype.sendAndLoad = function (url, resultXML)
    {
        var _this = this;
        _this.send(url);
        return _this.load(resultXML);
    };

    /**
     * @constructor
     */
    var Sound = function (target)
    {
        var _this = this;
        _this.target = target;
        _this.variables = {};
        _this.sounds = [];
        _this.volume = 100;
        _this.pan = 0;
        _this.transform = {ll: 100, lr: 100, rl: 100, rr: 100};
        _this.isStreamin = false;
        _this.movieClip = null;
    };

    /**
     * properties
     */
    Object.defineProperties(Sound.prototype,
    {
        onLoad: {
            get: function () {
                return this.getProperty("onLoad");
            },
            set: function (onLoad) {
                this.setProperty("onLoad", onLoad);
            }
        },
        onSoundComplete: {
            get: function () {
                return this.getProperty("onSoundComplete");
            },
            set: function (onSoundComplete) {
                this.setProperty("onSoundComplete", onSoundComplete);
            }
        }
    });

    /**
     * @param name
     * @returns {*}
     */
    Sound.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    Sound.prototype.setProperty = function (name, value)
    {
        this.variables[String(name)] = value;
    };

    /**
     * @param currentTime
     * @param loopCount
     */
    Sound.prototype.start = function (currentTime, loopCount)
    {
        var _this = this;
        var sounds = _this.sounds;

        var init = function (audio, time)
        {
            return function ()
            {
                audio.currentTime = time;
            };
        };

        var end = function (audio, sound)
        {
            return function ()
            {
                var volume = sound.volume;
                audio.loopCount--;
                if (audio.loopCount > 0) {
                    audio.volume = volume / 100;
                    audio.currentTime = 0;
                    audio.play();
                }

                var onSoundComplete = sound.onSoundComplete;
                if (onSoundComplete) {
                    onSoundComplete.apply(sound, [true]);
                }
            };
        };

        var audio;
        for (var id in sounds) {
            if (!sounds.hasOwnProperty(id)) {
                continue;
            }
            audio = sounds[id];
            audio.load();

            if (currentTime) {
                audio.addEventListener("canplay", init(audio, currentTime));
            }
            if (typeof loopCount === "number" && loopCount > 0) {
                audio.loopCount = loopCount;
                audio.addEventListener("ended", end(audio, _this));
            }

            audio.play();
        }
    };

    /**
     * stop
     */
    Sound.prototype.stop = function (id)
    {
        var sounds = this.sounds;
        var audio;
        if (id) {
            audio = sounds[id];
            if (audio) {
                audio.pause();
            }
        } else {
            for (var key in sounds) {
                if (!sounds.hasOwnProperty(key)) {
                    continue;
                }
                audio = sounds[key];
                audio.pause();
            }
        }
    };

    /**
     * @param url
     * @param bool
     */
    Sound.prototype.loadSound = function (url, bool)
    {
        var _this = this;
        _this.isStreamin = bool;

        var sounds = _this.sounds;
        var audio = _document.createElement("audio");
        audio.src = url;
        sounds[0] = audio;

        var onLoad = (function (audio, sound)
        {
            return function() {
                audio.load();
                audio.preload = "auto";
                audio.autoplay = false;
                audio.loop = false;
                var onLoad = sound.onLoad;
                if (typeof onLoad === "function") {
                    onLoad.apply(sound, [true]);
                }
            };
        })(audio, _this);
        audio.addEventListener("canplaythrough", onLoad);

        var onError = (function (audio, sound)
        {
            return function() {
                var onLoad = sound.onLoad;
                if (typeof onLoad === "function") {
                    onLoad.apply(sound, [false]);
                }
            };
        })(audio, _this);
        audio.addEventListener("error", onError);
    };

    /**
     * @param id
     */
    Sound.prototype.attachSound = function (id)
    {
        var _this = this;
        var sounds = [];
        var movieClip = _this.movieClip;
        var stage = movieClip.getStage();
        var exportAssets = stage.exportAssets;
        if (id in exportAssets) {
            var characterId = exportAssets[id];
            var tag = stage.sounds[characterId];
            if (tag) {
                var audio = _document.createElement("audio");
                audio.onload = function ()
                {
                    this.load();
                    this.preload = "auto";
                    this.autoplay = false;
                    this.loop = false;
                };
                audio.src = tag.base64;
                sounds[id] = audio;
            }
        }

        _this.sounds = sounds;
    };

    /**
     *
     * @returns {number}
     */
    Sound.prototype.getVolume = function ()
    {
        return this.volume;
    };

    /**
     *
     * @param volume
     */
    Sound.prototype.setVolume = function (volume)
    {
        var _this = this;
        var sounds = _this.sounds;
        _this.volume = volume;
        for (var id in sounds) {
            if (!sounds.hasOwnProperty(id)) {
                continue;
            }
            var audio = sounds[id];
            audio.volume = _max(0, _min(1, volume / 100));
        }
    };

    /**
     * @returns {number|*}
     */
    Sound.prototype.getPan = function ()
    {
        return this.pan;
    };

    /**
     * @param pan
     */
    Sound.prototype.setPan = function (pan)
    {
        this.pan = pan;
    };

    /**
     * @param object
     */
    Sound.prototype.setTransform = function (object)
    {
        var transform = this.transform;
        for (var name in object) {
            if (!object.hasOwnProperty(name)) {
                continue;
            }
            switch (name) {
                case "ll":
                case "lr":
                case "rl":
                case "rr":
                    transform[name] = object[name];
                    break;
            }
        }
    };

    /**
     * @returns {{ll: number, lr: number, rl: number, rr: number}|*}
     */
    Sound.prototype.getTransform = function ()
    {
        return this.transform;
    };

    /**
     * @returns {number}
     */
    Sound.prototype.getBytesLoaded = function ()
    {
        return 1;
    };

    /**
     * @returns {number}
     */
    Sound.prototype.getBytesTotal = function ()
    {
        return 1;
    };

    /**
     * @constructor
     */
    var SharedObject = function ()
    {
        var _this = this;
        _this.data = null;
        _this.name = null;
    };

    /**
     * @param name
     * @returns {SharedObject}
     */
    SharedObject.prototype.getLocal = function (name)
    {
        var _this = this;
        _this.name = name;
        var data = window.localStorage.getItem(name);
        if (!data) {
            data = {};
        } else {
            data = JSON.parse(data);
        }
        _this.data = data;
        return _this;
    };

    /**
     * flush
     */
    SharedObject.prototype.flush = function ()
    {
        var _this = this;
        window.localStorage.setItem(_this.name, JSON.stringify(_this.data));
        return true;
    };

    /**
     * @param mc
     * @constructor
     */
    var Color = function (mc)
    {
        var _this = this;
        _this.movieClip = mc;
        _this.variables = {};
    };

    /**
     *
     * @param name
     * @returns {*}
     */
    Color.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    Color.prototype.setProperty = function (name, value)
    {
        this.variables[String(name)] = value;
    };

    /**
     * @param int
     * @param alpha
     * @returns {{R: number, G: number, B: number, A: number}}
     */
    Color.prototype.intToRGBA = function (int, alpha)
    {
        alpha = alpha || 100;
        return {
            R: (int & 0xff0000) >> 16,
            G: (int & 0x00ff00) >> 8,
            B: (int & 0x0000ff),
            A: (alpha / 100)
        };
    };

    /**
     * @param offset
     */
    Color.prototype.setRGB = function (offset)
    {
        var _this = this;
        var mc = _this.movieClip;
        if (mc instanceof MovieClip) {
            offset |= 0;
            var obj = _this.intToRGBA(offset);
            var colorTransform = mc.getOriginColorTransform();
            if (colorTransform) {
                var transform = [obj.R, obj.G, obj.B, obj.A * 255, 0, 0, 0, 0];
                var multiColor = mc.cloneArray(transform);
                var color = mc.multiplicationColor(colorTransform, multiColor);
                mc.setColorTransform(color);
            }
        }
    };

    /**
     * @returns {*[]|*}
     */
    Color.prototype.getTransform = function ()
    {
        var _this = this;
        var mc = _this.movieClip;
        if (mc instanceof MovieClip) {
            return mc.getColorTransform();
        }
        return undefined;
    };

    /**
     * @param obj
     */
    Color.prototype.setTransform = function (obj)
    {
        var _this = this;
        var mc = _this.movieClip;
        if (mc instanceof MovieClip) {
            var colorTransform = mc.getOriginColorTransform();
            var transform = [
                obj.rb, obj.gb, obj.bb, obj.ab,
                obj.ra, obj.ga, obj.ba, obj.aa
            ];
            var multiColor = mc.cloneArray(transform);
            var color = mc.multiplicationColor(colorTransform, multiColor);
            mc.setColorTransform(color);
        }
    };

    /**
     * @constructor
     */
    var Mouse = function ()
    {
        this.events = {};
    };

    /**
     * @returns {undefined}
     */
    Mouse.prototype.show = function ()
    {
        return undefined;
    };

    /**
     * @returns {undefined}
     */
    Mouse.prototype.hide = function ()
    {
        return undefined;
    };

    /**
     * @param listener
     */
    Mouse.prototype.addListener = function (listener)
    {
        var _this = this;
        if (listener && typeof listener === "object") {
            var events = ["onMouseDown", "onMouseMove", "onMouseUp"];
            var variables = listener.variables;
            for (var i = 0; i < 5; i++) {
                var event = events[i];
                if (typeof listener[event] === "function") {
                    _this.events[event] = listener[event];
                } else if (variables && typeof variables[event] === "function") {
                    _this.events[event] = variables[event];
                }
            }
        }
        return true;
    };

    /**
     * @param listener
     */
    Mouse.prototype.removeListener = function (listener)
    {
        var _this = this;
        if (listener && typeof listener === "object") {
            var events = ["onMouseDown", "onMouseMove", "onMouseUp"];
            for (var i = 0; i < 5; i++) {
                var event = events[i];
                var variables = listener.variables;
                if (typeof listener[event] === "function" ||
                    (variables && typeof variables[event] === "function")
                ) {
                    _this.events[event] = undefined;
                }
            }
        }
        return true;
    };

    /**
     * @constructor
     */
    var Key = function ()
    {
        var _this = this;
        _this.variables = {};
        _this.codes = [];
        _this._listeners = [];
    };

    /**
     * properties
     */
    Object.defineProperties(Key.prototype,
    {
        onKeyDown: {
            get: function () {
                return this.getProperty("onKeyDown");
            },
            set: function (onKeyDown) {
                this.setProperty("onKeyDown", onKeyDown);
            }
        },
        onKeyUp: {
            get: function () {
                return this.getProperty("onKeyUp");
            },
            set: function (onKeyUp) {
                this.setProperty("onKeyUp", onKeyUp);
            }
        }
    });

    /**
     * @type {number}
     */
    Key.prototype.BACKSPACE = 8;
    Key.prototype.CAPSLOCK = 20;
    Key.prototype.CONTROL = 17;
    Key.prototype.DELETEKEY = 46;
    Key.prototype.DOWN = 40;
    Key.prototype.END = 35;
    Key.prototype.ENTER = 13;
    Key.prototype.ESCAPE = 27;
    Key.prototype.HOME = 36;
    Key.prototype.INSERT = 45;
    Key.prototype.LEFT = 37;
    Key.prototype.PGDN = 34;
    Key.prototype.PGDN = 34;
    Key.prototype.PGUP = 33;
    Key.prototype.RIGHT = 39;
    Key.prototype.SHIFT = 16;
    Key.prototype.SPACE = 32;
    Key.prototype.TAB = 9;
    Key.prototype.UP = 38;
    Key.prototype.codes = [];

    /**
     * @param name
     * @returns {*}
     */
    Key.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    Key.prototype.setProperty = function (name, value)
    {
        this.variables[String(name)] = value;
    };

    /**
     *
     * @param listener
     * @returns {boolean}
     */
    Key.prototype.addListener = function (listener)
    {
        var _this = this;
        var onKeyDown = listener.onKeyDown;
        if (onKeyDown) {
            _this.onKeyDown = onKeyDown;
        }
        var onKeyUp = listener.onKeyUp;
        if (onKeyUp) {
            _this.onKeyUp = onKeyUp;
        }
        return true;
    };

    /**
     * @param code
     * @returns {boolean}
     */
    Key.prototype.isDown = function (code)
    {
        var codes = keyClass.codes;
        for (var idx = 0; idx < codes.length; ++idx) {
            if (codes[idx] !== code) {
                continue;
            }
            if (_keyEvent) {
                _keyEvent.preventDefault();
            }
            return true;
        }
    };

    /**
     * @returns {*}
     */
    Key.prototype.getCode = function ()
    {
        var keyCode = (_keyEvent) ? _keyEvent.keyCode : null;
        if (96 <= keyCode && keyCode <= 105) {
            var n = keyCode - 96;
            switch (n) {
                case 0:
                    keyCode = 48;
                    break;
                case 1:
                    keyCode = 49;
                    break;
                case 2:
                    keyCode = 50;
                    break;
                case 3:
                    keyCode = 51;
                    break;
                case 4:
                    keyCode = 52;
                    break;
                case 5:
                    keyCode = 53;
                    break;
                case 6:
                    keyCode = 54;
                    break;
                case 7:
                    keyCode = 55;
                    break;
                case 8:
                    keyCode = 56;
                    break;
                case 9:
                    keyCode = 57;
                    break;
            }
        }
        return keyCode;
    };
    var keyClass = new Key();

    /**
     * @param event
     */
    function keyUpAction(event)
    {
        _keyEvent = event;
        var onKeyUp = keyClass.onKeyUp;
        if (typeof onKeyUp === "function") {
            onKeyUp.apply(keyClass, [event]);
        }

        var keyCode = keyClass.getCode();
        var index = keyClass.codes.indexOf(keyCode);
        if (index > -1) {
            keyClass.codes.splice(index, 1);
        }
    }

    /**
     * @param event
     */
    function keyDownAction(event)
    {
        _keyEvent = event;
        var keyCode = keyClass.getCode();
        if (keyClass.codes.indexOf(keyCode) === -1) {
            keyClass.codes.push(keyCode);
        }

        var keyHit = false;
        var i;
        var length;
        var obj;
        var onKeyDown = keyClass.onKeyDown;
        if (typeof onKeyDown === "function") {
            keyHit = true;
            onKeyDown.apply(keyClass, [event]);
        }

        var idx;
        length = stages.length;
        for (var pIdx = 0; pIdx < length; pIdx++) {
            if (!(pIdx in stages)) {
                continue;
            }

            var stage = stages[pIdx];
            var keyDownEventHits = stage.keyDownEventHits;
            var kLen = keyDownEventHits.length;
            if (kLen) {
                keyHit = true;
                for (idx = 0; idx < kLen; idx++) {
                    obj = keyDownEventHits[idx];
                    stage.executeEventAction(obj.as, obj.mc);
                }
            }

            var buttonHits = stage.buttonHits;
            var len = buttonHits.length;
            var isEnd = false;
            for (i = len; i--;) {
                if (!(i in buttonHits)) {
                    continue;
                }

                var hitObj = buttonHits[i];
                if (!hitObj) {
                    continue;
                }

                var button = hitObj.button;
                if (!button) {
                    continue;
                }

                var actions = button.getActions();
                if (!actions) {
                    continue;
                }

                var aLen = actions.length;
                for (idx = 0; idx < aLen; idx++) {
                    if (!(idx in actions)) {
                        continue;
                    }

                    var cond = actions[idx];
                    var CondKeyPress = cond.CondKeyPress;
                    switch (CondKeyPress) {
                        case 1: // left arrow
                            CondKeyPress = 37;
                            break;
                        case 2: // right arrow
                            CondKeyPress = 39;
                            break;
                        case 3: // home
                            CondKeyPress = 36;
                            break;
                        case 4: // end
                            CondKeyPress = 35;
                            break;
                        case 5: // insert
                            CondKeyPress = 45;
                            break;
                        case 6: // delete
                            CondKeyPress = 46;
                            break;
                        case 14: // up arrow
                            CondKeyPress = 38;
                            break;
                        case 15: // down arrow
                            CondKeyPress = 40;
                            break;
                        case 16: // page up
                            CondKeyPress = 33;
                            break;
                        case 17: // page down
                            CondKeyPress = 34;
                            break;
                        case 18: // tab
                            CondKeyPress = 9;
                            break;
                        case 19: // escape
                            CondKeyPress = 27;
                            break;
                    }

                    if (CondKeyPress !== keyCode) {
                        continue;
                    }

                    stage.buttonAction(hitObj.parent, cond.ActionScript);
                    stage.touchRender();
                    isEnd = true;
                    keyHit = true;
                    break;
                }

                if (isEnd) {
                    keyHit = true;
                    break;
                }
            }
        }

        if (keyHit || typeof onKeyDown === "function") {
            event.preventDefault();
            return false;
        }
    }

    /**
     * @constructor
     */
    var Global = function ()
    {
        this.variables = {};
    };

    /**
     *
     * @param name
     * @returns {*}
     */
    Global.prototype.getVariable = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     * @returns {*}
     */
    Global.prototype.setVariable = function (name, value)
    {
        this.variables[name] = value;
    };

    /**
     * @param name
     * @returns {*}
     */
    Global.prototype.getProperty = function (name)
    {
        return this.variables[name];
    };

    /**
     * @param name
     * @param value
     */
    Global.prototype.setProperty = function (name, value)
    {
        this.variables[name] = value;
    };

    /**
     * @constructor
     * @param stage
     */
    var packages = function (stage)
    {
        this.stage = stage;

    };

    /**
     * @type {*}
     */
    packages.prototype =
    {
        "flash": {
            "display": {
                "MovieClip": MovieClip,
                "Sprite": Sprite,
                "DisplayObjectContainer": DisplayObjectContainer,
                "InteractiveObject": InteractiveObject,
                "DisplayObject": DisplayObject,
                "Graphics": Graphics
            },
            "events": {
                "EventDispatcher": EventDispatcher,
                "MouseEvent": clipEvent
            },
            "text": {
                "StaticText": StaticText
            },
            "media": {
                "Sound": Sound
            },
            "system": {
                "fscommand": function ()
                {
                    var command = arguments[0];
                    var args = arguments[1];
                    if (args === undefined) {
                        args = "";
                    }

                    switch (command) {
                        case "quit":
                        case "fullscreen":
                        case "allowscale":
                        case "showmenu":
                        case "exec":
                        case "trapallkeys":
                            break;
                        default:
                            if (command) {
                                var _this = this;
                                var method = (_this.tagId) ? _this.tagId : _this.getName();
                                var body = method +"_DoFSCommand(command, args);";
                                var fscommand = new Func("command", "args", body);
                                fscommand(command, args);
                            }
                            break;
                    }

                    return true;
                }
            }
        }
    };

    /**
     * @constructor
     */
    var Stage = function ()
    {
        var _this = this;
        _this.id = stageId++;
        _this.name = "swf2js_" + _this.id;
        _this.intervalId = 0;

        _this.frameRate = 0;
        _this.fileSize = 0;
        _this.stopFlag = true;

        // options
        _this.optionWidth = 0;
        _this.optionHeight = 0;
        _this.callback = null;
        _this.renderMode = isWebGL;
        _this.tagId = null;
        _this.FlashVars = {};
        _this.quality = "medium"; // low = 0.25, medium = 0.8, high = 1.0
        _this.bgcolor = null;

        // event
        _this.mouse = new Mouse();

        // params
        _this.context = null;
        _this.canvas = null;
        _this.preContext = null;
        _this.hitContext = null;
        _this.matrix = [1,0,0,1,0,0];
        _this._matrix = [1,0,0,1,0,0];
        _this._colorTransform = [1,1,1,1,0,0,0,0];
        _this.characters = [];
        _this.initActions = [];
        _this.exportAssets = [];
        _this.packages = [];
        _this.registerClass = [];
        _this.buttonHits = [];
        _this.downEventHits = [];
        _this.moveEventHits = [];
        _this.upEventHits = [];
        _this.keyDownEventHits = [];
        _this.keyUpEventHits = [];
        _this.sounds = [];
        _this.loadSounds = [];
        _this.videos = [];
        _this.actions = [];
        _this.instances = [];
        _this.placeObjects = [];
        _this.fonts = [];
        _this.isAction = true;
        _this._global = new Global();
        _this.touchObj = null;
        _this.touchStatus = "up";
        _this.overObj = null;
        _this.touchEndAction = null;
        _this.imgUnLoadCount = 0;
        _this.scale = 1;
        _this.baseWidth = 0;
        _this.baseHeight = 0;
        _this.width = 0;
        _this.height = 0;
        _this.isHit = false;
        _this.isTouchEvent = false;
        _this.isLoad = false;
        _this.jpegTables = null;
        _this.backgroundColor = "transparent";
        _this.version = 8;
        _this.loadStatus = 0;
        _this.isClipDepth = false;
        _this.clipDepth = 0;
        _this.clipMc = false;
        _this.dragMc = null;
        _this.dragRules = null;
        _this.scaleMode = "showAll";
        _this.align = "";
        _this.avm2 = new packages(_this);
        _this.abc = new packages(_this);
        _this.symbols = [];
        _this.abcFlag = false;

        // render
        _this.doneTags = [];
        _this.newTags = [];

        // init
        var mc = new MovieClip();
        mc.setStage(_this);
        _this.setParent(mc);
    };

    /**
     * @returns {number|*}
     */
    Stage.prototype.getId = function ()
    {
        return this.id;
    };

    /**
     * @param id
     */
    Stage.prototype.setId = function (id)
    {
        this.id = id;
    };

    /**
     * @returns {*}
     */
    Stage.prototype.getParent = function ()
    {
        return this.parent;
    };

    /**
     * @param parent
     */
    Stage.prototype.setParent = function (parent)
    {
        this.parent = parent;
    };

    /**
     * @returns {number|*}
     */
    Stage.prototype.getVersion = function ()
    {
        return this.version;
    };

    /**
     * @param version
     */
    Stage.prototype.setVersion = function (version)
    {
        this.version = version;
    };

    /**
     *
     * @returns {string}
     */
    Stage.prototype.getBackgroundColor = function ()
    {
        return this.backgroundColor;
    };

    /**
     * @param r
     * @param g
     * @param b
     */
    Stage.prototype.setBackgroundColor = function (r, g, b)
    {
        this.backgroundColor = "rgb(" + r + "," + g + "," + b + ")";
    };

    /**
     * @returns {Array}
     */
    Stage.prototype.getGlobal = function ()
    {
        return this._global;
    };

    /**
     * play
     */
    Stage.prototype.play = function ()
    {
        var _this = this;
        _this.stopFlag = false;

        var enterFrame = function (stage) {
            return function () {
                requestAnimationFrame(function () {
                    if (stage.isLoad && !stage.stopFlag) {
                        stage.nextFrame();
                    }
                }, 0);
            };
        };
        _this.intervalId = _setInterval(enterFrame(_this), _this.getFrameRate());
    };

    /**
     * stop
     */
    Stage.prototype.stop = function ()
    {
        var _this = this;
        _this.stopFlag = true;
        _clearInterval(_this.intervalId);
    };

    /**
     * @returns {*}
     */
    Stage.prototype.getName = function ()
    {
        return this.name;
    };

    /**
     * @param name
     */
    Stage.prototype.setName = function (name)
    {
        this.name = name;
    };

    /**
     * @param options
     */
    Stage.prototype.setOptions = function (options)
    {
        if (options !== undefined) {
            var _this = this;
            _this.optionWidth = options.width || _this.optionWidth;
            _this.optionHeight = options.height || _this.optionHeight;
            _this.callback = options.callback || _this.callback;
            _this.tagId = options.tagId || _this.tagId;
            _this.renderMode = options.renderMode || _this.renderMode;
            _this.FlashVars = options.FlashVars || _this.FlashVars;
            _this.quality = options.quality || _this.quality;
            _this.bgcolor = options.bgcolor || _this.bgcolor;

            // quality
            switch (_this.quality) {
                case "low":
                    _devicePixelRatio = devicePixelRatio * 0.5;
                    break;
                case "high":
                    _devicePixelRatio = devicePixelRatio;
                    break;
            }
        }
    };

    /**
     * @returns {number}
     */
    Stage.prototype.getBaseWidth = function ()
    {
        return this.baseWidth;
    };

    /**
     * @param baseWidth
     */
    Stage.prototype.setBaseWidth = function (baseWidth)
    {
        this.baseWidth = baseWidth;
    };

    /**
     *
     * @returns {number}
     */
    Stage.prototype.getBaseHeight = function ()
    {
        return this.baseHeight;
    };

    /**
     * @param baseHeight
     */
    Stage.prototype.setBaseHeight = function (baseHeight)
    {
        this.baseHeight = baseHeight;
    };

    /**
     *
     * @returns {number}
     */
    Stage.prototype.getWidth = function ()
    {
        return this.width;
    };

    /**
     * @param width
     */
    Stage.prototype.setWidth = function (width)
    {
        if (width < 0) {
            width *= -1;
        }
        this.width = width;
    };

    /**
     * @returns {number}
     */
    Stage.prototype.getHeight = function ()
    {
        return this.height;
    };

    /**
     * @param height
     */
    Stage.prototype.setHeight = function (height)
    {
        if (height < 0) {
            height *= -1;
        }
        this.height = height;
    };

    /**
     * @returns {number}
     */
    Stage.prototype.getScale = function ()
    {
        return this.scale;
    };

    /**
     * @param scale
     */
    Stage.prototype.setScale = function (scale)
    {
        this.scale = scale;
    };

    /**
     * @returns {*}
     */
    Stage.prototype.getMatrix = function ()
    {
        return this.matrix;
    };

    /**
     * @param matrix
     */
    Stage.prototype.setMatrix = function (matrix)
    {
        this.matrix = matrix;
    };

    /**
     * @param id
     * @returns {*}
     */
    Stage.prototype.getCharacter = function (id)
    {
        return this.characters[id];
    };

    /**
     * @param id
     * @param obj
     */
    Stage.prototype.setCharacter = function (id, obj)
    {
        this.characters[id] = obj;
    };

    /**
     * @param id
     * @returns {*}
     */
    Stage.prototype.getInstance = function (id)
    {
        return this.instances[id];
    };

    /**
     * @param instance
     */
    Stage.prototype.setInstance = function (instance)
    {
        this.instances[instance.instanceId] = instance;
    };

    /**
     * @param instanceId
     * @param depth
     * @param frame
     * @returns {*}
     */
    Stage.prototype.getPlaceObject = function (instanceId, depth, frame)
    {
        var placeObjects = this.placeObjects;
        if (!(instanceId in placeObjects)) {
            return null;
        }
        var placeObject = placeObjects[instanceId];
        if (!(frame in placeObject)) {
            return null;
        }
        var tags = placeObject[frame];
        if (!(depth in tags)) {
            return null;
        }
        return tags[depth];
    };

    /**
     * @param placeObject
     * @param instanceId
     * @param depth
     * @param frame
     */
    Stage.prototype.setPlaceObject = function (placeObject, instanceId, depth, frame)
    {
        var _this = this;
        var placeObjects = _this.placeObjects;
        if (!(instanceId in placeObjects)) {
            placeObjects[instanceId] = [];
        }
        if (!(frame in placeObjects[instanceId])) {
            placeObjects[instanceId][frame] = [];
        }
        placeObjects[instanceId][frame][depth] = placeObject;
    };

    /**
     * @param instanceId
     * @param depth
     * @param frame
     */
    Stage.prototype.copyPlaceObject = function (instanceId, depth, frame)
    {
        var _this = this;
        var placeObject = _this.getPlaceObject(instanceId, depth, frame - 1);
        _this.setPlaceObject(placeObject, instanceId, depth, frame);
    };

    /**
     * @param instanceId
     */
    Stage.prototype.removePlaceObject = function (instanceId)
    {
        delete this.placeObjects[instanceId];
        // delete this.instances[instanceId];
    };

    /**
     * @returns {number}
     */
    Stage.prototype.getFrameRate = function ()
    {
        return this.frameRate;
    };

    /**
     * @param fps
     */
    Stage.prototype.setFrameRate = function (fps)
    {
        this.frameRate = (1000 / fps) | 0;
    };

    /**
     * loadStatus CountUp
     */
    Stage.prototype.loadEvent = function ()
    {
        var _this = this;
        switch (_this.loadStatus) {
            case 2:
                _this.resize();
                _this.loadStatus++;
                break;
            case 3:
                if (!_this.isLoad || !_this.stopFlag || _this.imgUnLoadCount > 0) {
                    break;
                }
                _this.loadStatus++;
                _this.loaded();
                break;
        }
        if (_this.loadStatus !== 4) {
            _setTimeout(function () { _this.loadEvent(); }, 0);
        }
    };

    /**
     * @param data
     * @param url
     */
    Stage.prototype.parse = function (data, url)
    {
        var _this = this;
        _this.isLoad = false;
        var bitio = new BitIO();
        var swftag = new SwfTag(_this, bitio);

        if (isXHR2) {
            bitio.setData(new Uint8Array(data));
        } else {
            bitio.init(data);
        }

        if (_this.setSwfHeader(bitio, swftag)) {
            var mc = _this.getParent();
            mc._url = location.href;

            // parse
            var tags = swftag.parse(mc);

            // mc reset
            mc.container = [];
            var frame = 1;
            var totalFrames = mc.getTotalFrames() + 1;
            while (frame < totalFrames) {
                mc.container[frame++] = [];
            }
            mc.instances = [];

            // build
            swftag.build(tags, mc);

            var query = url.split("?")[1];
            if (query) {
                var values = query.split("&");
                var length = values.length;
                while (length--) {
                    var value = values[length];
                    var pair = value.split("=");
                    if (pair.length > 1) {
                        mc.setVariable(pair[0], pair[1]);
                    }
                }
            }

            // FlashVars
            var vars = _this.FlashVars;
            for (var key in vars) {
                if (!vars.hasOwnProperty(key)) {
                    continue;
                }
                mc.setVariable(key, vars[key]);
            }
        }

        _this.isLoad = true;
    };

    /**
     * @param bitio
     * @param swftag
     * @returns {boolean}
     */
    Stage.prototype.setSwfHeader = function (bitio, swftag)
    {
        var _this = this;

        var data = bitio.data;
        if (data[0] === 0xff && data[1] === 0xd8) {
            _this.parseJPEG(data, swftag);
            return false;
        }

        // signature
        var signature = bitio.getHeaderSignature();

        // version
        var version = bitio.getVersion();
        _this.setVersion(version);

        // file size
        var fileLength = bitio.getUI32();
        _this.fileSize = fileLength;

        switch (signature) {
            case "FWS": // No ZIP
                break;
            case "CWS": // ZLIB
                bitio.deCompress(fileLength, "ZLIB");
                break;
            case "ZWS": // TODO LZMA
                alert("not support LZMA");
                //bitio.deCompress(fileLength, "LZMA");
                return false;
        }

        var bounds = swftag.rect();
        var frameRate = bitio.getUI16() / 0x100;
        bitio.getUI16(); // frameCount

        _this.setBaseWidth(_ceil((bounds.xMax - bounds.xMin) / 20));
        _this.setBaseHeight(_ceil((bounds.yMax - bounds.yMin) / 20));
        _this.setFrameRate(frameRate);

        _this.loadStatus++;

        return true;
    };

    /**
     * @param data
     * @param swftag
     */
    Stage.prototype.parseJPEG = function (data, swftag)
    {
        var _this = this;
        var image = _document.createElement("img");
        image.addEventListener("load", function ()
        {
            var width = this.width;
            var height = this.height;

            var canvas = cacheStore.getCanvas();
            canvas.width = width;
            canvas.height = height;
            var imageContext = canvas.getContext("2d");
            imageContext.drawImage(this, 0, 0, width, height);
            _this.setCharacter(1, imageContext);

            var shapeWidth = width * 20;
            var shapeHeight = height * 20;

            _this.setBaseWidth(width);
            _this.setBaseHeight(height);

            var shape = {
                ShapeRecords: [
                    {
                        FillStyle1: 1,
                        StateFillStyle0: 0,
                        StateFillStyle1: 1,
                        StateLineStyle: 0,
                        StateMoveTo: 0,
                        StateNewStyles: 0,
                        isChange: true
                    },
                    {
                        AnchorX: shapeWidth,
                        AnchorY: 0,
                        ControlX: 0,
                        ControlY: 0,
                        isChange: false,
                        isCurved: false
                    },
                    {
                        AnchorX: shapeWidth,
                        AnchorY: shapeHeight,
                        ControlX: 0,
                        ControlY: 0,
                        isChange: false,
                        isCurved: false
                    },
                    {
                        AnchorX: 0,
                        AnchorY: shapeHeight,
                        ControlX: 0,
                        ControlY: 0,
                        isChange: false,
                        isCurved: false
                    },
                    {
                        AnchorX: 0,
                        AnchorY: 0,
                        ControlX: 0,
                        ControlY: 0,
                        isChange: false,
                        isCurved: false
                    },
                    0
                ],
                fillStyles: {
                    fillStyleCount: 1,
                    fillStyles: [{
                        bitmapId: 1,
                        bitmapMatrix: [20, 0, 0, 20, 0, 0],
                        fillStyleType: 65
                    }]
                },
                lineStyles: {
                    lineStyleCount: 0,
                    lineStyles: []
                }
            };

            var bounds = {
                xMin: 0,
                xMax: shapeWidth,
                yMin: 0,
                yMax: shapeHeight
            };
            var data = vtc.convert(shape);

            _this.setCharacter(2, {
                tagType: 22,
                data: data,
                bounds: bounds
            });

            var parent = _this.getParent();
            var obj = new Shape();
            obj.setParent(parent);
            obj.setStage(_this);
            obj.setData(data);
            obj.setTagType(22);
            obj.setCharacterId(2);
            obj.setBounds(bounds);
            obj.setLevel(1);

            parent.container[1] = [];
            parent.container[1][1] = obj.instanceId;

            var placeObject = new PlaceObject();
            _this.setPlaceObject(placeObject, obj.instanceId, 1, 1);

            _this.init();
        });


        var length = data.length;
        var src = "";
        for (var i = 0; i < length; i++) {
            src += _fromCharCode(data[i]);
        }

        var jpegData = swftag.parseJpegData(data);
        image.src = "data:image/jpeg;base64," + swftag.base64encode(jpegData);
    };

    /**
     * resize
     */
    Stage.prototype.resize = function ()
    {
        var _this = this;
        var div = _document.getElementById(_this.getName());
        if (!div) {
            return 0;
        }

        var oWidth = _this.optionWidth;
        var oHeight = _this.optionHeight;

        var element = _document.documentElement;
        var innerWidth = _max(element.clientWidth, window.innerWidth || 0);
        var innerHeight = _max(element.clientHeight, window.innerHeight || 0);

        var parent = div.parentNode;
        if (parent.tagName !== "BODY") {
            innerWidth = parent.offsetWidth;
            innerHeight = parent.offsetHeight;
        }
        var screenWidth = (oWidth > 0) ? oWidth : innerWidth;
        var screenHeight = (oHeight > 0) ? oHeight : innerHeight;

        var baseWidth = _this.getBaseWidth();
        var baseHeight = _this.getBaseHeight();
        var scale = _min((screenWidth / baseWidth), (screenHeight / baseHeight));
        var width = baseWidth * scale;
        var height = baseHeight * scale;

        if (width !== _this.getWidth() || height !== _this.getHeight()) {
            // div
            var style = div.style;
            style.width = width + "px";
            style.height = height + "px";
            style.top = 0;
            style.left = ((screenWidth / 2) - (width / 2)) + "px";

            width *= devicePixelRatio;
            height *= devicePixelRatio;

            _this.setScale(scale);
            _this.setWidth(width);
            _this.setHeight(height);

            // main
            var canvas = _this.context.canvas;
            canvas.width = width;
            canvas.height = height;

            // pre
            var preCanvas = _this.preContext.canvas;
            preCanvas.width = width;
            preCanvas.height = height;

            var hitCanvas = _this.hitContext.canvas;
            hitCanvas.width = width;
            hitCanvas.height = height;

            // tmp
            if (isAndroid && isChrome) {
                var tmpCanvas = tmpContext.canvas;
                tmpCanvas.width = width;
                tmpCanvas.height = height;
            }

            var mc = _this.getParent();
            var mScale = scale * _devicePixelRatio / 20;
            _this.setMatrix(mc.cloneArray([mScale, 0, 0, mScale, 0, 0]));
        }
    };

    /**
     * loaded
     */
    Stage.prototype.loaded = function ()
    {
        // reset
        var _this = this;
        _this.buttonHits = [];
        _this.downEventHits = [];
        _this.moveEventHits = [];
        _this.upEventHits = [];
        _this.keyDownEventHits = [];
        _this.keyUpEventHits = [];
        _this.actions = [];

        // DOM
        _this.deleteNode();
        var div = _document.getElementById(_this.getName());
        if (div) {
            var mc = _this.getParent();
            mc.initFrame();
            mc.addActions(_this);
            _this.executeAction();

            // callback
            var callback = _this.callback;
            if (typeof callback === "function") {
                callback.call(window, mc);
            }

            _this.render();
            _this.renderMain();

            var ctx = _this.context;
            var canvas = ctx.canvas;

            // load sound
            if (isTouch) {
                var loadSounds = _this.loadSounds;
                var sLen = loadSounds.length;
                if (sLen) {
                    var loadSound = function ()
                    {
                        canvas.removeEventListener(startEvent, loadSound);
                        for (var i = sLen; i--;) {
                            if (!(i in loadSounds)) {
                                continue;
                            }
                            var audio = loadSounds[i];
                            audio.load();
                        }
                    };
                    canvas.addEventListener(startEvent, loadSound);
                }
            }

            canvas.addEventListener(startEvent, function (event)
            {
                _event = event;
                _this.touchStart(event);
            });

            canvas.addEventListener(moveEvent, function (event)
            {
                _event = event;
                _this.touchMove(event);
            });

            canvas.addEventListener(endEvent, function (event)
            {
                _event = event;
                _this.touchEnd(event);
            });

            div.appendChild(canvas);

            _this.play();
        }
    };

    /**
     * deleteNode
     */
    Stage.prototype.deleteNode = function (tagId)
    {
        var div = _document.getElementById(tagId ? tagId : this.getName());
        if (div) {
            var childNodes = div.childNodes;
            var length = childNodes.length;
            if (length) {
                while (length--) {
                    div.removeChild(childNodes[length]);
                }
            }
        }
    };

    /**
     * nextFrame
     */
    Stage.prototype.nextFrame = function ()
    {
        var _this = this;
        _this.downEventHits = [];
        _this.moveEventHits = [];
        _this.upEventHits = [];
        _this.keyDownEventHits = [];
        _this.keyUpEventHits = [];

        // mouse event
        var parent = _this.getParent();
        var mouse = _this.mouse;
        var mouseEvents = mouse.events;
        var onMouseDown = mouseEvents.onMouseDown;
        if (onMouseDown) {
            _this.downEventHits[_this.downEventHits.length] = {as: onMouseDown, mc: parent};
        }
        var onMouseMove = mouseEvents.onMouseMove;
        if (onMouseMove) {
            _this.moveEventHits[_this.moveEventHits.length] = {as: onMouseMove, mc: parent};
        }
        var onMouseUp = mouseEvents.onMouseUp;
        if (onMouseUp) {
            _this.upEventHits[_this.upEventHits.length] = {as: onMouseUp, mc: parent};
        }

        _this.putFrame();
        _this.addActions();
        _this.executeAction();
        _this.render();
        _this.renderMain();
    };

    /**
     * putFrame
     */
    Stage.prototype.putFrame = function ()
    {
        var _this = this;
        _this.newTags = [];
        var doneTags = _this.doneTags;
        var length = doneTags.length;
        if (length) {
            clipEvent.type = "enterFrame";
            var i = 0;
            while (i < length) {
                var tag = doneTags[i];
                tag.putFrame(_this, clipEvent);
                i++;
            }
        }
    };

    /**
     * addActions
     */
    Stage.prototype.addActions = function ()
    {
        var _this = this;
        var newTags = _this.newTags;
        var length = newTags.length;
        if (length) {
            var i = 0;
            while (i < length) {
                var tag = newTags[i];
                tag.addActions(_this);
                i++;
            }
        }
    };

    /**
     * render
     */
    Stage.prototype.render = function ()
    {
        var _this = this;
        _this.buttonHits = [];
        _this.doneTags = [];

        var ctx = _this.preContext;
        ctx.globalCompositeOperation = "source-over";
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        var backgroundColor = _this.getBackgroundColor();
        if (!backgroundColor || backgroundColor === "transparent") {
            // pre clear
            var canvas = ctx.canvas;
            ctx.clearRect(0, 0, canvas.width + 1, canvas.height + 1);
            // main clear
            var mainCtx = _this.context;
            mainCtx.clearRect(0, 0, canvas.width + 1, canvas.height + 1);
        } else {
            ctx.fillStyle = backgroundColor;
            ctx.fillRect(0, 0, _this.getWidth() + 1, _this.getHeight() + 1);
        }

        var mc = _this.getParent();
        mc.render(ctx, _this._matrix, _this._colorTransform, _this, true);
    };

    /**
     * executeAction
     */
    Stage.prototype.executeAction = function ()
    {
        var _this = this;
        if (_this.isAction && _this.actions.length) {
            _this.isAction = false;
            var i = 0;
            while (i < _this.actions.length) {
                var obj = _this.actions[i];
                i++;

                var mc = obj.mc;
                var args = obj.args || [];
                if (!mc.active) {
                    continue;
                }

                var actions = obj.as;
                if (typeof actions === "function") {
                    actions.apply(mc, args);
                } else {
                    var length = actions.length;
                    if (!length) {
                        continue;
                    }
                    var idx = 0;
                    while (idx < length) {
                        if (!(idx in actions)) {
                            continue;
                        }
                        var action = actions[idx];
                        if (typeof action === "function") {
                            action.apply(mc, args);
                        }
                        idx++;
                    }
                }
            }
        }
        _this.actions = [];
        _this.isAction = true;
    };

    /**
     * @param mc
     * @param as
     */
    Stage.prototype.buttonAction = function (mc, as)
    {
        var _this = this;
        _this.downEventHits = [];
        _this.moveEventHits = [];
        _this.upEventHits = [];
        _this.keyDownEventHits = [];
        _this.keyUpEventHits = [];
        as.execute(mc);
        _this.executeAction();
    };

    /*
     * main canvas
     */
    Stage.prototype.renderMain = function ()
    {
        var _this = this;
        var preContext = _this.preContext;
        var preCanvas = preContext.canvas;
        var width = preCanvas.width;
        var height = preCanvas.height;
        if (width > 0 && height > 0) {
            var ctx = _this.context;
            ctx.setTransform(1,0,0,1,0,0);
            ctx.drawImage(preCanvas, 0, 0, width, height);
        }
    };

    /**
     * reset
     */
    Stage.prototype.reset = function ()
    {
        var _this = this;
        _this.instanceId = 0;
        var mc = new MovieClip();
        mc.reset();
        mc.setStage(_this);
        _this.parent = mc;
        _this.characters = [];
        _this.instances = [];
        _this.buttonHits = [];
        _this.downEventHits = [];
        _this.moveEventHits = [];
        _this.upEventHits = [];
        _this.keyDownEventHits = [];
        _this.keyUpEventHits = [];
        _this.sounds = [];
        _this.loadSounds = [];
        _this.actions = [];
    };

    /**
     * init
     */
    Stage.prototype.init = function ()
    {
        var _this = this;
        var tagId = _this.tagId;
        var div;
        if (_this.getId() in stages) {
            if (tagId) {
                if (_document.readyState === "loading") {
                    var reTry = function ()
                    {
                        window.removeEventListener("DOMContentLoaded", reTry);
                        _this.init();
                    };
                    window.addEventListener("DOMContentLoaded", reTry);
                    return 0;
                }

                var container = _document.getElementById(tagId);
                if (!container) {
                    alert("Not Found Tag ID:" + tagId);
                    return 0;
                }

                div = _document.getElementById(_this.getName());
                if (div) {
                    _this.deleteNode();
                } else {
                    div = _document.createElement("div");
                    div.id = _this.getName();
                    container.appendChild(div);
                }
            } else {
                _document.body.insertAdjacentHTML("beforeend", "<div id='" + _this.getName() + "'></div>");
            }
        }

        div = _document.getElementById(_this.getName());
        if (div) {
            _this.initStyle(div);
            _this.loading();
        }

        if (!_this.canvas) {
            _this.initCanvas();
        }

        _this.loadStatus++;
        _this.loadEvent();
    };

    /**
     * @param div
     */
    Stage.prototype.initStyle = function (div)
    {
        var style;
        var _this = this;

        style = div.style;
        style.position = "relative";
        style.top = "0";
        style.backgroundColor = "transparent";
        style.overflow = "hidden";
        style["-webkit-backface-visibility"] = "hidden";

        var parent = div.parentNode;
        var oWidth = _this.optionWidth;
        var oHeight = _this.optionHeight;
        var width;
        var height;
        if (parent.tagName === "BODY") {
            width = (oWidth > 0) ? oWidth : window.innerWidth;
            height = (oHeight > 0) ? oHeight : window.innerHeight;
        } else {
            width = (oWidth > 0) ? oWidth : parent.offsetWidth;
            height = (oHeight > 0) ? oHeight : parent.offsetHeight;
        }

        style.width = width + "px";
        style.height = height + "px";
        style['-webkit-user-select'] = "none";
    };

    /**
     * init canvas
     */
    Stage.prototype.initCanvas = function ()
    {
        var _this = this;
        var style;
        var canvas = _document.createElement("canvas");
        canvas.width = 1;
        canvas.height = 1;

        style = canvas.style;
        style.zIndex = 0;
        style.position = "absolute";
        style.top = 0;
        style.left = 0;
        style.zoom = 100 / _devicePixelRatio + "%";
        style["-webkit-tap-highlight-color"] = "rgba(0,0,0,0)";

        style.MozTransformOrigin = "0 0";
        style.MozTransform = "scale(" + 1 / _devicePixelRatio + ")";

        if (isAndroid) {
            canvas.addEventListener("touchcancel", function ()
            {
                _this.touchEnd(_event);
            });
        }

        if (!isTouch) {
            window.addEventListener("keydown", keyDownAction);
            window.addEventListener("keyup", keyUpAction);
            window.addEventListener("keyup", function (event)
            {
                _keyEvent = event;
                _this.touchEnd(event);
            });
        }

        _this.context = canvas.getContext("2d");
        _this.canvas = canvas;

        var preCanvas = _document.createElement("canvas");
        preCanvas.width = 1;
        preCanvas.height = 1;
        _this.preContext = preCanvas.getContext("2d");

        var hitCanvas = _document.createElement("canvas");
        hitCanvas.width = 1;
        hitCanvas.height = 1;
        _this.hitContext = hitCanvas.getContext("2d");
    };

    /**
     * loading
     */
    Stage.prototype.loading = function ()
    {
        var _this = this;
        var div = _document.getElementById(_this.getName());
        var loadingId = _this.getName() + "_loading";
        var css = "<style>";
        css += "#" + loadingId + " {\n";
        // css += "z-index: 999;\n";
        css += "position: absolute;\n";
        css += "top: 50%;\n";
        css += "left: 50%;\n";
        css += "margin: -24px 0 0 -24px;\n";
        css += "width: 50px;\n";
        css += "height: 50px;\n";
        css += "border-radius: 50px;\n";
        css += "border: 8px solid #dcdcdc;\n";
        css += "border-right-color: transparent;\n";
        css += "box-sizing: border-box;\n";
        css += "-webkit-animation: " + loadingId + " 0.8s infinite linear;\n";
        css += "animation: " + loadingId + " 0.8s infinite linear;\n";
        css += "} \n";
        css += "@-webkit-keyframes " + loadingId + " {\n";
        css += "0% {-webkit-transform: rotate(0deg);}\n";
        css += "100% {-webkit-transform: rotate(360deg);}\n";
        css += "} \n";
        css += "@keyframes " + loadingId + " {\n";
        css += "0% {transform: rotate(0deg);}\n";
        css += "100% {transform: rotate(360deg);}\n";
        css += "} \n";
        css += "</style>";
        div.innerHTML = css;
        var loadingDiv = _document.createElement("div");
        loadingDiv.id = loadingId;
        div.appendChild(loadingDiv);
    };

    /**
     * @param url
     * @param options
     */
    Stage.prototype.reload = function (url, options)
    {
        var _this = this;
        _this.stop();

        if (_this.loadStatus === 4) {
            _this.deleteNode();
        }

        _this.loadStatus = 0;
        _this.isLoad = false;
        _this.reset();

        var swf2js = window.swf2js;
        return swf2js.load(url, {
            optionWidth: options.optionWidth || _this.optionWidth,
            optionHeight: options.optionHeight || _this.optionHeight,
            callback: options.callback || _this.callback,
            tagId: options.tagId || _this.tagId,
            renderMode: options.renderMode || _this.renderMode,
            FlashVars: options.FlashVars || _this.FlashVars,
            quality: options.quality || _this.quality,
            bgcolor: options.bgcolor || _this.bgcolor,
            stage: _this
        });
    };

    /**
     * @param url
     * @param frame
     * @param width
     * @param height
     * @returns {*}
     */
    Stage.prototype.output = function (url, frame, width, height)
    {
        var _this = this;
        if (!_this.isLoad || _this.stopFlag) {
            _setTimeout(function ()
            {
                _this.output(url, frame, width, height);
            }, 500);
            return 0;
        }

        _this.stop();
        frame = frame || 1;
        width = width || _this.getWidth();
        height = height || _this.getHeight();

        // resize
        var mc = _this.getParent();
        mc.reset();
        mc.gotoAndStop(frame);
        if (width !== _this.getWidth() || height !== _this.getHeight()) {
            _this.optionWidth = width;
            _this.optionHeight = height;
            _this.resize();
        }

        // action
        mc.addActions();

        // backgroundColor
        var canvas = _this.preContext.canvas;
        var style = canvas.style;
        style.backgroundColor = _this.backgroundColor;

        // render
        _this.render();

        // output
        var xmlHttpRequest = new XMLHttpRequest();
        xmlHttpRequest.open("POST", url, true);
        xmlHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xmlHttpRequest.onreadystatechange = function ()
        {
            var readyState = xmlHttpRequest.readyState;
            if (readyState === 4) {
                var status = xmlHttpRequest.status;
                switch (status) {
                    case 200:
                    case 304:
                        console.log("OUTPUT SUCCESS");
                        break;
                    default :
                        alert(xmlHttpRequest.statusText);
                        break;
                }
            }
        };
        var base64 = canvas.toDataURL();
        xmlHttpRequest.send("data=" + encodeURIComponent(base64));
    };

    /**
     * @param event
     */
    Stage.prototype.hitCheck = function (event)
    {
        var _this = this;
        _this.isHit = false;
        var buttonHits = _this.buttonHits;
        var length = buttonHits.length;
        if (!length) {
            return 0;
        }

        var div = _document.getElementById(_this.getName());
        var bounds = div.getBoundingClientRect();
        var x = window.pageXOffset + bounds.left;
        var y = window.pageYOffset + bounds.top;
        var touchX = 0;
        var touchY = 0;

        if (isTouch) {
            var changedTouche = event.changedTouches[0];
            touchX = changedTouche.pageX;
            touchY = changedTouche.pageY;
        } else {
            touchX = event.pageX;
            touchY = event.pageY;
        }

        touchX -= x;
        touchY -= y;
        var scale = _this.getScale();

        touchX /= scale;
        touchY /= scale;

        var ctx = _this.hitContext;
        var hitCanvas = ctx.canvas;
        var hitWidth = hitCanvas.width;
        var hitHeight = hitCanvas.height;
        var chkX = touchX * scale * _devicePixelRatio;
        var chkY = touchY * scale * _devicePixelRatio;

        if (_this.abcFlag) {
            var parent = _this.getParent();
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            ctx.clearRect(0, 0, hitWidth, hitHeight);
            var ret = parent.hitCheck(ctx, [1,0,0,1,0,0], _this, chkX, chkY);
            return (typeof ret === "object") ? ret : false;
        }

        for (var i = length; i--;) {
            if (!(i in buttonHits)) {
                continue;
            }

            var hitObj = buttonHits[i];
            if (hitObj === undefined) {
                continue;
            }

            var hit = false;
            if (touchX >= hitObj.xMin && touchX <= hitObj.xMax &&
                touchY >= hitObj.yMin && touchY <= hitObj.yMax
            ) {
                var matrix = hitObj.matrix;
                if (matrix) {
                    var mc = hitObj.parent;
                    var button = hitObj.button;

                    ctx.setTransform(1, 0, 0, 1, 0, 0);
                    ctx.clearRect(0, 0, hitWidth, hitHeight);
                    if (button) {
                        hit = button.renderHitTest(ctx, matrix, _this, chkX, chkY);
                    } else {
                        hit = mc.renderHitTest(ctx, matrix, _this, chkX, chkY);
                    }
                } else {
                    hit = true;
                }
            }

            if (hit) {
                event.preventDefault();
                _this.isHit = true;
                return hitObj;
            }
        }

        return 0;
    };

    /**
     * @param actions
     * @param caller
     * @param event
     */
    Stage.prototype.executeEventAction = function (actions, caller, event)
    {
        var args = event || [];
        if (actions) {
            if (typeof actions === "function") {
                actions.apply(caller, args);
            } else {
                var length = actions.length;
                if (length) {
                    var i = 0;
                    while (i < length) {
                        var action = actions[i];
                        if (typeof action === "function") {
                            action.apply(caller, args);
                        }
                        i++;
                    }
                }
            }
            this.executeAction();
        }
    };

    /**
     * @param event
     */
    Stage.prototype.touchStart = function (event)
    {
        var _this = this;
        if (_this.touchStatus === "up") {
            _this.touchStatus = "down";
            _this.isHit = false;
            _this.isTouchEvent = true;
            _this.touchEndAction = null;
            var downEventHits = _this.downEventHits;
            var length = downEventHits.length;
            var mc, as;
            if (length) {
                event.preventDefault();
                for (var i = 0; i < length; i++) {
                    var obj = downEventHits[i];
                    mc = obj.mc;
                    as = obj.as;
                    if (!as) {
                        as = mc.variables.onMouseDown;
                    }
                    _this.executeEventAction(as, obj.mc);
                }
                _this.downEventHits = [];
            }

            var hitObj = _this.hitCheck(event);
            if (_this.isHit) {
                mc = hitObj.parent;
                if (mc.active) {
                    mc.setButtonStatus("down");
                    if (mc instanceof TextField) {
                        _this.appendTextArea(mc, hitObj);
                    } else {
                        _this.executePress(mc, hitObj);
                    }
                }
                if (_this.touchObj === null) {
                    _this.touchObj = hitObj;
                }
            }
        }
    };

    /**
     * @param mc
     * @param hitObj
     */
    Stage.prototype.executePress = function (mc, hitObj)
    {
        var _this = this;
        var isRender = false;
        var events;
        var press;
        var onPress;
        var rollOver;
        var onRollOver;
        var cEvent = new ClipEvent();

        events = mc.events;
        if (isTouch) {
            rollOver = events.rollOver;
            if (rollOver) {
                cEvent.type = "rollOver";
                cEvent.target = mc;
                _this.executeEventAction(rollOver, mc, [cEvent]);
                isRender = true;
            }

            onRollOver = mc.variables.onRollOver;
            if (typeof onRollOver === "function") {
                _this.executeEventAction(onRollOver, mc);
                isRender = true;
            }
        }

        events = mc.events;
        press = events.press;
        if (press) {
            cEvent.type = "press";
            cEvent.target = mc;
            _this.executeEventAction(press, mc, [cEvent]);
            isRender = true;
        }
        onPress = mc.variables.onPress;
        if (typeof onPress === "function") {
            _this.executeEventAction(onPress, mc);
            isRender = true;
        }

        var button = hitObj.button;
        if (button) {
            events = button.events;

            if (isTouch) {
                rollOver = events.rollOver;
                if (rollOver) {
                    cEvent.type = "rollOver";
                    cEvent.target = button;
                    _this.executeEventAction(rollOver, button, [cEvent]);
                }

                onRollOver = button.variables.onRollOver;
                if (typeof onRollOver === "function") {
                    _this.executeEventAction(onRollOver, button);
                }
            }

            button.setButtonStatus("down");
            if (isTouch) {
                _this.executeButtonAction(button, mc, "CondIdleToOverUp");
            }

            var actions = button.getActions();
            var length = actions.length;
            if (length) {
                var touchObj = _this.touchObj;

                for (var idx = 0; idx < length; idx++) {
                    if (!(idx in actions)) {
                        continue;
                    }

                    var cond = actions[idx];
                    if (cond.CondOverDownToOverUp && touchObj === null) {
                        _this.touchEndAction = cond.ActionScript;
                        continue;
                    }

                    // enter
                    var keyPress = cond.CondKeyPress;
                    if (hitObj.CondKeyPress === 13 && hitObj.CondKeyPress !== keyPress) {
                        continue;
                    }

                    if (isTouch) {
                        if (keyPress === 13 ||
                            (keyPress >= 48 && keyPress <= 57) ||
                            cond.CondOverUpToOverDown
                        ) {
                            _this.buttonAction(mc, cond.ActionScript);
                        }
                    } else {
                        if (cond.CondOverUpToOverDown) {
                            _this.buttonAction(mc, cond.ActionScript);
                        }
                    }
                }
            }

            press = events.press;
            if (press) {
                cEvent.type = "press";
                cEvent.target = button;
                _this.executeEventAction(press, button, [cEvent]);
            }

            onPress = button.variables.onPress;
            if (typeof onPress === "function") {
                _this.executeEventAction(onPress, button);
            }

            var sprite = button.getSprite();
            sprite.startSound();

            button.addActions(_this);
            _this.executeAction();

            isRender = true;
        }

        if (isRender) {
            _this.touchRender();
        }

    };

    /**
     * @param textField
     * @param hitObj
     */
    Stage.prototype.appendTextArea = function (textField, hitObj)
    {
        var _this = this;
        textField.inputActive = true;
        var element = _document.getElementById(textField.getTagName());
        if (!element) {
            element = textField.input;
            var variable = textField.getProperty("variable");
            var text;
            if (variable) {
                var mc = textField.getParent();
                text = mc.getProperty(variable);
                if (text === undefined) {
                    text = textField.getVariable("text");
                }
            }
            if (text !== undefined) {
                element.value = text;
            }

            var maxLength = textField.getVariable("maxChars");
            if (maxLength) {
                element.maxLength = maxLength;
            }
            var border = textField.getVariable("border");
            if (border) {
                element.style.border = "1px solid black";
                var color = textField.getVariable("backgroundColor");
                element.style.backgroundColor = "rgba(" + color.R + "," + color.G + "," + color.B + "," + color.A + ")";
            }

            var scale = _this.getScale();
            var left = hitObj.xMin;
            var top = hitObj.yMin;
            var width = hitObj.xMax - left;
            var height = hitObj.yMax - top;
            element.style.left = _ceil(left * scale) - 3 + "px";
            element.style.top = _ceil(top * scale) - 3 + "px";
            element.style.width = _ceil(width * scale) + 6 + "px";
            element.style.height = _ceil(height * scale) + 6 + "px";

            var div = _document.getElementById(_this.getName());
            if (div) {
                div.appendChild(element);
                element.focus();
                var focus = function (el)
                {
                    return function ()
                    {
                        el.focus();
                    };
                };
                _setTimeout(focus(element), 10);
            }
        }
    };

    /**
     * @param event
     */
    Stage.prototype.touchMove = function (event)
    {
        var _this = this;
        var overObj = _this.overObj;
        var moveEventHits = _this.moveEventHits;
        var length = moveEventHits.length;
        var mc, as, button, events;
        var dragOver, onDragOver, dragOut, onDragOut, rollOver, onRollOver, rollOut, onRollOut;
        var cEvent = new ClipEvent();

        if (length) {
            event.preventDefault();
            for (var i = 0; i < length; i++) {
                var obj = moveEventHits[i];
                mc = obj.mc;
                as = obj.as;
                if (!as) {
                    as = mc.variables.onMouseMove;
                }
                _this.executeEventAction(as, mc);
            }
            _this.moveEventHits = [];
        }

        if (!isTouch || (isTouch && _this.isTouchEvent)) {
            var hitObj = null;
            var touchObj = _this.touchObj;
            if (touchObj || _this.touchStatus === "up") {
                hitObj = _this.hitCheck(event);
            }

            var sprite;
            var isRender = false;
            if (!isTouch) {
                var canvas = _this.canvas;
                if (_this.isHit || touchObj) {
                    if (hitObj) {
                        canvas.style.cursor = "pointer";
                    } else {
                        canvas.style.cursor = "auto";
                    }
                } else {
                    canvas.style.cursor = "auto";
                }
            }

            if (touchObj) {
                button = touchObj.button;
                mc = touchObj.parent;

                if (mc.active) {
                    _this.overObj = hitObj;
                    if (hitObj &&
                        hitObj.parent.instanceId === mc.instanceId &&
                        hitObj.button === button
                    ) {
                        if (mc.getButtonStatus() === "up") {
                            mc.setButtonStatus("down");
                            events = mc.events;
                            dragOver = events.dragOver;
                            if (dragOver) {
                                cEvent.type = "dragOver";
                                cEvent.target = mc;
                                isRender = true;
                                _this.executeEventAction(dragOver, mc, [cEvent]);
                            }
                            onDragOver = mc.variables.onDragOver;
                            if (typeof onDragOver === "function") {
                                isRender = true;
                                _this.executeEventAction(onDragOver, mc);
                            }
                        }

                        if (button && button.getButtonStatus() === "up") {
                            button.setButtonStatus("down");
                            sprite = button.getSprite();
                            sprite.startSound();

                            events = button.events;
                            dragOver = events.dragOver;
                            if (dragOver) {
                                cEvent.type = "dragOver";
                                cEvent.target = button;
                                isRender = true;
                                _this.executeEventAction(dragOver, button, [cEvent]);
                            }
                            onDragOver = button.variables.onDragOver;
                            if (typeof onDragOver === "function") {
                                isRender = true;
                                _this.executeEventAction(onDragOver, button);
                            }
                            button.addActions(_this);
                            _this.executeAction();
                        }
                    } else {
                        if (mc.getButtonStatus() === "down") {
                            events = mc.events;
                            dragOut = events.dragOut;
                            if (dragOut) {
                                cEvent.type = "dragOut";
                                cEvent.target = mc;
                                isRender = true;
                                _this.executeEventAction(dragOut, mc, [cEvent]);
                            }
                            onDragOut = mc.variables.onDragOut;
                            if (typeof onDragOut === "function") {
                                isRender = true;
                                _this.executeEventAction(onDragOut, mc);
                            }
                        }
                        mc.setButtonStatus("up");

                        if (button) {
                            if (button.getButtonStatus() === "down") {
                                button.setButtonStatus("up");

                                events = button.events;
                                dragOut = events.dragOut;
                                if (dragOut) {
                                    cEvent.type = "dragOut";
                                    cEvent.target = button;
                                    isRender = true;
                                    _this.executeEventAction(dragOut, button, [cEvent]);
                                }
                                onDragOut = button.variables.onDragOut;
                                if (typeof onDragOut === "function") {
                                    isRender = true;
                                    _this.executeEventAction(onDragOut, button);
                                }
                                button.addActions(_this);
                                _this.executeAction();
                            }
                        }
                    }
                }
            } else if (hitObj) {

                if (overObj) {
                    button = overObj.button;
                    if (button && button !== hitObj.button) {
                        mc = overObj.parent;
                        if (mc.active) {
                            button.setButtonStatus("up");
                            _this.executeButtonAction(button, mc, "CondOverUpToIdle");
                        }
                    }
                }

                button = hitObj.button;
                mc = hitObj.parent;
                if (!isTouch && mc.active) {
                    if (!overObj || overObj.parent !== mc) {
                        events = mc.events;
                        rollOver = events.rollOver;
                        if (rollOver) {
                            cEvent.type = "rollOver";
                            cEvent.target = mc;
                            isRender = true;
                            _this.executeEventAction(rollOver, mc, [cEvent]);
                        }

                        onRollOver = mc.variables.onRollOver;
                        if (typeof onRollOver === "function") {
                            isRender = true;
                            _this.executeEventAction(onRollOver, mc);
                        }
                    }
                }

                if (button) {
                    button.setButtonStatus("over");
                    sprite = button.getSprite();
                    sprite.startSound();
                    if (!isTouch) {
                        if (!overObj || overObj.button !== button) {
                            isRender = true;
                            _this.executeButtonAction(button, mc, "CondIdleToOverUp");

                            events = button.events;
                            rollOver = events.rollOver;
                            if (rollOver) {
                                cEvent.type = "rollOver";
                                cEvent.target = button;
                                isRender = true;
                                _this.executeEventAction(rollOver, button, [cEvent]);
                            }
                            onRollOver = button.variables.onRollOver;
                            if (typeof onRollOver === "function") {
                                _this.executeEventAction(onRollOver, button);
                            }
                        }
                    }
                    button.addActions(_this);
                    _this.executeAction();
                }

                _this.overObj = hitObj;
            } else if (_this.touchStatus === "up") {
                _this.overObj = null;
            }

            // RollOut
            if (!touchObj && overObj) {
                button = overObj.button;
                mc = overObj.parent;
                if (mc.active) {
                    if (!hitObj || hitObj.parent !== mc) {
                        mc.setButtonStatus("up");

                        events = mc.events;
                        rollOut = events.rollOut;
                        if (rollOut) {
                            cEvent.type = "rollOut";
                            cEvent.target = mc;
                            isRender = true;
                            _this.executeEventAction(rollOut, mc, [cEvent]);
                        }

                        onRollOut = mc.variables.onRollOut;
                        if (typeof onRollOut === "function") {
                            isRender = true;
                            _this.executeEventAction(onRollOut, mc);
                        }
                    }

                    if (button && (!hitObj || hitObj.button !== button)) {
                        button.setButtonStatus("up");
                        _this.executeButtonAction(button, mc, "CondOverUpToIdle");

                        events = button.events;
                        rollOut = events.rollOut;
                        if (rollOut) {
                            cEvent.type = "rollOut";
                            cEvent.target = button;
                            isRender = true;
                            _this.executeEventAction(rollOut, button, [cEvent]);
                        }

                        onRollOut = button.variables.onRollOut;
                        if (typeof onRollOut === "function") {
                            isRender = true;
                            _this.executeEventAction(onRollOut, button);
                        }
                        button.addActions(_this);
                        _this.executeAction();
                    }
                }
            }

            if (isRender) {
                _this.touchRender();
            }
        }

        var dragMc = _this.dragMc;
        if (dragMc) {
            event.preventDefault();
            dragMc.executeDrag();
            _this.isHit = true;
        }
    };

    /**
     * @param event
     */
    Stage.prototype.touchEnd = function (event)
    {
        var _this = this;
        var cEvent = new ClipEvent();
        var button, mc, as, events, release, onRelease, releaseOutside, onReleaseOutside;
        var touchObj = _this.touchObj;
        if (touchObj) {
            button = touchObj.button;
            if (button) {
                button.setButtonStatus("up");
            }
        }

        var upEventHits = _this.upEventHits;
        var length = upEventHits.length;
        if (length) {
            event.preventDefault();
            for (var i = 0; i < length; i++) {
                var obj = upEventHits[i];
                mc = obj.mc;
                as = obj.as;
                if (!as) {
                    as = mc.variables.onMouseUp;
                }
                _this.executeEventAction(as, obj.mc);
            }
            _this.upEventHits = [];
        }

        var hitObj = _this.hitCheck(event);
        var dragMc = _this.dragMc;
        if (dragMc) {
            hitObj = touchObj;
            _this.isHit = true;
        }

        var isRender = false;
        if (touchObj) {
            mc = touchObj.parent;
            mc.setButtonStatus("up");
            button = touchObj.button;

            if (_this.isHit) {
                var touchEndAction = _this.touchEndAction;
                if (mc.active) {
                    if (mc === hitObj.parent) {
                        if (touchEndAction !== null) {
                            _this.buttonAction(mc, touchEndAction);
                            isRender = true;
                        }

                        events = mc.events;
                        release = events.release;
                        if (release) {
                            cEvent.type = "release";
                            cEvent.target = mc;
                            _this.executeEventAction(release, mc, [cEvent]);
                            isRender = true;
                        }
                        onRelease = mc.variables.onRelease;
                        if (typeof onRelease === "function") {
                            _this.executeEventAction(onRelease, mc);
                            isRender = true;
                        }
                    }

                    if (button) {
                        if (button === hitObj.button) {
                            events = button.events;
                            release = events.release;
                            if (release) {
                                cEvent.type = "release";
                                cEvent.target = button;
                                _this.executeEventAction(release, button, [cEvent]);
                            }

                            onRelease = button.variables.onRelease;
                            if (typeof onRelease === "function") {
                                _this.executeEventAction(onRelease, button);
                            }
                        }

                        var status = "up";
                        if (!isTouch) {
                            if (hitObj && hitObj.button === button) {
                                status = "over";
                            }
                        }

                        button.setButtonStatus(status);

                        var sprite = button.getSprite("hit");
                        sprite.startSound();

                        button.addActions(_this);
                        _this.executeAction();

                        isRender = true;
                    }
                }
            }

            if (mc.active && (!hitObj || mc !== hitObj.parent)) {
                events = mc.events;
                releaseOutside = events.releaseOutside;
                if (releaseOutside) {
                    cEvent.type = "releaseOutside";
                    cEvent.target = mc;
                    _this.executeEventAction(releaseOutside, mc, [cEvent]);
                    isRender = true;
                }
                onReleaseOutside = mc.variables.onReleaseOutside;
                if (typeof onReleaseOutside === "function") {
                    _this.executeEventAction(onReleaseOutside, mc);
                    isRender = true;
                }
            }

            if (button && (!hitObj || button !== hitObj.button)) {
                isRender = true;

                events = button.events;
                releaseOutside = events.releaseOutside;
                if (releaseOutside) {
                    cEvent.type = "releaseOutside";
                    cEvent.target = button;
                    _this.executeEventAction(releaseOutside, button, [cEvent]);
                    isRender = true;
                }

                onReleaseOutside = button.variables.onReleaseOutside;
                if (typeof onReleaseOutside === "function") {
                    _this.executeEventAction(onReleaseOutside, button);
                }
                button.setButtonStatus("up");
                button.addActions(_this);
                _this.executeAction();
            }
        }

        _this.isHit = false;
        _this.isTouchEvent = false;
        _this.touchObj = null;
        _this.touchStatus = "up";

        if (!isTouch) {
            _this.hitCheck(event);
            var canvas = _this.canvas;
            if (_this.isHit) {
                canvas.style.cursor = "pointer";
            } else {
                canvas.style.cursor = "auto";
            }
        }

        if (hitObj) {
            var rollOver, onRollOver;
            mc = hitObj.parent;
            if (!touchObj || mc !== touchObj.parent) {
                events = mc.events;
                rollOver = events.rollOver;
                if (rollOver) {
                    isRender = true;
                    cEvent.type = "rollOver";
                    cEvent.target = mc;
                    _this.executeEventAction(rollOver, mc, [cEvent]);
                }

                onRollOver = mc.variables.onRollOver;
                if (typeof onRollOver === "function") {
                    isRender = true;
                    _this.executeEventAction(onRollOver, mc);
                }
            }

            button = hitObj.button;
            if (button) {
                if (!touchObj || button !== touchObj.button) {
                    events = button.events;
                    rollOver = events.rollOver;
                    if (rollOver) {
                        isRender = true;
                        cEvent.type = "rollOver";
                        cEvent.target = button;
                        _this.executeEventAction(rollOver, button, [cEvent]);
                    }

                    onRollOver = button.variables.onRollOver;
                    if (typeof onRollOver === "function") {
                        isRender = true;
                        _this.executeEventAction(onRollOver, button);
                    }
                }
            }
        }

        if (isRender) {
            event.preventDefault();
            _this.touchRender();
        }

        _keyEvent = null;
    };

    /**
     * @param button
     * @param mc
     * @param status
     */
    Stage.prototype.executeButtonAction = function (button, mc, status)
    {
        var _this = this;
        var actions = button.getActions();
        var length = actions.length;
        if (length) {
            for (var idx = 0; idx < length; idx++) {
                if (!(idx in actions)) {
                    continue;
                }

                var cond = actions[idx];
                if (!cond[status]) {
                    continue;
                }

                _this.buttonAction(mc, cond.ActionScript);
            }
        }
    };

    /**
     * touchRender
     */
    Stage.prototype.touchRender = function ()
    {
        var _this = this;
        _this.render();
        _this.renderMain();
    };

    /**
     * @constructor
     */
    var Swf2js = function () {};

    /**
     * @type {DropShadowFilter}
     */
    Swf2js.prototype.DropShadowFilter = DropShadowFilter;

    /**
     * @type {BlurFilter}
     */
    Swf2js.prototype.BlurFilter = BlurFilter;

    /**
     * @type {GlowFilter}
     */
    Swf2js.prototype.GlowFilter = GlowFilter;

    /**
     * @type {BevelFilter}
     */
    Swf2js.prototype.BevelFilter = BevelFilter;

    /**
     * @type {GradientGlowFilter}
     */
    Swf2js.prototype.GradientGlowFilter = GradientGlowFilter;

    /**
     * @type {ConvolutionFilter}
     */
    Swf2js.prototype.ConvolutionFilter = ConvolutionFilter;

    /**
     * @type {ColorMatrixFilter}
     */
    Swf2js.prototype.ColorMatrixFilter = ColorMatrixFilter;

    /**
     * @type {GradientBevelFilter}
     */
    Swf2js.prototype.GradientBevelFilter = GradientBevelFilter;

    /**
     * @type {BitmapFilter}
     */
    Swf2js.prototype.BitmapFilter = BitmapFilter;

    /**
     * @type {LoadVars}
     */
    Swf2js.prototype.LoadVars = LoadVars;

    /**
     * @param url
     * @param options
     */
    Swf2js.prototype.load = function (url, options)
    {
        // develop only
        if (url === "develop") {
            url = location.search.substr(1).split("&")[0];
        }

        if (url) {
            var stage = (options && options.stage instanceof Stage) ? options.stage : new Stage();
            stage.setOptions(options);
            stages[stage.getId()] = stage;
            stage.init();

            var xmlHttpRequest = new XMLHttpRequest();
            xmlHttpRequest.open("GET", url, true);
            if (isXHR2) {
                xmlHttpRequest.responseType = "arraybuffer";
            } else {
                xmlHttpRequest.overrideMimeType("text/plain; charset=x-user-defined");
            }

            xmlHttpRequest.onreadystatechange = function ()
            {
                var readyState = xmlHttpRequest.readyState;
                if (readyState === 4) {
                    var status = xmlHttpRequest.status;
                    switch (status) {
                        case 200:
                        case 304:
                            var data = (isXHR2) ? xmlHttpRequest.response : xmlHttpRequest.responseText;
                            stage.parse(data, url);
                            cacheStore.reset();
                            break;
                        default :
                            alert(xmlHttpRequest.statusText);
                            break;
                    }
                }
            };
            xmlHttpRequest.send(null);
        } else {
            alert("please set swf url");
        }
    };

    /**
     * @param url
     * @param options
     * @returns {*}
     */
    Swf2js.prototype.reload = function(url, options)
    {
        if (!stageId) {
            return this.load(url, options);
        }
        var stage = stages[0];
        for (var i in stages) {
            if (!stages.hasOwnProperty(i)) {
                continue;
            }
            var target = stages[i];
            target.stop();
            if (i) {
                target.deleteNode(target.tagId);
                target = void 0;
            }
        }

        stageId = 1;
        stages = [];
        loadStages = [];
        stages[0] = stage;
        stage.reload(url, options);
    };

    /**
     * @param width
     * @param height
     * @param fps
     * @param options
     * @returns {MovieClip}
     */
    Swf2js.prototype.createRootMovieClip = function(width, height, fps, options)
    {
        var stage = new Stage();
        width = width || 240;
        height = height || 240;
        fps = fps || 60;

        stage.setBaseWidth(width);
        stage.setBaseHeight(height);
        stage.setFrameRate(fps);
        stage.setOptions(options);
        stages[stage.getId()] = stage;
        stage.init();
        stage.isLoad = true;

        if (_document.readyState === "loading") {
            var reLoad = function()
            {
                window.removeEventListener("DOMContentLoaded", reLoad, false);
                stage.resize();
                stage.loaded();
            };
            window.addEventListener("DOMContentLoaded", reLoad, false);
        }
        return stage.getParent();
    };

    window.swf2js = new Swf2js();
})(window);}