2012년 10월 22일 월요일

jar sign


jar로 인증하는 방법입니다.

쭈욱 따라하시면 됩니다.
설명 필요하시면 댓글 남겨주세요~


1.     Keysotre 생성
Keystore 암호 : papp00
pappercom 키 암호 : papp00

>keytool -genkey -keystore pappercom.keystore -alias pappercom
keystore 암호를 입력하십시오:
새 암호를 다시 입력하십시오:
이름과 성을 입력하십시오.
  [Unknown]:  COM
조직 단위 이름을 입력하십시오.
  [Unknown]:  pappercom
조직 이름을 입력하십시오.
  [Unknown]:  pappercom
구/군/시 이름을 입력하십시오?
  [Unknown]:  GANGNAM
시/도 이름을 입력하십시오.
  [Unknown]:  SEOUL
이 조직의 두 자리 국가 코드를 입력하십시오.
  [Unknown]:  KR
CN=COM, OU=pappercom, O=pappercom, L=GANGNAM, ST=SEOUL, C=KR이(가) 맞습니까?
  [아니오]:  Y


2.     Selfcert

1.에서 만든 암호 입력 : papp00
3.     Keystore 확인

암호 : papp00
4.     Jarsigner를 이용한 인증

암호 : papp00
마지막 경고 문구 6개월 문구 주목.
정식 인증을 위해서는 인증서를 구매해서 인증해야 함.





C:\Users\aaa\CoP\81. gasori\12월 v2.0>keytool -genkey -keystore pappercom.keystore -alias pappercom
keystore 암호를 입력하십시오:
새 암호를 다시 입력하십시오:
이름과 성을 입력하십시오.
  [Unknown]:  COM
조직 단위 이름을 입력하십시오.
  [Unknown]:  pappercom
조직 이름을 입력하십시오.
  [Unknown]:  pappercom
구/군/시 이름을 입력하십시오?
  [Unknown]:  GANGNAM
시/도 이름을 입력하십시오.
  [Unknown]:  SEOUL
이 조직의 두 자리 국가 코드를 입력하십시오.
  [Unknown]:  KR
CN=COM, OU=pappercom, O=pappercom, L=GANGNAM, ST=SEOUL, C=KR이(가) 맞습니까?
  [아니오]:  Y

<pappercom>에 대한 키 암호를 입력하십시오.
        (keystore 암호와 같은 경우 Enter를 누르십시오):

C:\Users\aaa\CoP\81. gasori\12월 v2.0>keytool -selfcert -alias pappercom -k
eystore pappercom.keystore
keystore 암호를 입력하십시오:

C:\Users\aaa\CoP\81. gasori\12월 v2.0>keytool -list -keystore pappercom.key
store
keystore 암호를 입력하십시오:

Keystore 유형: JKS
Keystore 공급자: SUN

Keystore에는 1 항목이 포함되어 있습니다.

pappercom, 2011. 12. 28, PrivateKeyEntry,
인증서 지문(MD5): 7E:4B:4F:16:D2:DC:E4:F4:F1:47:44:B3:0C:30:B6:E1

C:\Users\aaa\CoP\81. gasori\12월 v2.0>jarsigner -keystore pappercom.keystore gasori.unsigned.jar pappercom
Enter Passphrase for keystore:

Warning:
The signer certificate will expire within six months.

C:\Users\aaa\CoP\81. gasori\12월 v2.0>DIR
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: 98B0-E2F1

 C:\Users\aaa\CoP\81. gasori\12월 v2.0 디렉터리

2011-12-28  오후 02:28    <DIR>          .
2011-12-28  오후 02:28    <DIR>          ..
2011-12-27  오후 05:04        30,684,225 gasori-client.zip
2011-12-27  오후 05:00        27,979,085 gasori-server.zip
2011-12-28  오후 02:26    <DIR>          gasori.unsigned
2011-12-27  오후 05:06        33,473,137 gasori.unsigned.jar
2011-12-28  오후 02:32    <DIR>          인증
               3개 파일          92,136,447 바이트
               4개 디렉터리  495,454,785,536 바이트 남음

C:\Users\aaa\CoP\81. gasori\12월 v2.0>jarsigner -verify gasori.sunsigned.jar
jarsigner: java.io.FileNotFoundException: gasori.sunsigned.jar (지정된 파일을 찾을 수 없습니다)

C:\Users\aaa\CoP\81. gasori\12월 v2.0>jarsigner -verify gasori.unsigned.jar
jar is unsigned. (signatures missing or not parsable)

C:\Users\aaa\CoP\81. gasori\12월 v2.0>cd 인증

C:\Users\aaa\CoP\81. gasori\12월 v2.0\인증>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: 98B0-E2F1

 C:\Users\aaa\CoP\81. gasori\12월 v2.0\인증 디렉터리

2011-12-28  오후 02:32    <DIR>          .
2011-12-28  오후 02:32    <DIR>          ..
2011-12-28  오후 02:14        35,041,223 gasori.jar
2011-12-28  오후 02:22    <DIR>          gasori.unsigned
2011-12-28  오후 02:30            38,081 RapidantSign.docx
2011-12-28  오후 02:03             1,255 pappercom.keystore
               3개 파일          35,080,559 바이트
               3개 디렉터리  495,418,814,464 바이트 남음

C:\Users\aaa\CoP\81. gasori\12월 v2.0\인증>jarsigner -verify gasori.jar
jar verified.

Warning:
This jar contains entries whose signer certificate will expire within six months.

Re-run with the -verbose and -certs options for more details.

JAVA : 현재 날짜 및 계산

java로 날짜 구하기 심플 예제

SimpleDateFormat frm= new SimpleDateFormat ("yyyy.MM.dd");
Calendar cal = Calendar.getInstance();
String today = frm.format(cal.getTime());
cal.add(Calendar.DATE, -7);
String aWeekAgo = frm.format(cal.getTime());
System.out.println("today: " + today);
System.out.println("aWeekAgo: " + aWeekAgo);


SimpleDateFormat 은 한글도 지원한다.


SimpleDateFormat frm= new SimpleDateFormat ("yyyy년 MM월 dd일 HH시 mm분");
Calendar cal = Calendar.getInstance();
String todayKor = frm.format(cal.getTime());
System.out.println("todayKor : " + todayKor);

JQuery Rowspan

(function($){
$.fn.rowspan = function(colIdx) {
return this.each(function(){
var that;
   $('tbody tr', this).each(function(row) {
    $('td:eq('+colIdx+')', this).each(function(col) {
       if ($(this).html() == $(that).html()) {
           rowspan = $(that).attr("rowSpan");
           if (rowspan == undefined) {
            $(that).attr("rowSpan",1);
            rowspan = $(that).attr("rowSpan");
           }
           rowspan = Number(rowspan)+1;
           $(that).attr("rowSpan",rowspan); // do your action for the colspan cell here
           $(this).hide(); // .remove(); // do your action for the old cell here
       } else {
           that = this;
          }
          that = (that == null) ? this : that; // set the that if not already set
      });
    });
  });
};
})(jQuery);

Spring Excel Download

1. org.springframework.web.servlet.view.document.AbstractJExcelView를 상속받아서 ExcelView를 생성한다.
public class DHExcelView extends AbstractJExcelView {
/-*
 * @Method Name : buildExcelDocument
 * @createDate : 2011. 3. 17.
 * @author : daeha
 * @param arg0
 * @param arg1
 * @param arg2
 * @param arg3
 * @throws Exception
 * @return : @see
 *         org.springframework.web.servlet.view.document.AbstractJExcelView
 *         #buildExcelDocument(java.util.Map, jxl.write.WritableWorkbook,
 *         javax.servlet.http.HttpServletRequest,
 *         javax.servlet.http.HttpServletResponse)
 * @description : 엑셀뷰~
 * @since :
 * 
 *-
protected void buildExcelDocument(Map<String, Object> model, WritableWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
IGenericExcelCommand excelCommand = (IGenericExcelCommand) model.get("excelCommand");
WritableCellFormat titleFormat = new WritableCellFormat(); // 스크립트 스타일
titleFormat.setBackground(Colour.GRAY_50);
String fileName = createFileName(excelCommand.getExcelTitle());
setFileNameToResponse(request, response, fileName);
List<Map<String, Object>> rows = excelCommand.getExcelContent();
Map<String, Object> headers = excelCommand.getExcelHeader();
List<String> headersOrder = excelCommand.getExcelColumnOrder();
WritableSheet sheet = workbook.createSheet(excelCommand.getExcelTitle(), 0);
int rowNumber = 0;
int cellNumber = 0;
for (String key : headers.keySet()) {
if (null != headers.get(key))
sheet.addCell(new jxl.write.Label(cellNumber, rowNumber, headers.get(headersOrder.get(cellNumber++)) + "", titleFormat));
}
rowNumber++;
for (Map<String, Object> row : rows) {
cellNumber = 0;
for (String key : row.keySet()) {
if (null != headers.get(key))
sheet.addCell(new jxl.write.Label(cellNumber, rowNumber, row.get(headersOrder.get(cellNumber++)) + ""));
}
rowNumber++;
}
}
}
2. Controller에 RequestMapping을 추가한다.
Return Type을 View로하고 위에서 만들었던 DHExcelView를 return한다.
DHExcelView가 인스턴스가 될 때 buildExcelDocument함수가 호출된다. 
@RequestMapping(value = "business/get.excel")
private View getExcelList(@ModelAttribute("command") BizInfoMasterCommand command, Model model) {
ExcelCommand excelCommand = new ExcelCommand();
// 순서상관없이 put
excelCommand.setExcelHeader(new HashMap<String, Object>() {
{
put("bizSrl", "사업번호");
put("bizName", "사업명");
put("bizDetailContents", "사업내용");
put("budgetCodeValue", "예산분류");
put("totalBizStartDate", "사업시작일시");
put("totalBizEndDate", "사업종료일시");
put("supportTypeValue", "지원대상");
}
});
// 순서대로 add
excelCommand.setExcelColumnOrder(new ArrayList<String>() {
{
add("bizSrl");
add("bizName");
add("bizDetailContents");
add("budgetCodeValue");
add("totalBizStartDate");
add("totalBizEndDate");
add("supportTypeValue");
}
});
excelCommand.setExcelTitle("사업정보");
excelCommand.setExcelContent(bizInfoMasterDao2.getAllList(command));
model.addAttribute("excelCommand", excelCommand);
return new DHExcelView();

2012년 1월 4일 수요일

eclipse svn 메뉴 설명

위 이미지는 eclipse에서 svn을 plug in 했을 때 나오는 team메뉴를 선택했을 때 나오는 sub menu이다.
보편적인 repository와 비교하고 update, commit을 하는 작업만 반복해서 하다 보니 다른 메뉴에 대한 사용법은 등한시 하고 예기치 않은 오류가 났을때 대처할 수 없어서 정리를 해보고자 한다.

최근 로컬에서 패키지를 삭제하고 repository에 commit을 하려고 했는데 "Item is out of date"의 오류가 발생하면서 적용이 안되었다.
인터넷에 찾아보니 update head > Mark Resolved > commit을 반복하라고 했는데
실제로는 Update Head 후 Mark Resolved가 활성화 되지 않아 commit을 하여 클래스만 먼저 삭제하고 다시 Synchronize width Repository를 하여 패키지 삭제를 commit하니 되었다.
알고 있었으면 간단하게 해결했을 문제인데 며칠을 미루고 있다 뒤늦게 처리한거다.

알고 있으면 문제가 발생했을 때 바로 바로 처리가 되겠지?

참고로 캡쳐한 svn 버전은 1.8이다.
==========================================================================
Synchronize with Repository : 선택한 항목(project, package, class, file)들을 저장소(repository)와 로컬을 비교하여 update 또는 commit할 항목을 보여준다.
Commit : 선택한 항목(project, package, class, file)들을 commit(로컬의 신규버전을 repository 저장)한다.
Update to HEAD : 저장소의 가장 최신 버전(버전이 높을 수록 위에 있으니 head라 했나보다)으로 로컬에 update한다.
Update to Version : 선택하면 여러가지 옵션을 선택하는 창이 뜬다. 그에 맞는 version을 나의 로컬에 update한다.
Create Patch : 저장소와 로컬의 asynch를 체크해서 patch를 만드는 것 같은데 좀더 봐야할듯.
Apply Patch : 위의 create patch를 통해 나온 결과물을 가지고 적용하는것 같은데 역시 좀 더 봐야할듯.
==========================================================================
이 메뉴는 설명(펌)으로 대체 할까한다. 용어정의지만 이해하는데 도움이 될 듯.


Trunk
  - 단어 자체의 뜻은 본체, 본문 등입니다.
  - 프로젝트에서 가장 중심이 되는 디렉토리입니다.
  - 모든 프로그램의 개발 작업은 trunk 디렉토리에서 이루어  집니다.
  - trunk 디렉토리 바로 아래에는 소스들의 파일과 디렉토리가 들어가게 됩니다.

Branch
  - trunk에서 뻗어져 나온 나무가지를 뜻합니다.
  - 프로그램을 개발하다 보면 trunk 디렉토리에서 또 다른 작은 분류로 빼서 따로 개발해야 할 경우가 있습니다. 프로젝트안의 작은 프로젝트라고 생각하면 됩니다.
  - branches 디렉토리 안에 또 다른 디렉토리를 두어 그 안에서 개발하게 됩니다.

Tag
  - tag는 꼬리표입니다.
  - 이 디렉토리는 프로그램을 개발하면서 정기적으로 릴리즈 할 때, 0.1 / 0.2 / 1.0 등 이런식의 버전 별로 소스 코드를 따로 저장하는 공간입니다.
  - 버전별로 태그를 붙여서 tag 디렉토리 안에 보관한다고 생각하면 됩니다.

Merge
  - Trunk에서 분기된 Branch를 다시 Trunk로 합쳐는 작업
출처 : http://tiger5net.egloos.com/5573344

Branch/Tag : 위 설명된 branch나 tag를 저장소에 만든다.
Merge : branch나 tag를 병합
Switch to another Branch/Tag/Revision : 다른 branch/tag/revision으로 대체

==========================================================================
Show History : 선택한 항목(project, package, class, file)의 history를 보여준다. commit을 할때 comment를 달아 두면 더욱 유용. history를 통해서 compare 가능하며 유용하게 쓸수 있다.
Show Tree Conflicts : 메뉴를 보면 사용자간의 충돌이 일어난 항목을 보여주는것 같은데 왜 반응이 없지? 일부러 충돌도 만들어서 해보았건만...
Show Properties :
Show Revision Properties :
Show Revision Graph :
==========================================================================
Add to Version Control :
Add to svn:ignore : 저장소와 무관(저장되지 말아야할)하게 사용할 파일일 경우 등록하면 된다. 등록하게 되면 Synchronize with repository를 했을 때 목록에 나오지 않는다.
Set Property :
==========================================================================
Revert : 저장소의 가장 최신 revision으로 로컬 파일을 변경(Replace width >> latest from Repository 와 같은 기능인듯)
Edit conflicts :
Edit property conflicts :
Mark Resolved :
==========================================================================
Copy :
Export :
Configure Branches/Tags :
==========================================================================
Refresh/Cleanup : 뭔가 알 수 없는 충돌이나 오류가 발생했다면 이걸 먼저 해보시길.
Upgrade
==========================================================================


2011년 12월 19일 월요일

JQuery HashMap

정말 유용..

프로그래밍 하신분이라면 설명이 따로 필요 없을듯...

Map.prototype = {
    put : function(key, value){
        this.map[key] = value;
    },
    get : function(key){
        return this.map[key];
    },
    containsKey : function(key){  
     return key in this.map;
    },
    containsValue : function(value){  
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){  
     return (this.size() == 0);
    },
    clear : function(){
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){  
     delete this.map[key];
    },
    keys : function(){
        var keys = new Array();
        for(var prop in this.map){
            keys.push(prop);
        }
        return keys;
    },
    values : function(){
     var values = new Array();
        for(var prop in this.map){
         values.push(this.map[prop]);
        }
        return values;
    },
    size : function(){
      var count = 0;
      for (var prop in this.map) {
        count++;
      }
      return count;
    }
};

2011년 12월 15일 목요일

JQuery, VML 이용하여 동적화면 구현

이전 프로젝트에서, 화면의 객체(최상위 node)를 선택하고 검색 조건을 입력한 후 검색을 하면,
그 하위 엘리먼트로 검색 결과의 정보를 보여주는 다중 계층 엘리멘트를 구성하는 동적 화면을 구성한적이 있다.
그런데 이번 프로젝트에도 비슷한 기능을 구현해야하는게 있다.
여긴 FLEX로 구현 하면 되는데, 기술자를 더 연장해야하는 비용의 문제로 이전 기억을 더듬어(프로젝트 소스는 가져오지 않기에 내겐 기억으로 밖에 존재하지 않는다.)  구현하기로 했다.
이번엔 BLOGGER에 정리를 해두고자 한다.
나도 다음에 사용할 수 있겠지만 다른 개발자들도 사용할 수 있으리라...
(나의 희소성이 떨어질려나? ㅋㅋ)

예전부터 생각한거지만 개발은 노가다가 아니라 창조라는 말을 하고싶다.
그리고 창조는 즐겁다.


1. 구현 목표
    a. 저장매체에 있는 데이터를 불러 와서 아래와 같은 화면을 구성한다.
    b. 주위에 있는 객체를 선택하면 그 객체와 관련된 데이터를 불러와 아래와 같은 화면을 구성한다.

2. 설계시 고려사항
 문서를 보고 개발 설계를 할때 고려해야할 사항들은 어떤게 있을까?
    a화면을 구성할때 
        - 모든 구성원들의 좌표 계산
              기준이 되는 parent node의 좌표
              기준을 중심으로 주위 구성원들의 좌표 자동계산
        - 구성원들의 연결(vml이용 기준과 자식 구성원의 좌표 계산)
    a에서 b화면으로 재구성 될때
        문서상의 요구사항은 기존 구성은 비활성화 되어야한다. 명시는 되어있지 않지만 두개의 구성체를 한 화면에 넣으려면 기존 구성체의 크기도 작아져야할 것이다.
위 그림에서 보자면 선택한 자식노드가 중앙으로 배치 되며 기존 구성체는 좌측 아래로 밀려야한다. 좌표 계산이 힘들지 않을까? 위 그림은 기존 구성체의 우측 상단 자식 구성원을 선택했으므로 기존 구성체는 좌측 아래로 밀리는것이지만 만약 좌측 아래 자식 구성원을 선택한다면 기존 구성체는 반대로 우측 상단으로 밀려야하나?
차라리 밀리는 방향은 하나로 정해두고 자식 구성원을 선택한 구성원을 기준으로 재배치 하는게 더 쉽지 않을까? 그런데 두가지 다 재밌을거 같다. 
또한 두번째 구성체는 기존 구성체의 경우 처럼 360도 모두를 사용하는게 아니라 기존 구성체를 차지하는 범위를 제외하고 구성해야해서 사용범위를 달리 정해야한다....

처리해야할 이벤트는 하나다. 자식 노드를 선택하면 그 자식 구성원을 기준으로 하나의 구성체만 더 만들면 되니까. 그리고 depth도 최대 2단계라고 했으니. 위 정도의 고려면 충분할듯.

자 이제 대충 고려사항은 나왔으니 개발 설계를 어떻게 할까?
설계에 대한 내용은 개발이 완료 되고 난 후 정리

(다른 개발자들이 느낄때 이 기능구현 난이도는 어떨까?)

기대하시라... ^^


4. 우선 기반 기술이 될 만한 소스를 수집하자.
이 업을 시작하기 초반에는 개발에 대한 기반 지식이 없어 강좌를 비롯 많은 책을 읽으면서 지식을 쌓았고 개발을 했다.
하지만 어느 정도 체계가 잡히고 나선 책보다는 구글링이라는 인터넷에서 많은 기술을 습득하게 되었다. 
그리고 새로운 기술이 나오거나 다른 언어 또는 플러그인을 접하게 되면 근간이 되는 흐름을 파악하고 대부분 api를 통해서 필요한 것들을 습득한다.
엄청 많은 기술과 세부적인 사항을 모두 강좌나 책으로 습득한다면 공부하기도 바쁠것이기에...

우선 좌표 구하기...
jquery 절대 좌표

  var p = $("p:last");
  var offset = p.offset();
  p.html( "left: " + offset.left + ", top: " + offset.top );

jquery 상대좌표

var p = $("p:first");
  var position = p.position();
  $("p:last").text( "left: " + position.left + ", top: " + position.top );

그리고 이번 프로젝트의 요구사항은 절대 좌표를 이용해서 구성원들을 배열해야할것이다.

<div id="maddog" class="aaaa" style='position: absolute; left: 2; top: 195; width: 230;
    height: 150; background-color: 666666; font-size: 11px; color: #5BF9F5; overflow: auto;
    padding-top: 5; padding-left: 5; padding-bottom: 5; filter: Alpha(Opacity=50);
    clip: rect(0 230 150 0); border: 1px none #000000; visibility: visible; z-index: 1'>
    여기에 구성원들을 넣기만 하면 된다.
</div>

위에서 내가 원하는건 위 빨간 색의 attribute이다. 절대 좌표로 위치 시키기...






<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
                <title>jQuery 1.3.2 VMl Test Stuff</title>
                <style type="text/css" media="screen">
                        #roundme {
                                width: 100px;
                                height: 100px;
                                position: relative;
                                background: red;
                        }
                </style>
                <script src="http://code.jquery.com/jquery-1.3.2.js" type="text/javascript"></script>
                <script type="text/javascript" charset="utf-8">
                        $(window).load(function() {
                                if ($.browser.msie) {
                                        if (!document.namespaces.v) {
                                                document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
                                                var ss = document.createStyleSheet().owningElement;
                                                ss.styleSheet.cssText = "v\\:*{behavior:url(#default#VML);}";
                                        }
                                        var roundrect = $('<v:roundrect arcsize="0.1" style="width:100px;height:100px; position: absolute; top: 0; left: 0;"></v:roundrect>');
                                        var roundme = $("#roundme");
                                        roundme.append(roundrect);
                                }
                        });
                </script>
        </head>
        <body>
                <div id="roundme">
                        <span class="goo">Foo</span>
                </div>
        </body>
</html>



개발 준비도 대충은 된듯하다.
이제 어떻게 조합하고 어떻게 구현하느냐....



한번 해봤다고 구현 시간이 줄었다.
근데 이렇게 했는데 팀장은 놀라지도 않네. ㅋ
당연히 할수 있는거 아니냐는 듯한 반응...
설명은 다음에...
개인적인 생각은 소스가 좀더 체계적(javascript class 개념이 부족해서)이었으면 좋겠다.

-----------------------------------------------------------------
vml.js
-----------------------------------------------------------------

var tm;
$(document).ready(function() {
$('#addCircle').click(function() {
var item = new Item('Paris', 'key', '', '', true);
   var map = new Map();
   map.put('key01', new Item('France', 'key01', 'A', false, false));
   map.put('key02', new Item('Soccer', 'key02', 'A', false, false));
   map.put('key03', new Item('Provence', 'key03', 'A', false, false));
   map.put('key04', new Item('Pont Neuf', 'key04', 'A', false, false));
   map.put('key05', new Item('Nice', 'key05', 'A', true, false));
   map.put('key06', new Item('Eiffel Tower', 'key06', 'A', false, false));
   map.put('key07', new Item('Cannes', 'key07', 'A', false, false));
   map.put('key08', new Item('Louvre', 'key08', 'A', true, false));
   map.put('key09', new Item('파리', 'key09', 'S', false, false));
   map.put('key10', new Item('France', 'key10', 'A', false, false));
   map.put('key11', new Item('Montmartre', 'key11', 'A', true, false));
   map.put('key12', new Item('Fly', 'key12', 'A', false, false));
   tm = new ThesaurusMap(item, map);
   tm.draw(true, true);
});
});

function viewMore(strkey)
{
var newMap = new Map();
var isStart = false;
var arrKeys = tm.map.keys();

for(var i=0; i<arrKeys.length; i++)
{
if(arrKeys[i] == strkey) isStart = true;
if(isStart) newMap.put(arrKeys[i], tm.map.get(arrKeys[i]));
}
for(i=0; i<arrKeys.length; i++)
{
if(arrKeys[i] == strkey) break;
newMap.put(arrKeys[i], tm.map.get(arrKeys[i]));
}
tm.erase();
tm.map = newMap;
tm.draw(false, false);
var newName = tm.map.get(arrKeys[i]).name;
var item = new Item(newName, 'key', '', '', true);
    var map = new Map();
    map.put('key01', new Item(newName+'1', 'key01', 'S', false, false));
    map.put('key02', new Item(newName+'2', 'key02', 'S', false, false));
    map.put('key03', new Item(newName+'3', 'key03', 'S', false, false));
    map.put('key04', new Item(newName+'4', 'key04', 'S', false, false));
    map.put('key05', new Item(newName+'5', 'key05', 'A', true, false));
    map.put('key06', new Item(newName+'6', 'key06', 'A', false, false));
    map.put('key07', new Item(newName+'7', 'key07', 'S', false, false));
    map.put('key08', new Item(newName+'8', 'key08', 'A', true, false));
    map.put('key09', new Item(newName+'9', 'key09', 'S', false, false));
    var newTm = new ThesaurusMap(item, map);
    newTm.draw(false, true);
}

ThesaurusMap = function(cItem, itemMap){
 this.map = new Object();
 this.cItem = cItem;
 this.map = itemMap;
};   

ThesaurusMap.prototype = {
    draw : function(isSingle, isMain){
   
        var obj = document.getElementById('roundme');
    var posi = getAbsolutePos(obj);

    //가운데 Item
   this.cItem.setPosition(getIPos(posi, obj, isMain));
   var roundme = $("#roundme");
   roundme.append($(getCircle(this.cItem.position, this.cItem, isMain)));
   
   var circleRound = isSingle || !isMain ? 360 : 300;
   var radian = circleRound/this.map.size()*Math.PI/180; 
   var arrKey = this.map.keys();
   for(var i=0; i<arrKey.length; i++)
   {
    if(isMain || i!=0)
    {
    var cnt = isSingle || !isMain ? i : radian*(i) > 2.4 ? i + this.map.size()*2/9: i;
    var sidP = getIPos(this.cItem.position, '', isMain, radian*(cnt));
    this.map.get(arrKey[i]).setPosition(sidP);
    roundme.append($(getDiv(sidP, this.map.get(arrKey[i]), isSingle, isMain))); 
    roundme.append($(getLine(getLinePos(this.cItem, this.map.get(arrKey[i]), radian*(cnt)), isMain))); 
    }
    if(!isSingle && isMain && i==0) //map 과 map사이의 선
    {
    roundme.append($(getLine(getLinePos(this.cItem, tm.cItem, Math.PI), isMain))); 
    }
   }
    },   
    erase : function () {
   var obj = document.getElementById('roundme');
   var cn = obj.childNodes;
   for(var i=cn.length-1; i>=0; i--)
    obj.removeChild(cn[i]);
    }
};

Item = function(name, key, type, isNew, isCenter)
{
this.name = name;
this.key = key;
this.type = type; //S, A
this.isNew = isNew; //true, false;
this.isCenter = isCenter; //true, false
};

Item.prototype = {
    setPosition : function(position){
    this.position = position;
    }
};

function getAbsolutePos(obj) {
var position = new Object;
position.x = position.y = 0;

if( obj ) {
position.x = obj.offsetLeft;
position.y = obj.offsetTop;

if( obj.offsetParent ) {
var parentpos = getAbsolutePos(obj.offsetParent);
position.x += parentpos.x;
position.y += parentpos.y;
}
}
return position;
}
//Item 좌표
function getIPos(posi, obj, isMain, radian)
{
var position = new Object;
var width = isMain ? 114 : 57;
if(radian == null)
{
position.x = posi.x + (obj.offsetWidth - width)/ (isMain ? 2 : 6);
position.y = posi.y + (obj.offsetHeight - width)/2;
}
else
{
width = isMain ? 70 : 50;
var wRadius = isMain ? 200 : 100;
var hRadius = isMain ? 200 : 100;
position.x = (posi.x + posi.w/2) + wRadius * Math.cos(radian) - width/2;
position.y = (posi.y + posi.h/2) + hRadius * Math.sin(radian) - width/2;
}
position.w = width;
position.h = width;
position.cx = position.x + width/2;
position.cy = position.y + width/2;
return position;
}

//line 좌표 
function getLinePos(cItem, sItem, radian)
{
var position = new Object;
//over pi 
sRadian = radian > Math.PI ? radian - Math.PI : radian + Math.PI;
var tampXa = cItem.position.cx + cItem.position.w/2 * Math.cos(radian); //center circle x
var tampYa = cItem.position.cy + cItem.position.h/2 * Math.sin(radian); //cneter circle y
var tampXb = sItem.position.cx + sItem.position.w/2 * Math.cos(sRadian); //side circle x
var tampYb = sItem.position.cy + sItem.position.h/2 * Math.sin(sRadian); //side circle y

if(radian >= Math.PI)
{
position.sx = tampXb;
position.sy = tampYb;
position.ex = tampXa - tampXb;
position.ey = tampYa - position.sy;
}
else
{
position.sx = tampXa;
position.sy = tampYa;
position.ex = tampXb - tampXa;
position.ey = tampYb - tampYa;
}
return position;
}

//원을 구성 string
function getCircle(posi, item, isMain)
{
var strObj = '<v:oval style="position:absolute;text-align:center;cursor:hand'
+ ';left:' + posi.x + ';top:' + posi.y
+ ';width:' + posi.w + ';height:' + posi.h
+ ';color:' + (isMain ? 'black' : 'gray') + ';padding-top:' + (posi.h/2-8)+'px;"'
+ ' strokecolor="' + (isMain ? 'gray' : '#cccccc')
+ '" strokeweight="' + (isMain ? '1.5pt' : '1pt') + '">'+item.name+'</v:oval>';
return strObj;
}

//div 객체 구성 string
function getDiv(posi, item, isSingle, isMain)
{
if(isMain) tColor = item.type == 'S' ? 'darkblue' : 'CC0099';
else  tColor = item.type == 'S' ? '#E9CFEC' : '#FCDFFF';
var clickEvent = !isSingle && isMain ? '' : ' onClick="viewMore(\''+item.key+'\');"';
var strObj = '<div ' + clickEvent + ' style="position:absolute;text-align:center;cursor:hand'
+ ';left:' + posi.x + ';top:' + posi.y
+ ';width:' + posi.w + ';height:' + posi.h
+ ';color:' + tColor
+ ';padding-top:'+(posi.h/2-8)+'px;">'+item.name+'</div>';
return strObj;
}

//lin 객체 구성 string
function getLine(posi, isMain)
{
var strObj = '<div style="position:absolute;left:'+posi.sx
+ ';top:' + posi.sy + ';width:0;height:0;background-color:yellow;">'
+ '<v:line from="0,0" to="' + posi.ex + ',' + posi.ey + '"'
+ ' strokecolor="'+(isMain ? 'gray' : '#cccccc')
+ '" strokeweight="' + (isMain ? '1.5pt' : '1pt') + '"><v:stroke dashstyle="dot"/></v:line>';
return strObj;
}



-----------------------------------------------------------------
vml.html
-----------------------------------------------------------------




<html  xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Cp Tag Thesaurus Map</title>
<style>
v\:* { behavior: url(#default#VML); }
</style>
<script src="http://code.jquery.com/jquery-1.3.2.js" type="text/javascript"></script> 
<script src="./map.js" type="text/javascript"></script> 
<script src="./vml.js" type="text/javascript"></script> 
</head>
<body>
<div>
<h2>VML test</h2>
<p>
<span id='addCircle' style="position:absolute;width:500;right:420px; top:28px;">
<a href="#">
<span>Draw</span>
</a>
</span>
</p>
<div id='roundme' style='background-color:#fff;width:100%;height:500px;'></div>
</div>
</body>
</html>



-----------------------------------------------------------------
vml.html
-----------------------------------------------------------------



Map = function(){
 this.map = new Object();
};   

Map.prototype = {   
    put : function(key, value){   
        this.map[key] = value;
    },   
    get : function(key){   
        return this.map[key];
    },
    containsKey : function(key){    
     return key in this.map;
    },
    containsValue : function(value){    
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){    
     return (this.size() == 0);
    },
    clear : function(){   
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){    
     delete this.map[key];
    },
    keys : function(){   
        var keys = new Array();   
        for(var prop in this.map){   
            keys.push(prop);
        }   
        return keys;
    },
    values : function(){   
     var values = new Array();   
        for(var prop in this.map){   
         values.push(this.map[prop]);
        }   
        return values;
    },
    size : function(){
      var count = 0;
      for (var prop in this.map) {
        count++;
      }
      return count;
    }
};