블로그 이미지

카테고리

데꾸벅 (194)
Publisher (39)
Scripter (97)
Programmer (1)
Designer (30)
Integrator (18)
Pattern Searcher (4)
News (2)
강좌 및 번역 (3)

최근에 올라온 글

최근에 달린 댓글





이전에 getBoundingClientRect()에 대해서 포스팅한적이 있다.  이 함수는 객체의 페이지내의 위치값과 너비, 높이값을 반환한다. 편리하게 사용했었는데 오늘 역시 이부분을 짚고 넘어가야할 문제에 봉착했다.
기존에 데꾸벅이 2002년도에 제작해 지금껏 수정하면서 써오고 있는 캘린더가 IE8에서 동작하지 않는것이다. 문제는 각 브라우저에서 객체의 offsetParent 값을 계산하는 방법이 틀려서 생긴 문제였다. 특히 IE8....

처음에는 body를 못찾는 문제인줄 알았는데 IE에서의 객체 위치값을 표현하는 offsetParent값에 문제가 있다는 것을 알게되었다. 관련내용을 찾다가 아주 좋은 글을 발견(?)하여 포스팅한다.



아래는 HappyFriday님의 글을 인용한것임을 밝힌다.

언제나 그렇지만 항상 IE가 문제다.
크로싱을 위한 최고의 난적은 대부분 IE이다. 일단 offsetParent는 무엇일까? 말그대로 현재 대상 엘리먼트를 transform 하는 상위 엘리먼트를 참조하는 속성이다. 일반적으로 parentNode와는 다른 개념으로 이것은 concatenate 가 적용되는 부모 엘리먼트에만 유효하기 때문에...꼭 대상 엘리먼트의 상위엘리먼트를 의미하지는 않는다. 
일반적으로 이 속성에 대한 설명이 자식을 offset하는 엘리먼트라고 나와있는데.. 이해하기가 난해하므로... 자식엘리먼트의 위치에 영향을 미치는 상위 엘리먼트를 치칭 하는것으로 이해하면 되겠다.
그렇다면 자식엘리먼트에 영향을 미친다는건 무엇일까? 바로 position 속성이다. 모든 엘리먼트의 position속성은 기본이 static이다. 즉 정적상태를 유지하는것... 따라서 position이 relative / absolute가 되었을때 바로 자식엘리먼트에 영향을(좌표상으로) 미치게 됨으로offsetParent를 반환하는 대상 엘리먼트는 반드시 position이 relative나 absolute가 되어야 한다. 
즉 수식으로는 getStyle(obj, 'position) != 'static' 으로 표현될 수 있다.

일반적으로 offsetParent가 많이 사용되는 부분은 대상엘리먼트를 글로벌 좌표포지션으로 변환한 후 이값을 활용하는 곳들이다.
그런데..IE브라우져(ie8 이하)의 경우 상위 엘리먼트의 position이 'relative' 일때 자신의 offsetLeft가 0이 되어야 하는데 디폴트로 최상위 엘리먼트의  padding (paddingLeft/paddingTop)값을 취하도록 설계되어 있다. 이해를 돕기 위해서 다음 코드를 보자.

<div id='a' style='background-color:#ff0000; padding:30px; '>
    <div id='b' style='position:relative; background-color:#000000; '>
        <div id='c' style='position:relative; background-color:#0ff000; '>
            <div id='d' style='background-color:#666666;'>
                aaaa<br>
                aaaa<br>
            </div>
        </div>
    </div>
<div>

위의 노드중 d의 입장에서는 부모 position이 relative이기 때문에 당연 자신의 offsetLeft는 0이어야 한다.
왜냐면 상대경로로 다시 초기화 되었으니..부모입장에서 padding값이 들어간게 아니니까... 만약 부모의 position이 relative가 아니면 그제서야 부모 offsetParent가 static이 됨으로 그때는 자신이 30의 값을 가져야 한다. 그런데 IE7는 무조건 30의 값을 가지고 시작한다. 즉 최상위 a의padding(padding-left)이 30임으로 그값에 영향을 받아서 표시를 하게된다. 바로 이게 문제다. 이 문제로 인해 최종 글로벌 포지션의 left30이어야 함에도 불구하고 60이 되어버린다. 결국 이중값이 들어간다는 말이다.
따라서 IE7의 경우에는 바로 이 최상위 엘리먼트, 즉 body바로 밑의 엘리먼트를 구한후 (이것은 해당 엘리먼트가 position이 relativeabsolute 아닌경우 반환하지 않으므로) 해당 엘리먼트의 padding값을 빼줘야 한다.





참고적으로 캘린더 소스 :  
(이미 다른 자바스크립트 라이브러리를 사용하는 중이라.. 별~)

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

자바스크립트 delete 연산자에 대한 고찰  (8) 2009.11.18
Javascript로된 Wiki Parser : Creole 1.0  (0) 2009.01.12
자바스크립트 가이드  (0) 2008.12.03
JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
Post by 넥스트리소프트 데꾸벅(techbug)
, |



AJAX에서 XMLHttpRequest 객체 삭제

ajax 강의중 메모리에 있는 객체를 삭제시키는 방법에 대해서 질문을 하신 분이 있었는데 사실 명시적으로 null 로 처리하면 되나 좀더 깔끔한 방법을 소개하고자 한다.
1function deletexhr(xhr) {
2    function doNothing() {
3    }
4    xhr.onreadystatechange = doNothing; //onreadystatechagne를 더미함수로 대체   
5    xhr.abort();                        //xhr 요청 중단
6    delete xhr;                         //xhr 객체 밑의 모든 프로퍼티 삭제
7}





자바스크립트에서 delete 연산자

자바스크립트의 delete연산자는 단항연산자로써 피연산자로 지정된 객체 프로퍼티, 배열원소 또는 변수의 삭제를 시도한다.
(C++ 프로그래머라면 자바스크립트의 delete연산자는 C++의 delete연산자와 전혀 닮지 않았다는 것을 발견할 것이다. 자바스크립트의 메모리 해제는 GC에 의해 자동으로 관리되기 때문에-순환참조 제외- 메모리를 직접 해제하는일에 신경 쓸 필요가 전혀없다.)

피연산자가 성공적으로 삭제되었을 경우 true를 반환하고, 삭제될 수 없는 경우 false를 반환한다. 모든 변수나 프로퍼티를 삭제할 수는 없다. 즉 객체자체는 삭제할 수 없으나 객체의 프로퍼티는 삭제할 수 있다는 것이다.  몇몇 핵심 프로퍼티나 클라이언트측 프로퍼티는 삭제하려 해도 소용으며  사용자가 var문으로 정의한 변수들도 삭제할 수 없다. 존재하지 않은 프로퍼티에 대해 delete을 호출할 경우 true가 반환되며 심지어 ECMAScript표준에 따르면 피연산자가 프로퍼티, 배열 원소 또는 변수가 아닐 경우에도 true를 반환한다고 명시되어 있다. 

01var o = {           //변수 정의 및 객체 초기화
02    x:1,
03    y:2
04}
05delete o.x;         //객체 프로퍼티중 하나를 삭제한다. true 반환
06typeof o.x;         //존재하지 않은 프로퍼티. "undefined" 반환
07delete o.x;         //존재하지 않는 프로퍼티를 삭제하려한다. true반환
08delete o;           //선언된 변수는 삭제할 수 없다. false 반환
09delete 1;           //정수도 삭제할 수 없다. true 반환
10x = 1;              //var 키워드를 사용하지 않고 암묵적으로 변수를 선언
11delete x;           //이와 같은 변수는 삭제할수 있다. true반환
12x;                  //런타임 에러, x는 삭제되었으므로 정의되지 않았다.<p></p>

삭제된 프로퍼티나 변수, 배열원소는 단순히 undefined값으로 설정된 것이 아니라는 것을 주의하라. 어떤 프로퍼티가 삭제되면 그 프로퍼티는 더이상 존재하지 않는다.
한가지 중요한 점은, delete이 영향을 미치는 것은 오직 프로퍼티 뿐이며 해당 프로퍼티가 참조했던 객체는 무관하다는 것이다.
1var my = new Object();      //'my'라는 이름의 객체를 생성
2my.hire = new Date();       //my.hire는 Date객체 참조
3my.fire = my.hire;          //my.fire도 동일한 객체 참조
4delete my.hire;             //hire속성 삭제 true반환
5document.write(my.fire);    //하지만 my.fire는 여전히 Date객체를 참조

javascript에서의 delete는 개체(object), 개체의 속성(property), 배열의 특정 인덱스에 있는 원소(element)를 지우는 연산자이며. 문법은 다음과 같다
1delete objectNamedelete objectName.property
2delete objectName[index]delete property // with 문장 안에서만 유효
objectName은 개체 이름이고, property는 개체에 존재하는 속성이고, index는 배열의 원소 위치를 나타내는 정수. 네 번째 형식은 개체의 속성을 지우는 코드인데, with 문장 안에서만 사용할 수 있다. 
암시적으로 선언된 변수를 지울 때는 delete 연산자를 사용할 수 있지만 단 var 문장을 이용해서 선언된 변수는 지울 수 없다. 
delete 연산자 실행이 성공하면, 속성이나 원소가 undefined로 설정되며. delete 연산자는 실행이 가능하면 true를 반환하고, 불가능하면 false를 반환하게 된다. 
1var x=42;
2var y= 43;
3var myobj=new Number();
4myobj.h=4;      // h라는 속성을 만듭니다
5delete x;       // returns true (암시적으로 선언된 변수는 지울 수 있습니다)
6delete y;       // returns false (var로 선언한 변수는 지울 수 없습니다)
7delete Math.PI; // returns false (미리 정의된 속성은 지울 수 없습니다)
8delete myobj.h; // returns true (사용자 정의 속성은 지울 수 있습니다)
9delete myobj;   // returns true (암시적으로 선언되었으므로 지울 수 있습니다)

당연하지만 배열의 원소를 지워도, 배열의 길이에는 변화가 없게 된다. 즉 delete로 지우는 경우 원소는 더이상 배열에 존재하지 않게 된다(undefined). 하지만 delete와 배열원소에 undefined를 할당한 경우 조금 의미가 다르다. 
먼저 아래 예제에서 trees[3]을 delete로 제거한 예제이다. 
1trees=new Array("redwood","bay","cedar","oak","maple");
2delete trees[3];
3if (3 in trees) {   // 이 블록은 실행되지 않는다.
4   alert(trees[3]);
5}
즉 trees라는 배열에는 trees[3] 이라는 배열원소 자체가 삭제(undefined)되어 실행되지 않지만 단지 배열 원소가 존재하긴 하지만 정의되지 않은 값을 가지도록 하고 싶다면 delete 연산자 대신 undefined 키워드를 사용하면 된다. 
다음은 trees[3]에 undefined 값을 할당하는 경우이다 
1trees=new Array("redwood","bay","cedar","oak","maple");
2trees[3]=undefinedif (3 in trees) {   // 이 블록은 실행된다.
3}
undefined 키워드로 배열의 값은 정의되지 않았지만 배열원소는 유지되므로 블록은 실행된다







- PHPSCHOOL에서 - 

delete는 해당 변수를 삭제하는 용도로 주로 쓰입니다. C언어에서는 클래스를 삭제하는 등등 뭐 여러가지로 잘도 쓰이죠. 하지만 JS에서는 그닥 delete문은 쓰잘데기 없습니다. 기본적인 사용예제입니다.
1var a=document.getElementsByTagName('div');
2 for(i=0;i<a.length;i++){ a;="" delete="" }="" ;="" a.style.backgroundcolor="green"></a.length;i++){>
a를 통해 div 태그를 모조리 뽑아온 다음 모조리 배경색을 초록으로 바꾸고 그변수를 버립니다. 물론 그 이후 a를 출력시키면 undefined로 나오죠. 뭐.. 객체 자체를 삭제할 수는 있지만 객체 안의 멤버를 삭제할 수는 없습니다.
 
1delete a.member;
이래봐야 결과값은 각설이처럼 죽지도 않고 또출력되네~ 입니다. 하지만 a의 동적 멤버, 즉, 프로토타입은 delete문으로 지울 수 있습니다. 
1delete Integer.prototype.toUnsigned;
물론 객체 자체를 삭제해야 하기 때문에 괄호를 붙이면 안되겠죠. 그렇다고 객체 자체가 삭제되지는 않으니 안심하세요. 어쨌든 이렇게 되면 잠시동안 프로토타입을 선언한 후 쓰다가 지울 수 있는 괴기한 스크립트를 만들 수 있겠습니다. 
01Object.prototype.extend=function(obj){
02       for(var x in obj)
03           this[x]=obj[x];
04       return this;
05 }
06 
07var ob={
08    a:'가',
09    b:'나',
10    c:function(){
11        return '다';
12      },
13    d:4
14};
15 
16ob=ob.extend({e:5,f:'라마바',g:(function(ret){return ret;})('사아자차카타파하')});
17delete Object.prototype.extend;
prototype 멤버를 delete로 삭제할 수 있다는 걸 알려주는것 뿐입니다.




자바스크립트에서의 가비지컬렉션

300.slimes님 블로그글중 일부발췌

먼저 순환 참조에 대한 일반적인 이해에는 Garbage Collector에 대한 간단한 작동 방식을 이해해야 된다.다음 코드를 보자.
1var Vervain = new Herb();
2var Verbena = Vervain;


Vervena 가 객체 Vervain을 참조하게 되면 scope 내에 Vervain에 대한 참조카운트가 1이 증가한다. 그리고 실행이 끝나고, scope 를 벗어날 때 해당 scope 내에 Verbena는 파괴되게 된다. 그렇게 되면 Vervain 객체에 대한 참조카운트는 다시 1이 감소한다. 그렇게 되면 GC에서는 Vervain의 참조카운트가 0이 되었으므로 더 이상 사용하지 않는 객체로 판단하고 메모리를 해제하게 된다.하지만 다음과 같은 경우는 어떨까?

1var Vervain = new Herb();
2var Verbena = new Herb(); 
3Vervain.see = Verbena; 
4Verbena.see = Vervain;

Vervain과 Verbena는 서로를 참조하고 있다. 이러한 경우를 순환참조라고 한다. 객체에 대한 참조를 따라가 보면 완전한 연결고리를 형성하게 된다. 
하지만 위와 같은 경우는 어떻게 메모리를 해제해야 될까? 
Vervain과 Verbena의 참조카운트는 모두 1이다. Vervain 를 해제하기 위해서는 Vervain에 대한 참조카운트가 0이 되어야 하는데, 이는 Verbena.see 가 Vervain을 참조하고 있다. 
역으로 Verbena에 대한 참조카운트도 0이 되어야 하나 Vervain.see 는 Verbena를 참조하고 있다. 
결국 이러한 순환참조는 메모리 누수현상을 가져오게 된다. (IE 7에선 이러한 문제가 해결되었다.) 이러한 메모리 누수현상을 방지하기 위해서는 위와 같은 순환 참조를 형성하지 않거나, Vervain.see = null 혹은 Verbena.see = null 을 할당함으로써 참조카운트를 0으로 만들어 GC에서 메모리를 해제하게끔 만들어야 한다.






extjs에서 delete 연산자 사용확인

extjs-base.js파일을 까보았다. 객체를 소멸시킬때 사용하는 destroy함수에도 쓰이고 있었다.
01//IE일 경우 모든 이벤트 및 프로퍼티 삭제
02if(Ext.isIE) {
03    function fnCleanUp() {
04        var p = Function.prototype;
05        delete p.createSequence;
06        delete p.defer;
07        delete p.createDelegate;
08        delete p.createCallback;
09        delete p.createInterceptor;
10        window.detachEvent("onunload", fnCleanUp);
11    }
12    window.attachEvent("onunload", fnCleanUp);
13}//객체 destory 처리
14destroy : function(){
15    Ext.each(arguments, function(arg){
16        if(arg){
17            if(Ext.isArray(arg)){
18                this.destroy.apply(this, arg);
19            }else if(Ext.isFunction(arg.destroy)){
20                arg.destroy();
21            }else if(arg.dom){
22                arg.remove();
23            }
24         }
25    }, this);
26},
27destroyMembers : function(o, arg1, arg2, etc){
28    for(var i = 1, a = arguments, len = a.length; i < len; i++) {
29        Ext.destroy(o[a[i]]);
30        delete o[a[i]];
31    }
32},
33remove : function(o){
34    var index = this.indexOf(o);
35    if(index != -1){
36        this.splice(index, 1);
37    }
38    return this;
39}











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

offsetParent의 모든것!  (0) 2010.02.04
Javascript로된 Wiki Parser : Creole 1.0  (0) 2009.01.12
자바스크립트 가이드  (0) 2008.12.03
JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
Post by 넥스트리소프트 데꾸벅(techbug)
, |



국내환경에서 Ajax Library/Framework에서만 제공하는 기본 컴포넌트/위젯으로는 어려운 경우가 많은데 오늘 우연찮게 블로그 유입경로를 보다가 유난히 틀고정 그리드를 찾는 분들이 많아 포스팅한다.
Ext js 의 포럼글에서 우연찮게 찾아 북마킹해 놓은 것이 있어 공개한다. 아래 소스를 조금 수정하여 열고정, 행고정이 되는 그리드 컴포넌트를 만들어 보는것도 좋을듯....






Markup
<html xmlns="http: //www.w3.org/1999/xhtml" xml: lang="en" lang="en">
<head>
<link rel="stylesheet" type="text/css" href="../lib/ext-3.0.3/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="columnLock.css" />
<script type="text/javascript" src="../lib/ext-3.0.3/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../lib/ext-3.0.3/ext-all.js"></script>
<script type="text/javascript" src="columnLock.js"></script>


컬럼모델 config 파일은 다음과 같이 작성한다.
columns: [
    {id:'company',header: "Company", width: 120, sortable: true, dataIndex: 'company', locked: true},
    {header: "Price", width: 150, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
    {header: "Change", width: 150, sortable: true, renderer: change, dataIndex: 'change'},
    {header: "% Change", width: 150, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
    {header: "Last Updated", width: 150, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
],



Ext js 3.0x 이상

Ext js 2.1~ 2.2 버전

























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

Ext.Updater 깜빡거림 방지  (0) 2009.05.06
Extjs Core 3.0 Beta 릴리즈  (4) 2009.04.07
Examples Of ExtJS in Action  (2) 2009.03.31
extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Post by 넥스트리소프트 데꾸벅(techbug)
, |


ext-js 2.x 이상 버전에서 Updater를 이용시 깜빡거릴 경우가 있다.
이는 로딩인디케이터 때문인데 이럴 경우에는 다음과 같이 처리해주면 깜빡거리지 않는다.
아래 소스는 ext2.2 버전에서 테스트한 경우이다.

참조 URL : http://extjs.com/deploy/dev/docs/output/Ext.Updater.html

<html>
<head>
 <title>Ext JS 2.2 Samples</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<script type="text/javascript" src="../lib/ext-2.2/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../lib/ext-2.2/ext-all.js"></script>
<body>

<div id="specialDiv" style="border:solid 1px black;width:300px;height:300px"></div>
<button onclick="testFunc('start')">오토 시작</button>
<button onclick="testFunc('stop')">오토 끝</button><span id="techbugNo"></span><br />

<button onclick="testFunc1()">클릭할때마다 시작</button>


<script type="text/javascript">

 function testFunc(a){
  var mgr = new Ext.Updater("specialDiv");
  mgr.showLoadIndicator = false;  <-- 깜빡거림 방지
  if(a=='start'){
   mgr.startAutoRefresh(1, {
    url: "test1.html",
    method: "post",
    scripts: true,
    refreshNow : true,
    callback : function(){

    }
   });
  }else {
   //IE6이상버전에서 이상하게 작동하지 않음! 소스를 까봐야 겠음!
   mgr.stopAutoRefresh();
  }

  var techbug =0;
  mgr.on("update",function(){
   Ext.get("techbugNo").update(techbug++);
  });
 }

 function testFunc1(){   
  Ext.get("specialDiv").load({    //보통은 load로 처리하여 요기를 Updater로 사용하는것이 바람직
         url: "./test1.html",
         scripts: true,
         params: "",
         text: "Loading Foo..."
    });

 }
</script>
</body>
</html>


IE7, Chrome, FF3, Opera9.64 에서 테스트결과




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

Ext js 틀고정 그리드  (1) 2009.11.15
Extjs Core 3.0 Beta 릴리즈  (4) 2009.04.07
Examples Of ExtJS in Action  (2) 2009.03.31
extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Ajax Framework 분석

Scripter/AJAX / 2009. 5. 4. 11:45


ajax framework 선택시 Matt Raible씨가 그의 고객에게 했던 분석을 그의 블로그에 포스팅 하였다. 항상 ajax framework선택시 어떠한 프레임웍을 선택할지 고민이 많이 된다. 재미있는것은 ajaxian 포스팅에 보면 다트판을 그려놓고 선택한다는 농담이.. ㅡ.,ㅡ;

여러분도 이 다트판을 돌려 선택하나요? 사실 일전에 데꾸벅이 포스팅한 ajax framework고르는법 글도 사내 프로젝트에 사용할 프레임웍을 고민하다 만든 포스팅이였는데 같은 고민을 하는데도 질이 틀리네요.. 한번씩 참조하시는것도 괜찮을듯 싶어 포스팅합니다.

아래표는 Matt Raible씨가 모든 프레임웍으로 직접 프로토타입형태의 애플리케이션을 만들어보고 중요한 기준이 되는 특징들로 도식화한 것입니다.

참조 : ajaxian, indeed, google doc




아래 그래프는 결론적으로 모든 ajax framework들이 웹애플리케이션을 만들기에 손색이 없었다고 합니다. 거의 대부분의 프레임웍들이 이제는 거의 비슷하게 성능평가가 나오고 있습니다. 정말로 다트판을 던져 고른다는 농담이 그냥 나온 얘기는 아니였네요.

아래는 각각의 프레임워크를 동일 가중치를 주어 평가한 그래프입니다.



여기서 비교된 프레임워크는 가장 인기있는 프레임웍을 대상으로 했다고는 하지만 jquery는 빠져있네요.

 

indeed 에서 검색한 결과 위 그래프와 같이 jQuery가 짧은 기간에 급상승하고 있네요.. 여기서는 dojo, yui, gwt, extjs, jquery,prototype.js까지 비교하고 있습니다. qooxdoo나 mootools는 빠져있네요 ㅡ.,ㅡ;


대부분의 ajax framework들이 prgressive enhancement하다고 합니다.  점점더 많은 요구와 사용자 committer들, 다양한 플러그인들, 각 프레임웍마다의 기능들을 서로 보완해 나가고 있습니다.
얼만전 ext3.0 core beta에서와 마찬가지로 서로의 장단점들을 보완해 가며 서서히 하나의 완전한 프레임웤들로 자리매김 하고 있네요~

하나의 프레임웍만 제대로 익힌다면 다음 프레임웍은 떡 주물듯이~ 가능할까요?









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

CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |



ext3.0 베타버전이 릴리즈 됐다.
1.01a 버전부터 써오면서 라이센스정책을 쭈욱 지켜봤는데 이번 ext core 3.0는  MIT라이센스로 배포가 되었다.
즉 아무렇게 배포,수정이 가능하다는 얘기다.

현재 안정버전인 ext2.2나 그 이전 버전의 경우 core 파일이 obfuscating되어 있었던 반면 이번 ext3.0 Beta버전의 경우는 MIT정책에 따라 모든 소스가 친절하게도 주석까지 달려있어서 분석하기도 한층 쉬워졌다.
시간되시는 분들은 한번씩 까(?) 보시는것도 괜찮을듯... ㅎㅎ 데꾸벅 생각이였음돠~

 



 

JSONP 소스

Ext.ns('Ext.ux');

Ext.ux.JSONP = (function(){
    var _queue = [],
        _current = null,
        _nextRequest = function() {
            _current = null;
            if(_queue.length) {
                _current = _queue.shift();
       _current.script.src = _current.url + '?' + _current.params;
       document.getElementsByTagName('head')[0].appendChild(_current.script);
            }
        };

    return {
        request: function(url, o) {
            if(!url) {
                return;
            }
            var me = this;

            o.params = o.params || {};
            if(o.callbackKey) {
                o.params[o.callbackKey] = 'Ext.ux.JSONP.callback';
            }
            var params = Ext.urlEncode(o.params);

            var script = document.createElement('script');
        
 script.type = 'text/javascript';

            if(o.isRawJSON) {
                if(Ext.isIE) {
                    Ext.fly(script).on('readystatechange', function() {
                        if(script.readyState == 'complete') {
                            var data = script.innerHTML;
                            if(data.length) {
                                me.callback(Ext.decode(data));
                            }
                        }
                    });
                }
                else {
                     Ext.fly(script).on('load', function() {
                        var data = script.innerHTML;
                        if(data.length) {
                            me.callback(Ext.decode(data));
                        }
                    });
                }
            }

            _queue.push({
                url: url,
                script: script,
                callback: o.callback || function(){},
                scope: o.scope || window,
                params: params || null
            });

            if(!_current) {
                _nextRequest();
            }
        },

        callback: function(json) {
            _current.callback.apply(_current.scope, [json]);
            Ext.fly(_current.script).removeAllListeners();
            document.getElementsByTagName('head')[0].removeChild(_current.script);
            _nextRequest();
        }
    }
})();


 

 

 JSONP를 이용한 Flickr이미지 가져오기

<script>
Ext.onReady(function() {
    var resultTemplate = new Ext.Template.from('result-template');
   
    var updateResults = function(data) {
        Ext.fly('resultset').update('');
        Ext.each(data.items, function() {
            this.src = this.media.m;
            resultTemplate.append('resultset', this);
        });
    }
   
    Ext.get('search-form').on('submit', function(ev) {
        ev.preventDefault();
        Ext.ux.JSONP.request('http://api.flickr.com/services/feeds/photos_public.gne', {
            callbackKey: 'jsoncallback',
            params: {
                format: 'json',
                tags: Ext.fly('search-value').dom.value,
                tagmode: 'all',
                lang: 'en-us'                           
            },
            callback: updateResults
        });
        return false;
    });
})
</script>

 

<form id="search-form" action="#">
    <input type="text" value="ExtJS, Screenshot" id="search-value">
    <button type="submit" id="search-button">Search</button>           
</form>

<div id="resultset"></div>

<textarea id="result-template" style="display: none">
    <div class="result">
        <div class="header">
            <h4>{title}</h4>
            <h5>{author}</h5>
        </div>
        <img src="{src}" title="{title}">
    </div>
</textarea>



 

 jQuery를 이용한 Flickr이미지 가져오기

  $.getJSON(
    "http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
    function(data){
      $.each(data.items, function(i,item){
        $("<img/>").attr("src", item.media.m).attr("onclick","location.href="+item.media.link).appendTo("#result");
        $(document.body).unblock();
      });
    });

 

 참고로 Rhio.Kim's Extjs 2.2.1 Class Diagram

 

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

Ext js 틀고정 그리드  (1) 2009.11.15
Ext.Updater 깜빡거림 방지  (0) 2009.05.06
Examples Of ExtJS in Action  (2) 2009.03.31
extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Post by 넥스트리소프트 데꾸벅(techbug)
, |

jQuery로 프로젝트 수행중 extjs의 grid기능을 맘껏 쓸수 없을까 하다가 데꾸벅! jqGrid라는 아주 좋은 녀석을 발견하였다.

여러가지 테마를 사용할수 있지만 구미에 맞도록 CSS를 바꿔 보았다. extjs의 경우 UI자체를 수정하기가 어려운 반면 이녀석은 상당히 수월하게 바꿀수 있었다.

소스다운로드 :





HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>서학리조트 컨텐츠</title>
<link rel="stylesheet" type="text/css" href="style/css/contents.css" media="screen" />
<link rel="stylesheet" type="text/css" href="style/css/grid.css" media="screen" />
<script type="text/javascript" src="style/js/jquery.js" charset="utf-8"></script>
<script type="text/javascript" src="style/js/jquery.jqGrid.js" charset="utf-8"></script>
<script type="text/javascript" src="style/js/contents_04.js"  charset="utf-8"></script>
</head>
<body>
    <table class="tbl_tit02"><tr><td>담보 사항</td></tr></table>
    <div class="gridJ" style="width:780px"><table id="gridList" class="scroll" ></table></div>


    <div id="rsperror" title="서버에러나오는곳"></div>

 


    <input type="button" value="선택된Row 데이터가져오기" id="a1" />
    <input type="button" value="Row삭제" id="a2" />
    <input type="button" value="마지막Row추가" id="a3" />
    <input type="button" value="id가 13인 Row선택하기" id="a4" /><br />
    <input type="button" value="tax컬럼 숨기기" id="a5" />
    <input type="button" value="tax컬럼 나타내기" id="a6" />

    <input type="button" value="id가 13 에디팅하기" id="a7" />
    <input type="button" value="저장" id="a8" />
    <input type="button" value="취소" id="a9" /><br />

    <input type="button" value="header값 스타일변경" id="a10" />
    <input type="button" value="셀값 스타일변경" id="a11" />
    <input type="button" value="데이타초기화" id="a12" />
    <input type="button" value="그리드 width/height조절" id="a13" /><br />
   
    <input type="button" value="HTML 테이블을 그리드화하기" id="a14" />
</body>
</html>




js file

var lastsel3;

$(document).ready(function(){

    $("#gridList").jqGrid({
        url:'server.json',
        mtype: 'GET',
        datatype: "json",
        colNames:['인벤토리','날짜', '고객', '수량','세금','합계','비고'],
        colModel:[
         {name:'id',index:'id', width:100,align:"center",key:true},
         {name:'invdate',index:'invdate', width:120,editable:true,sorttype:"date",formatter:'date', formatoptions:{srcformat:"Y-m-d",newformat:"d-M-Y"}},
         {name:'name',index:'name asc, invdate', width:100,editable:true,editoptions:{size:"20",maxlength:"30"}},
         {name:'amount',index:'amount', width:100, align:"right",editable:true,editable:true,editrules:{number:true,minValue:100,maxValue:350},formatter:'currency',formatoptions:{thousandsSeparator:","}},
         {name:'tax',index:'tax', width:100, align:"right",editable:true,edittype:"select",editoptions:{value:"FE:FedEx;IN:InTime;TN:TNT;AR:ARAMEX"}},
         {name:'total',index:'total', width:100,align:"right",editable:true,edittype:"checkbox",editoptions: {value:"Yes:No"}},
         {name:'note',index:'note', width:150, sortable:false,editable:true,edittype:"textarea", editoptions:{rows:"1",cols:"20"}}
        ],
        rowNum:30,    <-- 가끔씩 이넘이 말썽부릴때가...  -1로 해놓으면 가져온 데이타 모두 뿌려줍니다
        height:278,
        sortname: 'id',
        sortorder: "desc",
        viewrecords: true,
        multiselect: false,//앞에 체크박스처리
        multikey: "",//multikey: "ctrlKey/shiftKey",
        editurl: "server.json",
        /*onSelectRow: function(id){  //row가 선택되었을 경우
      if(id && id!==lastsel3){
       jQuery('#gridList').restoreRow(lastsel3);
       jQuery('#gridList').editRow(id,true,pickdates);
       lastsel3=id;
      }
     },*/
        /*jsonReader: {//스크롤할때마다 가져오기
      repeatitems : true,
      cell:"",
      id: "0"
     },*/
       afterInsertRow: function(rowid, aData){
         switch (aData.name) {
          case 'Client 1':
           jQuery("#gridList").setCell(rowid,'total','',{color:'green'});
          break;
          case 'Client 2':
           jQuery("#gridList").setCell(rowid,'total','',{color:'red'});
          break;
          case 'Client 3':
           jQuery("#gridList").setCell(rowid,'total','',{color:'blue'});
          break;
          
         }
        },
        loadError : function(xhr,st,err) {
         jQuery("#rsperror").html("Type: "+st+"; Response: "+ xhr.status + " "+xhr.statusText);
        },
        imgpath: 'style/grid'
    });

    $("#a1").click( function(){
     var id = jQuery("#gridList").getGridParam('selrow');
     if (id) {
      var ret = jQuery("#gridList").getRowData(id);
      alert("id="+ret.id+" invdate="+ret.invdate+"...");
     } else { alert("Row를 선택해주세요");}
    });
    $("#a2").click( function(){
     var su=jQuery("#gridList").delRowData(12);
     if(su) alert("id가 12인 Row삭제"); else alert("이미 지워졌삼~");
    });
    $("#a3").click( function(){
     var datarow = {id:"99",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"};
     var su=$("#gridList").addRowData(99,datarow);
     if(su) alert("마지막Row추가 성공- 서버쪽 업데이트처리해주세요"); else alert("처리가 되지 않았음.");
    });

    $("#a4").click( function() {
        $("#gridList").resetSelection();
     $("#gridList").setSelection("13");
    });

    $("#a5").click( function() {
     $("#gridList").hideCol("tax");
    });
    $("#a6").click( function() {
     $("#gridList").showCol("tax");
    });

    jQuery("#a7").click( function() {
     jQuery("#gridList").editRow("13");
     this.disabled = 'true';
     jQuery("#a8,#a9").attr("disabled",false);
    });
    jQuery("#a8").click( function() {
     jQuery("#gridList").saveRow("13");
     jQuery("#a8,#a9").attr("disabled",true);
     jQuery("#a7").attr("disabled",false);
    });
    jQuery("#a9").click( function() {
     jQuery("#gridList").restoreRow("13");
     jQuery("#a8,#a9").attr("disabled",true);
     jQuery("#a7").attr("disabled",false);
    });

    jQuery("#a10").click( function() {
     $("#gridList").setLabel("tax","Tax Amt",{'font-weight': 'bold','font-style': 'italic'});
    });
   
    jQuery("#a11").click( function() {
     $("#gridList").setCell("12","tax","",{'font-weight': 'bold',color: 'red','text-align':'center'});
    });
   
    jQuery("#a12").click( function() {
     $("#gridList").clearGridData();
    });

    jQuery("#a13").click( function() {
     $("#gridList").setGridWidth(500);
     $("#gridList").setGridHeight(400);

    });

    jQuery("#a14").click(function (){
     tableToGrid("#htmlGrid");
     $("#htmlGrid").setGridWidth(810);
    });

});

 

function pickdates(id){
 alert(id);
}




server.json

{
    "page": "1",
    "total": 27,
    "records": "13",
    "rows": [
        {
            "id": "5",
            "cell": [
                "5",
                "2007-10-06",
                "Client 3",
                "200.00",
                "0.00",
                "200.00",
                null
            ]
        },
        {
            "id": "4",
            "cell": [
                "4",
                "2007-10-05",
                "Client 2",
                "120.00",
                "12.00",
                "134.00",
                null
            ]
        },
        {
            "id": "3",
            "cell": [
                "3",
                "2007-10-05",
                "Client 1",
                "50.00",
                "10.00",
                "60.00",
                null
            ]
        },
        {
            "id": "2",
            "cell": [
                "2",
                "2007-10-05",
                "Client 3",
                "100.00",
                "0.00",
                "100.00",
                "no tax"
            ]
        },
        {
            "id": "1",
            "cell": [
                "1",
                "2007-10-04",
                "Client 3",
                "150.00",
                "0.00",
                "150.00",
                "no tax"
            ]
        }
    ]
}



 소스사용은 자유이나 태클달지 맙시다 ^^  현재버전의 jqGrid와 틀립니다..

Post by 넥스트리소프트 데꾸벅(techbug)
, |



Manning에서 나온 Extjs In Action 의 샘플파일을 링크걸어놓은 사이트가 있어 올린다.
고환율때문에 사기는 그렇고 아직 MEAP(Manning Early Access Program)상태라 어둠의 경로로 얻으려고 했으나 좋은건 사주는게... ㅡ.,ㅡ;



General Apps

  • CRM : [ demo ]
  • Ajax Yahoo IM : [ demo ]
  • Mail Center (Webmail) : [ demo ]
  • File Sports : [ demo ]
  • SeeMe (using Google Maps) : [ demo ]
  • ExtPlorer : [ demo ]
  • Group Office : [ demo ] (demo/demo)

Development Tools

Books

  • ExtJS In Action [ link ]
  • Learning ExtJS [ link ]



--------[ 이건 그냥 덤으로.. ^^ ] -------------------------------------------------------
IE7   [ 4.00 Kb ]
astar   [ 4.00 Kb ]
clock   [ 4.00 Kb ]
cssdropshadow   [ 4.00 Kb ]
gzoutput   [ 4.00 Kb ]
loopsbench   [ 4.00 Kb ]
pwdsecurity   [ 4.00 Kb ]
tris   [ 4.00 Kb ]
DOM.js   [ 2.42 Kb ]
ColorFader.html   [ 1.20 Kb ]
ColorFader.js   [ 3.01 Kb ]
CssSwitcher.js   [ 1.35 Kb ]
LoadVars.html.old   [ 2.48 Kb ]
FolderTree.swf   [ 58.21 Kb ]
LoadVars.html   [ 3.41 Kb ]
LoadVars.js.old   [ 9.11 Kb ]
LoadVars.js   [ 7.92 Kb ]
Sqlitei_Example.html   [ 12.56 Kb ]
SimpleSWF.js   [ 911 bytes ]
TEST_PHP4_MYSQL.zip   [ 1.51 Kb ]
TEST_SqliteMPTTA.zip   [ 1.37 Kb ]
adsense.html   [ 9.34 Kb ]
bigdollar.html   [ 2.96 Kb ]
browser_agent.html   [ 899 bytes ]
cssswitcher.html   [ 3.48 Kb ]
fap.example.js   [ 3.89 Kb ]
fakedom.html   [ 2.05 Kb ]
testf5.html   [ 1.16 Kb ]
flashPlayerVersion.html   [ 694 bytes ]
flashPlayerVersion.js   [ 1.11 Kb ]
jsbenchmark.html   [ 9.19 Kb ]
multipletest.html   [ 1.66 Kb ]
myfolder_flash.xml   [ 26.70 Kb ]
onload.html   [ 501 bytes ]
onload2.html   [ 1.89 Kb ]
onload3.html   [ 542 bytes ]
onload4.html   [ 2.61 Kb ]
onmousewheel.html   [ 33.91 Kb ]
onmousewheel.js   [ 1.18 Kb ]
othertest.swf   [ 810 bytes ]
othertest.txt   [ 652 bytes ]
simpleswf.html   [ 277 bytes ]
simpleswf.zip   [ 20.68 Kb ]
singletest.html   [ 1.17 Kb ]
singletest.swf   [ 765 bytes ]
singletest.txt   [ 656 bytes ]
singletesthalfheight.txt   [ 654 bytes ]
wheelscroll.gif   [ 1.90 Kb ]
testf5.swf   [ 629 bytes ]
testf5.txt   [ 648 bytes ]

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

Ext.Updater 깜빡거림 방지  (0) 2009.05.06
Extjs Core 3.0 Beta 릴리즈  (4) 2009.04.07
extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Extjs 3.0 Roadmap  (10) 2008.11.12
Post by 넥스트리소프트 데꾸벅(techbug)
, |


기본적으로 grid의 View에 editor와 같이 다른 컴포넌트를 붙이기는 기존 소스보다 컴포넌트를 붙이는 소스가 더 많이 들어가는 경우가 있다.
데꾸벅의 경우도 프로젝트중 각각의 그리드 Row마다 progress bar를 붙여야 하는 경우가 생겼었는데..
포럼글을 뒤지다가 다음과 같은 좋은 사용자 플러그인이 있어 분석을 위해 소스를 까 보았다.



위 그림에서 보는바와 같이 그리드위에 버튼을 만들어 올릴때 new Ext.button이 아닌 new Ext.Action을 사용하여 사용자 플러그인을 사용한 소스이다.
소스 API  및 관련 포럼글 

HTML
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" type="text/css" href="./css/icons.css">
 <link rel="stylesheet" type="text/css" href="./css/Ext.ux.grid.RowActions.css">
    <link rel="stylesheet" type="text/css" href="../../ext/ext-2.2/resources/css/ext-all.css" />
    <script type="text/javascript" src="../../ext/ext-2.2/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="../../ext/ext-2.2/ext-all.js"></script>
 <script type="text/javascript" src="./js/Ext.ux.grid.RowActions.js"></script>
 <script type="text/javascript" src="./js/Ext.ux.Toast.js"></script>
 <script type="text/javascript" src="rowactions.js"></script>
</head>
<body></body>
</html>





JS

Ext.BLANK_IMAGE_URL = '';
Ext.ns('Example');
Example.version = '1.0';

Example.Grid = Ext.extend(Ext.grid.GridPanel, {

  initComponent:function() {

   // Create RowActions Plugin
   this.action = new Ext.ux.grid.RowActions({
    header:'Actions'
   ,autoWidth:true
   ,keepSelection:true
   ,actions:[{
     iconIndex:'action1'
    ,qtipIndex:'qtip1'
    ,iconCls:'icon-open'
    ,tooltip:'Open'
   },{
     iconCls:'icon-wrench'
    ,tooltip:'Configure'
    ,qtipIndex:'qtip2'
    ,iconIndex:'action2'
    ,hideIndex:'hide2'
   },{
     iconIndex:'action3'
    ,qtipIndex:'qtip3'
    ,iconCls:'icon-user'
    ,tooltip:'User'
    ,style:'background-color:yellow'
   }]
   ,groupActions:[{
     iconCls:'icon-del-table'
    ,qtip:'Remove Table'
   },{
     iconCls:'icon-add-table'
    ,qtip:'Add Table - with callback'
    ,callback:function(grid, records, action, groupId) {
     Ext.ux.Message.msg('Callback: icon-add-table'
         , 'Group: <b>{0}</b>, action: <b>{1}</b>, records: <b>{2}</b>'
         , groupId
         , action
         , records.length);
    }
   },{
     iconCls:'icon-graph'
    ,qtip:'View Graph'
    ,align:'left'
   }]
   ,callbacks:{
    'icon-plus':function(grid, record, action, row, col) {
     Ext.ux.Message.msg('Callback: icon-plus'
         , 'You have clicked row: <b>{0}</b>, action: <b>{0}</b>'
         , row
         , action);
    }
   }
  });

  this.action.on({
   action:function(grid, record, action, row, col) {
    Ext.ux.Message.msg('Event: action', '클릭한 Row: <b>{0}</b>, 액션: <b>{1}</b>'
        , row
        , action);
   }
   ,beforeaction:function() {
    Ext.ux.Message.msg('Event: beforeaction', '핸들러에서 false반환시 액션취소.');
   }
   ,beforegroupaction:function() {
    Ext.ux.Message.msg('Event: beforegroupaction', '핸들러에서 false반환시 액션취소');
   }
   ,groupaction:function(grid, records, action, groupId) {
    Ext.ux.Message.msg('Event: groupaction', 'Group: <b>{0}</b>, action: <b>{1}</b>, records: <b>{2}</b>', groupId, action, records.length);
   }
  });

  Ext.apply(this, {
    store:new Ext.data.Store({
    reader:new Ext.data.JsonReader({
      id:'company'
     ,totalProperty:'totalCount'
     ,root:'rows'
     ,fields:[
      {name: 'company'}
        ,{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
        ,{name: 'industry'}
        ,{name: 'desc'}
        ,{name: 'action1', type: 'string'}
        ,{name: 'action2', type: 'string'}
        ,{name: 'action3', type: 'string'}
        ,{name: 'qtip1', type: 'string'}
        ,{name: 'qtip2', type: 'string'}
        ,{name: 'qtip3', type: 'string'}
        ,{name: 'hide2', type: 'boolean'}
     ]
    })
    ,proxy:new Ext.data.HttpProxy({url:'jsonData.json'})
    ,sortInfo:{field:'company', direction:'ASC'}
    ,listeners:{
     load:{scope:this, fn:function() {
      this.getSelectionModel().selectFirstRow();
     }}
    }
   })
   ,columns:[
    {id:'company',header: "Company", width: 40, sortable: true, dataIndex: 'company'}
    ,{header: "Industry", width: 20, sortable: true, dataIndex: 'industry'}
    ,{header: "Last Updated", width: 20, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
    ,this.action
   ]
   ,plugins:[this.action]
   ,loadMask:true
   ,viewConfig:{forceFit:true}
  });

  this.bbar = new Ext.PagingToolbar({
    store:this.store
   ,displayInfo:true
   ,pageSize:10
  });

  Example.Grid.superclass.initComponent.apply(this, arguments);
 }


 ,onRender:function() {
  Example.Grid.superclass.onRender.apply(this, arguments);
  this.store.load({params:{start:0, limit:10}});
 }
});

//컴포넌트 등록
Ext.reg('techbuggrid', Example.Grid);

Ext.onReady(function() {
    Ext.QuickTips.init();

    var win = new Ext.Window({
         width:600
  ,minWidth:320
        ,id:'agwin'
        ,height:400
        ,layout:'fit'
        ,border:false
  ,plain:true
        ,closable:false
        ,title:"그리드 Row Action 처리하기"
  ,items:{xtype:'techbuggrid',id:'actiongrid'}
    });
    win.show();
});




사용자 플러그인.JS

Ext.ns('Ext.ux.grid');
if('function' !== typeof RegExp.escape) {
 RegExp.escape = function(s) {
  if('string' !== typeof s) {
   return s;
  }
  return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
 };
}

Ext.ux.grid.RowActions = function(config) {
 Ext.apply(this, config);
 this.addEvents(
   'beforeaction'
  ,'action'
  ,'beforegroupaction'
  ,'groupaction'
 );
 Ext.ux.grid.RowActions.superclass.constructor.call(this);
};

Ext.extend(Ext.ux.grid.RowActions, Ext.util.Observable, {
  actionEvent:'click'
 ,autoWidth:true
 ,dataIndex:''
 ,header:''
 ,keepSelection:false
 ,menuDisabled:true
 ,sortable:false
 ,tplGroup:
   '<tpl for="actions">'
  +'<div class="ux-grow-action-item<tpl if="\'right\'===align"> ux-action-right</tpl> '
  +'{cls}" style="{style}" qtip="{qtip}">{text}</div>'
  +'</tpl>'
 ,tplRow:
   '<div class="ux-row-action">'
  +'<tpl for="actions">'
  +'<div class="ux-row-action-item {cls} <tpl if="text">'
  +'ux-row-action-text</tpl>" style="{hide}{style}" qtip="{qtip}">'
  +'<tpl if="text"><span qtip="{qtip}">{text}</span></tpl></div>'
  +'</tpl>'
  +'</div>'
 ,hideMode:'visiblity'
 ,widthIntercept:4
 ,widthSlope:21
 ,init:function(grid) {
  this.grid = grid;
  if(!this.tpl) {
   this.tpl = this.processActions(this.actions);
  }
  if(this.autoWidth) {
   this.width =  this.widthSlope * this.actions.length + this.widthIntercept;
   this.fixed = true;
  }
  var view = grid.getView();
  var cfg = {scope:this};
  cfg[this.actionEvent] = this.onClick;
  grid.afterRender = grid.afterRender.createSequence(function() {
   view.mainBody.on(cfg);
   grid.on('destroy', this.purgeListeners, this);
  }, this);

  if(!this.renderer) {
   this.renderer = function(value, cell, record, row, col, store) {
    cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell';
    return this.tpl.apply(this.getData(value, cell, record, row, col, store));
   }.createDelegate(this);
  }

  if(view.groupTextTpl && this.groupActions) {
   view.interceptMouse = view.interceptMouse.createInterceptor(function(e) {
    if(e.getTarget('.ux-grow-action-item')) {
     return false;
    }
   });
   view.groupTextTpl =
     '<div class="ux-grow-action-text">' + view.groupTextTpl +'</div>'
    +this.processActions(this.groupActions, this.tplGroup).apply()
   ;
  }

  if(true === this.keepSelection) {
   grid.processEvent = grid.processEvent.createInterceptor(function(name, e) {
    if('mousedown' === name) {
     return !this.getAction(e);
    }
   }, this);
  }
  
 }
 ,getData:function(value, cell, record, row, col, store) {
  return record.data || {};
 }

 ,processActions:function(actions, template) {
  var acts = [];

  Ext.each(actions, function(a, i) {
   if(a.iconCls && 'function' === typeof (a.callback || a.cb)) {
    this.callbacks = this.callbacks || {};
    this.callbacks[a.iconCls] = a.callback || a.cb;
   }

   var o = {
     cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
    ,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
    ,text:a.textIndex ? '{' + a.textIndex + '}' : (a.text ? a.text : '')
    ,hide:a.hideIndex
     ? '<tpl if="' + a.hideIndex + '">'
      + ('display' === this.hideMode ? 'display:none' :'visibility:hidden') + ';</tpl>'
     : (a.hide ? ('display' === this.hideMode ? 'display:none' :'visibility:hidden;') : '')
    ,align:a.align || 'right'
    ,style:a.style ? a.style : ''
   };
   acts.push(o);

  }, this);

  var xt = new Ext.XTemplate(template || this.tplRow);
  return new Ext.XTemplate(xt.apply({actions:acts}));

 }
 ,getAction:function(e) {
  var action = false;
  var t = e.getTarget('.ux-row-action-item');
  if(t) {
   action = t.className.replace(/ux-row-action-item /, '');
   if(action) {
    action = action.replace(/ ux-row-action-text/, '');
    action = action.trim();
   }
  }
  return action;
 }
 ,onClick:function(e, target) {

  var view = this.grid.getView();

  var row = e.getTarget('.x-grid3-row');
  var col = view.findCellIndex(target.parentNode.parentNode);
  var action = this.getAction(e);

  if(false !== row && false !== col && false !== action) {
   var record = this.grid.store.getAt(row.rowIndex);

   if(this.callbacks && 'function' === typeof this.callbacks[action]) {
    this.callbacks[action](this.grid, record, action, row.rowIndex, col);
   }

   if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, row.rowIndex, col)) {
    return;
   }
   else if(true !== this.eventsSuspended) {
    this.fireEvent('action', this.grid, record, action, row.rowIndex, col);
   }

  }

  t = e.getTarget('.ux-grow-action-item');
  if(t) {
   var group = view.findGroup(target);
   var groupId = group ? group.id.replace(/ext-gen[0-9]+-gp-/, '') : null;

   var records;
   if(groupId) {
    var re = new RegExp(RegExp.escape(groupId));
    records = this.grid.store.queryBy(function(r) {
     return r._groupId.match(re);
    });
    records = records ? records.items : [];
   }
   action = t.className.replace(/ux-grow-action-item (ux-action-right )*/, '');

   if('function' === typeof this.callbacks[action]) {
    this.callbacks[action](this.grid, records, action, groupId);
   }

   if(true !== this.eventsSuspended && false === this.fireEvent('beforegroupaction', this.grid, records, action, groupId)) {
    return false;
   }
   this.fireEvent('groupaction', this.grid, records, action, groupId);
  }
 }

});

Ext.reg('rowactions', Ext.ux.grid.RowActions);




요기 괜찮은 사용자 플러그인도 함 보세요





















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

Extjs Core 3.0 Beta 릴리즈  (4) 2009.04.07
Examples Of ExtJS in Action  (2) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Extjs 3.0 Roadmap  (10) 2008.11.12
Ext2.2 Release  (11) 2008.08.07
Post by 넥스트리소프트 데꾸벅(techbug)
, |

얼마전 부터 방명록에 몇몇 분들이 iframe 안에서 grid panel이 동작하지 않는다고 하여 포럼글을 찾아보던중 다음과 같은 버그가 존재하였다.

FF3, Opera, IE6에서 document를  load시 load타임이 맞지 않아서 생기는 버그였다. 기존 2.1버전에서 2.2 버전으로 업그레이드 하면서 생긴 버그이다. 만일  ext의 프리미엄 멤버라면 svn을 통해서 해당 내용들을 모두 업데이트 받았겠지만 가난한 나같은 개발자들은 그러지 못한 관계로 포스팅한다. 

먼저 가장 최근의 ext-2.2 버전을 다운로드한다. ( 같은 버전이라도 버그패치되는 대로 패치하기 때문에 매일매일 다를수 있다. 다운로드 받거든 이미 받았던 버전과 용량체크를 해 보면 바로 알수 있다.. ㅡ"ㅡ; )

ext-all-debug.js 를 이용한다면 다음과 같이 3개 메쏘드를 바꿔준다.

fireDocReady(),  initDocReady(), onDocumentRead()

1580줄 fireDocReady를 다음과 같이 바꿔준다.

 var fireDocReady = function(){
        docReadyState = true;
        if(Ext.isGecko) {
             document.removeEventListener("DOMContentLoaded", fireDocReady, false);
        }

        if(docReadyProcId){
             clearInterval(docReadyProcId);
             docReadyProcId = null;
        }

        if(docReadyEvent && !Ext.isReady){
            Ext.isReady = true;
            docReadyEvent.fire();
            docReadyEvent.clearListeners();
        }
    };


1597줄의 initDocReady를 다음과 같이 바꿔준다.

    var initDocReady = function(){
        docReadyEvent = new Ext.util.Event();

        if(Ext.isReady){ return;}

        E.on(window, 'load', fireDocReady);

        if(Ext.isGecko) {
            document.addEventListener('DOMContentLoaded', fireDocReady, false);
        }
        else if(Ext.isIE){
                                 
            document.onreadystatechange = function(){
                if(document.readyState == 'complete'){
                     document.onreadystatechange = null;
                     fireDocReady();
                }
            };
                                
             if(window == top){   //Use the default readystatechange/onload as primary detection mechanism for frames 
                   var doScrollChk = function(){
                    try{
                       document.documentElement.doScroll('left');
                       Ext.isReady || fireDocReady();
                   }catch(e){
                       Ext.isReady || setTimeout(doScrollChk ,0);
                   }
                  };
                  doScrollChk();
               }
          
        } else if(Ext.isOpera){
              document.addEventListener( 'DOMContentLoaded', function () {
                    if (Ext.isReady) return;
                    for (var i = 0; i < document.styleSheets.length; i++)
                          if (document.styleSheets[i].disabled) {
                                  setTimeout( arguments.callee, 0 );
                                  return;
                          }
                           Ext.isReady || fireDocReady();
                           document.removeEventListener("DOMContentLoaded", arguments.callee, false);

                }, false);
        }
        else if(Ext.isSafari){
           var re = /complete|loaded/i;
            docReadyProcId = setInterval(function(){
                if(re.test(document.readyState) ) {
                    Ext.isReady || fireDocReady();
                 }
            }, 10);
        }
    };


1743줄의 onDocumentReady를 다음과 같이 바꿔준다.

     onDocumentReady : function(fn, scope, options){
            if(!docReadyEvent){
                initDocReady();
            }
            if(docReadyState || Ext.isReady){
                options || (options = {});
                fn.defer(options.delay||1, scope);    ----------> 0을 1로 바꿔주면 된다.  defer(1)과 같은 효과
            }else{
                docReadyEvent.addListener(fn, scope, options);
            }
        },




직접 ext-all-debug.js를 수정하였을 경우는 다음과 같이 하면 되고 각각의 컴포넌트들을 수정하려면 아래 eventManager관련 파일을 찾아 덮어쓴후 다시 빌드하여 ext-all.js로 만들어 쓰면 된다. 





사족 : 조만간 올초에 3.0 이 나올텐데 다시 업그레이드 되겠죠~ 이클립스용 ext designer와 standalone designer도 벌써 3.0 버전용이 나와 있더군요.. ㅡ.,ㅡ;











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

Examples Of ExtJS in Action  (2) 2009.03.31
extjs RowAction 붙이기  (1) 2009.03.31
Extjs 3.0 Roadmap  (10) 2008.11.12
Ext2.2 Release  (11) 2008.08.07
ExtJS 2.1 릴리즈  (4) 2008.04.22
Post by 넥스트리소프트 데꾸벅(techbug)
, |

ExtJS의 border-layout 과 같은 효과를 내는 jQuery플러그인이 나와서 소개합니다.

UI.Layout 플러그인 제작자이며 초기프로젝트 제한자인 Kevin Dalman씨 또한 extJS border-layout을 보고 감명받아 jQuery Plug-in을 만들었다고 하며, 간단한 헤더레이아웃이나 사이드에서 부터 툴바, 메뉴, 헬프패널, status bars, sub-forms등을 가지고 있는 복잡한 애플리케이션까지 원하는 어떤 레이아웃도 만들수 있다고 하네요 ^^;
라이센스제한은 듀얼라이센스로  GPL과 MIT 라이센스를 따르고 있습니다. 아무렇게나 써도 된다는 것이네요 다만 제작자들이 원하는것은 사용해보고 괜찮으면 jQuery 플러그인사이트에서 "rate it" 투표나 해달라는거네요.. ㅡ"ㅡ;

jQuery stable version : 1.2.6
jQuery UI 1.5.2
UI.Layout Plugin 1.2.0

extjs의 기본 border-layout


솔직히 jQuery의 레이아웃매니저가 사용하기는 더 어렵게 느껴지는 이유는 무얼까요?




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

jQuery Grid Plugin : jqGrid  (16) 2009.04.06
jQuery pageSlide  (0) 2009.01.12
jQuery 링크에 걸린 파일 사이즈 자동으로 알아내기  (1) 2008.09.03
jQuery Loading바 구현하기  (2) 2008.07.08
Post by 넥스트리소프트 데꾸벅(techbug)
, |

jQuery pageSlide

Scripter/jQuery / 2009. 1. 12. 13:03

Web2.0이 대두되면서 기존의 레거시한 웹UI보다 조금은 다이나믹하고 다양한 Layout들이 나오기 시작했다.
탑-레프트-컨텐츠-푸터 방식의 eye-tracking이 중시되던 UI에서 기술적으로 많이 향상되기 시작하면서 다양한 메뉴와 UI화면들이 표현되기 시작했다.

마우스에 의한 각각의 메뉴표현 방식 : Ubiquity Pane in Mouse 

구현기술향상에 의한 다양한 UI를  소화하기 위해서 위의 그림과 같이 여러가지 메뉴가 만들어 졌다.
그중에 Firefox Mobile의 메뉴를 jQuery Plugin으로 구현한 소스가 있어 소개한다.
(위 메뉴중 2줄 좌측 Tray Menu)
복잡하고 표현하기 힘든 UI의 경우 잠시 페이지를 숨겼다 오른쪽 panel에서 보여줄때 상당히 유용할듯.(extjs의 panel collapse, expand와 기능이 비슷하나 jQuery pageSlide의 경우는 a 링크에 표현된 페이지를 오른쪽에서 보여준다는 면에서 다르다)

[사용법]
 <script type="text/javascript">   $(document).ready(function() {
       $('a').pageSlide({
           width: "350px"
       });
   });
 </script>

Demo-Source, Project Homepage



Post by 넥스트리소프트 데꾸벅(techbug)
, |


Beautifully Documented Code를 보다가 일전에 Wiki 관련 Parser에 대해서 관심을 가지게 되었다.
그때는 Java로 된, 혹은 Server side 언어쪽만 찾아봤었는데 이렇게 자바스크립트로 된 파서를 찾아서 꿈쳐둔다.

웹페이지를 퍼블리싱할때 디자인적인 측면이 많이들 강조된다. 타이포나 문장에 대한 배치등 그리드에 대한것도 전적으로 디자이너의 몫으로 넘기기 쉽다. 그러나 사실 이러한 문서 작업은 디자이너들 보다는 퍼블리셔나 산출물작업의 양만으로 따진다면 서버 프로그래머들의 작업량이 더 많다.

사내프로젝트를 진행하다가 든 생각중 하나가 실제 개발자들이 개발에 대한 지식이나 업무에 대해서 공유할수 있는 사이트를 만들어 보고 싶었다. 이슈(Trac, Bugzilla) 소스의 공유(CVS,SVN), 혹은 설계문서나 산출물들을 한곳에서 문서로 관리할수 잇다면 얼마나 좋을까 생각한것이 Wiki였다.  
Wiki를 만든다는것은 엄청난 자원의 손실이며 사실 마음에 드는 Wiki또한 없었다.  

뭐 잡설은 집어치고,  이전 레이텍(Latex)이나 HanTex과 같이 화면상에 문서를 구성할수 있게 실시간으로 스크립트를 이용해서 만들어 준다면 얼마나 좋을까? 바로 Creole Parser가 이러한 것을 가능하게 해주었다.
 





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

offsetParent의 모든것!  (0) 2010.02.04
자바스크립트 delete 연산자에 대한 고찰  (8) 2009.11.18
자바스크립트 가이드  (0) 2008.12.03
JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
Post by 넥스트리소프트 데꾸벅(techbug)
, |


예전 블로그에 포스팅한 글이 우연히 스쿨[PHPSchool]에서 보다가 발견했다.
그때에 2007년이였으니 Prototype 1.5.0 에서 적용되던 것이였는데...... 

extjs에서 크로스도메인 처리하던 localXHR.js와 같은 기능을 하는것이다.
localXHR에서 사용된 개념과 비슷하게 여기에서도 동적으로 <script>태그로 생성한다. 시간나실때 짬짬히 뜯어보시는것도... 괜찮을듯 하다.

    this.node = document.createElement('SCRIPT');
    this.node.type = 'text/javascript';
    this.node.src = arg.url;  <---- 

사용법은 간단한다. prototype.js 를 import한뒤 아래에 transport.js를 import해서 기존 Prototype.js의 Ajax.Request 클래스를 상속받아 쓰면 된다.

Prototype.js에서 사용할때와 같이

new Ajax.Request('myurl', {
    method: 'GET',
    crossSite: true,
    parameters: Form.serialize(obj),
    onLoading: function() {
        //things to do at the start
    },
    onSuccess: function(transport) {
        //things to do when everything goes well
    },
    onFailure: function(transport) {
        //things to do when we encounter a failure
    }
});

Prototype.js 1.6.0 버전에서 쓰려면 기존 transport.js 하단 상속부분을 다음과 같이 수정해 주면 된다.

[1.5.0 버전]
Ajax.Request.prototype = Object.extend(Ajax.Request.prototype,{
    initialize: function(url, options) {
        this.setOptions(options);
        this.transport = (!this.options.crossSite) ? Ajax.getTransport() : new scriptTransport;
        this.request(url);
        }   
});

[1.6.0 버전]
Ajax.Request.prototype = Object.extend(Ajax.Request.prototype, {
    initialize: function(url, options) {
        this.options = {
          method:       'post',
          asynchronous: true,
          contentType:  'application/x-www-form-urlencoded',
          encoding:     'UTF-8',
          parameters:   '',
          evalJSON:     true,
          evalJS:       true
        };
        Object.extend(this.options, options || { });

        this.options.method = this.options.method.toLowerCase();

        if (Object.isString(this.options.parameters))
          this.options.parameters = this.options.parameters.toQueryParams();
        else if (Object.isHash(this.options.parameters))
          this.options.parameters = this.options.parameters.toObject();
       
        this.transport = (!this.options.crossSite) ? Ajax.getTransport() : new scriptTransport;
        this.options.asynchronous = (!this.options.crossSite) ? this.options.asynchronous : false;
        //turns of the timed onLoad executer
        this.transport.respondToReadyState = this.respondToReadyState.bind(this);
        this.request(url);
        }
});





 
Post by 넥스트리소프트 데꾸벅(techbug)
, |

 

스크립트 관련 레퍼런스

 

JavaScript 시작

  1. <script type="text/javascript" defer="defer">
  2. //<![CDATA[
    1. // defer="defer" 옵션을 지정하면, JavaScript에서 문서 내용을 생성하지 않는다는 의미이며,
    2. // 이 때문에, 브라우져는 자바스크립트를 로딩하기 전에 먼저 웹 페이지를 먼저 처리한다.
    3. // 이 옵션은 IE에서만 작동한다.(2008년 현재)
  3. //]]>
  4. </script>
  • JavaScript의 위치
    • <body> : 페이지를 로드할 때 동적으로 웹페이지의 컨텐트를 생성하는 경우
    • <head> : 함수 안에서 정의하고 page 이벤트에 사용되는 자바스크립트 코드, body 태그가 읽히기 전에 미리 읽히게 된다.
  • JavaScript 선언시 HTML 주석 <!-- -->으로 감싸지 말것. XHTML 규칙에 어긋난다.

 

var 키워드와 영역(scope)

  • 함수 내에서 var 키워드로 변수를 선언하면 함수내 영역에 종속된다.
  • 함수 내에서 var 키워드 없이 변수를 선언하면 전역 영역에 종속된다.
  • 함수 밖 전역 영역에서는 var 키워드 사용 유무에 관계 없이 무조건 전역 영역에 종속된다.
  • undefined : 변수를 선언해 놓고서, 값을 지정하지 않은 상태.

 

좋은 코딩 습관

  • 함수 안의 변수는 항상 "var"로 선언하라.
  • 자바스크립트의 유무와 관계없이 싸이트의 정보를 모두 사용할 수 있도록 하라(JavaScript를 끄고 코딩하기).
  • JavaScript 접근성 튜토리얼(영문)
  • noscript : JavaScript가 작동안하면 출력되는 정적인 코드를 <noscript>태그 안에 넣는다. JavaScript가 작동할 경우에는 무시된다.
  • Mozilla JavaScrip Style Guide
  • &&, || 연산시 CPU를 더 적게 사용하는 식을 왼편에 두면 효율성이 높아진다.
  • 인라인 이벤트 등록 방식은 되도록 피하고, Javascript를 이용한 기본 이벤트 등록 방식을 사용하라. (object.onevent=func)

 

형(Type)

  • object.toString()
  • object.toBoolean() : "",숫자 0, NaN, Null, Undefined 일경우에는 false 나머지는 모두 true
  • parseInt(string)
  • parseFloat(string)
  • Number(string)
  • isNaN(변수) : 변수가 숫자가 아니면 true
  • const CURRENT_MONTH = 3.4;  // CURRENT_MONTH 는 상수가 되었다.
  • 정수/정수의 결과가 실수가 될수도 있다. 3/2 == 1.5

 

연산자

  • "3.0" == 3.0 : true - 동등 연산자, ==, !=
  • "3.0" === 3.0 : false - 일치연산자, ===, !==

 

구문

  • for (varName in 연관배열) { statements...; }
  • if (value in [연관배열|배열|객체])

 

내장 클래스

  • Number 클래스
  • String 클래스 : 문자열("...")은 자동으로 String 객체로 변환된다.
  • Date 클래스 : 날짜 연산.
    • Date.now : 현재 시각
    • Date.parse : Unix Time millisecond
    • 웹 사용자에게 보여줄 시간은 Local 시간을, 국제적으로 시간 비교가 필요할 때는 UTC 사용.
  • Math 클래스 : 대부분 정적 프라퍼티, 정적 메소드
    • Math.random() : 0~1 사이의 난수
  • Array 배열
    • array.slice(2,4) : 지정 범위를 배열로 반환
    • array.concat(배열) : 배열 연결
    • array.join("구분자") : 배열의 값들을 "구분자"로 연결한 하나의 문자열로 변환
    • 콜백 함수관련 메소드들 : 이 메소드들은 FireFox 1.5 이상에서만 지원된다. IE 7 이하, Opera 9.2 이하에서는 지원되지 않는다. prototype.js 를 사용하면 어느 정도 지원해 준다.
      • 각 콜백 함수는 element, index, array 를 인자로 받는다.
      • array.filter(func) : 특정 조건을 만족하지 못하는 원소 제거. 조건 체크는 함수 인자가.
      • array.forEach(func) : 배열 원소를 함수 인자에서 처리
      • array.every(func) : 배열의 모든 원소에 대해 false 값이 반환될 때까지 콜백 함수 실행
      • array.map(func) : 배열의 모든 원소에 대해 콜백 함수를 실행하고 결과를 배열로 리턴
      • array.some(func) : 배열의 모든 원소에 대해 true 값이 반환될 때까지 콜백 함수 실행
    • 배열인덱스 사용하기
  1. var langs = new Array('a', 'b', 'c');
  2. for (var itemIndex in langs) {
  3. alert(langs[itemIndex];
  4. }
  • 연관배열 : HashMap
  1. var assocArray = new Object();
  2. assocArray["one"] = "one";
  3. assocArray["two"] = "two";

 

정규표현식

  • JavaScript 정규표현식 Tutorial
  • new RegExp('+s')/+s/ 와 같다.
  • /pattern/i,new RegExp('pattern', 'i') : 대소문자를 구분하지 않는다.
    • i : 대소문자 구분 안함
    • g : 전역 매칭. 마지막 매칭 위치를 기억하고 있다가, 또 다시 regexp.exec(string)이 호출되면 그 이후부터 검색한다.
    • m : 여러줄 매칭
  • 특수문자
    • * : 0회 이상 반복
    • + : 1회 이상 반복
    • ? : 0 또는 1개 문자
    • . : 1개 문자

 

  1. // 정규표현식 replace
  2. var regExp = /\s\*/g;
  3. var str = "This *is *a *test *string";
  4. var resultString = str.replace(regExp, '-');

 

 

  1. // 정규표현식 매칭되는 문자열 찾기
  2. var regExp = /:\D*\s\d+\sd+/;
  3. var str = "This is a date: March 12 2005";
  4. var resultString = str.match(regExp);

 

  • 괄호
    • 매칭(String.match(regex)) 결과를 ["전체매칭문자열","첫번째괄호에들어가는문자열", "두번째", ...] 형태의 배열로 리턴한다.
    • 각 괄호에 매칭된 문자열을 $1, $2, .. 로 나타내어 대체가 가능하다. str.replace(regExp, "$2-$1")
  • /s{2}/ : 문자 s가 2회 나온다.

 

함수

함수도 객체다.

  • 익명함수 : 읽을때마다 파싱
    • new func = new Function("인자1", "인자2", ..., "함수몸체");
  • 함수 리터럴 : 단 한번만 파싱
  1. var func = function (인자들) {
  2.     statements;
  3. }
  • 클로저(closure) : 다른 함수 내에서 내부 객체로 생성된 함수 리터럴을 반환하여 호출 프로그램에서 이를 변수로 배정한 것
  • 함수명.length : 인자의 개수
    • arguments[i] : 각 인자의 값을 순서대로 가지고 있다.

 

이벤트

  • QuriksMode JavaScript
  • IE 에서는 window.event 에 이벤트 객체가 저장된다.
  • FireFox/Mozilla/Opera 등은 이벤트 함수에 event 객체가 인자로 전달된다.
  1. // 브라우저에 무관한 이벤트 처리
  2. function eventProc(nsEvent) {
    1. var theEvent = nsEvent ? nsEvent : window.event;
    2. // 이벤트 처리..
  3. }
  • 이벤트 함수 내의 this는 이벤트를 발생시킨 객체를 의미한다.
  • DOM 2 : 한 이벤트에서 여러개의 함수 호출 가능
    • object.addEventListener('event', function, 이벤트처리방식boolean);
    • object.removeEventListener()
    • object.dispatchEvent()
    • IE 에서는 이를 지원하지 않는다.
      • object.attachEvent, object.detachEvent() 사용.
      • 각 이벤트가 메모리를 차지하므로, window.onunload 이벤트를 받아서 attach된 이벤트들을 모두 제거해줘야만 한다.
  • HTML의 인라인 이벤트 메소드들은 FireFox의 경우 event 라는 이름으로 변수를 설정해야 이벤트 객체가 전달된다.

    • 이벤트 객체 체크방식은 기존과 마찬가지로 IE와 FF 방식을 분리해서 하는 방식을 사용하면 된다.
    1. <img src="test.jpg" onclick="testinlinemethod(event, arg1, arg2, ...);" />

 

도메인

document.domain = "somecompany.com";

이후부터는 자바스크립트가 *.somecompany.com와 소통 가능.

 

BOM

  • window.open
  • self.method() : 현재 윈도우 자신에게 메소드 수행
  • opener.method() : 현재 윈도우를 열어준 부모 윈도우 참조
  • screen : 화면 정보 availTop, availLeft, availWidth, availHeight, colorDepth, pixelDepth

 

Tips



함수 function

 자바스크립트는 숫자, 문자열, 부울값등 기본적은 데이터형을 지원할 뿐만 아니라,
 기본 데이터 집합체인 객체(object)와 배열(array)등 두가지 복합 데이터 형(complex data type)도 지원한다.
 대부분의 언어와 달리 자바스크립트는 데이터 형으로서 함수도 지원하여 자바스크립트 프로그램을 문자열처럼 취급할 수 있게 해준다. 
 
함수가 변수, 배열, 객체에 저장될 수 있고, 함수가 인자로 다른 함수에 전달 될 수 있다.
 함수가 객체의 프로퍼티에 할당되는 경우에 이 함수를 객체의 메소드라고 한다.
 자바스크립트의 함수는 구문일 뿐만 아니라 데이터이기도 하다
 
 - 함수 호출 연산자 () : 고정된 피연산자 개수를 가지고 있지 않다. 
 
 - 자바스크립트에서 문장은 프로그램에서 동적인 동작을 수행하는 반면 함수 정의는 프로그램의 정적인 구조를 나타낸다.
   문장이 실행 시간(runtime)에 수행되는 반면 
함수는 자바스크립트 코드가 파싱되거나 컴파일될 때 정의된다.
   자바스크립트 파서가 함수 정의를 만나면 그것은 함수의 본문을 구성하는 문장을 파싱하고 저장한다. (실행하지는 않는다.)
   그리고 나서 그 함수를 나타내는 이름과 같은 이름의 프로퍼티를 정의한다.

    alert(f(4));      // 16을 출력한다. f()는 그것이 정의되기 전에 호출쇨 수 있다.

                        // 단, 이벤트 핸드러에서 함수를 호출할 경우에는 미리정의 되어 있어야 한다.
    var f=0;          // f의 프로퍼티를 재설정한다.
    function f(x){  // 이 문장은 위 두 행이 실행되기 전에 함수 f를 정의한다.
       return x*x;
    }
   alert(f);           // 0을 출력한다. f()는 변수 f에 의해 재설정 된다.

위와 같은 결과가 나온 이유는 함수 정의가 변수 정의와 다른 시점에 발생하기 때문이다.


 -  a.sort(function(a,b){return a-b;});      // 함수를 정의하고 그것을 다른 함수에 전달
    var s = (function(x){return x*x;})(10); // 정의와 호출을 동시에


 arguments 객체 - 함수의 인자

arguments는 arguments 객체를 참조하는 호출 객체의 특정한 프로퍼티이다.
- arguments[] 배열의 요소는 함수에 전달된 인자의 값을 가지고 있다.
- 인자에 이름이 없더라도 해당 인자의 값에 접근할 수 있게 해준다. ex) arguments[0]


 콜백 함수

메소드 실행 시에 자동으로 호출되는 함수가 있다. 그러한 메소드로 filter, forEach, every, map, some이 있으며,

이때 사용되는 함수는 함수 리터럴로 일반적으로 콜백 함수라고 한다.


 함수 클로저

자바스크립트 프로그램에서는 새로운 영역을 생성할 때마다 이를 둘러싸기 위한 '연관 영역 버블'이 생성된다.

이는 함수에도 적용되며, 해당 함수는 자신의 영역에서 동작하게 된다.

function outFunc(base){   // 바깥쪽 함수

      var punc = "!";

      function inFunc(ext){   // 안쪽 함수

             return base + ext + punc;

      }

      return returnString;

}

일반적으로 함수가 종료되면, 그 영역에서 해제된다. 왜냐하면 더 이상 필요치 않기 때문이다.

하지만 안쪽 함수의 경우엔 이것이 바깥쪽 프로그램에 반환되고 외부 변수에 할당된다.

즉, 안쪽 함수의 영역이 바깥쪽 함수의 영역에 추가되고, 결국 호출 프로그램에 추가되는 것이다.

따라서 함수 리터럴 및 바깥쪽 함수의 인자와 변수를 유지하게 된다. 다른 함수 내에서 내부 객체로 생성된

함수 리터럴을 반환하여 호출 프로그램에서 이를 변수로 배정한 것을 자바스크립트에서는 클로저(closure)라고 한다.

즉, 함수가 동자하는 데 필요한 데이터 영역이 확장되는 것이다.

 

 

 

 callee 프로퍼티 
현재 실행되고 있는 함수를 참조, 이름이 없는 함수가 자기 자신을 호출할 때 유용하다.

 function(x){
    if(x>1) return x * arguments.callee(x-1);
    return x;
  }




 caller 프로퍼티 
현재 호출된 함수를 참조하는 것이 아니라 현재 호출된 함수의 arguments 객체를 참조한다.
 

 apply() 메소드 
함수를 마치 다른 객체의 메소드처럼 호출할 수 있게 해준다.




 객체 object

이름이 지정된 데이터의 여러 가지 부분을 포함하는 데이터 구조로, 이 데이터를 처리하기 위한 여러가지 메소드도 포함한다.
객체는 관련된 데이터 값과 메소드를 사용하기 쉽게 단일 패키지 안에 모아두기 때문에 코드의 모듈성(modularity)과 
재사용(reusability)을 증가시켜서 전반적으로 쉽게 프로그램을 작성할 수 있다.


- 객체 == 이름과 값을 가진 '프로퍼티(property)'의 집합
- 자바스크립트에서 객체는 임의의 수만큼 프로퍼티를 가질 수 있고, 객체에 동적으로 프로퍼티를 추가할 수 있다.


 

 객체생성 연산자 new

 - 객체 생성 var obj = new Object();
(delete : 객체의 프로퍼티, 배열의 요소를 삭제하거나 정의되지 않은 값으로 만든다.)
 

 객체접근 연산자 . / []

- [] 연산자를 사용하면 배열의 요소에 접근할 수 있고 객체의 프로퍼티에도 접근할 수 있다.


 생성자

자바스크립트에서는 new 연산자와 미리 정의된 Object(), Date(), Function() 같은 함수들을 이용하여

새로운 객체를 만들거나 초기화 할 수 있다. 그러나 사용자가 직접 객체형을 정의하여 사용하는 경우가 더 많다.
예를 들어, 사각형을 처리하는 Rectangle 클래스에 width, height 프로퍼티가 있을때,
width, height 같이 이미 정의된 프로퍼티를 가지는 객체를 생성하려면 새로운 객체를 생성하고
이 프로퍼티들을 초기화 할 수 있는 '생성자(construnctor)'가 필요하다.

- 생성자는 new 연산자를 통해 호출된다.
- 생성자는 this라는 특수 키워드의 값으로 새롭게 생성된 빈 객체의 참조를 넘겨 받아서  새로은 객체를 적절하게 초기화 한다.

// 생성자 함수 정의
  function Rectangle(w, h){
   this.width = w;
   this.height = h;
  }

// 객체를 생성하기 위해 생성자 함수를 호출, w, h를 전달해서 새로운 객체를 적절한 값으로 초기화한다는 것을 주의

var rect1 = new Rectangle(2,4);




- 생성자 함수는 일반적으로 반환 값이 없으며 this 값으로 넘겨받은 객체를 초기화하고 아무값도 반환하지 않는다.
  그러나 객체를 반환할 수는 있으며 그럴 경우에 반환되는 객체는 new 표현식의 값이된다. 이 경우에 this의 값의 객체는 버려진다.


 메소드 method 
객체에서 호출되는 함수


 prototype 프로퍼티

모든 함수에는 미리 정의된 '원형(prototype) 객체'를 참조하는 prototype 프로퍼티가 있다.
모든 객체는 원형을 가지므로 객체는 그것의 원형이 가진 프로퍼티를 모두 상속받는다.
이는 객체의 모든 프로퍼티가 그것을 상속한 객체의 프로퍼티처럼 보인다는 의미이다.
객체의 클래스에 대한 원형 객체를 지정하려면 적절한 객체에 생성자 함수의 prototype 프로퍼티의 값을 설정한다.
그 다음에 생성자로 새로운 객체를 호출할 때 자동으로 자바스크립트는 지정한 객체를 새로 생성되는 객체의 원형으로 사용한다.


 - 생성자 함수는 객체의 클래스를 정의하고, 클래스의 상태 변수로 사용되는 width, height 같은 프로퍼티를 초기화 한다.
   원형 객체는 생성자와 결합하므로 클래스의 각 멤버들은 원형으로부터 정확하게 똑같은 프로퍼티 집합을 상속받는다.
   이는
 원형 객체가 메소드나 다른 상수 프로퍼티를 정의하기에 가장 이상적인 위치라는 것을 의미한다.


 - 상속(inheritance)은 프로퍼티 값을 검색하는 과정의 일부로서 자동으로 발생한다.
   프로퍼티는 원형 객체로부터 
새로운 객체로 복사되는 것이 아니며 단지 해당 객체의 프로퍼티인 것처럼 보이는 것 뿐이다.


 - 원형 객체를 사용하여 프로퍼티를 상속할 수 있으므로 각 객체를 만들 때 필요한 
메모리의 양을 크게 줄일 수 있다.


 - 객체는 생성된 후에 원형에 
추가된 프로퍼티 값도 상속받을 수 있다.


 - 각각의 클래스에는 하나의 프로퍼티 집합을 가진 하나의 원형 객체가 있다.
   그러나 이 클래스로부터 많은 인스턴스를 만들 수 있고, 각 인스턴스에서는 원형의 프로퍼티를 상속받게 된다.
   객체 o의 프로퍼티 p를 읽을 때 자바스크립트는 먼저 o에 p라는 프로퍼티가 있는지 검사한다.
   p가 없는 경우에는 o의 원형 객체에 p라는 이름을 가진 프로퍼티가 있는지 검사한다.
   이것은 원형을 바탕으로 한 상속 작업이다.


  
프로퍼티 상속은 프로퍼티 값은 읽을 때만 발생하고 프로퍼티 값을 쓸 때는 발생하지 않는다.
   원형으로 부터 프로퍼티를 상속받은 객체 o에서 프로퍼티 p의 값을 설정하면 객체 o에서 직접 "새로운 프로퍼티 p가 생성"된다.
   이렇게 되면 객체 o는 p라는 이름이 지정된 자신만의 프로퍼티를 가지게 되고, "
그 결과로 객체의 원형으로부터 p의 값을
   더이상 상속 받을 수 없게 된다." (이때 원형의 프로퍼티 p는 가려진(shadow), 숨겨진(hide) 프로퍼티라고 부른다.)


  
 인스턴스 instance

클래스에 기초한 객체지향 언어와 자바스크립트에서는 같은 클래스에 여러 객체를 사용할 수 있다.
이런 객체를 클래스의 인스턴스라고 한다. == 클래스 안의 객체

- 인스턴스 변수
  모든 객체에는 자신만의 분리된 인스턴스 변수 복사본이 있다. 다시 말해 어떤 클래스에 10개의 객체가 있다면
  10개의 인스턴스 변수가 있는 셈이다.  예를 들어 Circle 클래스에서 모든 circle 객체에는 원의 반지름을 정의하는 프로퍼티 r이 있다. 
  이런 경우에 r은 인스턴스 변수이다.

   각 객체에는 자신만의 인스턴스 변수 복사본이 있으므로 각각의 객체를 통해 이 변수에 접근할 수 있다.
   객체의 프로퍼티 == 인스턴스 변수
 
- 인스턴스 메소드
  인스턴스 메소드는 this 키워드를 사용하여 처리하고 있는 객체나 인스턴스를 참조한다. 
  클래스의 모든 인스턴스에서 호출할 수 있지만 이것이 인스턴스 변수처럼

  모든 객체에서 자신만의 메소드 복사본을 가진다는 의미는 아니다.

  대신에 모든 클래스의 인스턴스는 인스턴스 메소드를 모두 공유한다.


 


 garbage collector 
자바스크립트에서는 수동으로 메모리를 반환하는 대신에 이 작업을 '가비지 수집(garbage collection)'이라는 기술에 맡긴다.
인터프리터는 프로그램에서 더 이상 쓰이지 않는 객체를 검색할 수 있다.
객체가 접근하기 어려운 위치(프로그램에서 변수가 더 이상 그 객체를 참조하지 않을 경우)에 있으면
인터프리터는 그 객체가 더 이상 필요하지 않다고 판단하고 그 객체에 사용된 메모리를 해제한다.

 var s = "hi";    // 문자열에 메모리를 할당
 var u = s.toUpperCse(); // 새로운 문자열을 생성
 s = u;     // 원래 문자열에 참조를 엎어씀



이 코드가 실행된 후에는 원래 문자열 "hello"에 더 이상 접근 할 수 없다.


 - 가비지 콜렉터 대부분은 
'표시, 제거(mark and sweep)' 라고 알려진

   기본적인 가비지 콜렉션 알고리즘을 약간씩 변형시켜서 사용한다.


 표시, 제거 mark and sweep

- 주기적으로 자바스크립트 환경에서 사용되는 모든 변수 목록을 검색한다.
   그리고 이 변수들이 참조하는 값에 표시를 한다.

   참조한 값이 객체나 배열이면 가비지 수집기는 그 객체의 프로퍼티나 배열의 요소에 표시를 한다.

   가비지 수집기는 값들로 이루어진 그래프와 트리를 순환적으로 검색하여 사용되고 있는 각각의 값에 표시를 할 수 있다.

   이러한 작업을 마친 후에 표시되지 않은 것은 가비지가 된다.


- 표시, 제거 가비지 수집기가 현재 사용되고 있는 값에 표시하는 작업을 끝낸 후에는 제거 단계를 수행한다.
   이 단계를 진행하는 동안 그 환경에서 사용되는 모든 값 목록을 찾고, 표시되지 않은 것에 할당되었던 메모리를 반환한다.
   전형적인 표시, 제거 가비지 수집기는 표시 작업과 제거 작업을 동시에 한다.
   이러한 작업은 가비지 수집을 수행하는 동안 시스템의 성능을 현저히 떨어뜨린다.
   좀더 정고하게 변화된 알고리즘은 상대적으로 작업을 효율적으로 처리하며, 시스템의 성능을 저하시키지 않고 
   백그라운드에서 수집 작업을 수행한다.



좋은 자료를 제공해 주신분은... (여기... )
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Extjs 3.0 Roadmap

Scripter/EXTJS / 2008. 11. 12. 16:42

벌써 2.2가 Release되고 3.0이 Extjs Forum글에서 언급되고 있다.
Extjs Roadmap을 포스팅한지가 지난 4월이였는데 이번주 Ajaxian사이트에 올라온 Extjs Designer동영상을 보니 다시금 불끈~~!!


Ext JS 2.1 (Released April 21, 2008)

View the release notes for full details.

  • Full REST support
  • Remote component loading samples
  • First class support for non-Ajax form submits
  • Upgrade all adapter libraries to current versions
  • Additional support for AIR platform
  • Grid filtering sample
  • StatusBar
  • Slider
  • CheckBox/Radio group controls (deferred to 2.2)

Ext JS 2.2 (Released August 4, 2008)

View the release notes for full details.

  • Styled Checkbox and Radio button controls
  • CheckBox/Radio group controls
  • Browser history support
  • XML TreeLoader sample
  • FileUpload field sample
  • MultiSelect and ItemSelector sample
  • Firefox 3.0 support
  • Slider (already released in 2.1)
  • Ext.Ajax enhancements (deferred to 3.0)
  • Support for reset styles scoped to Ext components only (deferred to 3.0 to coincide with additional CSS refactoring to enable easier custom theming)

Ext JS 3.0 (Early 2009)

  • All new lightweight, high-speed core base library
  • Flash Charting API
  • Ext.Direct - Remoting and data streaming/comet support
  • Integrated client-server data binding/marshaling of updates
  • ListView component
  • Enhanced Button and Toolbar components
  • ARIA/Section 508 accessibility improvements
  • CSS updates for reset style scoping and easier custom theming
  • Update the Ext event registration model
  • Ext.Ajax enhancements
  • Browser history support (released in 2.2)


출처 : http://extjs.com/products/extjs/roadmap.php

아래 동영상은 3.0 Release시 같이 출시된 designer tool 이다... 흠.. 이거 쓰면 괜찮겠는데.. Trust Form Designer랑 비슷하다.. ㅡ.,ㅡ;







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

extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Ext2.2 Release  (11) 2008.08.07
ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |

CSSHttpRequest

Scripter/AJAX / 2008. 11. 12. 15:11

XML과 XMLHttpRequest(XHR)의 시대는 물건너 갔는가?
JSON(JSONML)과 CSS만으로 통신을 하다 못해 이제는 CSSHttpRequest까지 나왔다.

CSS의 Hack을 이용한 것으로 cross-domain이 가능하다.
CSSHttpRequest(CHR)은 CSS 통신을 위한 크로스도메인 AJAX 메쏘드이다.

CSSHttpRequest.js
(function(){
    var chr = window.CSSHttpRequest = {};
   
    chr.ID = "__csshttprequest__";
    chr.DELAY = "50";
    chr.counter = 0;
    chr.requests = {};
    chr.timeout = null;
    chr.styleSheet = null;
    chr.styleElement = null;
   
   
    chr.get = function(url, callback) {
        var id = (++chr.counter);
        var s = chr.getStyleSheet();
        var item, index;
       
        // IE
        if(s.addImport) {
            index = s.addImport(url);
            item = s.imports(index);
        }
       
        // W3C
        else if(s.insertRule) {
            index = s.insertRule("@import url(" + url + ");", 0);
            item = s.cssRules[index];
        }
       
        chr.startPoll();
        return chr.requests[id] = {
            id: id,
            index: index,
            item: item,
            styleSheet: item.styleSheet || item,
            callback: callback
        };
    };
       
   
    chr.getStyleSheet = function() {
        if(chr.styleSheet)
            return chr.styleSheet;
       
        var s, e;
        if(document.createStyleSheet) {
            s = document.createStyleSheet();
            e = s.owningElement || s.ownerNode;
        } else {
            e = document.createElement("style");
            s = e.sheet;
        }
       
        e.setAttribute("type", "text/css");
        e.setAttribute("media", "print, csshttprequest");
        e.setAttribute("title", chr.ID);
        e.setAttribute("id", chr.ID);
       
        if(!e.parentNode)
            document.getElementsByTagName("head")[0].appendChild(e);
        if(!s)
            s = e.sheet;
       
        chr.styleSheet = s;
        chr.styleElement = e;
       
        return s;
    };
   
   
    chr.reset = function() {
        var s = chr.getStyleSheet();
        if(s.imports)
            while(s.imports.length)
                s.removeImport(0);
        else if(s.cssRules)
            while(s.cssRules.length)
                s.deleteRule(0);
        chr.requests = {};
    };
   
   
    chr.startPoll = function() {
        if(chr.timeout)
            return;
        chr.timeout = window.setTimeout(chr.poll, chr.DELAY);
    };
   
   
    chr.poll = function() {
        chr.timeout = null;
        var retry = false;
       
        for(var id in chr.requests) {
            var request = chr.requests[id];
            if(id != request.id)
                continue;
            var response = chr.parse(request.item.styleSheet || request.item);
            if(response === undefined)
                retry = true;
            else {
                delete chr.requests[id];
                request.callback(response, request);
            }
        }
       
        if(retry)
            chr.startPoll();
        else
            chr.reset();
    };
   
   
    chr.parse = function(s) {
        if(!s)
            return;
       
        var res = "";
        var d = decodeURIComponent;
       
        // IE
        if(s.imports && s.imports.length) {
            for(var i = 0; i < s.imports.length; i++) {
                var m = s.imports(i).href.match(/about\:chr\:(.+)/);
                if(m && m[1])
                    res += m[1];
            }
            return d(res);
        }
       
        // W3C
        else if(s.cssRules && s.cssRules.length) {           
            var rs = s.cssRules;
            for(var i = 0; i < rs.length; i++) {
                var r = rs[i];
                if(r.type != CSSRule.IMPORT_RULE)
                    continue;
                var m = r.cssText.match(/@import\s+url\("?about\:chr\:([^")]+)"?\)/);
                if(m && m[1])
                    res += m[1];
            }
            return d(res);
        }
    };
})();



XHR이 XML이나 JSON을 타겟으로 삼는다면 CHR은 CSS파일의 @import url()을 이용한다.
CSSJSON 이나 CSSML을 이용한것이 사실 XML/XSLT를 이용한것 보다 화면레더링하기는 사실 더 편하고 관리하기도 쉽다. CSS,JSON을 이용한다면 XML Node찾는것 보다 더 쉽지 않을까?


CHR을 이용하려면 AJAX Wrapper인 CSSHttpRequest.js 을 받아서 아래와 같이 입력한다.
    CSSHttpRequest.get(
        http://www.nb.io/hacks/csshttprequest/hello-world/helloworld.css,
        function(response) { alert(response); }
    );
보는바와 같이 css를 호출한다. 해당 css를 살펴보면
 @import url(about:chr:Hello%20World!);
와 같이 CSS의 about: 을 이용한다.

CHR은 XHR이나 JSONP와 같은 sampe-origin policy (같은 도메인에서 동작)과 다른 범주에 속한다. (흠..번역이..쩝..) JSONP와 같이 CHR도 GET Reqeust밖에 사용하지 못한다.


샘플 :


참조 사이트 :
http://nb.io/hacks/csshttprequest/



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

Ajax Framework 분석  (0) 2009.05.04
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

참고 : IBM Developer Site

JavaScript Object Notation (JSON)과 Asynchronous JavaScript + XML (Ajax)은 나란히 성장하고 있습니다. JSON은 JavaScript 객체로 바뀔 수 있는 데이터를 쉽게 전송할 수 있지만, 이 객체를 다루기 위해 커스텀 스크립팅이 필요합니다. JsonML은 JSON의 확장으로, JSON 유형 마크업을 사용하여 XML 데이터를 매핑하고, 이는 JSON 마크업에 기반하여 XML이나 XHTML 데이터를 쉽게 생성할 수 있고 사용자 인터페이스(UI) 엘리먼트를 쉽게 구현 및 교환할 수 있도록 합니다. 이 글에서, 이 편리한 툴을 사용법을 설명합니다

Ajax로 데이터 교환하기

Ajax 인터페이스가 가진 가장 일반적인 문제들 중 하나는 클라이언트 브라우저와 호스트 서버 간 정보 교환에 인코딩 및 사후 처리(post-processing) 또는 데이터 스트림을 JavaScript 애플리케이션 내에서 직접 사용할 수 있는 것으로 변환하는 파싱이 필요하다는 점이다.

Ajax에서, 캡슐화 방식은 XML이다. 전 세계적인 공통 이해를 기반으로 사용되고 있지만 문제와 한계도 안고 있다. XML의 기본적인 문제는 인코딩 프로세스가 비싸고, 대부분의 브라우저에서, XML 응답의 디코딩(decoding)에 시간이 많이 든다는 점이다.

Ajax 애플리케이션을 사용하면, 요청은 폼의 콘텐트에 기반하게 된다. 응답은 서버에 의해 리턴되는 정보가 자바 객체 시리즈로 표현된다. 결과 시퀀스는 그림 1과 비슷하다.


그림 1. 전형적인 Ajax 프로세스


 

그림 1의 프로세스는 두 가지 문제를 갖고 있다.

  • 첫 번째는 XML로 작동하는 것이 매우 복잡하다. XML을 처음부터 생성하든, 서버에서 XML을 소비하든, 프로세스는 시간이 많이 걸리고 지루하고, 대게는 비효율적이다.
  • 다른 문제는 데이터를 디스플레이 포맷으로 변환하는 것이 복잡하다는 점이다. DOM 모델을 사용하여 직접 UI 엘리먼트를 구현하든지, 사전 포맷된 XHTML로 데이터를 제공해야 한다. 후자 방식은 서버가 UI 엘리먼트를 제공해야 한다. (이 자체로 위험한 발상이다.) 전자는 오버헤드를 프로세스에 추가하게 된다.

DOM 모델은 다른 웹 브라우저들과 환경이 다른 DOM 구조와 인터페이스를 갖고 있다는 사실에도 영향을 받는다. 이는 여러분이 로딩하는 정보의 디스플레이에도 복잡성을 추가하게 되고, 이는 전혀 원하던 바가 아니다.

이러한 문제에 대한 솔루션이 많이 있다. 첫 번째 솔루션은 XML 포맷에서 JSON으로 데이터가 교환되는 방식을 변경하는 것이다. 이 부분은 이 글의 초점은 아니지만, JSON을 이해하면 JsonML을 더욱 쉽게 이해할 수 있을 것이다. JsonML은 정보의 실제 디스플레이를 더욱 쉽게 만드는 또 다른 솔루션도 제공한다. 먼저 JSON부터 보도록 하자.




위로


JSON 이해하기

JavaScript Object Notation (JSON)은 XML 사용, 콘텐트의 파싱, JavaScript 표준 기능을 사용하여 정보를 교환함으로써 가용성 있는 내부 구조로의 변환을 통해 대부분의 문제를 해결하려고 한다.

JSON의 중심은 여전히 텍스트 포맷이지만, 가독성이 더욱 강화되었고, 많은 언어들(Perl, PHP, Python, Java, Ruby)로 중첩된 데이터 객체들을 생성하는 방식과 호환되고, 이는 JavaScript 객체 포맷(중첩된 데이터 구조)와 잘 맞는다.

Listing 1처럼, XML로 비즈니스 리스트를 모델링 한다.


Listing 1. XML 주소 데이터
                
<business>
<name>One on Wharf</name>
<address>
    <street>1 Wharf Road</street>
    <town>Grantham</town>
    <county>Lincolnshire</county>
</address>
<phonenumbers>
    <phone>01476 123456</phone>
    <phone>01476 654321</phone>
</phonenumbers>
</business>

JavaScript 내에서 파싱 하려면 DOM 모델을 사용하여 컴포넌트에 액세스 해야 한다. 예를 들어, JavaScript 내 이 구조에서 전화 번호를 얻으려면, Listing 2의 코드를 사용해야 한다.


Listing 2. XML 데이터를 소비하는 웹 애플리케이션
                
<html>
<head>
<script type="text/javascript">
var xmlDoc;
function loadXML()
{
// code for IE
if (window.ActiveXObject)
  {
  xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
  xmlDoc.async=false;
  xmlDoc.load("/json/address.xml");
  getaddress();
  }
// code for Mozilla, Firefox, Opera, etc.
else if (document.implementation &&
document.implementation.createDocument)
  {
  xmlDoc=document.implementation.createDocument("","",null);
  xmlDoc.load("/json/address.xml");
  xmlDoc.onload=getaddress;
  }
else
  {
  alert('Your browser cannot handle this script');
  }
}
function getaddress()
{
document.getElementById("business").innerHTML=
xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue;
document.getElementById("phone").innerHTML=
xmlDoc.getElementsByTagName("phone")[0].childNodes[0].nodeValue;
document.getElementById("fax").innerHTML=
xmlDoc.getElementsByTagName("phone")[1].childNodes[0].nodeValue;
}
</script>
</head>
<body onload="loadXML()">
<h1>Address</h1>
<p><b>Business</b> <span id="business"></span><br />
<b>Phone:</b> <span id="phone"></span><br />
<b>Fax:</b> <span id="fax"></span>
</p>
</body>
</html>

Listing 2의 코드는 단 1 개의 주소 정보만 얻었고, 다음 로직을 사용하여 비즈니스 이름 같은 원래의 XML 문서 엘리먼트에 액세스 한다. xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue.

좋아 보이지는 않는다!

JSON에서 같은 정보를 보자. (Listing 3)


Listing 3. JSON 버전의 주소 데이터
                
{
    "business" : {
        "name" : "One on Wharf",
        "address" : {
            "street" : "1 Wharf Road",
            "town" : "Grantham",
            "county" : "Lincolnshire",
        },
        "phonenumbers" : [
                          "01476 123456",
                          "01476 654321",
                          ]
                      }
}

구조가 훨씬 읽기 쉽고 간단하다. JSON에 사용된 마크업 포맷은 eval()을 사용하여 파서 없이도 JavaScript에서 직접 계산될 수 있다. 그리고 결과는 또 다른 JavaScript 객체이다: var addressbookObj = eval('(' + jsontext + ')');.

주: 이상적으로는 eval()을 사용해서는 안된다. 임의의 텍스트를 실행하는데 사용될 수 있기 때문이다. JSON 텍스트만 파싱하는 많은 JSON 파서들이 있다.

정보가 JavaScript 내 객체라면, 표준 JavaScript 표기법을 사용하여 정보에 액세스 할 수 있다. 예를 들어, 객체로 파싱 되었던 JSON 데이터 소스에서 같은 이름과 전화 번호를 얻을 수 있다. (Listing 4)


Listing 4. 객체로 파싱된 JSON 데이터 소스
                
addressbookObj.business.name
addressbookObj.business.phonenumbers[0]
addressbookObj.business.phonenumbers[1]

Listing 4는 사용하고 액세스 하기가 더욱 간단하고 쉽다. 위 데이터 구조를 사용하고 원래의 XML 예제와 같은 정보를 제공하는 전체 페이지를 작성할 수 있다. (Listing 5)


Listing 5. JSON 데이터를 소비하는 웹 애플리케이션
                
<html>
<head>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">

function showaddress(req)
{
    var addressbookObj = eval('(' + req.responseText + ')');

    document.getElementById("business").innerHTML = 
       addressbookObj.business.name;
    document.getElementById("phone").innerHTML = 
       addressbookObj.business.phonenumbers[0];
    document.getElementById("fax").innerHTML = 
       addressbookObj.business.phonenumbers[1];
}

function getaddress()
{
   new Ajax.Request('address.json',
                    { method : 'get',
                      onSuccess : showaddress,
                      onFailure: function(){ 
              alert('Something went wrong...') }});
}
</script>
</head>
<body onload="getaddress()">
<h1>Address</h1>
<p><b>Business:</b> <span id="business"></span><br />
<b>Phone:</b> <span id="phone"></span><br />
<b>Fax:</b> <span id="fax"></span>
</p>
</body>
</html>

필자는 여기에서 Prototype 라이브러리를 사용하여 원래 JSON 파일을 로딩했지만, JSON 파일에 있는 정보를 실제로 파싱하고 디스플레이 하는 프로세스가 훨씬 더 간단하다는 것을 알 수 있다. 하지만, 여전히 DOM을 사용하여 JSON 파일에서 로딩되었던 정보로 아웃풋을 채워야 한다.

원래 JSON으로 돌아가서, JSON은 전송되는 데이터의 관점에서 볼 때 비교적 작고, 더 큰 Ajax 프로젝트에서 XML의 페이로드 오버헤드는 상당하다. 실제로 콘텐트를 처리하는 감소된 JavaScript 오버헤드와 이것을 합병할 때 오버헤드가 크다.

Listing 5의 예제는 JSON 표준의 단순함을 보여준다. 대부분의 Ajax 애플리케이션의 핵심은 정보 디스플레이의 커스터마이징이다. 이 예제에서, 여러분은 DOM 모델을 사용하여 XML 또는 JSON 데이터 콘텐트를 HTML 페이지로 삽입했다. 많은 Ajax 애플리케이션에서, 구현해야 할 더 복잡한 사용자 인터페이스 엘리먼트가 있다.






JsonML

JSON Markup Language는 JSON 데이터 교환 포맷의 기본 방식을 사용하고 이를 XML 표현에 적용하여 비슷한 텍스트 기반 마크업을 사용하여 XML 포맷 데이터를 교환할 수 있다.

XML을 직접 사용하는 대안으로서 XML 데이터를 모델링 하는 표기법 포맷으로 보인다. 하지만, 원래의 XML과 비교해서 JSON의 구조와 가독성을 생각해 보라. JsonML에서, 많은 장점들을 활용할 수 있다. 가독성과 데이터 크기는 분명한 이점이 된다.

JsonML의 주 초점은 UI 엘리먼트를 구현하는 툴로서이다. 전통적으로, 여러분은 이러한 종류의 브라우저 기반 사용자 인터페이스를 개발하는 두 가지 방식을 갖고 있다.

  • 서버에서 XHTML을 생성하여 DOM 객체의 innerHTML 애트리뷰트를 사용하여 현재 페이지로 삽입한다.
  • 클라이언트 DOM 인터페이스를 사용하여 DOM 구조를 직접 구현한다.

여러분도 보다시피, 이것은 다소 성가신 방법이다. 대형 데이터 세트의 경우, 포맷팅이라는 반복적인 특성이 프로세스를 혼돈시킬 수 있다. 게다가, 브라우저에 민감한 DOM 구현들을 다루는 문제도 있다.

바로 이것이 JsonML이 해결하고자 하는 것이다. JSON 마크업의 단순함과 대상 포맷으로서 DOM을 혼합한다. 여러분은 같은 효과를 볼 수 있다. 소스는 JavaScript 클라이언트에 의해 쉽게 로딩 및 파싱될 수 있다. (DOM 구현에 대해서는 걱정할 필요가 없다.) 또한, 여러분은 JsonML 문서에 모든 것을 기술할 수 있기 때문에, 데이터와 마크업을 하나의 파일로 효과적으로 삽입할 수 있다.

그림 1로 다시 돌아가면, Ajax 인터페이스를 통해 복원된 정보를 디스플레이 하는 전형적인 방식에는 데이터를 요청하고, XML을 파싱하고, XML을 브라우저에서 요구하는 DOM 구조로 변환하고, 아웃풋을 실제로 디스플레이 하는 것이 포함된다.

JsonML을 사용하여, 전체 XML, XHTML, DOM, JavaScript 단계를 하나의 단계로 대체할 수 있다.






JsonML 마크업

JsonML 예제를 보기 전에, JsonML의 구조부터 살펴보자. 이 구조의 공식 포맷은 Listing 6과 같다.


Listing 6. JsonML 마크업 포맷
                
element
    = '[' tag-name ',' attributes ',' element-list ']'
    | '[' tag-name ',' attributes ']'
    | '[' tag-name ',' element-list ']'
    | '[' tag-name ']'
    | json-string
    ;
tag-name
    = json-string
    ;
attributes
    = '{' attribute-list '}'
    | '{' '}'
    ;
attribute-list
    = attribute ',' attribute-list
    | attribute
    ;
attribute
    = attribute-name ':' attribute-value
    ;
attribute-name
    = json-string
    ;
attribute-value
    = json-string
    ;
element-list
    = element ',' element-list
    | element
    ;

복잡해 보이지만, 기본 구조는 매우 간단하다.

모든 엘리먼트는 텍스트이기 때문에 [ "ul" ] 같은 un-numbered list 엘리먼트를 만들 수 있다.

대괄호는 엘리먼트를 논리적으로 그룹핑 한다. 애트리뷰트를 이 엘리먼트에 추가하려면, 이 그룹 안에서 리스트의 다음 엘리먼트로서 중괄호를 사용한다. 예를 들어, 스타일을 변경하려면 다음과 같이 한다: [ "ul", { style : "list-style-type:none"} ].

물론, 이 리스트는 자식이 없이는 소용이 없다. 이들은 같은 리스트에 추가 엘리먼트로서 추가된다. (Listing 7)


Listing 7. 자식이 있는 리스트
                
[ "ul", { style : "list-style-type:none"}, 
    [ "li",
      "First item"],
    [ "li",
      "Second item"],
    [ "li",
      "Third item"],
    ];

아이템에 대한 애트리뷰트는 선택적이기 때문에, 생략할 수 있다. (Listing 7)






JsonML을 XHTML로 파싱하기

위 예제는 JSON이기 때문에, JavaScript 애플리케이션 내에서 정의할 수 있다. 콘텐트를 XHTML로서 디스플레이 하는 객체로 파싱하려면, JsonML 웹 사이트에서 JsonML 라이브러리를 가져온다. JsonML에서 JSON 객체를 XHTML로 파싱하려면 parseJsonML() 메소드를 사용한다. JsonML이 파싱되면, 페이지에 추가할 수 있는 UI 엘리먼트로서 액세스 할 수 있다.

Listing 8은 제공된 링크를 클릭할 때 마다 불리트(bulleted) 리스트를 사전 정의된 컨테이너에 추가할 전체 웹 페이지 모습이다.


Listing 8. JsonML 템플릿을 디스플레이 하는 웹 애플리케이션
                
<html>
<head>
<script type="text/javascript" src="JsonML.js"></script>
<script type="text/javascript">

var listblock = [ "ul",
                  [ "li",
                    "First item"],
                  [ "li",
                    "Second item"],
                  [ "li",
                    "Third item"],
                  ];

function addblock (id,jsonmlblock)
{
    var aselement = listblock.parseJsonML();
    var container = document.getElementById(id);
    container.appendChild(aselement);
}

</script>
</head>
<body>
<a href"#list" onclick="addblock('container', 
 listblock);return false;">Add block</a>
<div id="container"/>
</body>
</html>

그림 2는 웹 페이지 실행 모습이다. 페이지로 반입된 불리트 리스트와 함께 2회 정도 링크가 클릭된 후 보여지는 보습이다.


그림 2. JSON 예제 실행 모습
JSON 예제 실행 모습

예제를 단계 별로 살펴보자. 첫 번째 블록은 리스트 아이템을 나타내는 JsonML이다. (Listing 9)


Listing 9. 리스트 아이템을 나타내는 JsonML
                
var listblock = [ "ul",
                  [ "li",
                    "First item"],
                  [ "li",
                    "Second item"],
                  [ "li",
                    "Third item"],
                  ];

이것은 단순한 JavaScript 객체이다. 사용자가 링크를 클릭하면, addblock() 함수가 호출된다. 단 세 단계만 거친다. 첫 단계는 JavaScript 객체를 XHTML로 파싱하는 것이다. parseJsonML() 메소드를 적용한다: var aselement = jsonmlblock.parseJsonML();.

이제 목적지 컨테이너를 얻는다: var container = document.getElementById(id);.

마지막으로, 생성된 XHTML을 추가하고 이를 컨테이너에 붙인다: container.appendChild(aselement);.






기존 XHTML이나 XML을 JsonML로 파싱하기

JsonML을 사용하는 애플리케이션을 구현하기 전에, JsonML로 변환하고자 하는 XML 또는 XHTML 구조가 있어야 한다. JsonML 사이트(참고자료)에서는 XML 소스에서 JsonML을 만들어 내는 XSL 변형을 제공한다.

이를 사용하려면, JsonML.xsl 변형을 다운로드 한 다음, xsltproc을 사용하여 변환을 수행한다. Listing 10의 XML 구조를 보자.


Listing 10. XML 구조
                
<table class="maintable">
<tr class="odd">
<th>Situation</th>
<th>Result</th>
</tr>
<tr class="even">
<td><a href="driving.html" title="Driving">Driving</a></td>
<td>Busy</td>
</tr>
...
</table>

Listing 10을 JsonML로 변환할 수 있다: $ xsltproc JsonML.xsl.xml samp.xml.

결과 아웃풋은 Listing 11에 나타나 있다. 실제 아웃풋 내에 스트링 엘리먼트로서 보존되었지만 XSL이 아웃풋에서는 새로운 라인을 제거했다.


Listing 11. 결과 아웃풋
                
["table",
 {"class":"maintable"},
 "\n",
 ["tr",
  {"class":"odd"},
  "\n",
  ["th",
   "Situation"],
  "\n",
  ["th",
   "Result"],
  "\n"],
 "\n",
 ["tr",
  {"class":"even"},
  "\n",
  ["td",
   ["a",
    {"href":"driving.html",
     "title":"Driving"},
    "Driving"]],
  "\n",
  ["td",
   "Busy"],
  "\n"],
 ]

이 메소드를 사용하여 어떤 구조라도 XML에서 JsonML로 변환할 수 있다.






기존 DOM 구조를 JsonML로 파싱하기

또 다른 상황은 XML 또는 XHTML 블록을 구현하고 이를 JsonML로 변환해야 하는 상황이다. 변환에 JavaScript 함수를 사용할 수 있다. JsonML 웹 사이트를 참조하라.






UI 빌더로서 JsonML 사용하기

여러 가지 방식으로, UI 빌더로서 JsonML을 사용할 수 있다. 한 가지 쉬운 방법은 구현하고자 하는 UI로서 작동하는 JsonML에 JavaScript 구조를 만드는 것이다. JavaScript 구조를 구현하면, 한 가지 액션에 있는 구조를 XHTML로 변환하고 생성된 XHTML을 이 페이지에 삽입한다. Listing 12는 테이블의 행에 검은 셀 또는 흰 셀을 추가할 수 있고, 행을 테이블에 추가하고, 테이블을 만드는 테이블 셀용 UI 빌더를 나타내고 있다. 이것은 JsonML 소스 및 상응하는 XHTML 소스이다.


Listing 12. JsonML 기반 테이블 빌더
                
<html>
<head>
<script type="text/javascript" src="JsonML.js"></script>
<script type="text/javascript">

var blackcell = [ "td", {"style" : "background-color:black"},"CELL"];
var whitecell = [ "td", {"style" : "background-color:white"},"CELL"];

var currentrow = new Array();
var currenttable = new Array();

function onLoad()
{
    initrow();
    inittable();
}

function initrow()
{
    currentrow = new Array();
    currentrow.push("tr");
}

function inittable()
{
    currenttable = new Array();
    currenttable.push("table");
    currenttable.push({"border" : "1"});
}

function addrow()
{
    currenttable.push(currentrow);
    currentrow = new Array();
    currentrow.push("tr");
    showsource();
}

function addcell(color)
{
    if (color == "black")
        {
            currentrow.push(blackcell);
        }
    else
        {
            currentrow.push(whitecell);
        }
}

function showsource()
{
    var tablelement = currenttable.parseJsonML();
    var container = document.getElementById("viewabletable");
    container.removeChild(container.lastChild);
    container.appendChild(tablelement);

    var srccontainer = document.getElementById("sourceoutput");
    srccontainer.value = currenttable.toSource();

    var domcontainer = document.getElementById("domsourceoutput");
    domcontainer.value = container.innerHTML;
 }

function showtable()
{
    showsource();
    initrow();
    inittable();
}

</script>
</head>
<body onload="onLoad()">
<a href"#addrow" onclick="addrow();return false;">Add Row</a><br/>
<a href"#addbcell" onclick="addcell('black');return false;"
  >Add Black Cell</a><br/>
<a href"#addwcell" onclick="addcell('white');return false;"
  >Add White Cell</a><br/>
<a href"#showtable" onclick="showtable();return false;"
  >Show Table</a><br/>
<a href"#showsource" onclick="showsource();return false;"
  >Show Source</a><br/>

<div id="viewabletable">
</div>
<b>JsonML Source</b>
<textarea rows="20" cols="120" id="sourceoutput"></textarea><br/>
<b>DOM Source</b>
<textarea rows="20" cols="120" id="domsourceoutput"></textarea><br/>

</body>
</html>

이 애플리케이션의 연산은 매우 단순하다. Add Black/White Cell 버튼을 클릭할 때마다, JsonML 포맷에 올바른 정보(이 경우, 올바른 텍스트와 포맷팅을 가진 테이블 셀)로 채워진 어레이를 현재 행을 구성하는 어레이에 추가한다. Add Row를 클릭하면, 그 행과 관련된 어레이를 전체 테이블과 관련된 어레이로 추가하게 된다. 각각의 경우, JsonML 표기법에 내부 객체의 정의를 확장한다.

Show Table을 클릭하면, 내부 JavaScript 구조가 XHTML로 파싱되어 parseJsonML() 메소드를 사용하는 테이블을 디스플레이 하여 원래 JsonML을 렌더링 된 포맷으로 변환한다. 또한, JsonML 소스(JavaScript 객체에 toSource() 메소드 사용)를 디스플레이 하고, 생성된 XHTML 소스를 덤핑함으로써 XHTML 소스를 디스플레이 한다. 여러분은 내부 JavaScript 구조만 다루기 때문에, DOM 인터페이스를 사용하여 HTML을 생성할 필요가 없다. XHTML을 최종 문서로 삽입하는 것을 제외하고는 말이다.

정적 텍스트 엘리먼트를 사용하여 테이블을 구현했지만, 이것이 행으로 삽입되기 전에 테이블 데이터를 변경할 수 있다. JsonML 템플릿은 단순한 JavaScript 객체이기 때문에, 간단한 할당을 사용하여 콘텐트를 업데이트 할 수 있다: blackcell[2] = "Some other content";.

이 템플릿의 콘텐트는 쉽게 업데이트 되기 때문에 복잡한 UI 구현 프로세스의 한 부분이 제거되었다. (Ajax 연결을 통해 여러분이 로딩한 정보를 디스플레이에 사용된 XHTML로 변환하는 방법)






Behaviors를 JsonML 엘리먼트에 바인딩 하기

위 예제(Listing 12)는 매우 간단한 메소드를 제공하여 동등한 XHTML을 생성하는데 사용되는 내부 구조를 사용하여 테이블을 생성한다. 하지만, 특정 스타일에 맞도록 아웃풋을 변경하거나, 현재 페이지 내에서 사용할 수 있는 스타일 및 아웃풋 가이드라인과 매치하도록 기존 템플릿을 변경해야 한다면?

JsonML을 사용하는 한 가지 방법은 로딩 시 페이지로 반입하는 UI 템플릿을 생성하는 것이다. (Listing 13)


Listing 13. 로딩 시, UI 템플릿을 생성하는 JsonML
                
<script type="text/javascript" src="resultstable.js"></script>
<script type="text/javascript" src="resultline.js"></script>
<script type="text/javascript" src="resultselectionpopup.js"></script>

이제 페이지의 다른 엘리먼트를 만들어 내기 위해, 각 객체를 로딩하고, parseJsonML을 사용하여 이를 XHTML로 변환하고, 결과를 만들어 낸다. 지금까지 CSS가 사용되었지만, Listing 13의 방식은 어떤 유형의 정보나 데이터를 아웃풋에 도입하는 방법을 제공하지 않는다.

전통적인 Ajax/DOM 모델에서, 각 엘리먼트를 개별적으로 파싱하고 다른 엘리먼트들을 적용할 시기와 장소를 선택할 수 있기 때문에 이러한 결정들을 내릴 수 있다. 같은 방식으로 JsonML 파싱 프로세스와 상호 작동 할 수 없지만, 콜백 바인딩을 파싱 단계에 추가할 수 있다. 이 단계에서는 각 엘리먼트를 파싱하고 결과를 낸다.

바인딩 함수는 JsonML 소스에서 생성된 각 XHTML 태그에 대해 실행되고, 아이템은 표준 HTML 객체이기 때문에, 비교 또는 결정 메소드를 사용하여 템플릿의 아웃풋을 변경할 수 있다.

예를 들어, Listing 14는 또 다른 JsonML 포맷 테이블을 보여준다.


Listing 14. 테이블용 JsonML 템플릿
                
var table = ["table",{ "border" : "1" },
             ["tr",
              ["td",{"class" : "highlight-effect"},"Highlighted row"],
              ],
             ["tr",
              ["td",{"class" : "non-highlight"},"Non-Highlighted row"],
              ],
             ];

파싱될 때 이것은 간단한 테이블을 생성한다. 여러분은 CSS 클래스를 사용하여 원하는 포맷팅을 제공한다. 하지만, 테이블에 삽입하고자 하는 데이터에 중요한 것이 들어있을 때에만 행을 강조하고 싶을 수도 있다.

이렇게 하려면, 엘리먼트를 하이라이트 효과 클래스로 바꾸는 바인딩 함수를 작성하여 다른 배경색을 갖도록 한다. (Listing 15)


Listing 15. 바인딩 함수를 JsonML 템플릿에 추가하기
                
function actionbindings(elem) {
    if (elem.className.indexOf("highlight-effect") >= 0) {
        elem.style.backgroundColor = "red";
    }
    return elem;
}

바인딩을 사용하려면, 바인딩 함수를 parseJsonML() 함수에 제공한다: var renderedtable = table.parseJsonML(actionbindings);.

바인딩 함수는 원래 JsonML에서 포맷된 것처럼 엘리먼트로 완전히 액세스 할 수 있기 때문에 바인딩은 태그 이름, 클래스 이름, ID 같은 많은 애트리뷰트를 기반으로 포맷을 검사 및 변경할 수 있다. 여러분이 해야 할 일은 이것을 파서로 리턴하기 전에 엘리먼트를 수정하여 변경된 엘리먼트를 DOM 트리에 삽입하는 것이다.





JsonML과 XML

비록 이 글이 XHTML에 대한 솔루션으로서 JsonML의 사용에 초점을 맞춰 설명한 것이지만, 거의 모든 XML 데이터에도 JsonML을 사용할 수 있다. XHTML은 메인 XML 표준의 하위 세트이기 때문이다.

이 장치는 정적인 XML 조각들을 JavaScript 애플리케이션에 저장해야 할 경우 유용하지만, 애플리케이션이 정보를 다른 클라이언트와 교환할 때 문서를 XML로 교환하는 기능을 갖고 있다.

특히, JsonML은 JavaScript 내 구조로서 액세스 할 수 있으므로, XML 구조의 콘텐트를 쉽게 업데이트 하고 이를 서버로 보낼 수 있다.

Listing 16처럼 간단한 XML 구조의 예를 들어보자.


Listing 16. XML 구조
                
<name>
<firstname>Martin</firstname>
<lastname>Brown</lastname>
</name>

JsonML에서, 이것은 Listing 17처럼 생성된다.


Listing 17. JsonML 구조
                
["name", 
  ["firstname", "Martin"],
  ["lastname", "Brown"],
]

JavaScript 객체로서 여러분은 상세를 업데이트 할 수 있다. (Listing 18)


Listing 18. JavaScript로 업데이트 하기
                
address[1][1] = "Sharon";

parseJsonML()을 사용하여 XML 버전의 객체를 생성하여 Listing 19에서 보이는 것을 만든다.


Listing 19. parseJsonML이 XML 버전의 객체를 생성한다.
                
<name>
<firstname>Sharon</firstname>
<lastname>Brown</lastname>
</name>

위 프로세스는 DOM 파서를 사용하여 XML 구조를 생성하거나, 텍스트 조작(남용 및 실수 여지가 많음)을 사용하는 것 보다 훨씬 단순하다. 각 경우 기본 템플릿은 작고 즉각적으로 액세스 가능하다.


요약

Ajax 애플리케이션을 사용하면 많은 문제들이 야기된다. 데이터를 효과적으로 교환하는 방법, 애플리케이션에 데이터를 디스플레이 하기 위해 포맷하는 방법 등이 문제가 된다. Ajax 애플리케이션의 정보는 동적이고, 이 정보를 포맷하는데 사용되는 XHTML을 구현해야 한다. XHTML을 직접 작성하기에는 시간이 오래 걸리고 DOM 모델을 사용하면 문제가 많이 생긴다. DOM 인터페이스는 브라우저 구현들마다 다르기 때문이다.

JsonML은 JSON을 기반으로 구현되고 JavaScript 표기법을 사용하여 사용자 인터페이스 엘리먼트를 모델링 할 수 있다. 따라서 XHTML이나 DOM 기반 엘리먼트를 직접 생성하는 것 보다 훨씬 구현하기가 쉽고 호환성도 높다. JavaScript 구조를 XHTML로 변환할 때에는 DOM 인터페이스나 차이를 걱정할 필요가 없다.

이 글에서는 JSON과 JsonML 표준의 기초를 설명했고, 이들을 쉽게 업데이트 하는 방법도 설명했다. 또한 JsonML을 사용하여 UI 엘리먼트를 구현하는 방법도 다루었다. 파싱 단계 동안 프로세싱을 함으로써 JsonML 콘텐트를 확장하는 방법과 XHTML 아웃풋 방식을 확장 및 강화하는 방법도 배웠다.



참고자료


Post by 넥스트리소프트 데꾸벅(techbug)
, |

http://www.p3pwriter.com/LRN_111.asp



HTTP Header
해당 웹페이지가 보내지기 전에 META Information을 포함하여  페이지에 대한 정보 및 브라우저에 전달하는 정보

HTTP/1.1 200 OK
Date: Wed, 11 Oct 2003 01:11:50 GMT
Server: Apache/1.2.0
Last-Modified: Fri, 01 Jun 2003 11:16:44 GMT
ETag: "31f9e-620-44ca89c1"
Content-Length: 3112
Accept-Ranges: bytes
Connection: close
Content-Type: text/html


META HTTP-EQIV
웹페이지 화면 상단에 위치한 META태그로 해당 페이지 전체에 적용된다.
아래와 같이 입력시

<META HTTP-EQUIV="Author" CONTENT="P3Pwriter">

브라우저에서는 다음과 같은 Header정보를 보여준다.

HTTP/1.1 200 OK
Date: Wed, 11 Oct 2003 01:11:50 GMT
Server: Apache/1.2.0
Last-Modified: Fri, 01 Jun 2003 11:16:44 GMT
ETag: "31f9e-620-44ca89c1"
Content-Length: 3112
Accept-Ranges: bytes
Author: P3Pwriter
Connection: close
Content-Type: text/html

브라우저가 웹페이지문서를 받기전에 해당페이지에 Header값이 전달된다.
HTML이 브라우저에 의해 decoded되기 될때 브라우저에 Header값이 전달된다는 얘기이다.

HTTP-EQUIV의 정의에서 보면 이것은 "응답 HEADER안의 특별한 문자로 된 정보"를 읽는다는 것이다. 진실은 대부분의 서버가 META tag를 이용하여 페이지에 Header값들을 추가한다는 것이다.  P3P의 경우는 compat policy가 반드시 응답Header에 의해 전달되는것은 별로 좋지 않다.

실제 P3P의 동작원리
P3P Header는 브라우저에 의해 compact policy를 전달되고, 응답Header 안에 포함된 cookie값에 의해 페이지에 전달된다. 전달된 Policy는 브라우저의 privacy 셋팅에 의해 결정지어진다.
HTTP Header는 두가지 다른 방법으로 페이지에 추가될수 있다. 첫번째는 선택된 페이지나 모든 페이지의 header정보를 전달하는 서버파일들을 수정하는 것이고,  두번째는 asp,php,perl,cold funsion과 같은 프로그램언어를 사용하는 방법이다. 

프로그램언어를 이용해서 P3P Header를 추가하기 위해서는 HTML을 렌더링하기 전에 작성해야 한다.

<?여기다가 공백없이 작성해 주세요 UTF-8일때 NOBOM으로도... ?>
<HTML>
<HEAD>
<TITLE>MyPage</TITLE>
</HEAD>
<BODY>
Information
</BODY>
</HTML>






 

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

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |
꿈쳐두기 습관이 도졌나 보다!
아래는 http://www.mimul.com/pebble/default/2008/01/29/1201616760000.html 에서 꿈쳐온 것입니다. 부디 너그러운 맘으로 용서하시길..



1. JavaScript 최적화 도구
2. CSS 최적화 도구
3. 웹 사이트 성능 개선

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

자바스크립트 가이드  (0) 2008.12.03
JsonML 활용하기  (0) 2008.11.12
자바스크립트 단축키 사용 [Key Binding]  (0) 2008.08.19
자바스크립트 최적화(반복문)  (0) 2008.07.30
자바스크립트 코딩가이드 #1  (1) 2008.07.02
Post by 넥스트리소프트 데꾸벅(techbug)
, |

location.href =====> protocol :// hostname : port / pathname ? search # hash





히스토리기능으로 고민하던 어느분이 메일로 도움을 청해와서 찾아보던중
Ajax History기능구현이 가능하다는걸 알았다.
관련글을 찾아보던중 번역문서를 찾아 포스팅 한다.

원문 :  [ajax-history-libraries],
번역문 : [한빛미디어]
국내관련글 : juDe's blog(친절히도 설명해 놓았다.. ^^) [1],[2],[3],[4]

관련 파일 : dhtmlHistory.js



===========================================================
여기서는 AJAX 응용 프로그램에 즐겨찾기와 뒤로 이동을 지원하는 오픈 소스 자바스크립트 라이브러리를 소개할 것이다. 이 글을 마지막까지 보게된다면 구글맵스지메일과 같은 곳들조차 제대로 지원하지 못했던 웹 사이트에서의 즐겨찾기, 앞으로 이동, 뒤로 이동과 같은 AJAX의 문제들을 해결할 수 있게 될 것이다.

"AJAX: 즐겨찾기와 뒤로 이동을 다루는 방법"에서는 에 이잭스(AJAX) 응용 프로그램들이 현재 당면하고 있는 즐겨찾기, 뒤로 이동과 같은 중요한 문제들을 설명하고, 이 문제를 해결하기 위한 오픈 소스 프레임워크인 RSH(Really Simple History) 라이브러리를 소개하고, 실제 예제들을 몇 가지 소개할 것이다.

([1]역주: 영어사전에서 AJAX는 에이잭스로 소개되며, 그리스의 신화의 아이아스나 오딧세이의 아이아스를 의미한다. sys-con의 AJAX & Rich Internet Applications 프레젠테이션에서도 그 발음을 확인할 수 있다. 네덜란드 축구팀의 이름을 딴 아약스, 또는 아작스 등으로 불리기도 한다. 한편, 세제 제조회사인 Ajax Industries는 "에이잭스社"로 옮긴다.)

여 기서 소개할 프레임워크의 주요 부분은 두 개로 나뉘어진다. 첫째는 클라이언트의 정보를 갖고 있는 임시 세션 캐시를 사용하기 위해 숨겨진 HTML 폼을 사용한다. 이 캐시는 어떤 지점으로 이동하거나 페이지를 떠나는 경우에도 사용할 수 있다. 둘째는 A 태그와 숨겨진 iframe을 사용해서 브라우저 히스토리를 가로챈 다음 브라우저 히스토리 이벤트를 기록하고, 뒤로 이동과 앞으로 이동 버튼에 연결한다. 이 두 가지 기능은 모두 개발시에 쉽게 이용할 수 있도록 간단한 자바스크립트 라이브러리로 되어있다.

문제

즐 겨찾기와 뒤로 이동은 여러 페이지로 구성된 전형적인 웹 응용 프로그램에서 매우 유용하다. 사용자가 웹 사이트를 항해할 때, 브라우저의 주소 창은 새로운 URL로 업데이트되며, 나중에 이 주소를 다시 방문하기 위해 이메일에 붙여넣거나 즐겨찾기에 추가할 수 있다. 뒤로 이동, 앞으로 이동 버튼들도 올바르게 동작하며, 사용자는 방문했던 페이지들을 자유로이 이동할 수 있다.

그 러나, 에이잭스 응용 프로그램은 웹 페이지 하나에서 동작하는 복잡한 프로그램이기 때문에 예외적인 응용 프로그램이다. 브라우저는 이런 기능을 위해 만들어지지 않았다. 웹 응용 프로그램들이 마우스 클릭을 할 때마다 완전히 새로운 페이지를 가져올 수 있게 되었어도 브라우저는 과거에 사로잡혀 있다.

지메일과 같은 AJAX 소프트웨어에서 사용자가 기능을 선택하고, 응용 프로그램의 상태를 바꾸어도 브라우저의 주소창은 항상 같은 위치만 갖고 있기 때문에 응용 프로그램의 특정 화면에 대해 즐겨찾기를 축가하는 것은 불가능하다. 뿐만 아니라, 사용자가 이전 작업을 원상태로 되돌리기위해 뒤로 이동 버튼을 클릭하면 브라우저가 응용 프로그램의 웹 페이지를 떠나기 때문에 사용자들은 놀라게 된다.

해결책

오픈소스 RSH(Really Simple History) 프레임워크는 이러한 문제를 해결하고, 에이잭스 응용 프로그램에서 즐겨찾기, 뒤로 이동, 앞으로 이동을 제어할 수 있게 해준다. RSH는 현재 베타 버전이며, 파이어폭스 1.0, 넷스케이프 7 이상, 인터넷 익스플로러 6 이상의 버전에서 동작하며, 사파리는 현재 지원되지 않는다.(이에 대한 설명은 나의 웹 로그에서 Coding in Paradise: Safari: No DHTML History Possible를 참고하기 바란다.)

몇몇 에이잭스 프레임워크는 현재 즐겨찾기와 히스토리 문제를 해결하려 하고 있지만 이들 프레임워크는 구현 방식 때문에 몇가지 중요한 버그들을 해결하지 못하고 있다.(보다 자세한 사항은 "Coding in Paradise: AJAX History Libraries"를 참고한다) 게다가, 많은 에이잭스 [2]히스토리 프레임워크는 BackbaseDojo 같은 보다 큰 라이브러리안에 통합된 형태로 되어 있다. 이들 프레임워크는 AJAX 응용 프로그램에 대해서 완전히 다른 프로그래밍 모델을 제공하고 있으며, 개발자가 히스토리 기능을 사용하기 위해서는 완전히 새로운 접근방법을 사용할 것을 강요받게 된다.

([2]역주: 히스토리(History)는 "기록" 또는 "방문기록"으로 옮기지만 대부분이 히스토리에 익숙하기 때문에 번역하지 않았다)

반면에, RSH는 기존 AJAX 시스템에 포함시킬 수 있는 간단한 모듈로 되어 있다. 뿐만 아니라, RSH 라이브러리는 다른 히스토리 프레임워크들이 갖고 있는 버그들을 피하기 위한 기술들을 사용하고 있다.

RSH 히스토리 프레임워크는 자바스크립트 클래스 DhtmlHistory, HistoryStorage로 구성되어있다.

DhtmlHistory 클래스는 에이잭스 응용 프로그램을 위한 히스토리 추상화를 제공한다. 즉, 에이잭스 페이지는 브라우저에 새 위치와 그에 대한 히스토리 데이터를 지정한 히스토리 이벤트를 추가하는 add()를 호출한다. DhtmlHistory 클래스는 A 태그에 #new-location과 같은 해쉬를 사용해서 브라우저의 현재 URL을 업데이트하고, 히스토리 데이터와 새 URL을 연결한다. 에이잭스 응용 프로그램 자체를 히스토리 리스너(listener)로 등록하고, 사용자가 뒤로 이동, 앞으로 이동 버튼을 사용해서 이동할 때 마다 히스토리 이벤트는 add() 호출로 저장했던 히스토리 데이터와 브라우저의 위치를 제공한다.

개발자는 HistoryStorage 클래스를 사용해서 어떤 크기의 히스토리 데이터라도 저장할 수 있다. 일반 페이지에서 사용자가 새로운 웹 사이트로 이동할 때 브라우저는 이전 웹 페이지의 모든 응용 프로그램을 제거하고, 응용 프로그램과 자바스크립트의 상태를 정리한다. 따라서, 사용자가 뒤로 이동 버튼을 사용하여 이전 페이지로 돌아가면 모든 데이터가 사라진다. HistoryStorage 클래스는 put(), get(), hasKey()와 같은 간단한 해쉬 테이블 메서드를 제공하는 API를 통해서 이 문제를 해결한다. 사용자가 웹 페이지를 떠난 다음에도 개발자는 이들 메서드를 사용해서 데이터를 저장할 수 있다. 사용자가 뒤로 이동 버튼을 사용해서 돌아오면 HistoryStorage 클래스를 사용해서 데이터에 접근할 수 있다. 이 기능은 사용자가 웹 페이지를 떠난 후에도 브라우저가 폼 양식에 있는 값들을 자동으로 저장한다는 사실을 이용한 것으로, 숨겨진 폼 필드를 사용해서 구현됐다.

예제

이제 바로 간단한 예제로 살펴보자.

먼저, RSH 프레임워크를 사용할 페이지는 dhtmlHistory.js 스크립트를 반드시 인클루드해야 한다.

<!-- Load the Really Simple
       History framework -->
<script type="text/javascript"
            src="../../framework/dhtmlHistory.js">
</script>


DHTML 히스토리 응용 프로그램은 에이잭스 웹 페이지가 있는 디렉터리에 blank.html 파일을 포함시켜야 한다. 이 파일은 RSH 프레임워크에 함께 들어있으며, 인터넷 익스플로러에서 사용한다. 여담이지만, 인터넷 익스플로러는 히스토리 변경 사항을 추적하고 추가하기 위해 "숨겨진 iframe"을 사용한다. 히스토리 기능이 제대로 동작하기 위해서는 실제 위치를 가리키는 것이 필요한데, 이를 위해 비어있는 iframe을 사용한다. 그래서 이름도 blank.html이다.

RSH 프레임워크는 브라우저 히스토리를 조작하기 위한 진입점으로 dhtmlHistory라는 전역 객체를 생성한다. dhtmlHistory로 작업하는 첫번째 단계는 페이지 로딩이 끝난 후에 이 객체를 초기화하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();

다음으로, 개발자는 히스토리 변경 이벤트를 [3]구 독하기 위해 dhtmlHistory.addListener()를 사용해야 한다. 이 메서드는 DHTML 히스토리가 변경될 때, 페이지의 새로운 위치, 히스토리 변경 이벤트와 관련된 히스토리 데이터를 인자로 받는 콜백 함수를 인자로 받는다.

([3]역주: 이벤트 구독보다는 이벤트 감시가 더 적절한 표현이지만 원문이 subscribe이므로 구독으로 옮김)

window.onload = initialize;
     
function initialize() {
// DHTML 히스토리 프레임워크를 초기화한다
   dhtmlHistory.initialize();
   
// HTML 히스토리 변경 이벤트를 구독한다
   dhtmlHistory.addListener(historyChange);

historyChange() 메서드는 사용자가 새 위치로 이동한 후의 newLocation과 히스토리 변경 이벤트와 관계된 historyData를 인자로 받는 함수로 이해하기 쉽다.

/** 히스토리 변경 이벤트를 받기 위한 콜백 */
function historyChange(newLocation,
                                  historyData) {
   debug("A history change has occurred: "
            + "newLocation="+newLocation
            + ", historyData="+historyData,
            true);
}


위에서 사용한 debug() 메서드는 예제 소스 파일에 정의된 유틸리티 함수로 전체 예제 안에 포함되어 있다. debug()는 웹 페이지안에 메시지를 출력한다. 두번째 인자는 불리언(boolean) 타입으로 위 인자에서는 true를 사용했다. true를 사용하면 앞의 메시지를 모두 정리한 다음에 새로운 디버그 메시지를 출력한다.

개 발자는 add() 메서드를 사용해서 히스토리 이벤트를 추가한다. 히스토리 이벤트를 추가하는 것은 edit:SomePage와 같이 히스토리 변경 이벤트와 함께 저장할 historyData 값을 제공하기 위한 새 위치를 지정하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

add() 를 호출하는 즉시 사용자 브라우저의 URL 툴바에 새로운 위치가 표시된다. 예를 들어, http://codinginparadise.org/my_ajax_app 주소의 에이잭스 웹 페이지에서 dhtmlHistory.add( "helloworld", "Hello World Data" )를 호출하면 사용자는 브라우저의 URL 툴바에서 다음 주소를 보게 된다.

http://codinginparadise.org/my_ajax_app#helloworld

사 용자는 이 페이지를 북마크할 수 있다. 사용자가 이 북마크를 이용하면 에이잭스 응용 프로그램은 #helloworld 값을 읽어서 웹 페이지를 초기화할 수 있다. 해시 # 다음의 위치 정보는 RSH 프레임워크에 의해 투명하게 인코딩과 디코딩이 수행된다.

historyData 는 URL을 사용하는 것 보다 쉽게 에이잭스 위치 변화에 따른 복잡한 상태들을 저장할 수 있게 해준다. historyData는 숫자, 문자열, 객체와 같은 자바스크립트 타입으로 사용할 수 있는 값이다. 이를 사용하는 예로는 사용자가 DHTML 텍스트 에디터에서 다른 페이지로 이동하는 경우에 에디터의 내용을 모두 저장하는 것이다. 사용자가 뒤로 이동 버튼을 클릭해서 다시 DHTML 페이지로 돌아오면 브라우저는 히스토리 변경 리스너에게 객체를 돌려주게 된다.

개발자는 중첩된 객체나 복잡한 상태를 표현하는 배열과 같은 자바스크립트 객체 전체를 historyData에 사용할 수 있다. 히스토리 데이터에는 간단한 데이터 타입이나 NULL 타입 뿐만 아니라 JSON(자바스크립트 객체 표기법)으 로 표현할 수 있는 무엇이든 될 수 있다. 그러나, DOM 객체에 대한 참조나 XMLHttpRequest와 같은 스크립트 가능한 브라우저 객체는 저장되지 않는다. historyData는 북마크와 함께 보존되는 데이터가 아니며, 브라우저를 종료하거나, 브라우저 캐시를 제거하거나, 사용자가 히스토리를 정리하면 사라지는 데이터이다.

dhtmlHistory를 사용하는 마지막 단계는 isFirstLoad() 메서드이다. 어떤 브라우저에서는 웹 페이지를 항해할 때 다른 페이지로 이동했다가 뒤로 이동 버튼을 눌러서 처음 사이트로 돌아오면 첫번째 페이지가 완전히 재로딩되고 onload 이벤트가 발생한다. 페이지를 반복적으로 재로딩하는 경우가 아니라 처음 로드될 때만 특별한 방법으로 초기화하길 원하는 코드를 망쳐버릴 수 있다. isFirstLoad() 메서드는 웹 페이지를 처음 로드한 경우와 사용자가 브라우저 히스토리에 저장된 웹 페이지에서 뒤로 이동한 경우를 구별해준다.

예제 코드에서는 페이지가 처음 로드되었을 때 히스토리 이벤트를 추가하길 원한다. 페이지가 로드된 다음에 사용자가 페이지로 돌아가기 위해 뒤로 이동 버튼을 누른 경우에는 히스토리 이벤트를 추가하지 않는다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

이제 historyStorage 클래스를 살펴보자. dhtmlHistory, historyStorage와 마찬가지로 historyStorage라는 전역 객체 하나를 통해서 모든 기능을 제공한다. 이 객체는 해시 테이블을 시뮬레이션 하기 위해 put(keyName, keyValue0, get(keyName), hasKey(keyName)과 같은 메서드를 제공한다. 키 이름은 반드시 문자열이어야하며, 키 값은 XML로 된 문자열이나 자바스크립트 객체와 같이 복잡한 것도 사용할 수 있다. 예제에서는 페이지가 처음 로드될 때 historyStorage안에 간단한 XML을 저장하기 위해 put()을 사용하고 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }

다음으로, 사용자가 다른 페이지로 이동한 다음에 뒤로 이동 버튼을 통해 돌아온 경우에 get() 메서드를 사용해서 저장된 값을 가져올 수 있고, hasKey()를 사용해서 키가 있는지 확인할 수 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }
   
   // retrieve our values from the history
   // storage
   var savedXML =
                     historyStorage.get("fakeXML");
   savedXML = prettyPrintXml(savedXML);
   var hasKey =
                historyStorage.hasKey("fakeXML");
   var message =
      "historyStorage.hasKey('fakeXML')="
      + hasKey + "<br>"
      + "historyStorage.get('fakeXML')=<br>"
      + savedXML;
   debug(message, false);
}

preetyPrintXml()은 전체 예제에 포함된 유틸리티 메서드로 디버깅을 위해 웹 페이지에 XML을 출력한다.

데 이터는 오직 현재 페이지의 히스토리에서만 유지된다는 점에 주의해야 한다. 즉, 브라우저를 종료하거나 사용자가 새로운 창을 열고 에이잭스 응용프로그램의 주소를 다시 입력하는 경우에 히스토리 데이터는 이용할 수 없다. 히스토리 데이터는 오직 뒤로, 앞으로 이동 버튼에 한해서만 유지된다. 사용자가 브라우저를 종료하거나 캐시를 정리하면 사라진다. 실제로, 오랜시간 유지할 수 있는 방법을 원한다면 에이잭스 대용량 저장 시스템(AMAXX, Ajax Massive Storage System)을 보기 바란다.

예제는 이것으로 끝났으며, 데모를 체험해보거나 전체 소스 코드를 다운로드하기 바란다.

예제2: 오라일리 메일

두 번째 예제는 Gmail과 유사한 오라일리 메일이라는 가짜 AJAX 이메일 응용프로그램이다. 오라일리 메일을 통해 dhtmlHistory 클래스를 사용해서 브라우저 히스토리를 제어하는 방법, historyStorage 객체를 사용해서 히스토리 데이터를 캐시하는 방법에 대해 설명할 것이다.

오라일리 메일 사용자 인터페이스는 두 부분으로 구성되어 있다. 페이지의 왼쪽 사이즈는 받은 편지(Inbox), 임시 보관(Draft) 같은 다양한 이메일 폴더와 옵션으로 구성된 메뉴다. 사용자가 받은 편지 같은 메뉴를 선택하면 페이지의 오른쪽에 내용을 업데이트한다. 실제 응용프로그램에서는 선택된 메일박스의 내용을 가져와서 표시해야하지만 오라일리 메일에서는 선택한 옵션만 간단하게 보여준다.

오라일리 메일은 브라우저 히스토리에 메뉴 변경사항을 추가하고, 위치 바를 업데이트하는데 RSH 프레임워크를 사용하기 때문에 사용자가 응용프로그램을 북마크하고 브라우저의 뒤로, 앞으로 이동 버튼을 사용해서 메뉴 변경 이전으로 이동할 수 있다.

historyStorage 사용법을 설명하기 위해 Address Book(주소록)이라는 메뉴를 추가했다. 주소록은 이름과 이메일 주소를 자바스크립트 배열로 저장하고 있지만 실제 응용프로그램이라면 원격 서버에서 배열 내용을 가져와야 할 것이다. 그러나, 오라일리 메일에서는 배열을 직접 생성하고, 이름과 이메일 주소를 몇 개 추가한 다음에 historyStorage 객체에 저장한다. 사용자가 웹 페이지를 떠난후에 돌아오면 오라일리 메일 응용프로그램은 원격 서버에서 접속하는 대신 캐시에서 주소록을 가져온다.

주소록은 initialize() 메서드에서 저장하고 가져올 수 있다.

/** 페이지가 로딩을 끝낸 후에 초기화를 수행하는 함수 */
function initialize() {
    // DHTML 히스토리 프레임워크를 초기화한다
    dhtmlHistory.initialize();
   
    // DHTML 히스토리 리스너에 등록한다
    dhtmlHistory.addListener(handleHistoryChange);

    // 주소록을 가져올 수 없으면 직접 추가한 주소록을
    // 히스토리 저장소에서 캐싱한다.
   if (window.addressBook == undefined) {
         // 주소록을 전역 객체로 저장한다
       // 실제 응용프로그램에서는 백그라운드에서 서버로부터
       // 주소록을 가져와야한다
         window.addressBook =
             ["Brad Neuberg 'bkn3@columbia.edu'",
               "John Doe 'johndoe@example.com'",
               "Deanna Neuberg 'mom@mom.com'"];
               
         // 주소록이 있으면 이를 캐시에 보관한다
       // 사용자가 페이지를 떠난 후에 뒤로 이동으로 돌아온 경우에도 사용한다
       historyStorage.put("addressBook",
                                     addressBook);
    }
    else {
         // 히스토리 저장소에서 캐시된 주소록을 가져온다
         window.addressBook =
                      historyStorage.get("addressBook");
    }

히 스토리 변경을 다루는 코드는 이해하기 쉽다. 아래 코드에서처럼 사용자가 뒤로 또는 앞으로 이동 버튼을 클릭할 때 handleHistoryChange가 호출된다. 그러면 newLocation을 갖게 된다. 이를 이용해서 사용자 인터페이스를 올바른 상태로 업데이트할 수 있다.

/** 히스토리 변경 이벤트를 처리한다 */
function handleHistoryChange(newLocation,
                                           historyData) {
    // 위치가 없으면 수신함의 기본 위치를 보여준다
    if (newLocation == "") {
         newLocation = "section:inbox";
    }
   
    // 위치 변화가 있으면 표시할 섹션을 추출한다.
    // newLocation은 "section:"으로 시작한다
    newLocation =
             newLocation.replace(/section\:/, "");
   
    // DHTML 히스토리 변경에 따라 브라우저를 업데이트한다
    displayLocation(newLocation, historyData);
}

/** 오른쪽 컨텐트 영역에 주어진 위치를 표시한다*/
function displayLocation(newLocation,
                                     sectionData) {
    // 선택한 메뉴 항목을 가져온다
    var selectedElement =
                  document.getElementById(newLocation);
                 
    // 이전에 선택된 메뉴 항목을 제거한다
    var menu = document.getElementById("menu");
    for (var i = 0; i < menu.childNodes.length;
                                                               i++) {
         var currentElement = menu.childNodes[i];
         // DOM 요소 노드인지 확인한다
         if (currentElement.nodeType == 1) {
             //모든 클래스 이름을 제거한다
             currentElement.className = "";
         }                                                         
    }
   
    // UI에서 다르게 나타나는 새로 선택된 메뉴 항목
    selectedElement.className = "selected";
   
    // 화면 오른쪽에 새로운 섹션을 표시한다.
    // sectionData에 따라 섹션을 결정한다

    // 앞서 캐시된 로컬 주소 데이터를 사용해서 주소록을 보여준다
    if (newLocation == "addressbook") {
         // 주소록을 보여준다
         sectionData = "<p>Your addressbook:</p>";
         sectionData += "<ul>";
         
         // 주소록이 아직 없다면 캐시에서 주소록을 가져온다
         if (window.addressBook == undefined) {
             window.addressBook =
                      historyStorage.get("addressBook");
         }
         
         // 주소록을 표시한다
         for (var i = 0;
                      i < window.addressBook.length;
                               i++) {
             sectionData += "<li>"
                                    + window.addressBook[i]
                                    + "</li>";                          
         }
         
         sectionData += "</ul>";
    }
   
    // sectionData가 없다면 원격으로 가져온다. 이 예제에서는 주소록을 제외한
    // 모든 데이터는 가짜를 이용하고 있다
    if (sectionData == null) {
         // 실제 응용프로그램에서는 섹션의 내용을 원격으로 가져와야한다
         sectionData = "<p>This is section: "
             + selectedElement.innerHTML + "</p>";  
    }
   
    // 제목과 내용을 업데이트한다
    var contentTitle =
             document.getElementById("content-title");
    var contentValue =
             document.getElementById("content-value");
    contentTitle.innerHTML =
                                    selectedElement.innerHTML;
    contentValue.innerHTML = sectionData;
}


오라일리 메일 데모를 체험하거나 소스 코드를 다운로드할 수 있다.

결론

RSH(Really Simple History) API를 사용해서 에이잭스 응용프로그램에서 즐겨찾기와 뒤로, 앞으로 이동 버튼을 사용하기 위한 방법을 보았으며 직접 에이잭스 응용프로그램을 작성하는데 발판이 될 수 있는 예제 코드들을 살펴보았다. 나는 즐겨찾기와 히스토리를 완전히 지원하는 여러분의 에이잭스 발명품들을 볼 수 있기를 기대한다.

감사의 글

이 글을 검토해준 모든 사람과 RSH 프레임워크를 제작한 모두에게 감사드린다: Michael Eakes, Jeremy Sevareid, David Barrett, Brendon Wilson, Dylan Parker, Erik Arvidsson, Alex Russell, Adam Fisk, Alex Lynch, Joseph Hoang Do, Richard MacManus, Garret Wilson, Ray Baxter, Chris Messina, and David Weekly.

참고자료
  • 이 기사의 모든 소스코드 다운로드
  • RSH 프레임워크 다운로드
  • 오라일리 메일 데모와 오라일리 메일 소스 코드 다운로드. 전체 예제 다운로드에는 시험할 수 있는 보다 많은 예제들이 있다
  • Coding in Paradise(코딩천국): AJAX, DHTML, 자바 기술, 위키위키와 같은 협업 기술 등을 다루는 저자의 웹로그
  • "Coding in Paradise: AJAX: Bookmarks and Back Buttons, Advanced Example"
  • "Coding in Paradise: Safari: No DHTML History Possible"
  • "Coding in Paradise: AJAX Tutorial: Saving Session Across Page Loads Without Cookies, On The Client Side"
  • "Coding in Paradise: AJAX History Libraries"
Bard Neuberg는 모질라, JXTA, 자카르타 피드 파서 등에 코드를 공헌하고 있으며, 오픈소스 커뮤니티에서 폭넓은 공헌을 하고 있다.


출처 :
http://network.hanbitbook.co.kr/



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

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

오늘자 ajaxian에 올라온 포스트 중에 jQuery Tip으로 링크에 걸린 파일사이즈를 자동으로 알아낸뒤 링크뒤에 출력해주는 포스팅을 번역해서 올려본다.
이미 올려져 있는 소스의 파일사이즈를 알아내 오는것으로 파일업로드시 파일사이즈 체크는 달리 해야 한다.


jQuery(function($){
        $('a[href$=".pdf"], a[href$=".doc"], a[href$=".mp3"], a[href$=".m4u"]').each(function(){
                // looking at the href of the link, if it contains .pdf or .doc or .mp3
                var link = $(this);
                var bits = this.href.split('.');
                var type = bits[bits.length -1];
               
               
                var url= "http://json-head.appspot.com/?url="+encodeURI($(this).attr('href'))+"&callback=?";
       
                // then call the json thing and insert the size back into the link text
                 $.getJSON(url, function(json){
                        if(json.ok && json.headers['Content-Length']) {
                                var length = parseInt(json.headers['Content-Length'], 10);
                               
                                // divide the length into its largest unit
                                var units = [
                                        [1024 * 1024 * 1024, 'GB'],
                                        [1024 * 1024, 'MB'],
                                        [1024, 'KB'],
                                        [1, 'bytes']
                                ];
                               
                                for(var i = 0; i <units.length; i++){
                                       
                                        var unitSize = units[i][0];
                                        var unitText = units[i][1];
                                       
                                        if (length>= unitSize) {
                                                length = length / unitSize;
                                                // 1 decimal place
                                                length = Math.ceil(length * 10) / 10;
                                                var lengthUnits = unitText;
                                                break;
                                        }
                                }
                               
                                // insert the text directly after the link and add a class to the link
                                link.after(' (' + type + ' ' + length + ' ' + lengthUnits + ')');
                                link.addClass(type);
                        }
                });
        });
});
 


소스에서 굵게 표시된 json.headers 를 이용해서 컨텐츠의 사이즈를 알아온뒤 link.after메쏘드로 화면에 출력해 주었다...
너무도 간단하게.. ㅡ.,ㅡ; 



샘플사이트 : http://natbat.net/code/clientside/js/addSizes/
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>addSizes</title>
    <!-- Date: 2008-07-29 -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
    <script src="addSizes.js" type="text/javascript"></script>
</head>
<body>
    <h1>addSizes</h1>
    <p>This is a link to a remote <a href="http://clearleft.com/worksheet/client-worksheet.doc">doc</a> file.</p>
    <p>This is a link to a remote <a href="http://www.onyx.com/pdf/CustomerMgmtBrochure.pdf">pdf</a> file.</p>
    <p>This is a link to a remote <a href="http://media.giantbomb.com/podcast/giantbombcast-071708.mp3">mp3</a> file.</p>
   
    <p>This is a link to a local <a href="media/test.doc">doc</a> file.</p>
    <p>This is a link to a remote <a href="media/test.pdf">pdf</a> file.</p>
    <p>This is a link to a remote <a href="media/test.mp3">mp3</a> file.</p>
</body>
</html>


[ 다른 방법 : jQuery와 같이 3-party library를 사용하지 않고 단순 xhr을 이용한 방법 ]
function filesize (url) {
    var req = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
    if (!req) throw new Error('XMLHttpRequest not supported');
    
    req.open ('HEAD', url, false);
    req.send (null);
    
    if (!req.getResponseHeader) {
        try {
            throw new Error('No getResponseHeader!');
        } catch(e){
            return false;
        }
    } else if (!req.getResponseHeader('Content-Length')) {
        try {
            throw new Error('No Content-Length!');
        } catch(e){
            return false;
        }
    } else {
        return req.getResponseHeader('Content-Length'); 
    }
}


사용법 : filesize('파일주소');

테스트샘플
<html>
<head><title>테스트</title></head>
<body>
<script type="text/javascript">
function filesize (url) {
    var req = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
    if (!req) throw new Error('XMLHttpRequest not supported');

    req.open ('HEAD', url, false);
    req.send (null);

alert("전체 헤더======================\n\n"+req.getAllResponseHeaders());

try{
if(!req.getResponseHeader('Content-Length')) alert(req.getResponseHeader('Content-Length') + 'bytes');
}catch(e){
alert("에러");
}
}


</script>
<button onclick="filesize('https://t1.daumcdn.net/cfile/tistory/190E4E1249F6B2D7E2');" >이미지 파일사이즈</button><br />
<button onclick="filesize('http://techbug.tistory.com/122');" >웹페이지 파일사이즈(Entity header가 없는경우)</button><br />
<button onclick="filesize('http://www.webmasterworld.com/forum91/1920.htm');" >웹페이지 파일사이즈(Entity header가 있는경우)</button><br />
</body>
</html>

이경우 맨위의 버튼은 이미지 파일사이즈를 가져오는 것이라 content-Length가 표시되지만 아래의 경우 text/html의 content-length가 되지 않아 사이즈를 정할수 없다.
보통 HTTP header의 경우 (특히 Entity-Header)의 경우는 request를 보내는 브라우저의 종류에 따라 보내주는 정보가 틀려서 content-length를 구하는 방법이 어렵다.
위에서 보는 바와 같이 똑같이 웹페이지 파일사이즈를 정하는데 text/html일 경우 Fiddler나 다른 HTTP Header정보를 볼수 있는 프로그램으로 보면 서로 달리 나오는것을 확인할수 있다.




파일업로드시 용량을 체크하려면 File System Object 를 사용해야 한다. 
스크립트 보안 메세지가 뜨는 단점이 있다.
<html>
<head>
<script language="JavaScript">
function A(){
    var oas = new ActiveXObject("Scripting.FileSystemObject");
    var d = document.a.b.value;
    var e = oas.getFile(d);
    var f = e.size;
    alert(f + " bytes");
}
</script>
</head>
<body>
<form name="a">
<input type="file" name="b">
<input type="button" name="c" value="SIZE" onClick="A();">
</form>
</body>
</html>










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

jQuery Grid Plugin : jqGrid  (16) 2009.04.06
ext의 border-layout과 같은 jQuery UI.Layout Plugin  (0) 2009.01.14
jQuery pageSlide  (0) 2009.01.12
jQuery Loading바 구현하기  (2) 2008.07.08
Post by 넥스트리소프트 데꾸벅(techbug)
, |
가끔씩 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>


소스다운로드 :




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

JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
자바스크립트 최적화(반복문)  (0) 2008.07.30
자바스크립트 코딩가이드 #1  (1) 2008.07.02
getBoundingClientRect in FF3  (3) 2008.06.25
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Ext2.2 Release

Scripter/EXTJS / 2008. 8. 7. 10:30

일전에 포스팅한 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 등이 더 추가되었다.

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






오픈소스 라이센스에 대한 이해~!! 는 다음 파일을 참조하세요








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

extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Extjs 3.0 Roadmap  (10) 2008.11.12
ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |


자바스크립트 최적화하기 코드는 웹상에서 비법을 전수해 주는 사이트는 많이 있다.
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--) {}

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

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



Post by 넥스트리소프트 데꾸벅(techbug)
, |
이미 아는 사람들은 다아는 로딩바 원하는 형태로 만들기 사이트(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를 모르는 분들은 여기서 부터 시작하세요



Post by 넥스트리소프트 데꾸벅(techbug)
, |
유지보수의 용이성과 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









 
Post by 넥스트리소프트 데꾸벅(techbug)
, |

IE5에서 첫선을 보인 객체.getBoundingClientRect는 객체의 offset의 top, left,width,height를 반환하는 멋진놈이다.
일반적으로 gecko 엔진을 사용하는 FF2에서는 getBoxObjectFor(객체)를 사용했으나
FF3에서는  getBoundingClientRect도 지원한다.

FF3가 출시되자 마자 기존 스크립트들을 많이 바꾸고 있는데 이중에 getBoundingClientRect가 포함되었다.
드디어 FF도 IE의 손을 들어준것인가? IE8에서는 얼마만큼 오픈소스진영의 손을 들어줄것인지 귀추가 주목된다.

Mozilla사이트의 getBoundingClientRect에 관련된 Reference 참조

사용자 삽입 이미지
John Resig의 getBoundingClientRect is Awesome 포스트 참조

데꾸벅이 사용하는 객체의 offset left,top,width, height반환하는 함수
/**
 * tag 객체의 위치값 및 너비/높이값을 반환한다.
 * @param {objId} DOM객체 : document.getElementById()
 * @return {ret} left,top,width,height 를 반환한다.
 * @author 데꾸벅
 */
function getBoundsObject(objId){
    var techbug = new Object();
    var tag = document.getElementById(objId);

    if(tag !=null && tag != undefined ){
        if(tag.getBoundingClientRect){ //IE, FF3
            var rect = tag.getBoundingClientRect();
            techbug.left = rect.left + (document.documentElement.scrollLeft || document.body.scrollLeft);
            techbug.top = rect.top + (document.documentElement.scrollTop || document.body.scrollTop);
            techbug.width = rect.right - rect.left;
            techbug.height = rect.bottom - rect.top +1; // +1 = Moz와 맞춤
        } else  if (document.getBoxObjectFor) { // gecko 엔진 기반 FF3제외
            var box = document.getBoxObjectFor(tag);
            techbug.left = box.x;
            techbug.top = box.y;
            techbug.width = box.width;
            techbug.height = box.height;
        }else {
            techbug.left = tag.offsetLeft;
            techbug.top = tag.offsetTop;
            techbug.width = tag.offsetWidth;
            techbug.height = tag.offsetHeight  + 3;  // +1 = Moz와 맞춤
            var parent = tag.offsetParent;
            if (parent != tag) {
                while (parent) {
                    techbug.left += parent.offsetLeft;
                    techbug.top += parent.offsetTop;
                    parent = parent.offsetParent;
                }
            }
            // 오페라와 사파리의 'absolute' postion의 경우 body의 offsetTop을 잘못 계산 보정
            var ua = navigator.userAgent.toLowerCase();
            if (ua.indexOf('opera') != -1 || ( ua.indexOf('safari') != -1 && getStyle(tag, 'position') == 'absolute' )) {
                techbug.top -= document.body.offsetTop;
            }

        }
        return techbug;
    }
}

Post by 넥스트리소프트 데꾸벅(techbug)
, |

엊그제 Firefox 3.0 정식버전이 출시되자 마자 설치를 했다.
조금더 빨라진거, UI가 조금더 고급(?)스러워진것, 주소표시줄에 사용자접근성을 고려해서 나오는 아이콘들... 뭐 여튼 이건 설치해 보면 자연스레 알게 될것이고..
알려진 버그로는 URL에 한글이 들어가면 깨진다는거... 기본셋팅이 UTF-8이 아니라서 그런거라 about:config에서 변경해주면 그만이겠고....
여튼 다 좋은데.......

IE전용인줄 알았던 window.showModalDialog 로 먹는것이 아닌가? 
항상 스크립트에서..

if(window.showModalDialog) {
  //IE전용
}else {
   //그외 브라우저
}


일케 작업했었는데 하나를 더 주가해 주게 생겼다.

if(window.showModalDialog && document.all) {
  //IE전용
}else {
  //그외 브라우저
}


참고 사이트 : http://developer.mozilla.org/en/docs/DOM:window.showModalDialog

흠.. Mozilla 사이트에서 MSDN 사이트를 참조하란다...  ㅡ.,ㅡ;


그외의 window객체에 새로추가된것들이 많은데..  여기(모질라개발자사이트) 에서 확인하기 바란다.

뭐 간단히 정리하면 ...
window.applicationCache
window.fullScreen
window.returnValue
window.postMessage <-- 이녀석 드디어 추가됐다..


IE6, IE7에서 지원하던 스크립트도 FF3에서 지원을 하기 시작한 메쏘드나 프로퍼티가 있는데 아래 사이트를 참조하면 도움이 될듯하다.
DOM Client Object Cross-Reference:document







사용자 삽입 이미지




Post by 넥스트리소프트 데꾸벅(techbug)
, |

유독 IE6에서 position:fixed로 했을 경우 고정되지 않는 버그패치
javascript로 패치할 경우 : http://www.doxdesk.com/file/software/js/fixed.js 참조

* { margin:0; padding:0;} /* html, body 사이의 간격을 제거 */
html { _overflow-y:hidden;} /* 기본 스크롤 제거 */
body{ height:100%; _overflow-y:auto;} /* 대체 스크롤 생성 */
#content { width:580px; height:1000px; margin:20px; padding:10px; background:#eeeeee;}
#aside { position:fixed; _position:absolute; left:650px; top:20px; width:100px; height:300px; padding:10px; background:#dddddd;}

일케 해봤으나 스크롤이 이상하게 생기는 문제가 발생하여


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<style>
html { filter: expression(document.execCommand("BackgroundImageCache", false, true)); _overflow: hidden;  }
:root html { overflow: hidden; }
body { _overflow: hidden; height: 100%; width: 100%; }
:root body { overflow: hidden; }
#dvwrap { width: 100%; height: 100%; overflow-y: scroll; overflow-x: hidden; position:relative; z-index:1; /* padding-bottom: 40px; */}
#SLIPAREA { position:fixed; _position:absolute;  bottom: 0px; width: 900px; z-index: 199; margin: 0 0 -1px 10px; _margin: 0 0 -1px 10px; }
:root #SLIPAREA { width: 100%; }
</style>

<body topmargin="0" leftmargin="0" marginwidth="0" marginheight="0">
<div id="dvwrap">
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
 1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />1<br />
</div>
<div id="SLIPAREA">
 <script type="text/javascript">
 function techbug(){
  var h = document.getElementById('testSlip').style.height;
  if( parseInt(h) < 30 )
   document.getElementById('testSlip').style.height = "200px";
  else
   document.getElementById('testSlip').style.height = "20px";

 }
 </script>
 <div id="testSlip" style="border:#000 solid 1px width:888px;height:20px;background:orange">
  <a href="javascript:techbug();">눌러</a>
 </div>

</div>
</body>
</html>



샘플 :



 

Post by 넥스트리소프트 데꾸벅(techbug)
, |