NOTICE 
Scripter 에 해당하는 글 : 75 개
2008/08/07 :: Ext2.2 Release (11)
2008/04/22 :: ExtJS 2.1 릴리즈
2008/04/16 :: RESTFul 한 ExtJS
2008/04/16 :: Extjs Qtips 사용하기 (2)
2008/04/15 :: HTML include하기
2008/04/01 :: ExtJS 로드맵
올바른 성장과 따뜻한 나눔이 있는 넥스트리
가끔씩 Ajax 웹어플리케이션을 만들다 보면 단축키를 요구하는 고객들이 많다.
그러한 이유로 단축키 관련 스크립트를 찾아보던중에 prototype el.observe() (extjs에서는 el.on())를 이용한 keycode를 바인딩해주는
스크립트를 찾았다.

얼마전에 ajaxian post에 올라왔던 글이였는데 잊고 있었던거.. ㅡ.,ㅡ;
잠시 꼬불쳐 두다... 키값 참조할때도 좋겠다.

참조URL , 샘플URL

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/scriptaculous.js"></script>
<script type="text/javascript" >
var Key = {

// -- Alphabet
  A: 65,
  B: 66,
  C: 67,
  D: 68,
  E: 69,
  F: 70,
  G: 71,
  H: 72,
  I: 73,
  J: 74,
  K: 75,
  L: 76,
  M: 77,
  N: 78,
  O: 79,
  P: 80,
  Q: 81,
  R: 82,
  S: 83,
  T: 84,
  U: 85,
  V: 86,
  W: 87,
  X: 88,
  Y: 89,
  Z: 90,

  a: 97,
  b: 98,
  c: 99,
  d: 100,
  e: 101,
  f: 102,
  g: 103,
  h: 104,
  i: 105,
  j: 106,
  k: 107,
  l: 108,
  m: 109,
  n: 110,
  o: 111,
  p: 112,
  q: 113,
  r: 114,
  s: 115,
  t: 116,
  u: 117,
  v: 118,
  w: 119,
  x: 120,
  y: 121,
  z: 122,

// -- Special Keys
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  END: 35,
  HOME: 36,
  ARROW_LEFT: 37,
  ARROW_UP: 38,
  ARROW_RIGHT: 39,
  ARROW_DOWN: 40,
  DELETE: 46,
  QUESTION_MARK: 63

};

/*
 * Tie into ? and ESCAPE
 */
document.onkeypress = function(e) {
    if (e.charCode == Key.QUESTION_MARK) {
        KeyBindingsHUD.toggle();
    } else if (e.keyCode == Key.ESCAPE) {
        KeyBindingsHUD.hide();
    } else {
        KeyBindings.actionForKey(e);
    }
}

var KeyBindings = (function() {
    var bindings = {};

    var findNumber = function(key) { // get the name of the key from the charCode
        for (var name in Key) {
            if (Key[name] == key) {
                return name;
            }
        }
    }

    return {
        add: function(binding) {
            if (typeof binding.keys == "string") { // short circuit for non-arrays
                binding.keys = [ binding.keys ];
            }
            bindings[binding.eventname] = binding;
        },

        remove: function(eventname) {
            bindings[eventname] = undefined;
        },

        html: function() {
            var html = [];

            for (var key in bindings) {
                if (bindings.hasOwnProperty(key)) {
                    var binding = bindings[key];
                    var keys = binding.keys.collect(function(key) {
                        return Object.isNumber(key) ? findNumber(key) : key;
                    });
                    html.push(keys.join(', ') + ': ' + binding.description);
                }
            }

            return html.join('<br/>');
        },

        actionForKey: function(e) {
            for (var action in bindings) {
                if (bindings.hasOwnProperty(action)) {
                    var keys = bindings[action].keys;
                    keys.each(function(key) {
                        if (e.charCode == 0) {
                            if (e.keyCode == key) {
                                document.fire(action, e);
                                throw $break;
                            }
                        }
                        if (String.fromCharCode(e.charCode) == key) {
                            document.fire(action, e);
                            throw $break;
                        }
                    });
                }
            }
        },

        caseInsensitive: function(keys) {
            if (typeof keys == "string") keys = [ keys ]; // array wrap
            var newArray = [];
            keys.each(function(c) {
               newArray.push(c, c.toUpperCase());
            });
            return newArray;
        }
    }
})();

/*
 *
 */
var KeyBindingsHUD = (function() {
    var isShown = false;
    var headerId = '_keybindings_header';

    return {
        toggle: function() {
          (isShown) ? this.hide() : this.show();
        },
        show: function() {
            if (!isShown) {
                var header = $(headerId);
                if (!header) {
                    header = this.createElement();
                }
                header.innerHTML = KeyBindings.html();
                header.blindDown({duration: 0.2});
                isShown = true;
            }
        },
        hide: function() {
            if (isShown) {
                $(headerId).blindUp({duration: 0.2});
                isShown = false;
            }
        },
        createElement: function() {
            var header = document.createElement('div');
            header.id = headerId;
            header.align = 'center';
            header.style.display = 'none';
            header.style.width = '100%';
            header.style.backgroundColor = '#111111';
            header.style.color = '#eeeeee';
            header.style.fontSize = '10pt';
            header.style.paddingTop = '4px';
            header.style.paddingBottom = '4px';
            header.style.borderBottom = '1px solid #333';

            var body = document.body;
            body.insertBefore(header, body.firstChild);

            return $(headerId);
        }
    }
})();




KeyBindings.add({
   eventname: 'action:move-up',
   keys: ['p', Key.ARROW_UP ],
   description: "Move up the stack"
});

KeyBindings.add({
   eventname: 'action:move-down',
   keys: ['n', Key.ARROW_DOWN ],
   description: "Move down the stack"
});

document.observe('action:move-up', function(e) {
    alert(1)
});

document.observe('action:move-down', function(e) {
    alert(2);
});

</script>


소스다운로드 :




이올린에 북마크하기(0) 이올린에 추천하기(0)
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback :: http://techbug.tistory.com/trackback/120
올바른 성장과 따뜻한 나눔이 있는 넥스트리

일전에 포스팅한 extjs 로드맵에서 언급했듯이 extjs 2.2 가 릴리즈 됐다.

그리고 데꾸벅의 한글화 부분(ext-2.2/build/locale/ext-lang-ko.js)도 추가적으로 패키징되어 들어가 있다. 캬햐햐
/**
 * Korean Translations By nicetip
 * 05 September 2007
 * Modify by techbug / 25 February 2008
 */
트리로더 확장과 불여우2에서 스크롤문제(overflow)와 IE6 버그(relative)일때 스크롤문제부분(스쿨학우여러분 땡큐 많이 참조했어요.. ^^)도 부분적으로 채택이 됐네요.. 크크..
FF3 Beta1에서 에러나던 버튼위치부분은... 쿨럭.. ㅡ.,ㅡ;

2.2 (2008 여름) : 릴리즈노트

  • Ext.Ajax 기능향상
  • Ext컴포넌트만의 초점을 맞춘(scoped) REST style지원
  • XML을 포함한 추가적인 TreeLoader 데이타로딩
  • 슬라이더 컴포넌트
  • 파일업로드 필드

릴리즈노트에 나타난 새롭게 추가된 기능들은 다음과 같다.

[ CheckboxGroup / RadioGroup ]

... },{
    xtype: 'checkboxgroup',
    fieldLabel: 'Multi-Column (horizontal)',
    columns: 3,
    items: [
        {boxLabel: 'Item 1', name: 'cb-horiz-1'},
        {boxLabel: 'Item 2', name: 'cb-horiz-2', checked: true},
        {boxLabel: 'Item 3', name: 'cb-horiz-3'},
        {boxLabel: 'Item 4', name: 'cb-horiz-4'},
        {boxLabel: 'Item 5', name: 'cb-horiz-5'}
    ]
},{ ...

각 개별적인 item으로만 적용했던 라디어버튼과 체크박스를 그룹핑하여 만든 컴포넌트가 추가되었다. [샘플보기]
사용자 삽입 이미지



[히스토리컴포넌트 추가] [샘플보기]
listeners: {
    'tabchange': function(tabPanel, tab){
        // Ignore tab1 since it is a separate tab panel and we're managing history for it also.
        // We'll use its handler instead in that case so we don't get duplicate nav events for sub tabs.
        if(tab.id != 'tab1'){
            Ext.History.add(tabPanel.id + tokenDelimiter + tab.id);
        }
    }
}

사용자 삽입 이미지

위의 탭기능중 sub-tab3 를 선택한위 상단 탭을 클릭햇을 경우 다시 돌아왔을때 sub-tab3이 선택되어 있게 만들어준다. 이전 버전에서는 별도의 글로벌변수로 선언해준뒤 사용했었는데 이번 릴리즈에는 포함되어 나와서 상당히 편할듯하다..
히스토리 컴포넌트 사용시에는 URL 핸들링이 하는지 URL자체가 변하는 효과를 볼수 있다.
사용자 삽입 이미지


[MultiSelect / ItemSelector] [데모보기]
이 기능은 사용자 확장기능에 올라왔던 기능인데 이번 릴리즈에 포함되었있다.
전통적인 방식의 <select multiple> 기능을 extjs로 구현한 것이다.
사용자 삽입 이미지


[FileUploadField][데모보기]
사용자 확장기능에서 가장 많이 찾던 swfFileUpload에서 차용한듯한 소스가 이번 릴리즈에 포함되어 있다. ajax fileupload를 찾는 국내 사용자에게는 상당히 유용한 컴포넌트가 될듯하다. Form 컴포넌트에 포함되어 있다.
var fibasic = new Ext.form.FileUploadField({
    renderTo: 'fi-basic',
    width: 400
});


[XmlTreeLoader] [데모보기]
사용자 확장기능이다. 이전 treeLoader와 사용법은 비슷하다.


[GMapPanel][데모보기]
사용자 삽입 이미지
구글맵을 보여줄수 있는 panel이 추가되었다.
이것또한 사용자 확장기능이였으나 이번 릴리즈에 포함되었다.








사실 새롭게 추가된 기능들만 보자면 로드맵에 나왔던 내용들은 거의 없다.
향상된 기능들이라면 Firefox3 및 IE8 버전을 지원하다는 내용과 드래그앤드랍 기능(특정DnD영역그리드패널 Row의 드래그엔 드랍, 폼내널에서의 드래그엔드랍), 그리도 속도 향상, status기능등이 많이 향상되었다.

또한 축약어 들이 많이 등장했는데 Ext.MessageBox()를 msg()와 같이 표현한다던가, 브라우저 버전에 대한 글로벌 변수 Ext.isGecko2, Ext.isGecko3 등이 더 추가되었다.

그외 아래 링크로 확인하시기 바랍니다.














이올린에 북마크하기(0) 이올린에 추천하기(0)
크리에이티브 커먼즈 라이선스
Creative Commons License

'Scripter > EXTJS' 카테고리의 다른 글

Ext2.2 Release  (11) 2008/08/07
ExtJS 2.1 릴리즈  (0) 2008/04/22
RESTFul 한 ExtJS  (0) 2008/04/16
Extjs Qtips 사용하기  (2) 2008/04/16
Extjs 기본레이아웃에 각종 패널붙이기  (0) 2008/04/16
ExtJS를 이용한 EditorGrid 붙이기  (0) 2008/04/07
Trackback :: http://techbug.tistory.com/trackback/118
김자영
헤매다가 여기까지왓네요 ...
질문도 받아주시는지?
제가 라디오버튼을 만드는데요
types[config.xtype || defaultType] is not a constructor
[Break on this error] return new types[config.xtype || defaultType](config); 이런에러가 떨어집니다.
무슨 문제인지 못찾겟어요 .. 시작한지 2틀됏는데 너무 어렵네요 좀 가르챠주세요
2008/08/12 19:35
어쩌다가...이곳까지 오셨어요~ ^^

2008/08/13 17:58

김자영
아 소스는 xtype: 'radiogroup',
fieldLabel: 'Auto Layout',
columns: 1,
items: [
{boxLabel: 'Item 1', name: 'rb-auto', inputValue: 1},
{boxLabel: 'Item 2', name: 'rb-auto', inputValue: 2, checked: true},
{boxLabel: 'Item 3', name: 'rb-auto', inputValue: 3},
{boxLabel: 'Item 4', name: 'rb-auto', inputValue: 4},
{boxLabel: 'Item 5', name: 'rb-auto', inputValue: 5}
]
샘플가져다 하는데 이러네요.....
2008/08/12 19:36
소스를 부분적으로만 봐서는 어떻게 됐는지 확인하기 어렵겠는데요? 그 상위 컴포넌트의 xtype은 정의해 주셨는지요?

2008/08/13 17:58

extjs 를 처음 접하면서 데꾸벅님 블로그를 정말 많이 참고하고 있습니다. ㅠ
다름이 아니라 2.1에서 form 전송시, 폼위치가 변경되는 문제때문에, 2.2로 갈아타니, 폼 버그는 수정된듯 한데요.
2.1 에서 잘되던 iframe 안의 tabpanel이. 2.2에서 tab 이 사라지는 문제가 발생중입니다.
혹시 왜 그런지 알수있을까요? ㅠ_ㅠ
2008/08/14 02:59
2.2 release될때 tabpanel에서는 별로 변경된게 없는데요.. position:"bottom"일때 구분자(" ")부분과 브라우저에 대한 패치밖에 없었는데.. 실제 option상에 빠진것이 있지 않나 쉽네요... 각 tabpanel로 쓰이는 item panel에 id값만 제대로 넣어주시면 깨지는 일은 별로 없습니다. 2.1에서 폼전송시 깨지는 부분도 form panel과 field item들의 id 값이 제대로라면 깨지지 않습니다.

2008/08/14 09:16

tasiks
지금 개발중인데요
css폴더를 샘플에서 복사해서 다른곳에 위치하구요 그곳으로 링크를 잡아줫는데..
css가 적용이 안됩니다. 샘플위치로 바꾸엇더니 적용이 잘되는데요 다른곳으로 옮겻을떄 왜 안되는걸까요?
2008/08/20 15:55
스타일쉬트는 해당 css를 호출하는 페이지를 기준이 아니라 css 파일 기준으로 모든 파일을 처리하기때문에 이미지나 import된것의 path가 틀려져서 그런겁니다. css파일 옮길때 images파일을 같이 옮기거나 이미지 경로를 모두 바꿔줘야 합니다.

2008/08/21 00:23

tasiks
네 그렇군요 ... 경로들을 다 수정하던지 해야하는군요 .. 감사합니다.
2008/08/21 14:11

kidscool
안녕하세요.. 질문 올려도 되는지요...
현재 Ext.form.ComboBox 이용하는데요..콤보박스 밑에 activex 그리드가 있습니다.
그래서 콤보박스가 그리드에 가려지는데요... 어케 나오는 방법 없을까요...ㅜ.ㅜ
2008/08/25 17:55
어떤종류의 activeX인가요? 플래쉬라면 wmode="trasparent"와 같이 바꿀수 있을텐데.. 만약 div로 된 레이어가 activeX 그리드위에 올라가지 않는다면 ComboBox도 올라가지 않습니다. 꽁수를 쓰셔야 할듯~~

2008/08/25 22:49

올바른 성장과 따뜻한 나눔이 있는 넥스트리


자바스크립트 최적화하기 코드는 웹상에서 비법을 전수해 주는 사이트는 많이 있다.
sun.com의 frontend 개발자인 Gregory Reimer는 다음과 같이 HTML 콜렉션 반복문을 추천해 주었다. (난 왜 이런 생각을 못하지?? ㅡ.,ㅡ;)

// looping a dom html collection
for (var i=0, node; node = hColl[i++];) {
    // do something with node
}


var i = arr.length; while (i--) {}

나름대로 장단점은 있겠지만 속도면에서는 월등히 뛰어나다는게 그의 블로그에 벤치마킹결과로 나타나 있다.

참고적으로 다음 사이트들도 한번 봐두길...



이올린에 북마크하기(0) 이올린에 추천하기(0)
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback :: http://techbug.tistory.com/trackback/115
올바른 성장과 따뜻한 나눔이 있는 넥스트리
이미 아는 사람들은 다아는 로딩바 원하는 형태로 만들기 사이트(http://www.ajaxload.info/)에서 좋은 Loading Indicator(Spinner)를 구한 다음에 아래와 같이 간단히 구현할 수 있다.

jQuery(function($){
        // Create img dom element and insert it into our document
        var loading = $('<img alt="loading" src="/images/loading.gif" />')
        .appendTo(document.body).hide();
        // Make the image appear during ajax calls
        $(window).ajaxStart(loading.show);
        $(window).ajaxStop(loading.hide);
});



참고적으로 요기도 보아두면 좋아요~~
jQuery를 모르는 분들은 여기서 부터 시작하세요



이올린에 북마크하기(0) 이올린에 추천하기(0)
크리에이티브 커먼즈 라이선스
Creative Commons License

'Scripter > jQuery' 카테고리의 다른 글

jQuery Loading바 구현하기  (0) 2008/07/08
Trackback :: http://techbug.tistory.com/trackback/111
올바른 성장과 따뜻한 나눔이 있는 넥스트리
유지보수의 용이성과 JSDOC으로 API를 추려내기 위해서 다음과 같이 자바스크립트 코딩하시기 바랍니다.


1. 페이지 상단 파일설명
/**
 * @fileoverview 테이블소트 클래스로 Javascript만으로 테이블을 소트한다.
 * @author 데꾸벅
 * @version 0.1
 * @since 2008.06.11
 */


2. 클래스 및 메쏘드 설명
/**
 *  테이블 소트 클래스
 * @class 테이블 소트 클래스로 Javascript만으로 테이블을 {@link http://sourceforge.net/projects/jsdoc JSDoc} 소트한다.
 * @constructor
 * <code>var sortTable = new SortTableClass("tblSort",true,'img','images/button/bts_down.gif','images/button/bts_downon.gif');</code>
 * @param {string} sTableID 소팅할 테이블의 아이디
 * @param {string} isReverse 오름차순,내림차순 가능여부
 * @param {string} markType 이미지나 마크처리 (예: 'txt', 'img')
 * @param {string} defaultMark 기본마크
 * @param {string} descMark 내림차순일때 마크
 * @param {string} ascMark 오름차순일때 마크
 * @throws
 * @author 데꾸벅
 * @since 2008.06.11
 */


 

  • @class : 클래스설명을 기술한다.
    @constructor : 생성자일때만 표기
    @param : 인자값들을 표기한다.
                  표기방법 : @param {타입} 인자명 인자설명
                  표기예 : @param {int} tableLength 테이블의 갯수
    @throws : 예외처리에 대해 기술한다.
    @extends : 상속
    @author : 작성자
    @version : 버전
    @since : 작성일
    @see : 참조
          표기방법 : @see 참조문서
          표기예 : @see #generateTableMethod  -> 현재 클래스의 generateTableMethod참조
                      @see sortTable.generateTableMethod
     @link : API문서에 링크걸때 표기
          표기방법 : {@link 링크할주소 링크표시}              

    @type : 메쏘드 타입
    @return : 반환값 있을 경우 (반환값이 한개일때 경우 )
    @returns : 반환값 있을경우 (반환값이 여러개일 경우)

    그외 : 각 설명(description)에는 HTML Tag로 표기하되 스타일시트가 없는 형태로 넣는다



3. 클래스 변수 및 메쏘드 변수표기

 /***소팅한 컬럼 저장 */
 this.tmpSortICol = "";

 /*** 초기(내림차순) 정렬방식 저장 */
 this.tmpADESC = "desc";

 /*** 소팅한 테이블의 아이디 */
 this.sTableID = sTableID;

 /*** 오름차순,내림차순 가능여부 */
 this.isReverse = (isReverse!=null) ? isReverse : false;

 /*** 이미지나 마크처리 */
 this.markType = (markType !=null) ? markType : null ;

 /*** 기본마크 */
 this.defaultMark = (defaultMark !=null) ? defaultMark : '▽';

 /*** 내림차순일때 마크 */
 this.descMark = (descMark !=null) ? descMark : '▼';

 /*** 오름차순일때 마크 */
 this.ascMark = (ascMark !=null) ? ascMark : '▲';

표기법 참조 문서 (첨부파일참조) :




위와 같이 해주시면 다음과 같이 유지보수시 다음 사용자가 쉽게 해당 메쏘드나 클래스를 사용할수 있도록 API문서로 도출이 가능합니다.

사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

JSDOC 사용법은 http://jsdoc.sourceforge.net/ 을 참조하세요




자바스크립트 명명법

자바스크립트 파일명명법은 java,jsp 파일명과 동일하게 camel 표기법으로 명명한다.

주의사항 :
    - 다른 파일냉의 동일한 메쏘드명으로 작성하지 않는다.
    - js 파일은 항상 인코딩 타입을 euc-kr (ms949 ascii)타입으로 코딩하되  파일자체가 Ajax를 통해 불러오는 페이지의
      경우는 반드시 UTF-8 형태로 작성한다. (<script type="text/javascript" src="흠.js" charset="euc-kr"></script>)
    - 각 비즈니스(여기서는 메뉴)별 스크립트를 나누어 작성하되 필요한 경우에만 불러 사용한다.
    - 공통 클래스는 클래스임을 표기하여 준다. (sortTableClass.js)
    - 해당 비즈니스(여기서는 메뉴)에만 전용으로 쓰는 스크립트가 많다면 비즈니스명으로 폴더링한다.


표준 자바스크립트 사용

객체 접근은 cross-browser를 지원하는 표준 메쏘드나 프로퍼티를 사용한다.
1. document.all 보다는 document.getElementById, document.getElementsByName 등으로 접근한다.
2. eval은 되도록이면 자제하도록한다.
3. 객체가 create되면 반드시 destory할때 null처리를 한다.
4. try~ catch~throws 문은 필요한 부분에서만 사용한다.


AJAX사용시 Callback함수 표기방법 (jQuery기준)

1. 기본표기법 : 해당 메쏘드를 XHR 형태와 혼합해서 쓰는경우

$.getJSON(
 "http://localhost:8080/BetmanWeb/cartGetSportsLottery.so",
 { gameId: "G015", gameRound: "1553", memberId: "200804474628" },
 function(json) {
  $("#result").html(json.length);
 }

);

2. Callback함수 별도지정 : Callback함수를 공통으로 사용하는 경우

$.getJSON(
 "http://localhost:8080/BetmanWeb/cartGetSportsLottery.so",
 { gameId: "G015", gameRound: "1553", memberId: "200804474628" },
 callbackFunc
);  <-- 주의: 인자값이 들어가 있지 않습니다. (closure 문제발생)

//콜백함수는 반드시 호출한 함수의 바로 밑에서 시작합니다.

function callbackFunc(json){  <-- 인자값이 들어가 있습니다. (자바스크립트의 유연성을 이용-closure
 //blah blah blah~ ^^
}


 

참고사이트

  1. Mozilla Developer Center: Core JavaScript 1.5 Reference Manual
  2. Yahoo UI개발자인 Douglas Crockford's JavaScript
  3. Yahoo Developer Network : YUI Theater  ( 더글라스 크록포드의 동영상 강의 수록)
  4. Peter-Paul Koch의 QuirksMode
  5. http://home.cogeco.ca/~ve3ll/jstutor0.htm
  6. 모질라 개발자 사이트 (DOM Reference)
  7. koxo.com <-- 누구신지 대단하죠?
    두곳만 알아도.. 대단.. 쿨럭~~


그외 볼만한 사이트

  1. JavaScript: The Definitive Guide by David Flanagan
  2. Pro JavaScript Techniques by John Resig
  3. Ajax and REST Recipes by Christian Gross
  4. ppk on JavaScript by Peter-Paul Koch
  5. Professional JavaScript for Web Developers by Nicholas C. Zakas
  6. Ajax Patterns and Best Practices by Christian Gross
  7. Dynamic HTML by Danny Goodman
  8. Head First Design Patterns by Freeman & Freeman

OO JavaScript 참조사이트

  1. How to achieve private, public, and privileged members in JavaScript, by Dustin Diaz.
    : Method scoping에 대한 좋은 설명
  2. OOP in JS, Part 1 : Public/Private Variables and Methods, by Gavin Kistner.
    : Scoping 및 prototyping에 대한 샘플
  3. A JavaScript Module Pattern, by Eric Miraglia in YUI Blog.
    : 더글라스 크록포드가 얘기한 module pattern에 대한 설명
  4. Again with the Module Pattern - reveal something to the world, by Christian Heilmann.
    : 모듈패턴
  5. JavaScript for Object-Oriented Programmers (Appendix B of the book AJAX in Action).

DOM