'FLEX'에 해당되는 글 48건

  1. 2010.07.12 [FLEX 3.0] swc 디컴파일 - swc decompiler
  2. 2010.07.12 [AIR] AIR에서 navigateToURL을 사용해서 데이터 넘길때 POST는 죽어도 않된다.ㅠ.ㅠ
  3. 2010.07.12 [AIR] AIR Window Basics
  4. 2010.07.12 [AS3.0] navigateToURL()과 sendToURL() 함수 차이
  5. 2010.07.12 [AS3.0] How to prevent pop-up blocking in Firefox
  6. 2010.07.12 Flex - 새로운 Open Source Data Service - BlazeDS
  7. 2010.07.12 Flex - DataService (FDS[지금은 LCD], AMFPHP, OpenAMF, XML, JSON, TEXT)
  8. 2010.07.12 Flex 3 - FireFox SSL Cache 설정
  9. 2010.07.12 6. 개인 프로젝트를 시작하기위해서..... 분석6
  10. 2010.07.12 FLEX3 - FlexLog 사용법 (spitzer.flair_1.0.3 사용) - swf파일 trace로 log보기

[FLEX 3.0] swc 디컴파일 - swc decompiler

FLEX/FLEX 2010. 7. 12. 21:37

이런 글을 올려도 되는건지는 모르겠지만 내가 필요해서 해본거라 올려야겠다.

문제가 되면 바로 바로 삭제 하겠음...^^

개발을 하다가 다른사람이 만들어 놓은 swc 를 사용하여 개발을 하게 되는데 도대체 어떻게 만들었을까 궁금해하다가
java에 class도 디컴파일이 되는데 swc는 않될까 궁금하게 생각하다가 한번 시도해보기로 했다.

방식은 그냥 단순하다... 웹에 올라와있는 수 많은 블러그 글들을 참조 하면서..ㅎㅎ

이 방법이 잘되는지 않되는지는 각자 않아서 판단하시길.. 나는 내가 한방법으로 내가 원하는 결과를 구했기 때문에...
(참.. 책임없는 말이다..쩝)

일단 디컴파일할 swc가 필요하겠죠..

일단 빵집을 사용하여 무심결에 swc를 풀어보았다.
(구지 빵집이 아니여도 됩니다. 그런데 저는 빵집을 사용하기 때문에 그냥 빵집으로..

아래 그림과 같이 무심결에 마우스 오른쪽 클릭을 했는데 미리보기가 되는것이다... 허거덩



빵집에 알아서 풀기를 했더니.. 파일명과 동일한 디렉토리가 생성되면서 압축이 풀린다.


압축이 풀린 디렉토리를 보니 위와 같이 파일들이 나온다... 헉 SWF가 나왔당...
자 그럼 SWF를 디컴파일 하면되겠다....ㅋㅋ

swf 디컴파일러는 Sothink SWF Decomplier 4.1를 사용했다.

Sothink SWF Decompiler에 자세한 사용법은 잘 모르나 swf를 디컴파일 할수 있다는것은안다..ㅋㅋ

자 swf 디컴파일러를 실행하면 아래와 같은 화면을 볼수 있다.
그럼 저기에다가 SWF를 선택하고 실행만 하면된다.


Quick Open에 해당 swf를 선택하고 Export중에 Export To FLA를 클릭한다.

그럼 아래와 같이 물어보는 창이 나타나는데 너무 짧을 영어라 구지 설명을 않해도 그냥 때려 맞출수 있을것이다.

내가 필요한건 SWC안에 들어있는 액션스크립트 파일만 필요하기 때문에 (라이브러리 파일에 flv가 필요없기 때문에)

제일 위에 것만 선택하고 OK클릭


그 다음은 어떤한 flash 버전으로 flv를 만들거냐고 물어보는데 이건 대충 현재 깔려있는 버전 선택하고 통과

끝날때 까지 기다리면
flv파일을 열어볼거냐 막물어보는데 난 액션 스크립트 파일만 있으면 되기 때문에 패스.. no클릭

자 다 끝났다.. 이제 처음 swf가 있던 디렉토리를 보면 as파일이 만들어 저있을것이다.

끝...... 필요한 AS파일을 찾아서 보면된다.

자 이제 마지막 정리

1. 해당 swc파일을 빵집이나 압축을 풀수있는 프로그램을 선택하여 압축을 푼다.
2. 압축이 풀린 디렉토리에 .swf파일이 존재하는지 확인한다.
3. swf를 디컴파일할 수 있는 swf compiler를 실행한다.
4. Quick Open에 해당 swf를 선택하고 Export중에 Export To FLA를 클릭한다.
5. 필요한 옵션 선택.. 제일 위에 것을 선택하지 않으면 액션스크립트 파일을 볼수 없다.
6. 모든 작업이 끝나면 해당 디렉토리를 다시 살펴본다..



:

[AIR] AIR에서 navigateToURL을 사용해서 데이터 넘길때 POST는 죽어도 않된다.ㅠ.ㅠ

FLEX/FLEX 2010. 7. 12. 21:32

성급한 결론일지를 모르지만...ㅠ.ㅠ

AIR에서 파일 다운로드를 위한 데이터 값을 post 방식으로 navigateToURL 이걸 사용하면

익스플로우 창에서 그래도 파라미터 값으로 나타나버린다... GET방식처럼.

var url:URLRequest = new URLRequest(filePath);
url.method = URLRequestMethod.POST;

이렇게 해도 맞찬가지 이고.. 

url.method = "POST" 

이렇게 해도 맞찬가지 이다...

adobe 버그 리포트 게시판을 보면.. 개발팀에 전달했다고 하고.. 해당 이슈를 fixed시켜버렸다.. 어쩌라는건지...ㅠ.ㅠ

사용자에게 않보여줄라면.. base64로 인코딩해서 넘겨야되나.ㅠ.ㅠ
:

[AIR] AIR Window Basics

FLEX/FLEX 2010. 7. 12. 21:31

AIR Window Basics

  • 두 가지 Window APIs
NativeWindow Class JavaScript Window Class

Flash-oriented

Flash stage와 display list 사용

visual object 추가:

Window stage의 display list에 추가

HTML-oriented

HTML, CSS, JavaScript 사용

visual object 추가:

DOM에 추가

  • HTML Window의 nativeWindow property
    • HTML Window는 NativeWindow의 특수한 카테고리
    • nativeWindow 프로퍼티를 통해서 기본이 되는 Native Window 인스턴스에 접근할 수 있다.
  • Event-based programming model
    • Window object의 프로퍼티를 변경하거나 외형, 행동에 영향을 미치는 메서드를 호출하면 Event가 발생하여 관련된 컴포넌트들이 적절하게 반응하도록 한다.
  • 참고
    • window API에 대한 보다 상세한 문서:

http://livedocs.adobe.com/labs/flex/3/langref/mx/core/Window.html http://livedocs.adobe.com/labs/flex/3/langref/flash/display/NativeWindow.html

  • 기본 윈도우
    • 어플리케이션 기술 파일(ProjectName-app.xml)의 rootContent element에 지정한 파라미터를 이용하여 자동으로 생성된다.
    • rootContent가 SWF 파일이면 NativeWindow가, HTML 파일이면 HTML Window가 생성된다. (# 이후 HTML 윈도우에 대한 내용은 생략 #)
  1. <rootContent systemChrome="standard" transparent="false" visible="true">
    [SWF reference is generated]</rootContent>

  •  Styles of Native Windows
    • Type, System Chrome, Transparency 값의 조합으로 설정한다.
    • OS에 따라 각 설정 조합이 아래와 같이 표현되며, 함께 쓰일 수 없는 조합에 주의해야 한다.

window_styles_1.jpg

window_styles_2.jpg

 Window 생성하기

  •  Window 생성 방법
    • 기본 윈도우(First window): 어플리케이션 기술 파일의 rootContent에 파라미터로 기술된 속성을 가지고 자동으로 만들어진다. Stage.window property를 통해서 접근
    • NativeWindowInitOptions object 생성/설정 후 window 생성자에 넘겨서 생성(ActionScript): 생성자가 돌려주는 object reference로 직접 접근
    • JavaScript Window.open() method: 생략 
  1. //create the init options
  2. var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    options.transparent = false;
    options.systemChrome = NativeWindowSystemChrome.STANDARD;
    options.type = NativeWindowType.NORMAL;
  3. //create the window
    var newWindow:NativeWindow = new NativeWindow(false,options);
    newWindow.title = "A title";
    newWindow.width = 600;
    newWindow.height = 400;
  4. //add a sprite to the window
    newWindow.stage.align = StageAlign.TOP_LEFT;
    newWindow.stage.scaleMode = StageScaleMode.NO_SCALE;
    //show the new window
  5. newWindow.visible = true;

Window 조작하기

  • NativeWindow instance 얻기 
    • Window 생성자: var win:NativeWindow = new NativeWindow(false, options);
    • Window stage: stage.window
    • 같은 stage 위에 있는 Display Object: myDisplayObject.stage.window
    • 윈도우 이벤트: 이벤트의 target property가 이벤트를 발생시킨 윈도우를 가리킨다.
    • HTMLControl 또는 HTML Window: window.nativeWindow property를 통해서 접근
  • 기타 작업 
    • 최대화: win.maximize();
    • 최소화: win.minimize() ;
    • 이전 크기로: win.restore();
    • 닫기
      • NativeWindow.close() method 호출
      • 비동기 작동: closing 프로세스가 진행되는 동안 어플리케이션은 계속 실행된다.
      • 닫는 작업이 모두 완료되면 close event를 발생시킨다.
      • 닫기가 종료된 후에도 NativeWindow object는 유효하지만 속성이나 메서드에 접근하면 대부분 IllegalOperationError가 발생한다.
      • closed property로 창이 닫혔는지 여부를 확인할 수 있다.
    • 윈도우 작업 취소(Cancellation) 
      • System Chrome에 대한 사용자 조작은 이벤트를 발생시키기 때문에 해당 event를 받아서 preventDefault()를 호출하면 해당 동작을 취소할 수 있다.
      • System Chrome이 아닐 경우 해당 작업에 대한 notification event를 발생시키고 preventDefault()가 호출되지 않았는지 확인하는 방법으로 취소를 구현할 수 있다.
  1. public function onCloseCommand(event:MouseEvent):void{
        var closingEvent:Event = new Event(Event.CLOSING,true,true);
        dispatchEvent(closing);
        if(!closingEvent.isDefaultPrevented()) { 
  2.         win.close();
        }
    }
:

[AS3.0] navigateToURL()과 sendToURL() 함수 차이

FLEX/FLEX 2010. 7. 12. 21:31

navigateToURL 함수

AS3 어플리케이션으로부터 또 하나의 브라우저 윈도우를 열고 싶을 때, 또는 현재 AS3 어플리케이션을 실행중의 윈도우를 다른 HTML 컨텐츠로 바꾸고 싶을 때, navigateToURL() 함수를 사용한다. AS3이전 버전에서는 getURL()을 사용하였다. 아래는 navigateToURL() 함수의 정의.

public function navigateToURL(request:URLRequest, window:String = null):void

1 첫 번째 인수에는 URLRequest 오브젝트를 지정한다. 이 오브젝트에는 URLLoader 클래스에서는  리퀘스트 하는 URL이나 헤더 정보, 그리고 송신하는 데이터와 송신 방법 등을 지정한다.

2 번째의 인수에는 리퀘스트의 결과를 수신하는 윈도우 target을 지정한다. 이것은 디폴트값이 정의되고 있기 때문에 생략이 가능하다. 생략 했을 경우에는 새로운 윈도우가 열린다. 즉 "_blank" 를 지정한 것과 동등의 결과가 된다.

아래의 샘플에서는 navigateToURL()의 두번째 인수를 "_self"로 하고 있기 때문에 AS3 어플리케이션을 실행하고 있는 윈도우에서 컨텐츠를 열게 된다.

import flash.net.navigateToURL;

var myVars:URLVariables = new URLVariables();
myVars.currentTime = new Date().getTime();
myVars.city = "Tokyo";

var myReq:URLRequest = new URLRequest();
myReq.url = "http://www.sample.com/weather";
myReq.data = myVars;

// 현재의 윈도우에 컨텐츠를 연다.
navigateToURL(myReq, "_self");



navigateToURL() 함수를 사용하려면 import해서 사용한다.



sendToURL() 함수
AS3 어플리케이션으로부터 서버에 데이터를 보내고 싶지만 결과는 무시하고 싶을 때는 sendToURL() 함수를 사용한다. 사용법은 위에서 소개한 것처럼 navigateToURL() 함수와 거의 비슷하다. 차이는 2번째의 인수가 없다는 것이다.

public function sendToURL(request:URLRequest):void


import flash.net.sendToURL;

var myVars:URLVariables = new URLVariables();
myVars.user = "foo";
myVars.data = "hello";

var myReq:URLRequest = new URLRequest();
myReq.url = "http://www.sample.com/post";
myReq.data = myVars;
myReq.method = URLRequestMethod.POST;
// 서버에 데이터를 송신
sendToURL(myReq);
:

[AS3.0] How to prevent pop-up blocking in Firefox

FLEX/FLEX 2010. 7. 12. 21:30

How to prevent pop-up blocking in Firefox

Using navigateToURL() method causes pop-up blocking feature activation in Firefox since Flash is a plug-in:


navigateToURL(new URLRequest("http://test.test.com")",_blank");

Use window.open() JavaScript method instead. I have created URLUtil class with openWindow() static method to make it easier:


package {
     import flash.external.ExternalInterface;
     public class URLUtil {
     protected static const WINDOW_OPEN_FUNCTION : String = "window.open";
     public static function openWindow(url : String, window : String = "_blank", features : String = "") : void
     {
        ExternalInterface.call(WINDOW_OPEN_FUNCTION, url, window, features);
     }
   }
}


Now my blog could be opened in a new browser window this way:

URLUtil.openWindow("http://test.test.com");

:

Flex - 새로운 Open Source Data Service - BlazeDS

FLEX/FLEX 2010. 7. 12. 21:30

그동안 간단한 작업들은 TEXT 방식에 HttpService를 사용하여 작업을 했는데...얼마전 Open Source 기반에 DataService가 나왔다고 해서 미루다 미루다 이제서야 정리시작..ㅋㅋㅋ

BlazeDS(Blaze Data Service)는 2007년 12월 말에 Adobe에서 Open Source 일환으로 만들어진 Data Service라고 한다.. 그런데 왜 나는 이제 알았을까..ㅠ.ㅠ

BlazeDS가 제공하는 기능중 하나가 바로 바로 Remote Object Service방식이다.  Remote Object Service 방식은 LCDS(구 FDS)와 동일한 AMF3(Action Message Format 3)프로토콜을 이용해 통신하는 방식을 취한다는것이다.  몇 가지 기능만 뺀다면 유료인 LCDS와 같다는것이다... 이제 무료로 RemoteObject 방식을 사용할수 있다는것이다.

그렇다면 무료인 BlazeDB 와 LCDS는 어떤 차이가 있을까.
바로 LCDS에서 몇가지 기능을 삭제한것이 BlazeDB인것이다.(ADOBE도 먹고 살아야하니까....)

LCDS의 핵심기능은 바로 Data Managemnet / Remote Object Service / Messaging 기능인데.. 다른것도 있지만 다른것들은 않쓰니까 전부 패스..ㅋㅋ BlazeDB는 이중에서 Data Managemnet 기능을 제외한 2가지 기능을 지원한다고 한다. 하지만 Remote Object Service의 경우는 BlazeDB와 LCDS간에 성능차이는 없다고 하지만 Messaging의 경우는 LCDS가 CPU당 1000개 수준의 Dedicated messaging service를 지원하는 반면, BlazeDS에서는 CPU당 100개 수준의 Web container-based messaging service만을 지원한다는것이다.

그럼 BlazeDS란?
BlazeDS는 Adobe에서 만든 LCDS(Life Cycle Data Service)와 유사한 개념의 Open Source
프로젝트입니다.

참고로, LCDS와 BlazeDS의 기능상의 차이는 다음 그림에 잘 나와 있습니다.
전체 사각형은 LCDS의 기능 집합이여, 아래에 컬러(하늘색)로 표시된 부분만 BlazeDS의 기능입니다.

가장 중요한 기능은 Flex에서 Remote Object란 개념으로 서버상의 Java method를 바로 Call할 수
있다는 것입니다.
BlazeDS 없이는 Remote Object (이하 RO라 표현)를 사용할 수 없으며, 따라서 기존에는
HTTPService 혹은 SOAP을 이용한 WebService방식으로 서버상의 기능을 호출하게 됩니다.
가장 기본적인 방식인 HTTPService의 경우 servlet을 호출하여 결과값을 xml이나 text형식으로
받아서 사용합니다.

가장 기본적인 사용방법은 다음과 같습니다.
 <mx:HTTPService id="srvHTTP" method="POST" useProxy="false" url="simple" showBusyCursor="true" fault="Alert.show(event.fault.faultString, 'Error');"/>
위의 예에서 url 부분에 servlet의 경로가 들어가게 되고 POST방식으로 호출한다는 의미입니다.

하지만 RO를 사용하게 되면,
서버상의 remoting-config.xml 파일에 destination에 해당하는 부분을 정의한 후
 java method를 바로 호출하고 결과값을 primitive type혹은 javabean 형태로 받을 수 있습니다.

 [remoting-config.xml 파일]

    <destination id="simpleLoadService">
        <properties>
            <source>flexintegration.sample.SimpleLoadService</source>
        </properties>
    </destination>   


다음은 전체 mxml파일입니다.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#FFFFFF">
 
 <mx:Script>
 <![CDATA[
  import mx.controls.Alert;
 ]]>
 </mx:Script>
 
 <mx:RemoteObject id="srvRO" destination="simpleLoadService" showBusyCursor="true" fault="Alert.show(event.fault.faultString, 'Error');" />
 
 <mx:DataGrid dataProvider="{srvRO.getAll.lastResult}" width="100%" height="100%"/>
<mx:Button label="Get Data" click="srvRO.getAll()"/>
 
</mx:Application>


"Get Data"란 버튼을 클릭하면 simpleLoadService를 호출하고 데이터를 가지고 오게 됩니다.


다음은 실제 simpleLoadService로 선언한
flexintegration.sample.SimpleLoadService.java 파일의 내용입니다.

package flexintegration.sample;

import java.util.List;
import com.mydomain.data.SimpleIbatis;

public class SimpleLoadService {

    public List getAll()
    {
     List beans;
    
     try
     {
      beans = SimpleIbatis.selectAllSimpleBean();
     }
     catch(Exception e)
     {
         return null;
     }
     return beans;
    }

}


Database에서 값을 가져온 후 결과 데이터를 List로 만들어서 Flex쪽으로 리턴합니다.


이와 같이 RO를 이용하게 되면 서버쪽의 Java method를 바로 호출할 수 있어서 개발 속도를 향상 시킬수
있으며, xml이나 text파일이 아닌 serialize된 object를 전달받게 되므로 대용량 데이터의 경우 데이터
전송 속도의 향상도 기대할 수 있습니다.

위 내용은 DukeRick 의 블러그에서 많이 참조하여 재구성한것입니다.

:

Flex - DataService (FDS[지금은 LCD], AMFPHP, OpenAMF, XML, JSON, TEXT)

FLEX/FLEX 2010. 7. 12. 21:29

Flex에서 DB와 연동하여 작업을 할려면 다양한 방법이 있지만 가장 많이 사용하는것이 아마 HTTPService일것이다.
처음 사용했던것이 데이터를 XML를 받아와서 파싱하는것인데.. 이것은 자동적으로 Flex에서 Bind할수 있어서 좋기는 하지만
XML TAG를 사용해야되서리.. 데이터 량이 많아 지면 데이터보다 TAG의 량이 기하급수적으로 늘어난다.

그래서 찾아보던중... 아마 옛날에 찾아봤지..ㅋㅋㅋ 이제서야 정리 하는것이지만...
지돌스타님의 블러그에 가면 다양한 DataService의 종류와 성능에 대하여 자세히 나와있다..

기존에는 FDS(지금은 LCD), AMFPHP, OpenAMF, XML, JSON, TEXT이렇게가 가장많이 사용되고 있다.
기존것들에 대하여 간단히 분석된 결과를 보면

아마 그냥 위에 그래프만 보아도 어떤것 사항인지 알것이다..
데이터량에 따라서 수행도는 속도를 비교한것이다.

첨에 구현할때야 어느정도에 량이 될지 모르기때문에 그냥 구현만 하는데 나중에는 이것때문에 골치 아프게될것이다.

FDS야 유료이기도 하고 ADOBE에서 만들어진것이기때문에 가장 좋은 성능을 보일것은 당연하고 단순 처리 속도 뿐만 아니고
여러 기능들도 있지만 문제는 바로 유료인것..ㅠ.ㅠ 그것도 너무 비싸당...

그래서 내가 사용하는것이 TEXT방식이다... 위에 결과에 알수 있듯이 FDS와 별 차이를 보이지 않는다. 하지만 TEXT를 파싱해야되고 BIND시켜야 하는 것이 번거롭기 때문에 많이는 쓰지 않는듯....

하지만 파서는 한번 만들어 계속 쓰면 되는것이기때문에 별로 문제없다..ㅋㅋ

간단히 보면 아래와 같이 데이터가 들어온다면

TITLE | DATE | VALUE
제목1 | 20080801 | 12312
제목2 | 20080802 | 12312


데이터를 읽어드려서 '|' 구분자로 구별을 해서 ArratCollection에 넣어주고 BIND시키면 된다..ㅎㅎ 알고 보면 간단한데..ㅋㅋ

어떤것을 사용할건지는 개발자 마음이다.. 하지만 돈을 받고 하는 작업이면 개발자 마음이 아니고..ㅋㅋ 잘상의 해서...

위 내용은 지돌스타님의 블러그 내용을 참조 하여 재구성한것입니다.
:

Flex 3 - FireFox SSL Cache 설정

FLEX/FLEX 2010. 7. 12. 21:29

Flex로 프로젝트를 하다가 정말 신기한 일이 있어서... 어의가 어의가..ㅠ.ㅠ
Flex로 구현된 싸이트에 가장 큰 장점이자 단점은 SWF를 다운받아야 된다는것이다.

한번 SWF를 다운받으면 Cache가 작동하기 때문에 다음에는 다시 않받아도 된다는 장점이 있지만
초기에 다운받기 위해서는 늦는 다는것이다.

더 웃기는 일은 IE에서는 SSL을 적용하여도(https://) 자동적으로 cache가 작동하지만 FireFox에서는 그렇지 않다는것이다.
FireFox에서는 사용자가 직접 config 파일을 조절해야된다.

FireFox 주소표시줄에 about:config라고 치면 아래와 같이 설정창이 나타난다. 거기에서 해당 내용을 떠블 클릭하면 된다.ㅠ.ㅠ

:

6. 개인 프로젝트를 시작하기위해서..... 분석6

FLEX/FLEX Project 2010. 7. 12. 21:27

그동안 정신 없이 바쁘다가 이제조금 정신을 차리고 다시 진행하려고 한다.ㅠ.ㅠ
(잠이 부족해ㅠ.ㅠ.)

다시 마음을 잡고....

분석 5에 있던... 유명한 아저씨의 소스를 분석 해보기로 했당... 잘 될랑가 모르겠다...

아래 이 소스...ㅋㅋ
원본 소스 :
DisplayShelf.zip 

일단 파일 구조는 : MXML Application 파일 한개와 AS 파일 2개로 구성되어 있고 img 디렉토리 밑에 관련이미지가 들어있다
이미지를 잘 보면알겠지만 이미지를 전부 400 * 400 으로 편집해 놓았다. 왜 그랬는지는 화면 보면 뻔히 알수 있는거고
하지만 이미지 파일을 올리다 보면 가로로 긴것도 있을거고 세로로 긴것도 있을것이다. 그러니 파일을 업로드 할때부터 썸네일을 만들던 해서 이미지 보정을 해야될거 같다.. 보여지는것은 멋져보이지만 뒤에서는 열심히 삽질을 하고 있다는..ㅋㅋ

구조 : Final.mxml <- MXML Application file
       : DisplayShelf.as <- AS file
       : TiltingPane.as <- AS File
       : /img <- IMG Directory

이분이 친절하게 주석을 달아주셨다.. 그런데 영어를 거의 못하기 때문에.. 무슨 소리인지 하나도 모르겠당..ㅎㅎ
그래서 내 맘대로 다시 분석 시작..ㅋㅋ 그런데 주석을 참고 하기는 해야지..ㅎㅎ


파일
1. Final.mxml
<?xml version="1.0" encoding="utf-8"?>

<Application xmlns="http://www.adobe.com/2006/mxml" xmlns:local="*"
 height="100%" width="100%" layout="absolute">
 
 <!--
 Binding 이놈은 컴포너트와 데이터간의 상호 연결을 할수 있다. 그런데 한쪽 방향으로만 적용이 된다는점.. source-> destincation으로만.
 source와 destination으로 구성되는데 source에 선언된 변수가 바뀔때 destination의 값도 같이 변경시킨다.
 그러나 destincation의 값이 변화되었다고 해서 source의 값이 변경되는것은 아니다.
  -->
 <Binding source="sel.value" destination="shelf.selectedIndex"/>
 <Binding source="shelf.selectedIndex" destination="sel.value"/>
 
 <Binding source="angle.value" destination="shelf.angle"/>
 
 <Binding source="pop.value" destination="shelf.popout"/>
 
 <!--
 Data를 이미 Array 구조에다가 넣어 놓았다. 이 배열이 DataProvider와 연결이된다.
 추후 실 데이타를 사용할경우 HTTPService와 같은것으로 연결하면 될거 같다.
 -->
 <Array id="dataSet">
  <String>img/photos400/photo01.jpg</String>   
  <String>img/photos400/photo02.jpg</String>   
  <String>img/photos400/photo03.jpg</String>   
  <String>img/photos400/photo04.jpg</String>   
  <String>img/photos400/photo05.jpg</String>   
  <String>img/photos400/photo06.jpg</String>   
  <String>img/photos400/photo07.jpg</String>   
  <String>img/photos400/photo08.jpg</String>   
  <String>img/photos400/photo09.jpg</String>   
  <String>img/photos400/photo10.jpg</String>   
  <String>img/photos400/photo11.jpg</String>   
  <String>img/photos400/photo12.jpg</String>   
  <String>img/photos400/photo13.jpg</String>   
  <String>img/photos400/photo14.jpg</String>   
  <String>img/photos400/photo15.jpg</String>   
  <String>img/photos400/photo16.jpg</String>   
  <String>img/photos400/photo17.jpg</String>   
  <String>img/photos400/photo18.jpg</String>   
  <String>img/photos400/photo19.jpg</String>     
 </Array>

 
 <local:DisplayShelf id="shelf"  horizontalCenter="0" verticalCenter="0"
  borderThickness="10" borderColor="#FFFFFF" dataProvider="{dataSet}" 
  enableHistory="false" width="100%"/>

 <!--
 슬라이더별 기능
 id값별
 angle : 메인 이미지 외에 주변에 나타나 있는 이미지들의 각도
 sel : 메인에 보여줄 이미지 선택
 pop : 메인 이미지 외에 주변에 나타나 있는 이미지들의 크기
 -->

 <VBox horizontalCenter="0" bottom="10" horizontalAlign="center" verticalAlign="middle">
  <HBox>
   <Label text="Angle:" />
   <!--
   HSlider의 간단한 설명
   id = 슬라이더의 id값
   liveDragging = 슬라이더의 드래그를 할지 말지 설정하는것 true : 드래그 가능
   minimum = 슬라이더 컨트롤의 최소치
   maximum = 슬라이더 컨트롤의 최대
   value = 슬라이더 컨트롤의 기본값
   snapInterval = 슬라이더 컨트롤의 증가 값 , .1은 0.1씩 증가
   width = 슬라이더의 width값
   -->
   <HSlider liveDragging="true" id="angle" minimum="5" value="35" maximum="90" snapInterval=".1" width="400" />
  </HBox>

  <HBox>
   <Label text="Selection:" />
   <HSlider liveDragging="true" id="sel" minimum="0" value="0" maximum="{shelf.dataProvider.length}" snapInterval="1" width="400" />
  </HBox>

  <HBox>
   <Label text="pop:" />
   <HSlider liveDragging="true" id="pop" minimum="0" value=".43" maximum="1" snapInterval=".01" width="400" />
  </HBox>

 </VBox>
</Application>



2. DisplayShelf.as
package
{
 import flash.events.Event;
 import flash.events.EventDispatcher;
 import flash.events.KeyboardEvent;
 import flash.events.MouseEvent;
 import flash.events.TimerEvent;
 import flash.filters.DropShadowFilter;
 import flash.geom.Matrix;
 import flash.ui.Keyboard;
 import flash.utils.Dictionary;
 import flash.utils.Timer;
 
 import mx.collections.ArrayCollection;
 import mx.collections.ICollectionView;
 import mx.collections.IList;
 import mx.collections.XMLListCollection;
 import mx.controls.Image;
 import mx.core.ClassFactory;
 import mx.core.IDataRenderer;
 import mx.core.IFactory;
 import mx.core.UIComponent;
 import mx.effects.AnimateProperty;
 import mx.effects.easing.Quadratic;
 import mx.events.CollectionEvent;
 import mx.managers.HistoryManager;
 import mx.managers.IFocusManagerComponent;
 import mx.managers.IHistoryManagerClient;

 // defining styles on the DisplayShelf.  By defining these styles here in metadata, developers will be allowed
 // to specify values for these styles as attributes on the MXML tag.  Note that this component doesn't actually
 // use these styles...instead, the TiltingTiles it contains use them. But this component assigns _itself_ as the
 // stylename for those TiltingTile instances. That makes the tiltingTile inherit all the style values defined on this component.
 // Thus by defining the styles on this component, we are automatically passing them through to the contained subcomponent.
 // this is a common practice for aggregating subcomponents.
 [Style(name="borderThickness", type="Number")]
 [Style(name="borderColor", type="Number")] 
 // defining the change event. This event is dispatched whenever the selectedIndex of this component changes. By declaring it
 // here, in metadata, we allow developers to specify a change handler on our MXML tag. 
 [Event("change")] 
 // defining the default property.  By declaring dataProvider as our defaultProperty, we are allowing developers to specify the value of
 // default property as the content of the DisplayShelf tag, without having to explciitly call it out as the value for defaultProperty.
 [DefaultProperty("dataProvider")]
 
 /* our custom component. Note a few things:
 /  1. we're extending UIComponent, not Canvas or some other Container. It's a common misconception that if you're going to
 /  have children, you must extend Container. Not True. Extend container if you want to do what containers do...namely, aggregate children
 /  specified in MXML...if you want easy access to a container's predefined layout algorithm...or if you want scrolling and clipping capabilities
 /  out of the box.  Otherwise, using UIComponent as your base class is much simpler. All UIComponents can contain children for implementation purposes.
 /  2. We're implementing the IHistoryManagerClient interface. This allows us to save off our state whenever someone tells the history manager to save.
 /  we're making this component behave like the navigator classes...optionally, you can have the back button navigate back to previous selections of this component.
 /  3.  We're implementing IFocusManager component.  We do that to let the Focus Manager know that we want to accept focus and keyboard events.  All of the functionality
 /  to do this is already supported in UICompoent, our base class...all we need to do is add this 'marker' interface, and override the keyDownHandler method to add our
 /  logic to interpret keystrokes. 
 */
 public class DisplayShelf extends UIComponent implements IHistoryManagerClient, IFocusManagerComponent
 {
  //---------------------------------------------------------------------------------------
  // constants
  //---------------------------------------------------------------------------------------

  // how far, in pixels, each child will overlap when stacked sideways. This probably should be a percentage of the size of the children...i.e., overlap 1/5th...but
  // we're taking a shortcut here by defining it in pixels.  
  private const kPaneOverlap:Number = 40;

  //---------------------------------------------------------------------------------------
  // private state
  //---------------------------------------------------------------------------------------

  // how far our selected item should 'pop' in front of the non-selected items.  We'll use this value to compute a scale-down factor for
  // the non-selected items.
  private var _popout:Number = .43;
  // storage for our data provider property. Note that we're requiring all of our dataproviders to implement the IList interface. We could have
  // chosen Arrays, but then we wouldn't be able to detect when the developer added or removed items from the list. We also could have chosen
  // ICollectionView, but that's a heavier interface that requires us to use cursors...something we don't really need to do.  IList provides
  // a nice compromise between functionality and simplicity.  Note that all of the collection classes...ArrayCollection and XMLListCollection...defined
  // by the framework implement the IList interface.
  private var _dataProvider:IList;
  // a flag to let us know when our children are dirty. We're going to be putting our children creation logic in our commitProperties function. Often
  // there's more than one set of update logic that goes into commitProperties, so it's nice to store an extra flag to let you know whether a particular
  // bit of updateLogic needs to be run. We'll set this flag when anything changes that requires us to regenerate our children.
  private var _itemsDirty:Boolean = true;
  // our array of children.  These are the TiltingTiles that we'll generate, one for each item in the dataProvider.
  private var _children:Array = [];
  // the tilt angle for the non-selected children. This can be set by the developer.
  private var _angle:Number = 35;
  // the current selected index, as set by the developer.
  private var _selectedIndex:Number = 0;  
  // the index (or rather, value, since it can be fractional) of the item at the center of our list as currently displayed. Since we animate from
  // selected index to selected index when it changes, our 'current' position is different from the 'selected' position. By keeping track of this
  // value, we can make sure that when we draw we're always drawing the 'current' index as it animates towards the selected index.
  private var _currentPosition:Number = 0;
  // a map that allows us to use an itemRenderer (actually, a tiltingTile) as a key to map back to the index of the it represents. We'll use this when
  // the user clicks on one of the tilting tiles to decide what our new selected index is.  We _could_ just iterate over our children list to
  // find the index on click, but there are lots of use cases where you need to store extra data about an itemRenderer that can't be easily looked up.
  // in those cases, Dictionaries are really useful tools. So we'll use one here just as a demonstration.
  private var _itemIndexMap:Dictionary;
  // a flag to control whether we want to automatically enable history management when the selected index changes.  This way the component
  // can be used in scenarios where it doesn't represent a 'location' to the user.
  private var _enableHistory:Boolean = false;
  // these are structures we'll need temporarily when calculating layout. Rather than allocating them again and again on update, we'll just allocate them
  // once and hold on to them.
  private var lCP:ChildPosition = new ChildPosition();
  private var rCP:ChildPosition = new ChildPosition();
  // the selected index, clamped to the range defined by the dataProvider. We store this separate from the actual selected index as assigned by the developer.
  // we want to calculate it only once and then store it off. But if we stored it back into our selected index property, we'd need to worry about scenarios where
  // the selectedIndex gets assigned before the dataprovider does. So we store it in a separate variable, so as not to trample the 'true' selected index.
  private var _safeSelectedIndex:Number;
  // storage for the item renderer factory, that will generate item renderer interfaces for us as necessary.
  private var _itemRenderer:IFactory;
  // the effect we'll use to animate from old to new selected index.  If the user changes selected index in the middle of an animation, we'll want to cancel
  // the old one, so we keep a reference to it.
   private var _animation:AnimateProperty;
   
   // whether or not we should automatically select a child when the user clicks on a particular item.  It's generally good practice to avoid hard coding UI gestures
   // into your component if you can avoid it...if possible, a good component will provide a default UI gesture, a way to disable it, and a programmatic way to
   // build an alternate UI gesture. In this case, by default we select an item on click, we allow the developer to turn that off, and we allow the developer to
   // set the selectedIndex programmatically so they can select on, say mouse over.   
   private var _selectOnClick:Boolean = true;
  

  //---------------------------------------------------------------------------------------
  // constructor
  //---------------------------------------------------------------------------------------

  public function DisplayShelf()
  {
   super();
   // define a default empty dataprovider. Rather than deal with this property being null, it's easiest to always
   // assume there's something, and substitute empty 'somethings' for null dataproviders.
   dataProvider = new ArrayCollection();
   
   // we register with the history manager to let it know that we will want to save state whenever someone tells the history manager to remember
   // the current state of the application.
   HistoryManager.register(this);

   _itemIndexMap = new Dictionary(true);
   // set up a default item renderer. We could require the developer to always specify one, but if we've got an 80% use case, it's nice to define
   // a default one.  Note that this does force the compiler to link in the Image class, even if the developer turns around and redefines the itemRenderer
   // property, so there is a potential price to pay in application size. Chances are pretty good the developer is using Image somewhere though.
   _itemRenderer = new ClassFactory(Image);

  }


  //---------------------------------------------------------------------------------------
  // public properties
  //---------------------------------------------------------------------------------------

  /*  True if the developer wants us to automatically save changes to the selectedIndex in the history manager or not.
  */
  public function set enableHistory(value:Boolean):void
  {
   _enableHistory = value;   
  }
  public function get enableHistory():Boolean
  {
   return _enableHistory;
  }
  

  /*  How far out the selected item should 'pop' from the background items.  A value of 0 doesn't pop it out at all, while a value of 1 will receed
  * the background items infinitely to the horizon.   Basically, the value is inverted and used as a scale factor for the background items.
  * pick something appropriate.
  * FWIW, now that I look at this, it really should be a style, not a property
  */  
  [Bindable] public function set popout(value:Number):void
  {
   _popout = value;
   /*  Being a good flex component, we don't want to recalculate every time someone changes this value. Instead, we store the change,
   *  and invalidate so we'll get to redrew the next time the screen is going to be updated.
   */
   invalidateDisplayList();
  }
  public function get popout():Number
  {
   return _popout;
  }
  
  /* the index of the currently selected item in the dataProvider.  Note that since this component animates its position, this is not necessarily the
  * same as the item we are currently looking at. We might be in the middle of animating towards the selected item.
  * note that since we are going to dispatch a well defined, named event when this value changes, we specify that
  * event in the binding metadata. That let's flex know that we're going to be reponsible for dispatching the event ourselves.
  * Otherwise the binding metadata would result in _another_ event being dispatched, which would be wasteful.
  */
  [Bindable("change")]
  public function set selectedIndex(value:Number):void
  {
   // save time and performancing by doing nothing if the selected index is already the new value.
   if(_selectedIndex == value)
    return;
    
   // store off the new value.
   _selectedIndex = value;
   
   // since we are going to use this value to index into the item renderers, we want to make sure we don't use a value outside the range of
   // existing renderers. Rather than having to liter our code with those checks all over the place, we'll clamp it to the legal range once now
   // and store off the 'safe' value.
   _safeSelectedIndex = Math.max(0,Math.min(_selectedIndex,_children.length-1));

   // dispatch an event letting listeners know that
   dispatchEvent(new Event("change"));   
   // when the selected index changes, we'll want to kick-start our animation.
   startAnimation();
   
   // tell the history manager that something significant to the history has changed.
   if(_enableHistory)
    HistoryManager.save();
  }
  
  public function get selectedIndex():Number
  {
   return _selectedIndex;
  }


  /* This property represents the current position in the child items that our component is looking at.
  * All of our rendering is done off of this property. By exposing it as a public property, we can animate
  * it, which means that even though we're incorporating animation, our rendering will always be in sync
  * with the internal state of our application
  */
  public function set currentPosition(value:Number):void
  {
   _currentPosition = value;
   invalidateDisplayList();
  }
  public function get currentPosition():Number
  {
   return _currentPosition;
  }
  
  /* where we're getting our data from.  We're going to follow the flex SDK convention of leaving our dataProvider property
  * untyped, and automatically wrapping raw Arrays and XMLLists as a convenience. For this component, we're going to require
  * that our dataProvider either implement the IList interface, or be something we can convert into an IList implementation.
  */
  [Bindable] public function set dataProvider(value:Object):void
  {
   /* first, if we have a previous dataProvider, we're going to want to remove any event listeners from it
   */
   if(_dataProvider != null)
   {
    _dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,dataChangeHandler,false);
   }
   /* Now, as a convenience to the caller, convert our dataProvider into an IList implementation */
         if (value is Array)
         {
             _dataProvider = new ArrayCollection(value as Array);
         }
         else if (value is IList)
         {
             _dataProvider = IList(value);
         }
   else if (value is XMLList)
   {
    _dataProvider = new XMLListCollection(value as XMLList);
   }
   
   /*  Add an event listener so we know and can react when our dataProvider changes. Note that the convention in flex is that
   * list-like components are only responsible for detecting and reacting to changes in the list itself, _not_ changes
   * to the properties of the items themselves.  It's the responsibility of the item renderers to do that as necessary.
   *
   * Also, note that we're using a weak listener here. Since the data provider is being passed in by an external caller,
   * we don't know what the lifetime of the dataProvider is w/relation to our lifetime. Since we don't have a constructor,
   * we won't ever get a chance to remove our listener. So we use a weak listener to make sure we don't get locked into
   * memory by this.
   */   
   _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,dataChangeHandler,false,0,true);   
   
   /* since we now need to re-allocate our item renderers, we'll set a flag and invalidate our properties.  As with layout and size,
   * by putting the item renderer generation into commitProperties, we avoid having to run it too often
   */
   _itemsDirty = true;
   invalidateProperties();
   /*  Our measured size is dependent on our number and size of items in the dataProvider, so we need to invalidate it here*/
   invalidateSize();
  }
  public function get dataProvider():Object
  {
   return _dataProvider;
  }
  
  /* The UIComponent that we'll use to render our items.  Since we need to create multiple of these...one for each item in the
  * dataprovider...we need not an itemRenderer, but a _factory_ that can create itemRenderers on demand.  That's why we type
  * this property as an IFactory. IFactory is a special interface that signals to the compiler that we need an object that implements
  * the factory pattern. When the MXML compiler sees a property of type IFactory, it allows the developer specify it's value in one
  * of three ways:
  * By specifying an object that implements the IFactory interface (that's normal).
  * By specifying the name of a class...it automatically wraps the class in an instance of ClassFactory and assigns that to the property.
  * By defining a component inline via <mx:Component>...it defines an implicit class, wraps it in a ClassFactory instance, and assigns that.
  */
  public function set itemRenderer(value:IFactory):void
  {
   _itemRenderer = value;
   /* store off the value, and set the flag to say that we need to re-generate all of our item renderers*/
   _itemsDirty = true;
   invalidateProperties();
   invalidateSize();   
  }
  public function get itemRenderer():IFactory
  {
   return _itemRenderer;
  }

  /* The angle of the background non-selected items*/
  public function set angle(value:Number):void
  {  
   _angle = value;
   invalidateDisplayList();
  }
  
  public function get angle():Number
  {
   return _angle;
  }
  
   /* whether or not we should automatically select a child when the user clicks on a particular item.  It's generally good practice to avoid hard coding UI gestures
   * into your component if you can avoid it...if possible, a good component will provide a default UI gesture, a way to disable it, and a programmatic way to
   * build an alternate UI gesture. In this case, by default we select an item on click, we allow the developer to turn that off, and we allow the developer to
   * set the selectedIndex programmatically so they can select on, say mouse over.   
  */
  public function set selectOnClick(value:Boolean):void
  {
   _selectOnClick = value;  
  }
  public function get selectOnClick():Boolean
  {
   return _selectOnClick;
  }
  
  //---------------------------------------------------------------------------------------
  // property management
  //---------------------------------------------------------------------------------------

  /* this is the standard function where components put performance intensive computations and side-effects
  * from changes to their properties.  By calling invalidateProperties(), a component guarantees that this function
  * will get called by the layout manager before the next time the screen is going to be updated, before their
  * measure() or updateDisplayList() functions are called (if necessary).  Note that there is no guarantee about the
  * order in which commitProperties is called from component to component (i.e., it's not parent before child or vice versa).
  */
  override protected function commitProperties():void
  {
   /*  as components get more and more complicated, this function often grows to do more and more processing.
   * as a performance optimization, it's usually good to put guards around different computations to make sure
   * you're only re-calculating what you need to on any given pass. In this case, we've defined a flag to let us
   * know when something has changed that requires us to regenerate our item renderers.
   *
   * When a component creates children/sub-components, there's generally two places it should consider doing it.
   * For 'static' sub components, that don't come and go as the component is used, it's best to create them in the
   * createChildren() function.  But for children that are created and destroyed as the component is used,
   * it's best to muck with them in the commitProperties() function. Adding children to a component automatically
   * invalidates its size and display.  Since commit properties runs before measure() and updateDisplayList() runs,
   * adding children here won't accidentally trigger _another_ validation pass, which would happen if you tried to create them
   * in updateDisplayList().
   */
   if(_itemsDirty)
   {
    _itemsDirty = false;
    /* we're going to create an item renderer for each item in the data provider */

    /*  first, let's clear out our old item renderers.  Now this is horribly inefficient...if, say, the
    * developer just added a single item to the data provider, there's no reason we need to throw all the
    * old ones away. But we're going to do it for simplicity's sake here. In your code, be more efficient ;)
    */
    for(i = numChildren-1;i>=0;i--)
    {
     removeChildAt(numChildren-1);
    }
    
    /* clear out our children list and child -> index map, since we just threw away all of our children */
    _itemIndexMap = new Dictionary(true);
    _children = [];    
    
    for(var i:int = 0;i<_dataProvider.length;i++)
    {
     /* first, create a tilting Tile for the item, since that's going to give us our 3D effect */
     var t:TiltingPane = new TiltingPane();
     /*  put an entry in our dictionary mapping our tilting tile to its index in the dataProvider.
     * When the user clicks on one of our tilting tile, we'll use this map to figure out the index
     * of the item they just clicked on, and hence what our new selected index should be */
     _itemIndexMap[t] = i;
     /*  add a click event handler to our tiltingPane, so we can automatically update the selected index.
     * note that we're again using weak references here for our event listener.  In this case, since these are
     * entirely self contained objects, we don't actually need to use a weak listener here.  But we're a bit lazy,
     * and since we know that we're not going to run into any of the pitfalls of weak references, we go ahead
     * and use them anyway. Alternatively, we could have been explicit about removing the listener when we
     * removed the tilting panes later on.
     */
     t.addEventListener(MouseEvent.CLICK,itemClickHandler,false,0,true);
     /* set the tiltingTile's styleName to us, the parent componment. This is common practice for styling sub-components
     * of a parent component.  By doing this, the TiltingTile inherits _all_ of our styles...not just the inheriting ones...
     * which allows us to easily facade style values from the children up through us for our component developers to specify in CSS.
     */
     t.styleName = this;
     /*  add the tilting tile to our array of children*/
     _children[i] = t;

     /*  Now it's time to use our itemRenderer factory.  We've created a TiltingTile for our item, but our TiltingTile needs to
     * know exactly what it is that it's going to be tilting.  To do that, we ask our itemRenderer factory to create an instance for us.
     */
     var content:UIComponent = UIComponent(_itemRenderer.newInstance());
     /*  of course, in order to render our data, the itemRenderer instance  needs to know what it's going to be rendering. 
     * In flex, things that render data implement the IDataRenderer interface.  Since we can't imagine someone using this component
     * in a way that didn't require the individual item renderers to know what data they're rendering, we're going to go ahead
     * and assume that our new item renderer instance implements the IDataRenderer interface.  So we'll use it to assign the nth item
     * out of the dataProvider to our nth item renderer instance.
     */
     IDataRenderer(content).data = _dataProvider.getItemAt(i);
     /* OK, we've got an item renderer instance that now owns an item from the dataProvider. We'll put that in our tilting tile,
     * and add the tilting tile as a child.
     */
     t.content = content;
     addChildAt(t,0);
    }
   }
   
   /*  since the size of our dataProvider might have just changed, we'll revalidate our selected index to make sure it's
   * a valid index into the data.
   */
   _safeSelectedIndex = Math.max(0,Math.min(_selectedIndex,_children.length-1));
   
   /* since we've just recalculated our state, chances are pretty good we need to re-render ourselves now.
   */
   invalidateDisplayList();
  }

  //---------------------------------------------------------------------------------------
  // measurement
  //---------------------------------------------------------------------------------------
  
  /* this is our measurement function.  A component's measure() routine is where it should calculate what it's 'natural' size should be...
  * i.e., how big it would like to be if the developer doesn't assign it an explicit size. The layout manager calls this function whenever
  * it thinks your component needs to remeasure itself. That happens under a number of circumstances. a) some state that your measured size
  * uses in calculation changes, so your component explicitly calls invalidateSize() (i.e., see the set angle() function).  b) the measured or explicit
  * of one of your children changes size...the layout manager assumes that your measured size relies on the size of your children, so it will ask you to
  * re-measure.  Note that according to the conventions of the SDK, your measured size is generally ignored if you have an explicit size set. That means
  * that the layout manager might optimize by not calling your measure() routine if you have an explicit size. So don't do any calculation in here that
  * _must_ happen for your component to function properly.
  */
  override protected function measure():void
  {
   var mHeight:Number = 0;
   var mWidth:Number = 0;
   var t:TiltingPane;
  
   /*  Since each child could potentially be at the middle of the component at its natural size with the other children stacked along side it, we need to
   * look at each child to calculate our measured size.
   * So for each child:
   */
   for(var i:int = 0;i<_children.length;i++)
   {
    t = _children[i];
    /*  our measured size will just be the largest measured size of all of our children, to make
    * sure we can correctly render all of them*/    
    mHeight = Math.max(t.measuredHeight,mHeight);
    /*  our measured width, however, is more complicated. For each child, we want to calculate how big we need to be if that child was selected.
    * That's the size of the child plus the amount of space we need to stack the other children in the background.  Now since each child has a different
    * number of children to the left and right, that will be different for each child.  But we want the child to stay in the middle, so we need it to be
    * symmetrical. So our calculation is:  figure out how many children go to the left and right of child N.  Take the maximum of that, figure out how
    * much space we need to stack those children in the background, and double that, since we want the same amount of space on each side.  Now add the measured
    * width of child N, since it's going to be at the middle. That's how much space we need for Child N.  Now for all children, calculate that, and take the
    * largest number we find
    */
    mWidth = Math.max(mWidth, t.measuredWidth  + Math.max(i,_children.length - i - 1) * kPaneOverlap * 2);
   }
   /* store off our measured sizes. If we were really being good, we'd probably calculate a minimum size too. But since our component doesn't adjust its layout   
   * to match its size, we don't really have a minimum size. i.e., if we wanted to we could squeeze the overlapping children in the background together if we
   * didn't have enough space.  If that were the case, our minimum size would be the same calculation above, but for whatever we consider our 'minimum' overlap
   * to be.  I'll leave that as an exercise for the reader
   */
   measuredHeight = mHeight;
   measuredWidth = mWidth;
  }
  
  //---------------------------------------------------------------------------------------
  // layout
  //---------------------------------------------------------------------------------------

  /* this is our main function that does all of our layout and rendering.  The LayoutManager takes care of making sure this function gets called
  * right before the screen is updated if our component needs updating. We tell the layout manager that we need updating by calling
  * invalidateDisplayList(). This automatically happens whenever our component changes size.
  */  
  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
  {
   /*  often list-like components need to do things like check values of the first child as a starting point for calculations.
   * that assumes we have a first child, and can RTE if we're empty. Rather than putting guards all over the place, we'll just
   * bail out here if we have no children at all
   */
   if(_children.length == 0)
    return;

   /* ChildPosition is a simple value structure we use to pass around the calculated position and angle of a single child.  It's defined at
   * the end of this file.
   */
   var c:ChildPosition = new ChildPosition();
   var t:TiltingPane;
   var m:Matrix;
   
   /* for each child to the left of our currently selected child...
   */
   for(var i:int = 0;i<=_currentPosition;i++)
   {
    /* calculate where it should be based on our currently focused position. This function is defined below.
    */
    calcPositionForSelection(i,_currentPosition,c);    
    t = _children[i];
    /*  and put it there. Note that the first thing we do is set the size of the child to its requested (measured or explicit size).
    * in Flex, it's always that parent's responsibility to tell a child what size it should be. If we didn't do this, the child
    * would just sit at size 0, even if we or someone else explciitly set the width/height properties.  
    * In this case, we're not doing any resizing of the children.  So we just want them to be their measured size, or their explicit
    * size if someone has set an explicit size on them.  This is such a common if/or calculation that UIComponents have a convenience function
    * defined called 'getExplciitOrMeasuredWidth/Height().'
    * Note that we use the setActualSize function.  Components sizing their children should _always_ use this function.  It serves two purposes:
    * first, it differentiates between the _explicit_ size that might be set by the developer, and the 'current actual' size that the parent wants
    * the child to be.  Since explicit size probably is used in a parent's computed measurement and layout algorithms, we don't want to confuse
    * the inputs to those algorithms with the output (the actual size). Second, since explicit size of a child is usually an input to the parent's
    * measurement and layout, any time they change the parent needs to be invalidated and re-layout. So if we accidentally set the explicit size here,
    * it would trigger another invalidation and layout, and potentially an infinite layout loop.  Instead, setting the actual size doesn't invalidate
    * the parent (since it assumes child actual size is _not_ an input to the parent's measurement/layout algorithm).
    */
    t.setActualSize(t.getExplicitOrMeasuredWidth(),t.getExplicitOrMeasuredHeight());
    /*  we need to set the angle of the child here.  Now TiltingTiles use their angle as an input to their measured size. And as with all UICompoennts,
    * their measured size is an input to their parent's measurement and layout algorithms. So if we weren't careful, setting their angle here would
    * force another measurement/layout on our component, causing an infinite loop. So as with size, we need to differentiate between the explicit
    * angle of the TiltingTile, and the 'acutal' angle as assigned by the parent (us).
    */
    t.setActualAngle(c.angle);
    /*  we want the children on the left to stack up from left to right. Children with higher indexes go above children with lower indexes, so we'll
    * set the child index here to get it stacking correctly
    */
    setChildIndex(t,i);
    /*  Lastly, we'll set the scale of the tilting tile. Selected children are displayed at full scale, while background children are scaled down a bit
    * to make it look like they're receeding into the distance.  Now scale is _also_ an input into the measured size of a child (set its scale to 2X,
    * and its measured size doubles) so we have the same problem with scale as we do with sizing and angle above.  Flex unfortunately doesn't differentiate
    * between explicit and actual scale. But we can work around it by manipulating the child component's scale factors in its matrix, which shortcuts    
    * the part of the framework that causes the invalidate and potentially the infinite loop. This is a hack, one we hopefully won't need in an upcoming release
    * of the SDK.
    */
    m = t.transform.matrix;
    /*  assign the scale */
    m.a = m.d = c.scale;
    /*  and assign the matrix back to the item.  Matrices are copy on access...meaning when you ask for the matrix of an object, you get a copy of it.
    * So our changes won't affect the object until we assign it back to the child as its transform matrix
    */
    t.transform.matrix = m;
    /* set its location */
    t.move(c.x,c.y);
   }

   /*  this is exactly the same logic as the previous loop, except that we want to stack each child on the right hide side _below_ the previous child.
   */
   for(i = Math.floor(_currentPosition)+1; i< _children.length;i++)
   {
    calcPositionForSelection(i,_currentPosition,c);    
    t = _children[i];
    t.setActualSize(t.getExplicitOrMeasuredWidth(),t.getExplicitOrMeasuredHeight());
    t.setActualAngle(c.angle);
    t.move(c.x,c.y);
    /*  each time we move to the next child, we set its index to 0. This bumps all previous children up one level, and puts this child at the bottom,
    *   ensuring it ends up below the child to its left
    */
    setChildIndex(t,0);
    m = t.transform.matrix;
    m.a = m.d = c.scale;
    t.transform.matrix = m;
   }
   
   /* lastly, we make sure the currently selected child is on top
   */
   setChildIndex(_children[Math.round(_currentPosition)],numChildren-1);
   
   
  }
  
  /*  this function calculates the scale, angle, and position a child should be given a particular
  * selected position.  Since we animated our 'currentPosition', we need to be able to calculate  
  * selected position for any real positive number. To do that, we calculate two different positions
  * and average them out. If, for example, the current position was 3.7, we'll calculate the values
  * for a currentPosition of 3, and a currentPosition of 4, and average .7 of the first and .3 of the second
  */
  private function calcPositionForSelection(i:Number,sel:Number,c:ChildPosition):void
  {
   var delta:Number = sel - Math.floor(sel);
   /* if sel is already an integer, we just calculate our position for that integer, and return
   */
   if(delta == 0)
   {
    calcPositionForIndexSelection(i,sel,c);
    return;
   }
   /* otherwise, calculate our position for the previous and next integers
   */
   calcPositionForIndexSelection(i,sel-delta,lCP);
   calcPositionForIndexSelection(i,sel-delta+1,rCP);
   /* and compute a weighted average
   */
   c.angle = lCP.angle + delta * (rCP.angle - lCP.angle);
   c.scale = lCP.scale + delta * (rCP.scale - lCP.scale);
   c.x = lCP.x + delta * (rCP.x - lCP.x);
   c.y = lCP.y + delta * (rCP.y - lCP.y);
  }

  /* this function calculates the position for a given child assuming our currentPosition value is 'sel.'
  * unlike the previous funciton, this one assumes that sel is an integer.
  */
  private function calcPositionForIndexSelection(i:Number,sel:Number,c:ChildPosition):void
  {   
   var t:TiltingPane = _children[i];
   var selected:TiltingPane = _children[sel];
   var adjacent:TiltingPane;
   var a:Number = _angle;

   if(i == sel)
   {
    /* if the item we're calculating the position for _is_ the selected item,
    * then we know exactly where it goes...smack dab in the middle, at full size, full scale,
    * with an angle of 0.
    */
    c.scale = 1;
 
    c.x = unscaledWidth/2 - t.getExplicitOrMeasuredWidth()/2;
    c.y = unscaledHeight/2 - t.getExplicitOrMeasuredHeight()/2;
    c.angle = 0;
   }
   else if (i < sel)
   {
    /*  otherwise, if it's to the left of the selected item,
    * we want to scale it down to make it look like it's receding into the background...
    */
    c.scale = (1-_popout);
    /* tilt it in towards the selected item */
    c.angle = _angle;
    /*  and push it off to the left.  To do that, we need to calculate it's position. Most of the children to the left just go a fixed distance
    * from the child to its right, and so we don't care about their actual size. But the first child immediately to the left of the selected child
    * is mostly visible, so we need to position it so that only a little bit overlaps. Which means we need to know it's size. 
    * so first, let's calculate the position of that first child to the left.
    * that's going to be the left edge of the selected item, minus approximately 8/10th of the widths of the next item over (since we want it to
    * overlap by about 2/10ths.)
    */
    adjacent= _children[sel-1];
    var leftBase:Number = unscaledWidth/2 - selected.widthForAngle(0)/2 - (adjacent.getExplicitOrMeasuredWidth()/2 +adjacent.widthForAngle(a)*2/10) * c.scale;
    /* now that we know where that first item to the left sits, we can calculate the position of our child as a simple fixed distance based on how many
    * children sit between it and that first item to the left
    */
    c.x = leftBase - kPaneOverlap*(sel-1-i),
    /* lastly, center it vertically */
    c.y = unscaledHeight/2 - t.getExplicitOrMeasuredHeight()* (1-_popout)/2;
   }
   else
   {
    /*  this is basically the same logic as above, but for children to the right of the selection.  It sets it to
    * a negative angle, and calculates the position as a distance from the first child to the right of the selection.
    */
    c.scale = (1-_popout);
    adjacent = _children[sel+1];
    var rightBase:Number  =  unscaledWidth/2 + selected.widthForAngle(0)/2 + (adjacent.widthForAngle(-_angle)*3/10 - adjacent.getExplicitOrMeasuredWidth()/2) * c.scale;
    c.angle = -_angle;
    c.x = rightBase + kPaneOverlap*(i-(sel+1));
    c.y = unscaledHeight/2 - t.getExplicitOrMeasuredHeight() * (1-_popout)/2;
   }
  }
  
 
  //---------------------------------------------------------------------------------------
  // interaction
  //---------------------------------------------------------------------------------------

  /* this is our event handler for when a user clicks on an item.
  */
  private function itemClickHandler(e:MouseEvent):void
  {
   /* again, if the developer wants different UI behavior, allow them to disable this */
   if(_selectOnClick == false)
    return;
   
   /*  find out what the index is of the selected item.  To do this, we map back from the
   *   item clicked on to an index in our itemIndexMap.  Since we re-order our children
   *   to get depth and layering correct, we couldn't necessarily just ask for the child index...
   * the child index would be different from the item's index in the dataProvider.  We could
   * iterate over the children array to find the one that was clicked on, but that might have
   * bad performance implications. Instead, we use a dictionary to quickly map from a child to
   * an index.  This is a really useful way to generally store metadata about your items/renderers
   * in custom components.
   */
   var index:Number = _itemIndexMap[e.currentTarget];
   selectedIndex = index;
  }
  
  /*  this is our event handler for when our dataProvider changes.
  * in this case, all we do is set a flag indicating that we want to regenerate our item renderers,
  * and invalidate our properties.  The change event from the collection typically carries additional
  * data...was an item added, removed, or just changed?  We could, and really should, optimize how
  * we respond to this event based on what really happened...i.e., if an item was added, there's no
  * need to regenerate _all_ our item renderers. Exercise for the reader ;)
  */
  private function dataChangeHandler(event:CollectionEvent):void
  {
   _itemsDirty = true;
   invalidateProperties();
  }

  //---------------------------------------------------------------------------------------
  // Keyboard Management
  //---------------------------------------------------------------------------------------

  /* this event handler is where we respond to key presses when we have focus. Note that this event handler
  * is already defined by the UIComponent base class...so we didn't have to add it anywhere. Instead, by
  * simply implementing the marker interface IFocusManagerComponent, and overriding this method, we get to
  * handler key down events.
  */
     override protected function keyDownHandler(event:KeyboardEvent):void
     {
      super.keyDownHandler(event);
   switch(event.keyCode)
   {
    case Keyboard.LEFT:
     selectedIndex = Math.max(0,selectedIndex-1);
     event.stopPropagation();
     break;
    case Keyboard.RIGHT:
     selectedIndex = Math.min(_dataProvider.length-1,selectedIndex+1);
     event.stopPropagation();
     break;     
   }
     }

  //---------------------------------------------------------------------------------------
  // animation
  //---------------------------------------------------------------------------------------
  
  /* This is where we do our animation.  This function is called whenever the selected index changes.
  */
     private function startAnimation():void
     {
      /*  when you add animation to a component, you need to decide what will happen if two animations
      * try to fire at once. What happens, in this case, if the user sets the selected index while we're
      * still animating towards a previous selected index?
      * Our decision here is to finish the previous animation (i.e., jump directly to the end of the animation).
      */
      if(_animation != null && _animation.isPlaying)
      {
       _animation.end();
      }
    
   /*  our animation is simple. Since our component tracks 'selectedIndex' and 'currentPosition' as separate concepts,
   * animating is just a question of tweening the currentPosition variable to the selectedIndex value.
   * every time the animation updates currentPosition, our component will invalidate and redraw. Easy as pie.
   */
   _animation = new AnimateProperty(this);
   _animation.property = "currentPosition";
   _animation.toValue = _selectedIndex;
   _animation.target = this;
   /*  if we picked a fixed duration, we'd have to deal with the fact that sometimes we're only moving a single position,
   * and sometimes we may be moving a thousand.  Either short distances would be way too slow, or long distances would go
   * way to fast and look bad.  Instead, we'll calculate a duration based on how far we're animating.  We also put in a minimum animation
   * so short distances don't go too quickly. We probably should also putting a cap so even in large data sets animations don't take too long.
   * We could tweak this endlessly.
   */
   _animation.duration = Math.max(500,Math.abs(_selectedIndex - _currentPosition) * 200);
   _animation.easingFunction = mx.effects.easing.Quadratic.easeOut;
   _animation.play();
  }


  //---------------------------------------------------------------------------------------
  // history managmeent
  //---------------------------------------------------------------------------------------
  
  /*  These are the two methods a component needs to implement in order to save state with the history manager.
  * in our constructor, we registered ourselves as a history enabled component with the history manager. Once that
  * happens, any time someone tries to save a state in the history, the manager will call this function to let our
  * component store off whatever values it needs to capture its current state
  */
  public function saveState():Object
  {
   if(_enableHistory == false)
    return {};
   /* all we really need to store is our selected index. */
   var index:int = _safeSelectedIndex == -1 ? 0 : _safeSelectedIndex;
   return { selectedIndex: index };
  }

  /* this function, in turn, gets called whenever someone tries to navigate (back or forth) to a stored state.
  * this funciton gives us a chance to read out our stored state and react accordingly.
  */  
  public function loadState(state:Object):void
  {
   if(_enableHistory == false)
    return;
  
   var newIndex:int = state ? int(state.selectedIndex) : 0;
   if (newIndex == -1)
    newIndex = 0;
   if (newIndex != _safeSelectedIndex)
   {
    // When loading a new state, we don't want to
    // save our current state in the history stack.
    var eh:Boolean = _enableHistory;
    _enableHistory = false;
    selectedIndex = newIndex;
    _enableHistory = eh;
   }
  }
 }
}
 import flash.events.EventDispatcher;
 

/* this little doodad is just a value object we use to store the position information for a single child.
* by defining the class here, we don't clutter up the global namespace. This class is only visible inside this file.
*/
class ChildPosition
{
 public var angle:Number;
 public var x:Number;
 public var y:Number;
 public var scale:Number;
}

3.TiltingPane.as
/*
Copyright (c) 2006 Adobe Systems Incorporated

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

package
{
 import mx.core.UIComponent;
 import flash.geom.Matrix;
 import flash.display.Sprite;
 import flash.display.Shape;
 import flash.display.Graphics;
 import flash.events.Event;
 import flash.display.Bitmap;
 import flash.display.BitmapData;
 import flash.display.GradientType;
 import flash.geom.Rectangle;
 import flash.geom.Point;
 import flash.display.DisplayObject;
 import flash.display.CapsStyle;
 import flash.display.LineScaleMode;
 import flash.display.JointStyle;
 import mx.events.FlexEvent;

 /* by defining a default property, we are allowing a developer to use our component and specify the value of this property as the
 * 'content' of the TiltingPane tag in their MXML.  This is a reasonable thing to do when there is a property that reasonably maps
 * to the developer's concept of the 'content' of the component. It wouldn't make sense, for example, to set the angle of the
 * component to be the default property, since a developer doesn't think of the tilting pane as 'containing' the angle.  But
 * naturally, our 'content' property is a good match here.  This is a convenient way to make what is sometimes referred to as a
 * 'custom container.'  to the developer, this component looks and feels like a container, even though it doesn't extend the
 * container base class.
 */
 [DefaultProperty("content")]

 /* by defining our styles here, we allow the developer to specify their values on the tag in MXML. If we didn't declare them here
 * the compiler would not recognize these styles as legal attributes in MXML.
 */
 [Style(name="borderThickness", type="Number")]
 [Style(name="borderColor", type="Number")] 
 /*  As a new custom component that defines basic control behavior, we'll choose to extend UIComponent. Since we want this to be a full fledged
 * flex component, our choices are essentially UIComponent, a Container, or some other previously existing component. No existing component
 * is close to the behavior we want, so that leaves UIComponent or Container. Container defines all sorts of great functionality...scrolling, clipping,
 * etc.  But we don't need any of that. The only things that component does define is the ability to have children, and the ability to define those
 * children in MXML. But we get children as well when extending UIComponent, and using templating and defaultProperty (see below and above) we can
 * let develoeprs specify children in MXML as well.  so we'll go with UIComponent.
 */
 public class TiltingPane extends UIComponent
 {
  //---------------------------------------------------------------------------------------
  // constructor
  //---------------------------------------------------------------------------------------

  public function TiltingPane()
  {
   super();
  }

  //---------------------------------------------------------------------------------------
  // initialization
  //---------------------------------------------------------------------------------------
 
  /* createChildren() is the right place to create the sub components that we'll need to implement our TiltingPane.  If you need to dynamically
  * create children based on the state of the component, you should do that in commitProperties (see DisplayShelf for an example). But for
  * sub-components that will be needed for the lifetime of the component, doing it here gets you the best performance at initialization time.
  *
  * for our component rendering, we're going to be needing a bunch of flash display objects...shapes and bitmaps...to get the effect we're looking for.
  * When writing a UIComponent, it's perfectly legal to use raw flash display objects as sub-components to get whatever effect you need.
  * a UIComopnent is like your own little sandbox...in here, you can use whatever flash and flex APIs and objects you want.
  */
  override protected function createChildren():void
  {
   // first, create a simple shape object. We'll use this as a mask to give our content a perspective trapezoid.
   _mask = new Shape();
   // next, create another simple shape obecjt. This one will overlay our content and be used to draw a nice border.
   _border = new Shape();
   // add these as our children. While the mask doesn't technically get shown on screen, it still needs to go on the display List somehwere.
   // flash let's you use any arbitrary displayObject on the displayList as a mask...it doesn't necessarily need to have the same parent as the
   // thing it's masking.  Flash will just look at the postiion of the two objects on screen, see where they overlap, and clip the content
   // accordingly.  But it's easiest to figure out how the mask, content, and border will relate to each other if they're in the same coordinate space...
   // i.e., if 0,0 means the same thing to all of them...so we'll make all three children. We don't create or attach our content here, since we're going
   // to let the user of our compoennt dictate what that is.
   addChild(_mask);
   addChild(_border);

   
   // if we already have our content object, we tell it to our our mask object as its mask. If not, we'll do it later when we get our content.
   if(_content != null)
    _content.mask = _mask;
  }
  

  //---------------------------------------------------------------------------------------
  // constants
  //---------------------------------------------------------------------------------------

  // some constants that affect how we render our fake 3D and reflection.  Good rule of thumb...constants like these are
  // usually prime candidates for turning into styles.
  private static const kPerspective:Number = .15;
  private static const kFalloff:Number = .4;

  //---------------------------------------------------------------------------------------
  // private state
  //---------------------------------------------------------------------------------------
  // the subcomponent that we'll be applying our faux 3d effect to. We know very little about this component.
  private var _content:UIComponent;
  // the shape we'll use to clip off the content into a perspective trapezoid.
  private var _mask:Shape;
  // the shape we'll draw the border around the content into.
  private var _border:Shape;
  // the bitmap object we'll copy the content into to create a reflection.
  private var _reflectionBitmap:Bitmap;

  // the tilt angle requested from the developer.  This value is used to calculate measured size.
  private var _explicitAngle:Number = 0;
  // the actual angle being used to render the component. By default, when the explicit angle is set,
  // this is set too. But a parent component compositing the tilting tile can assign an actual angle,
  // which will be used to render but not in measurement calculations.
  private var _actualAngle:Number = 0;
  // the shear factor we assign to our content based on the current actual angle.
  private var _verticalShear:Number;
  // how far, in pixels, our content is offset as a result of the verticalshear.
  private var _verticalShearEffect:Number;
  // how much we scale down our content based on the current actual angle.
  private var _horizontalScale:Number;
  //---------------------------------------------------------------------------------------
  // properties
  //---------------------------------------------------------------------------------------

  /* the actual content we'll be applying our faux 3D effect to.   By defining a property of type
  * UIComponent, we actually are allowing the developer to specify the content in MXML or actionscript.
  * This ability to parameterize the content of a component is often referred to as Templating.
  */
  public function set content(value:UIComponent):void
  {
   // if we had a previous content assigned, we need to clean up from it.
   if(_content != null)
   {
    // remove it from our display tree.
    removeChild(_content);
    // stop listening for update events.
    _content.removeEventListener(FlexEvent.UPDATE_COMPLETE,contentUpdateHandler);
   }
   _content = value;
   if(_content != null)
   {
    // add the new child. We want the content to be behind the frame, so we add it at
    // index 0.
    addChildAt(_content,0);    
    // in order to make sure we can update the reflection whenever our content
    // updates, we need to listen for the update complete event, which fires whenever
    // updateDisplayList runs on a component.  One thing to note is that this event
    // doesn't bubble, which means that we won't necessarily know if a sub-component
    // updates.  That's a limitation you might run into if you use this to
    // display more complex content.
    _content.addEventListener(FlexEvent.UPDATE_COMPLETE,contentUpdateHandler);
    _content.cacheAsBitmap = true;
    _content.mask = _mask;
   }
   
   // our 'natural' size is based on the size of our content, so when our content changes,
   // we need to remeasure.
   invalidateSize();
   // since we have new content, we'll need to update our reflection to match.
   invalidateReflection();
  }
  
  public function get content():UIComponent
  {
   return _content;
  }

  /* the tilt angle we'll use to display our content at.
  */
  public function set angle(value:Number):void
  {
   // store off the value.  Since we track explicit and actual angle separately, the value
   // gets stuffed back into both of them.
   _explicitAngle = _actualAngle = value;
   invalidateSize();
   invalidateDisplayList();
  }
  public function get angle():Number
  {
   return _actualAngle;
  }

  /* When components aggregate a TiltingTile, the angle is sometimes both an input to their layout
  * computation (as part of this component's measure() calculations) and an output...something
  * they explicitly set. As such, we  need to differentiate between explicit angle, and parent
  * calculated actual angle.
  */
  public function setActualAngle(value:Number):void
  {
   _actualAngle = value;
   invalidateDisplayList();
  }


  //---------------------------------------------------------------------------------------
  // measurement
  //---------------------------------------------------------------------------------------

  /* the measure function is where every component declares what their 'natural' size is...i.e., the most reasonable default size
  * given their content and state, assuming the developer hasn't assigned a specific size.  In this csae, our 'natural' height is going
  * to be just the measured height of our content.  That's potentially a little problematic...since we're skewing the content, it will actually be
  * a little taller than its measured size. We could account for that in our measured size, but instead I'll just report the same measured size.
  * what does that mean? It means that by default, we'll actually stick a little outside of our assigned bounds.  Which is a perfectly legal thing to do
  * in flex, if you think it's the right thing for your component to do (i.e., there's nothing about flex or flash that _prevents_ your from doing it).
  * For the measuredWidth, we'll calculate how wide our content would be at our currently explicitly assigned angle.
  */
  override protected function measure():void
  {
   if(_content != null)
   {
    measuredHeight = _content.getExplicitOrMeasuredHeight();
    measuredWidth = widthForAngle(_explicitAngle);
   }
  }
  
  /* a utility function that measures how wide we'll be at a given tilt angle.  essentially, we use a little faux 3d math to compute a horizontal
  * scale factor based on the angle.
  */
  public function widthForAngle(angle:Number):Number
  {
   /* take our value from -90 to 90, and turn it into a value from 0 to 1. */
   var p:Number = (Math.abs(angle)/90);
   /*  now take the square root. When you watch something turn away from you, it doesn't squeeze in your vision linearly. Again, this is
   * faux 3d...what we care about is that it 'looks' right, not that it is right. */
   p = Math.sqrt(p);
   /*  invert the result to get a scale factor...i.e., an angle of 0 should
   * mean a scale of 1, and an angle of 90 should mean a scale of zero */
   var scale:Number = 1 - p;
   /* finally, multiply our scale factor by the measured (or explicit) size of the content */
   var r:Number = _content.getExplicitOrMeasuredWidth() * scale;
   return r;
  }
  
  //---------------------------------------------------------------------------------------
  // rendering and layout
  //---------------------------------------------------------------------------------------

  /* updateDisplayList() is where we'll do all of our actual layout and rendering. This funciton is called by the
  * layout manager, whenever we (or our base class code) indicate we need to be updated by calling invalidateDisplayList().
  */
  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
  {
   /* make sure we have content before we bother with any of the tough stuff */
   if(_content)
   {
    var contentWidth:Number = _content.getExplicitOrMeasuredWidth();
    var contentHeight:Number= _content.getExplicitOrMeasuredHeight();
    var centerX:Number = unscaledWidth/2;

    /*  first calculate some values based on our current angle, and the size of our content.
    * we'll do this once, store it off, and use it in our various layout subroutines. Normally,
    * this is the kind of thing we'll do in our commitProperties function. But because this is based
    * on actualAngle, which is potentially set by our parents during their layout pass, this is really
    * the only time for us to do it.  our parent's updateDisplayList function is typically called after our
    * commitProperties and mesure function, so the only place to do this that we can guarantee will be called
    * after our parent has the chance to set our actualSize is here.
    */
    
    /* take our value from -90 to 90, and turn it into a value from 0 to 1. */
    var p:Number = (Math.abs(_actualAngle)/90);
    /*  now take the square root. When you watch something turn away from you, it doesn't squeeze in your vision linearly. Again, this is
    * faux 3d...what we care about is that it 'looks' right, not that it is right. */
    p = Math.sqrt(p);
 
    /*  invert the result to get a scale factor...i.e., an angle of 0 should
    * mean a scale of 1, and an angle of 90 should mean a scale of zero */
    _horizontalScale = 1 - p;

    // now compute a shear factor based on how far we're turned, and whether we're turned positive or negative.
    if(_actualAngle >= 0)
    {
     _verticalShear = p * -kPerspective;
    }
    else
    {
     _verticalShear = p * kPerspective;
    }
    /*  based on the size of our content, figure out how far the right edge of our content will be offset from its untransformed
    * location. */
    _verticalShearEffect = contentWidth/2 * _verticalShear;

    /*  first set the actual size of our content.  It's the responsibility of each parent to set the actual size of each
    * of its UIComponent children during layout. In our case, we're going to set the size of our content to its explicit
    * or measured size, which is the conventional way to size children in flex. */
    _content.setActualSize(contentWidth,contentHeight);

    /*  now that our child is sized to its default, we're going to manipulate its transform matrix to get the scaling
    * and shearing that will give us half of our 3D effect.
    */
    perspectiveDistort(centerX,0,contentWidth,contentHeight);     
    
    /* now draw the border.  if no border color or thickness is specified, we can skip this step. */
    var borderColor:Number = getStyle("borderColor");
    var borderThickness:Number = getStyle("borderThickness");
    
    /* all drawing in flex/flash is done into a graphics object. FLash is 'retained' mode, meaning all the drawing
    * that happens in a graphics object stays there until you explicitly clear it. So the first thing we need to do
    * is call clear(). It's a common mistake to forget to do this, and end up drawing over and over again on top
    * of the previous drawing. Even if you're drawing on top, it doesn't 'remove' the old graphics. Eventually,
    * you'll notice a slowdown in the application as a result of all the duplicate drawing.
    * Similarly, even if we don't have a border color or thickness, we still need to clear out the border shape graphics,
    * to make sure a border isn't lying around from a previous update.
    */
    var g:Graphics = _border.graphics;
    g.clear();
    if(!isNaN(borderColor) && !isNaN(borderThickness))
    {
     /* set the linestyle in the border shape graphics object, and draw the border trapezoid. */
     g.lineStyle(borderThickness,borderColor,1,false,LineScaleMode.NORMAL,CapsStyle.NONE,JointStyle.MITER);
     drawPerspectiveFrame(g,centerX,0, contentWidth, contentHeight);
    }     
   
    /*  if we don't have a reflection bitmap, we need to first generate a new one from our content. Whenever our content
    * changes we throw away our bitmap and redraw it. We could be a little more intelligent here...reuse the bitmap if the
    * content size isn't changing, reuse some of the pieces used in the rendering step....but that's an exercise for the reader
    */
    if(_reflectionBitmap == null)
    {
     createReflectionBitmap(_content);
    }
    /* lastly, put a matrix transform on the bitmap to get it inverted, in place, and sheared correctly. */
    positionReflectionBitmap(centerX,0,contentWidth,contentHeight);
   }
  }
  
  
  /* this internal layout utility assigns a perspective distortion and mask to its target, based on our previously computed values from the angle.
  */  
  private function perspectiveDistort(centerX:Number, yPosition:Number, frameWidth:Number, frameHeight:Number ):void
  {
   /*  grab the transformation matrix from our content. Remember that the transformation is copy on read...meaning that when
   * you ask for the matrix, you're getting a copy. Which means any changes we make will only have an effect if we assign
   * the matrix back to the transform when we're done.
   */
   var m:Matrix = _content.transform.matrix;

   /* set the shear and horizontal scale to get the basic 3D effect
   */ 
   m.b = _verticalShear;
   m.a = _horizontalScale;
   /*  position the content.  we want it centered horizontally. vertically, we want it to look as though it's at yPosition, but
   * with 3D perspective.  So we need to offset by the effect of the shearing.
   */
   m.tx = centerX - frameWidth/2 * _horizontalScale;
   m.ty = yPosition - _verticalShearEffect;

   /* make sure our changes actually affect it! */
   _content.transform.matrix = m;
   
   /*  shearing is only half the faux 3d effect. We also need to turn our content into a trapezoid, to make it look like it's
   * receeding into the distance. To do that, we'll draw a trapezoid into our mask shape, which will clip the content.
   */
   
   // first clear out any previous graphics.
   _mask.graphics.clear();
   // now begin a fill. It actually doesn't matter what kind of fill we use here, since masks are not visible on screen. But
   // we do need _some_ fill.   
   _mask.graphics.beginFill(0,0);
   // and draw our trapezoid
   drawPerspectiveFrame(_mask.graphics,centerX,yPosition,frameWidth, frameHeight);
   _mask.graphics.endFill();
  }
  
  /*  this utility function draws a trapezoid based on our currently computed actual angle.  Since we'll use this to
  * draw both our mask and border, we pass in the graphics object we'll draw into as a parameter. We also assume that
  * the caller has already set up the fill and linestyle they want, just as with the built in drawRect, etc. functions.
  * 
  * Nothing interesting going on in this function, just some math.  If I could draw a diagram in comments, I'd show
  * the basis for the math, but it's not too complicated (in fact, I arrived at this via trial and error ;)  Another
  * exercise for the reader ;)
  */
  private function drawPerspectiveFrame( g:Graphics, centerX:Number, yPosition:Number, frameWidth:Number, frameHeight:Number ):void
  {
   var frameLeft:Number= centerX - frameWidth/2 * _horizontalScale;
   if(_actualAngle >= 0)
   {
    g.moveTo(frameLeft,-_verticalShearEffect);
    g.lineTo(frameLeft,frameHeight - _verticalShearEffect);
    g.lineTo(frameLeft + frameWidth*_horizontalScale,frameHeight + _verticalShearEffect);
    g.lineTo(frameLeft + frameWidth*_horizontalScale,3*-_verticalShearEffect);
    g.lineTo(frameLeft,-_verticalShearEffect);
   }
   else
   {
    g.moveTo(frameLeft,3*_verticalShearEffect);
    g.lineTo(frameLeft, frameHeight - _verticalShearEffect);
    g.lineTo(frameLeft+frameWidth*_horizontalScale, frameHeight + _verticalShearEffect);
    g.lineTo(frameLeft+frameWidth*_horizontalScale, _verticalShearEffect);
    g.lineTo(frameLeft,+3*_verticalShearEffect);
   }
  }
  
  /*  this event handler gets called whenever our content updates its layout and rendering.  We'll want to update our reflection bitmap
  * to match, we we just invalidate our reflection
  */  
  private function contentUpdateHandler(event:Event):void
  {
   invalidateReflection();
  }

  /*  this function clears out any cached data we have about our reflection, and invalidates our display list. 
  * as with all of our other code, we don't just rebuild our reflection whenever anything changes...we set a flag
  * (in this case, just the fact that our reflection bitmap is null will be enough of a flag) and do the heavy lifting
  * when our updateDisplayList() function is called
  */
  private function invalidateReflection():void
  {
   // throw out any previously existing reflection bitmap. It's worth pointing out that this is a little bit of overkill..
   // there's some data that we could likely reuse when the content updates...i.e., if the content doesn't change size,
   // we can use the same bits, but just redraw into them.  Exercise for the reader ;)
   if(_reflectionBitmap != null)
    removeChild(_reflectionBitmap);   
   _reflectionBitmap = null;
   // request an update.
   invalidateDisplayList();
  }
  
  /*  this utility function creates our reflection bitmap from our content. It gets called whenever the
  * content changes. I should point out that this code was culled from the reflection example created
  * by the great Narcisso Jaramillo.
  */
  private function createReflectionBitmap(target:UIComponent):void
  {
   // first, figure out how big our bitmap needs to be. Flash bitmap APIs don't like
   // 0x0 bitmaps, so we'll constrain it to make sure we at least create a 1x1 bitmap.
   var tw:Number = Math.max(1,target.width);
   var th:Number = Math.max(1,target.height);
   var rect: Rectangle = new Rectangle(0, 0, target.width, target.height);

   // Create a temporary alpha gradient bitmap.  When we draw our content into our
   // reflection bitmap, we'll combine it with this to get our fadeout effect.   
   // note that in the code below, we create a shape, draw into it, then blit it into
   // our bitmap, and throw the sprite away, all without ever actually adding the shape
   // to the display list.  DisplayObjects can be useful even if they never end up on screen.   
   var alphaGradientBitmap:BitmapData = new BitmapData(tw, th, true, 0x00000000);

   var gradientMatrix: Matrix = new Matrix();
   var gradientShape: Shape = new Shape();
   gradientMatrix.createGradientBox(tw, th * kFalloff, Math.PI/2,
    0, th * (1.0 - kFalloff));
   gradientShape.graphics.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0xFFFFFF],
    [0, 1], [0, 255], gradientMatrix);
   gradientShape.graphics.drawRect(0, th * (1.0 - kFalloff),
    tw, th * kFalloff);
   gradientShape.graphics.endFill();
   alphaGradientBitmap.draw(gradientShape, new Matrix());
   
   // create a temporary bitmap to hold the image of our content.
   var targetBitmap:BitmapData = new BitmapData(tw, th, true, 0x00000000);
   // initialize it to empty. Note that's not an RGB value, but an ARGB value.
   // the bitmap API adds alpha values to typical RGB hex values. 
   targetBitmap.fillRect(rect, 0x00000000);
   // we need to temporariliy remove the mask from the target component before
   // we can grab its bits.  Otherwise we'd get the clipped version.   
   var mm:DisplayObject = target.mask;
   target.mask = null;
   // capture the bits.
   targetBitmap.draw(target, new Matrix());
   // restore the mask.
   target.mask = mm;

   // now create the final bitmap for our reflection.
   var reflectionData:BitmapData = new BitmapData(tw, th, true, 0x00000000);         
   // initialize it to empty. Again, we're using RGBA values
   reflectionData.fillRect(rect, 0x00000000);
   // copy in the bits from our content, and merge it with the gradient bitmap as an alpha channel.
   reflectionData.copyPixels(targetBitmap, rect, new Point(), alphaGradientBitmap);

   // alright, now we've got our reflection bitmap data. To actually put it on the display list, we need to
   // wrap it up in a Bitmap object, which is a DisplayObject.   
   _reflectionBitmap = new Bitmap(reflectionData);
   // give it that nice faint transparent look by setting its alpha down.
   _reflectionBitmap.alpha = .3;
   // and add it to our display list.
   addChildAt(_reflectionBitmap,0);
  }
  
  /*  this utility function takes our reflection bitmap and sets up its matrix transform to invert it, shear it, and place it below
  * our content.
  */
  private function positionReflectionBitmap(centerX:Number, yPosition:Number, frameWidth:Number, frameHeight:Number ):void
  {
   // grab the matrix transform
   var m:Matrix = _reflectionBitmap.transform.matrix;
   
   // assign the shear and horizontal scale.
   m.b = _verticalShear;
   m.a = _horizontalScale;
   // we need our reflection to be upsidown. So set its vertical scale to -1.
   m.d = -1;
   // center it horizontally.
   m.tx = centerX - frameWidth/2 * _horizontalScale;
   // and position it _below_ our content.  if our content is normally at yPosition, the bottom of our sheared content is at yPosition plus the
   // pixel distance of the shear effect, plus the height of the content.  However, since our reflection has a vertical scale of -1, it will stick
   // _up_ from wherever we place it. So we need to add the size of the reflection bitmap to our position, to guarantee that the _bottom_ of the bitmap,
   // which is extending upwards, ends up at the bottom of the content.
   m.ty = yPosition - _verticalShearEffect + 2*frameHeight;
   
   // reassign the matrix.
   _reflectionBitmap.transform.matrix = m;
  }
 }
}

:

FLEX3 - FlexLog 사용법 (spitzer.flair_1.0.3 사용) - swf파일 trace로 log보기

FLEX/FLEX 2010. 7. 12. 21:24

FLEX를 개발 하다 보면 다른 swf를 Loader해서 사용할 경우가 많다...
이럴때 파라미터도 받아야 되고 참 debug하기도 힘들고 애매 할때가 있는데...
이경우 trace()를 이용해서 swf의 log를 사용하면 참 사용하기 편하다.

---- 사용방법 -----
1. 제어판 프로그램 추가/삭제 에서 기존 플래쉬 플레이어를 삭제한다.

2. 플래쉬 디버그 플레이어(어도비 사이트에서 받아도 된다)를 설치한다.

3. spitzer.flair_1.0.3.zip 파일을 압축 풀어 이클립스 플러그인 디렉토리에 넣는다.

4. mm.zip파일을 C:\Documents and Settings\(자신의 로그인계정명디렉토리)\ 밑에 넣고 압축을 푼다.

5. 재부팅.

6. 이클립스 OR FLEX 빌더 실행

7. Window -> Preferences -> Flair -> Flash Log 선택.(C:\Documents and Settings\(자신의 로그인 계정명)\Application Data\Macromedia\Flash Player\Logs\flashlog.txt 를 선택한다.)

8. Window -> Show View -> Other -> Flair -> Flash Log 선택.

9. 로그보면 마구 개발.

----------- 실행 화면 ------------------------------


---------- FlexLog로 다른 사이트 보기 -------------------- ㅋㅋ
FlexLog를 이용해서 다른 사이트를 한번 봐봤다.
재미 있는 현상들이 나오는데...ㅋㅋ
xml파일로 구성된 data도 다 나오네...ㅎㅎ


: