블로그 이미지

카테고리

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

최근에 올라온 글

최근에 달린 댓글

'Scripter'에 해당되는 글 97건

  1. 2008.03.06 자바스크립트로 구현한 3D 포토갤러리
  2. 2008.03.06 JsonSQL: JSON parser, SQL style
  3. 2008.03.06 Native CSS selectors with querySelector
  4. 2008.03.06 IE minimize 2
  5. 2008.03.03 Javascript Performance Stack 2
  6. 2008.02.28 Extjs 크로스 도메인 관련
  7. 2008.02.27 Extjs 한국어로 나타내기 (locale설정) 9
  8. 2008.02.26 ExtJS을 이용한 RestFul한 통신하기 2
  9. 2008.02.26 GWT Ext 2.0 이 릴리즈됐다.
  10. 2008.02.26 ExtJS를 지원하는 IDE 개발이 시작되다.
  11. 2008.02.26 ExtTLD : XML을 이용한 Extjs 컴포넌트 생성
  12. 2008.02.26 Extjs를 이용한 PHP 플러그인 Simplicity
  13. 2008.02.26 Extjs v2.02 릴리즈
  14. 2008.02.25 Extjs 기본레이아웃에 grid panel 붙이기
  15. 2008.02.25 Extjs Apatana ext2.0 plugin
  16. 2008.02.22 Extjs Grid panel 및 데이타 축출하기 8
  17. 2008.02.22 Extjs 레이아웃에서 center region 숨기기 꽁수
  18. 2008.02.21 Extjs 그리드안에서 프로세스바 구현하기 3
  19. 2008.02.21 Extjs 프로세스바 구현하기
  20. 2008.02.21 Extjs을 이용한 특정이벤트 컨텍스트 메뉴달기
  21. 2008.02.20 Extjs와 Adobe AIR를 이용한 Window.gadget만들기
  22. 2008.02.19 Extjs ASP.NET Web Controls
  23. 2008.02.18 Extjs를 이용한 에러핸들링 2
  24. 2008.02.18 Extjs 서로 다른 도메인일 경우 ScriptTagProxy를 이용한 Ajax통신 5
  25. 2008.02.18 팁 : 크로스브라우저 지원 스크롤바 바꾸기
  26. 2008.02.18 Extjs Sample :: Viewport를 이용한 기본레이아웃잡기 3
  27. 2008.02.18 Extjs Sample :: 기본문서구조만들기
  28. 2008.02.18 Extjs Sample :: 기본문서구조
  29. 2008.02.18 Extjs Qtips 사용하기
  30. 2008.02.18 Extjs Template 사용하기
Gerard Ferrandez의 순수 자바스크립트로 구현한 3D 포토갤러리


사용자 삽입 이미지

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"
>
<html>
<head>
<title>autumn II - interactive DHTML</title>
<meta name="Author" content="Gerard Ferrandez at http://www.dhteumeuleu.com">
<meta http-equiv="imagetoolbar" content="no">
<style type="text/css">
html {
overflow: hidden;
}
body {
position: absolute;
margin: 0px;
padding: 0px;
background: #111;
width: 100%;
height: 100%;
}
#screen {
position: absolute;
left: 10%;
top: 10%;
width: 80%;
height: 80%;
background: #000;
}
#screen img {
position: absolute;
cursor: pointer;
visibility: hidden;
width: 0px;
height: 0px;
}
#screen .tvover {
border: solid #876;
opacity: 1;
filter: alpha(opacity=100);
}
#screen .tvout {
border: solid #fff;
opacity: 0.7;
}
#bankImages {
display: none;
}
</style>

<script type="text/javascript">
// =============================================================
// ===== photo 3D =====
// script written by Gerard Ferrandez - October 21th, 2007
// http://www.dhteumeuleu.com
// =============================================================

/* ==== library ==== */
var Library = {};
Library.ease = function () {
this.target = 0;
this.position = 0;
this.move = function (target, speed)
{
this.position += (target - this.position) * speed;
}
}

var tv = {
/* ==== variables ==== */
O : [],
screen : {},
grid : {
size : 4, // 4x4 grid
borderSize : 6, // borders size
zoomed : false
},
angle : {
x : new Library.ease(),
y : new Library.ease()
},
camera : {
x : new Library.ease(),
y : new Library.ease(),
zoom : new Library.ease(),
focalLength : 750 // camera Focal Length
},

/* ==== init script ==== */
init : function ()
{
this.screen.obj = document.getElementById('screen');
var img = document.getElementById('bankImages').getElementsByTagName('img');
this.screen.obj.onselectstart = function () { return false; }
this.screen.obj.ondrag = function () { return false; }
/* ==== create images grid ==== */
var ni = 0;
var n = (tv.grid.size / 2) - .5;
for (var y = -n; y <= n; y++)
{
for (var x = -n; x <= n; x++)
{
/* ==== create HTML image element ==== */
var o = document.createElement('img');
var i = img[(ni++) % img.length];
o.className = 'tvout';
o.src = i.src;
tv.screen.obj.appendChild(o);
/* ==== 3D coordinates ==== */
o.point3D = {
x : x,
y : y,
z : new Library.ease()
};
/* ==== push object ==== */
o.point2D = {};
o.ratioImage = 1;
tv.O.push(o);
/* ==== on mouse over event ==== */
o.onmouseover = function ()
{
if (!tv.grid.zoomed)
{
if (tv.o)
{
/* ==== mouse out ==== */
tv.o.point3D.z.target = 0;
tv.o.className = 'tvout';
}
/* ==== mouse over ==== */
this.className = 'tvover';
this.point3D.z.target = -.5;
tv.o = this;
}
}
/* ==== on click event ==== */
o.onclick = function ()
{
if (!tv.grid.zoomed)
{
/* ==== zoom in ==== */
tv.camera.x.target = this.point3D.x;
tv.camera.y.target = this.point3D.y;
tv.camera.zoom.target = tv.screen.w * 1.25;
tv.grid.zoomed = this;
} else {
if (this == tv.grid.zoomed){
/* ==== zoom out ==== */
tv.camera.x.target = 0;
tv.camera.y.target = 0;
tv.camera.zoom.target = tv.screen.w / (tv.grid.size + .1);
tv.grid.zoomed = false;
}
}
}
/* ==== 3D transform function ==== */
o.calc = function ()
{
/* ==== ease mouseover ==== */
this.point3D.z.move(this.point3D.z.target, .5);
/* ==== assign 3D coords ==== */
var x = (this.point3D.x - tv.camera.x.position) * tv.camera.zoom.position;
var y = (this.point3D.y - tv.camera.y.position) * tv.camera.zoom.position;
var z = this.point3D.z.position * tv.camera.zoom.position;
/* ==== perform rotations ==== */
var xy = tv.angle.cx * y - tv.angle.sx * z;
var xz = tv.angle.sx * y + tv.angle.cx * z;
var yz = tv.angle.cy * xz - tv.angle.sy * x;
var yx = tv.angle.sy * xz + tv.angle.cy * x;
/* ==== 2D transformation ==== */
this.point2D.scale = tv.camera.focalLength / (tv.camera.focalLength + yz);
this.point2D.x = yx * this.point2D.scale;
this.point2D.y = xy * this.point2D.scale;
this.point2D.w = Math.round(
Math.max(
0,
this.point2D.scale * tv.camera.zoom.position * .8
)
);
/* ==== image size ratio ==== */
if (this.ratioImage > 1)
this.point2D.h = Math.round(this.point2D.w / this.ratioImage);
else
{
this.point2D.h = this.point2D.w;
this.point2D.w = Math.round(this.point2D.h * this.ratioImage);
}
}
/* ==== rendering ==== */
o.draw = function ()
{
if (this.complete)
{
/* ==== paranoid image load ==== */
if (!this.loaded)
{
if (!this.img)
{
/* ==== create internal image ==== */
this.img = new Image();
this.img.src = this.src;
}
if (this.img.complete)
{
/* ==== get width / height ratio ==== */
this.style.visibility = 'visible';
this.ratioImage = this.img.width / this.img.height;
this.loaded = true;
this.img = false;
}
}
/* ==== HTML rendering ==== */
this.style.left = Math.round(
this.point2D.x * this.point2D.scale +
tv.screen.w - this.point2D.w * .5
) + 'px';
this.style.top = Math.round(
this.point2D.y * this.point2D.scale +
tv.screen.h - this.point2D.h * .5
) + 'px';
this.style.width = this.point2D.w + 'px';
this.style.height = this.point2D.h + 'px';
this.style.borderWidth = Math.round(
Math.max(
this.point2D.w,
this.point2D.h
) * tv.grid.borderSize * .01
) + 'px';
this.style.zIndex = Math.floor(this.point2D.scale * 100);
}
}
}
}
/* ==== start script ==== */
tv.resize();
mouse.y = tv.screen.y + tv.screen.h;
mouse.x = tv.screen.x + tv.screen.w;
tv.run();
},

/* ==== resize window ==== */
resize : function ()
{
var o = tv.screen.obj;
tv.screen.w = o.offsetWidth / 2;
tv.screen.h = o.offsetHeight / 2;
tv.camera.zoom.target = tv.screen.w / (tv.grid.size + .1);
for (tv.screen.x = 0, tv.screen.y = 0; o != null; o = o.offsetParent)
{
tv.screen.x += o.offsetLeft;
tv.screen.y += o.offsetTop;
}
},

/* ==== main loop ==== */
run : function ()
{
/* ==== motion ease ==== */
tv.angle.x.move(-(mouse.y - tv.screen.h - tv.screen.y) * .0025, .1);
tv.angle.y.move( (mouse.x - tv.screen.w - tv.screen.x) * .0025, .1);
tv.camera.x.move(tv.camera.x.target, tv.grid.zoomed ? .25 : .025);
tv.camera.y.move(tv.camera.y.target, tv.grid.zoomed ? .25 : .025);
tv.camera.zoom.move(tv.camera.zoom.target, .05);
/* ==== angles sin and cos ==== */
tv.angle.cx = Math.cos(tv.angle.x.position);
tv.angle.sx = Math.sin(tv.angle.x.position);
tv.angle.cy = Math.cos(tv.angle.y.position);
tv.angle.sy = Math.sin(tv.angle.y.position);
/* ==== loop through all images ==== */
for (var i = 0, o; o = tv.O[i]; i++)
{
o.calc();
o.draw();
}
/* ==== loop ==== */
setTimeout(tv.run, 32);
}
}

/* ==== global mouse position ==== */
var mouse = {
x : 0,
y : 0
}
document.onmousemove = function(e)
{
if (window.event) e = window.event;
mouse.x = e.clientX;
mouse.y = e.clientY;
return false;
}

</script>
</head>

<body>

<div id="screen"></div>

<div id="bankImages">
<img alt="" src="wi23.jpg">
<img alt="" src="wt06.jpg">
<img alt="" src="wt47.jpg">
<img alt="" src="wt16.jpg">

<img alt="" src="wt43.jpg">
<img alt="" src="wt19.jpg">
<img alt="" src="wt27.jpg">
<img alt="" src="wt46.jpg">

<img alt="" src="wt14.jpg">
<img alt="" src="wt21.jpg">
<img alt="" src="wt35.jpg">
<img alt="" src="wt48.jpg">

<img alt="" src="wt55.jpg">
<img alt="" src="wt40.jpg">
<img alt="" src="wt53.jpg">
<img alt="" src="wt25.jpg">

</div>

<script type="text/javascript">
/* ==== start script ==== */
onresize = tv.resize;
tv.init();
</script>

</body>
</html>

출처 : dhteumeuleu소스다운로드 :






Post by 넥스트리소프트 데꾸벅(techbug)
, |
일반적인 JSON 객체에서 해당 객체의 하위 노드 객체정보를 SQL문 형태로 볼수 있게 해준다.
사용법:
jsonsql.query("select * from json.channel.items order by title desc", json);
jsonsql.query("select title,url from json.channel.items where (category=='javascript' || category=='vista') order by title,category asc limit 3", json);

기본소스


var jsonsql = {
       
    query: function(sql,json){

       var returnfields = sql.match(/^(select)\s+([a-z0-9_\,\.\s\*]+)\s+from\s+([a-z0-9_\.]+)(?: where\s+\((.+)\))?\s*(?:order\sby\s+([a-z0-9_\,]+))?\s*(asc|desc|ascnum|descnum)?\s*(?:limit\s+([0-9_\,]+))?/i);
       
        var ops = {
            fields: returnfields[2].replace(' ','').split(','),
            from: returnfields[3].replace(' ',''),
            where: (returnfields[4] == undefined)? "true":returnfields[4],
            orderby: (returnfields[5] == undefined)? []:returnfields[5].replace(' ','').split(','),
            order: (returnfields[6] == undefined)? "asc":returnfields[6],
            limit: (returnfields[7] == undefined)? []:returnfields[7].replace(' ','').split(',')
        };

        return this.parse(json, ops);       
    },
   
    parse: function(json,ops){
        var o = { fields:["*"], from:"json", where:"", orderby:[], order: "asc", limit:[] };
        for(i in ops) o[i] = ops[i];

        var result = [];       
        result = this.returnFilter(json,o);
        result = this.returnOrderBy(result,o.orderby,o.order);
        result = this.returnLimit(result,o.limit);
               
        return result;
    },
   
    returnFilter: function(json,jsonsql_o){
       
        var jsonsql_scope = eval(jsonsql_o.from);
        var jsonsql_result = [];
        var jsonsql_rc = 0;

        if(jsonsql_o.where == "")
            jsonsql_o.where = "true";

        for(var jsonsql_i in jsonsql_scope){
            with(jsonsql_scope[jsonsql_i]){
                if(eval(jsonsql_o.where)){
                    jsonsql_result[jsonsql_rc++] = this.returnFields(jsonsql_scope[jsonsql_i],jsonsql_o.fields);
                }
            }
        }
       
        return jsonsql_result;
    },
   
    returnFields: function(scope,fields){
        if(fields.length == 0)
            fields = ["*"];
           
        if(fields[0] == "*")
            return scope;
           
        var returnobj = {};
        for(var i in fields)
            returnobj[fields[i]] = scope[fields[i]];
       
        return returnobj;
    },
   
    returnOrderBy: function(result,orderby,order){
        if(orderby.length == 0)
            return result;
       
        result.sort(function(a,b){   
            switch(order.toLowerCase()){
                case "desc": return (eval('a.'+ orderby[0] +' < b.'+ orderby[0]))? 1:-1;
                case "asc":  return (eval('a.'+ orderby[0] +' > b.'+ orderby[0]))? 1:-1;
                case "descnum": return (eval('a.'+ orderby[0] +' - b.'+ orderby[0]));
                case "ascnum":  return (eval('b.'+ orderby[0] +' - a.'+ orderby[0]));
            }
        });

        return result;   
    },
   
    returnLimit: function(result,limit){
        switch(limit.length){
            case 0: return result;
            case 1: return result.splice(0,limit[0]);
            case 2: return result.splice(limit[0]-1,limit[1]);
        }
    }
   
};


참조웹사이트 : trentrichardsonhttp://www.trentrichardson.com/jsonsql/

Post by 넥스트리소프트 데꾸벅(techbug)
, |
Ext2.0의 Dom Query와 비슷하게 사파리에서 querySelector 와 querySelectorAll 메쏘드를 정의하다.
흠..이게 표준일줄이야 이제야 알다니...
2007년 12월 21일에 W3C에 Working Draft되어 있다. (URL : http://www.w3.org/TR/selectors-api/ )

많은 자바스크립트 라이브러리들이 이러한 돔쿼리를 제공하고 있는데
prototype.js, jQuery, Ext2.0 의 speed test 결과 Ext2.0이 조금 빨랐다..(단 내경우에... 크~ : IE7 on Vista)
넥스트리 내부에서 AJAX Framework 벤치마킹할때 이점도 가만이 됐는 부분이라 다시한번 정리해본다.


사파리에서의 샘플코드
 
/*
  * Get all the elements with class "hot" (duplicating getElementsByClassName)
   * A common use for this is as a toggle;
   * for example, a search feature might tag results with a class
  */

  document.querySelectorAll(".hot");

  /*
   * Get the currently hovered element
   */

  document.querySelector(":hover");

  /*
   * Get every other element in the <li> with id "large"
   * This is mostly useful for doing "zebra stripe" alternating rows.
   * Once CSS3 becomes more widespread, doing this directly via CSS will be more practical
  */

  document.querySelectorAll("#large:nth-child(even)");


Ext2.0에서 번역했던 DomQuery부분 : http://extjs.com/learn/Tutorial:DomQuery_Basics%28Korean%29


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

IE minimize

Scripter/JAVASCRIPT / 2008. 3. 6. 09:50
자바스크립트만으로 브라우저 최소화 최대화를 구현해보고 싶어 소스를 찾다 괜찮은 것들만 추려본다.
(IE6이전 버전 기준)


<script type="text/javascript">
function minimize() {
    document.getElementById("minimizer").Click();
}
</script>

<span onclick="minimize()" style="cursor:pointer;">최소화</span>

<object id="minimizer" type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Minimize">
</object>


<SCRIPT LANGUAGE="JavaScript">

function Minimize()
{
window.innerWidth = 100;
window.innerHeight = 100;
window.screenX = screen.width;
window.screenY = screen.height;
alwaysLowered = true;
}

function Maximize()
{
window.innerWidth = screen.width;
window.innerHeight = screen.height;
window.screenX = 0;
window.screenY = 0;
alwaysLowered = false;
}
</SCRIPT>

<A HREF="javascript:onClick=Minimize()">Minimize</A>
<A HREF="javascript:onClick=Maximize()">Maximize</A>
출처 : htmlgoodies



최소화가 아니라 다른창 뒤에 숨기기위해 간단한 소스
function Minimize(){
 window.blur()
}
출처 : rahmanhadi








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

자바스크립트의 퍼포먼스는 대부분 브라우저에 의해 결정된다.


사용자 삽입 이미지



































끊임없이 회자되어 왔던 자바스크립트 퍼포먼스에 대한 이슈는 다음과 같은 중요한 키로 정리될수 있다.
  • 자바스크립트
  • 객체모델(Object Models : XMLHttpRequest, Timers, Browser, DOM and CSS)
  • Parsing
  • Rendering

흥미로운것은 브라우저안에서 퍼포먼스에 중요하게 영향을 끼치는것으로 자바스크립트를 삼았다.( by John resig) 그러나 자바스크립트는 철저하게 퍼포먼스를 극복하기위해 많이 향상되어왔으면 최근에는 DOM,CSS, 렌더링레이어에서 바틀렉(bottlenecks)이 걸린다는것이다.
여기서 흥미로운 이슈사항이 대두되는데.
  • 브라우저 밖에서의 자바스크립트 퍼포먼스는 브라우저안쪽에서의 그것보다 더 빠르다는것이다. 객체모델의 오버헤드와 객체모델과 관련된 중요한 보안체크에 대한것은 매우 다르다는것이다.
  • 적절하지 못한 자바스크립트코딩은 렌더링엔진에 퍼포먼스에 많은 영향을 끼친다.

원본 : http://ejohn.org/blog/javascript-performance-stack/


윗글에서도 보다시피 자바스크립트의 퍼포먼스는 어떤 브라우저를 선택하느냐에 따라 크게 달라질수도 있다는것이다.
이번에 버전업된 firefox3 의 퍼포먼스가 상당히 향상되었다는 오늘자 ajaxian에 포스팅글을 인용한다.

사용자 삽입 이미지
















윈도우머신에서 테스트한 선스파이터 자바스크립트 벤치마크 결과에 의하면
이번 파이어폭스3의 성능은 이전버전 2.0.0.12와는 달리 크게 향상되었다는것을 느낄수 있다.  파이어폭스 Nightly(어떻게 번역해야 하는거야..매일빌드? ) 버전이 나오기전에는 오페라9.5.9807이 제일 빠른것이였는데 브라우저 시장에서 다시한번 파이어폭스의 선전이 기대된다.


  1. Firefox 3 Nightly (PGO Optimized): 7263.8ms
  2. Firefox 3 Nightly (02/25/2008 build): 8219.4ms
  3. Opera 9.5.9807 Beta: 10824.0ms
  4. Firefox 3 Beta 3: 16080.6ms
  5. Safari 3.0.4 Beta: 18012.6ms
  6. Firefox 2.0.0.12: 29376.4ms
  7. Internet Explorer 7: 72375.0ms






Post by 넥스트리소프트 데꾸벅(techbug)
, |
서버없이 ExtJS를 사용하고 싶다면 아래 파일을 다운로드 받아 HTML에 넣고 사용한다.
사내솔루션(넥스트리)개발시 서버와 상관없이 작업을 하려다 찾은소스이다.




소스를 까(?)보면 Ext.lib.Ajax 부분을 오버라이드하고 있는데 기본적으로 크로스 도메인에서는 다른 프록시를 써서 해당 우회하는 방법으로 크로스도메인을 사용한다.
그러나 ExtJS에서는 로컬을 우회 프록시로 사용하는갑다..

중간에 XhrHeader값을 보면 "X-Requested-With"를 보면 커스텀헤더를 생성해서 사용하는것을 볼수 있는데 이것은 Prototype.js나 jQuery에서도 이와 같은 방법을 사용하고 있다.

제로보드에도 이러한 스크립트가 있던데 함 뜯어봐야겠다..


[실제 PHP로 헤더값만들기]
function isAjax() {
 return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
     $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest';
}


[사용법]
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="../extjs/adapter/ext/localXHR.js"></script>


[실제 localXHR.js 소스]

Ext.apply( Ext.lib.Ajax ,
{ forceActiveX:false,
  createXhrObject:function(transactionId)
        {
            var obj={  status:{isError:false}
                     , tId:transactionId}, http;
            try
            {
               
        if(Ext.isIE7 && !!this.forceActiveX){throw("IE7forceActiveX");}
               
        obj.conn= new XMLHttpRequest();
       
            }
            catch(e)
            {
                for (var i = 0; i < this.activeX.length; ++i) {
                    try
                    {
                        obj.conn= new ActiveXObject(this.activeX[i]);
           
                        break;
                    }
                    catch(e) {
                    }
                }
            }
            finally
            {
                obj.status.isError = typeof(obj.conn) === undefined;
            }   
            return obj;
           
        },
       
        getHttpStatus: function(reqObj){
       
            var statObj = {  status:0
                    ,statusText:''
                    ,isError:false
                    ,isLocal:false
                    ,isOK:false
                    ,error:null};
           
            try {
                if(!reqObj)throw('noobj');
                statObj.status = reqObj.status;
               
                statObj.isLocal = !reqObj.status && location.protocol == "file:" ||
                           Ext.isSafari && reqObj.status === undefined;
               
                statObj.isOK = (statObj.isLocal || (statObj.status > 199 && statObj.status < 300));
                statObj.statusText = reqObj.statusText || '';
            } catch(e){ //지원하지 않는 status 혹은 너무 빨리 호출했을 경우
              }
           
            return statObj;
       
        },
        handleTransactionResponse:function(o, callback, isAbort)
        {
   
       
        callback = callback || {};
        var responseObject=null;
       
         if(!o.status.isError){
            o.status = this.getHttpStatus(o.conn);        
            /* 필요하다면 적당한 status와 XMLDOM을 이용하여 생성하거나 향상시킨다.*/
             responseObject = this.createResponseObject(o, callback.argument);
         }
       
          if(o.status.isError){
            /* exception이 발생했을 경우 다시 체크한다.*/
           responseObject = Ext.applyIf(responseObject||{},this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false)));
          
         }
       
         responseObject.options = o.options;
         responseObject.stat = o.status;
         
         if (o.status.isOK && !o.status.isError) {
            if (callback.success) {
                if (!callback.scope) {
                    callback.success(responseObject);
                }
                else {
                    callback.success.apply(callback.scope, [responseObject]);
                }
            }
          } else {

            if (callback.failure) {
                if (!callback.scope) {
                    callback.failure(responseObject);
                }
                else {
                    callback.failure.apply(callback.scope, [responseObject]);
                }
            }

         }
       
        if(o.options.async){
            this.releaseObject(o);   
            responseObject = null;
        }else{
            this.releaseObject(o);
            return responseObject;
        }
           
    },
    createResponseObject:function(o, callbackArg)
    {
        var obj = {};
        var headerObj = {},headerStr='';

        try{  //잘못된 인코딩일경우
        obj.responseText = o.conn.responseText;
        }catch(e){obj.responseText ='';}

        obj.responseXML = o.conn.responseXML;

        try{
        headerStr = o.conn.getAllResponseHeaders()||'';
        } catch(e){}

        if(o.status.isLocal){

           o.status.isOK = ((o.status.status = (!!obj.responseText.length)?200:404) == 200);

           if(o.status.isOK && (!obj.responseXML || obj.responseXML.childNodes.length == 0)){

            var xdoc=null;
            try{   //MS ActiveX가 작동하지 않을 경우
                if(typeof(DOMParser) == 'undefined'){
                    xdoc=new ActiveXObject("Microsoft.XMLDOM");
                    xdoc.async="false";
                    xdoc.loadXML(obj.responseText);

                }else{
                    try{  //Opera 9 가 xml contents 파싱에 실패할 경우
                        var domParser = new DOMParser();
                        xdoc = domParser.parseFromString(obj.responseText, 'application\/xml');
                    }catch(ex){}
                    finally{domParser = null;}

                }
            } catch(ex){
                o.status.isError = true;
                o.status.error = ex;

                }

            obj.responseXML = xdoc;
            }

            if(obj.responseXML){

            var parseBad = (obj.responseXML.parseError || 0) != 0 || obj.responseXML.childNodes.length == 0;
            if(!parseBad){
                headerStr = 'Content-Type: ' + (obj.responseXML.contentType || 'text\/xml') + '\n' + headerStr ;
                }               
            }       


        }   

       var header = headerStr.split('\n');
       for (var i = 0; i < header.length; i++) {
            var delimitPos = header[i].indexOf(':');
            if (delimitPos != -1) {
            headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
            }
        }

        obj.tId = o.tId;
        obj.status = o.status.status;
        obj.statusText = o.status.statusText;
        obj.getResponseHeader = headerObj;
        obj.getAllResponseHeaders = headerStr;
        obj.stat = o.status

        if (typeof callbackArg !== undefined) {
        obj.argument = callbackArg;
        }

        return obj;
        },
       
    request : function(method, uri, cb, data, options) {
               
                 options = Ext.apply({async:true,
               headers:false,
               userId:null,
               password:null,
               xmlData:null }, options||{});
                                       
                    var hs = options.headers;
                    if(hs){
                        for(var h in hs){
                            if(hs.hasOwnProperty(h)){
                                this.initHeader(h, hs[h], false);
                            }
                        }
                    }
                    if(options.xmlData){
                        this.initHeader('Content-Type', 'text/xml', false);
                        method = 'POST';
                        data = options.xmlData;
                    }
                               
            return this.makeRequest(method, uri, cb, data, options);
           
        },
        asyncRequest:function(method, uri, callback, postData)
        {
            var o = this.getConnectionObject();

            if (!o || o.status.isError) {
                return null;
            }
            else {
                o.options = options;
                try{
            o.conn.open(method, uri, true);
        } catch(ex){
            o.status.isError = true;
            o.status.error = ex;
            return Ext.apply(o,this.handleTransactionResponse(o, callback));
           
        }
       
       
        if (this.useDefaultXhrHeader) {
            if (!this.defaultHeaders['X-Requested-With']) {
            this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
            }
        }

        if(postData && this.useDefaultHeader){
            this.initHeader('Content-Type', this.defaultPostHeader);
        }

         if (this.hasDefaultHeaders || this.hasHeaders) {
            this.setHeader(o);
        }

        this.handleReadyState(o, callback);
       
        try{ o.conn.send(postData || null);
        } catch(ex){
            o.status.isError=true;
            o.status.error = ex;
            return Ext.apply(o,this.handleTransactionResponse(o, callback));
        }
           
                   
        return o;
            }
        },
       
        makeRequest:function(method, uri, callback, postData, options)
        {
            var o = this.getConnectionObject();
                    
            if (!o || o.status.isError) {
                return null;
            }
            else {
                o.options = options;   
                try{
            o.conn.open(method, uri, options.async, options.userId, options.password);
        } catch(ex){
            o.status.isError = true;
            o.status.error = ex;
            var r=this.handleTransactionResponse(o, callback);
            return Ext.apply(o,r);
        }

        if (this.useDefaultXhrHeader) {
            if (!this.defaultHeaders['X-Requested-With']) {
            this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
            }
        }

        if(postData && this.useDefaultHeader){
            this.initHeader('Content-Type', this.defaultPostHeader);
        }

         if (this.hasDefaultHeaders || this.hasHeaders) {
            this.setHeader(o);
        }

        if(o.options.async){ //blocking call때문에 Timer가 작동하지 않음
            this.handleReadyState(o, callback);
        }
       
        try{ o.conn.send(postData || null);
        } catch(ex){
            //Ext.apply(o,this.handleTransactionResponse(o, callback));
        }
               
        return options.async?o:Ext.apply(o,this.handleTransactionResponse(o, callback));
            }
   }});
   
Ext.lib.Ajax.forceActiveX = (document.location.protocol == 'file:');

Post by 넥스트리소프트 데꾸벅(techbug)
, |
한국어 로케일 설정파일을 수정할 일이 생겨 뜯어보다 보니
이번 2.0.2 버전에 추가된 부분은 빠져있는 것을 발견했다.
extjs의 locale 담당 개발자에게 메일을 보내 다음 패키징때 추가적으로 넣어달라고 부탁했는데 다음 패키징땐 소스에 데꾸벅이란 닉을 발견할수  있을까? 크크크

사용자 삽입 이미지













위와 같이 사용하려면 HTML에
    <script type="text/javascript" src="../../ext-all.js"></script>
    <script type="text/javascript" src="../../source/locale/ext-lang-ko.js" charset="utf-8"></script>
와 같이 넣으면 된다.


Post by 넥스트리소프트 데꾸벅(techbug)
, |
ExtJS에서 RESTful하게 HTTP status를 관리하게 생겼다. ㅠ.,ㅠ;
API 및 ExtJS의 코어을 뜯어본 결과 이넘들은 xhr.status >=200 && xhr.status <300 인경우만 처리하고 나머지는 모두 xhr.status 를 13030 으로 주고 있었다.

ext-base.js 에 보면 아래와 같이 처리 ㅠ.,ㅠ;

{
            if (!J) {
                this.releaseObject(I);
                return
            }
            var G,
            F;
            try {
                if (I.conn.status !== undefined && I.conn.status != 0) {
                    G = I.conn.status
                } else {
                    G = 13030
                }
            } catch(H) {
                G = 13030
            }
            if (G >= 200 && G < 300) {
                F = this.createResponseObject(I, J.argument);
                if (J.success) {
                    if (!J.scope) {
                        J.success(F)
                    } else {
                        J.success.apply(J.scope, [F])
                    }
                }
            } else {
                switch (G) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    F = this.createExceptionObject(I.tId, J.argument, (E ? E: false));
                    if (J.failure) {
                        if (!J.scope) {
                            J.failure(F)
                        } else {
                            J.failure.apply(J.scope, [F])
                        }
                    }
                    break;
                default:
                    F = this.createResponseObject(I, J.argument);
                    if (J.failure) {
                        if (!J.scope) {
                            J.failure(F)
                        } else {
                            J.failure.apply(J.scope, [F])
                        }
                    }
                }
            }
            this.releaseObject(I);
            F = null
        }



그리고 나서 모두 exception처리를 하고 있었는데

ScriptTagProxy의 경우 loadexception ( Object This, Object o, Object arg, Object e ) 에서 argument형태로 처리되고 있으며
Ext.Ajax의 경우는 requestexception : ( Connection conn, Object response, Object options ) 형태로 처리되고 있다.

기존의 예외처리를 위의 2개 이벤트를 사용해 처리해 줘야 하는데 아래와 같이 처리하면 되겠다..  ....

 Ext.Ajax.on('requestexception', function(c, r, o) {
            debugger;
            if (r.status == 402) {
              
                //alert('Your session has expired');
              
            }
            else{
                  var error = r.responseText;
                 if (!error) error = r.statusText;
                 App.aSendError.createWindow(error);
                
            }
        })


Ext.Ajax.on('beforerequest', function(conn, options) {
console.log(arguments);
} );

Ext.Ajax.on('requestcomplete', function(conn, response, options) {
console.log(arguments);
} );

Ext.Ajax.on('requestexception', function(conn, response, options) {
console.log(arguments);
} );




Ext.Ajax.request({
       url: '/techbug.json?method=saveBranch',
       success: function(response) {
               branchList_store.load();
               branchList.reconfigure(branchList_store, branchList_cm);
            Ext.MessageBox.alert('Status', 'Your changes were saved successfully.');
       },
       failure: function(response) {
               if(response.getResponseHeader.CustomHeader == "585")
                Ext.MessageBox.alert('Status', 'You do not have permission to do this.');
            else
               Ext.MessageBox.alert('Status', 'There was an unknown error saving your changes.<P>Please try again.<P>Detailed Error: ' + response.getResponseHeader.CustomHeaderMessage);
       },
       headers: {
          
       },
       params: {
           ....
       }
})



    Ext.data.HttpProxy.on("loadexception", function(options, response, e) {
        // do stuff for all load failures
    });
    Ext.data.HttpProxy.on("load", function(options, arg) {
        // do stuff for all load successes
    });


 
Ext.Ajax.on('requestexception', function(c, r, o) {
            debugger;
            if (r.status == 402) {
              
                //alert('Your session has expired');
              
            }
            else{
                  var error = r.responseText;
                 if (!error) error = r.statusText;
                 App.aSendError.createWindow(error);
                
            }
        })



// set up the connection to load the initial data
var loadConn = new Ext.data.Connection();
loadConn.request({method: 'POST', timeout: 120000, url: 'get_tasks_for_job.cfm' , params: { client_code: localClient, job: localJob, employee: localEmployee } });
// handle the response
loadConn.on('requestcomplete', function(sender, param) {
    try
    {
        var response = Ext.util.JSON.decode(param.responseText);
    } catch (e) {
        alert('There was a problem loading the tasks for this job.');
        return;
    }
    if (response.success == 'Y') {
        // the init function will run loadData() as needed
        localTaskData = response.data;
        Ext.taskEditApp.main.init();
    } else {
        alert('There was a problem loading the tasks for this job.');
    }
}, { scope: this });
loadConn.on('requestexception', function(conn, response, options) {
    alert('failed!!!');
    for (property in response) {
        alert(property);
        alert(response[property]);
    }       
}, { scope: this }); 

ds.load({params:{start:0, limit:pageCount},
    callback:function(record, options, success){
        if(!success){
            alert('error occure.');
            Ext.get('grid_id').unmask();
        }
    }
});




참고URL :
Post by 넥스트리소프트 데꾸벅(techbug)
, |
이전블로그(http://blog.naver.com/techbug)에 포스팅했던 글을 백업받아 옵니다.
--------[이후]-----------------------


GWT Ext 2.0 이 릴리즈됐다.
2007년 ajax framework and libraries에서 prototype.js, jQuery다음 3위를 차지하더니만 선전하고 있는 extjs 아자...
맞바람을 탄다는 기분은 이런 것이라... 데꾸벅 뽯팅!

관련사이트 :
http://ajaxian.com/archives/gwt-ext-20-released
http://gwt-ext.com/
http://gwt-ext.googlecode.com/files/gwtext-2.0.1.zip

데모 : http://gwt-ext.com/demo/


사용자 삽입 이미지

Post by 넥스트리소프트 데꾸벅(techbug)
, |
ExtJS 관련 IDE를 정리하다. 관련URL : http://ajaxian.com/archives/ext-js-ide-support-roundup
ExtJS 2.0 API탑재 Aptana plug-in download : http://orsox.mocis.at/download.php?view.1

사용자 삽입 이미지




There’s been a lot of talk lately about the different IDEs and the support they offer for the various JavaScript libraries. Ext’s uber-coder, Jack Slocum, has put up a blog entry explaining which IDEs support the Ext JS framework:

The Ext 2.0 API is very extensive and remembering all of the functions, properties or configs available is virtually impossible. The API documentation is very thorough, but it would be nice if IDEs would provide code assist options in JavaScript as they do in other languages such as Java and C#. Luckily, there are some IDEs and plugins available that do just that — and also have direct support for Ext 2.0.

Included in the mix are:

This great news and it shows that IDE vendors are taking JavaScript frameworks seriously. Anything that makes development easier is definitely welcome.



Post by 넥스트리소프트 데꾸벅(techbug)
, |
사용자 삽입 이미지

















Jaroslav Benc는 XML을 이용하여 Java 프로젝트용 ExtJS JSP Taglib 생성기를 만들었다.

아직 완변하진 않지만 그의 사이트에 포스트된 로드맵을 보자면

  • Hibernate integration - HibernateStore component
  • DWR integration etc.
  • Eclipse plugin
  • UX Tags: Ext.ux.*, Ext.portal.*, Ext.feedreader.*, Ext.desktop.*
을 지원한다고 하는데 ExtJS의 config 옵션을 모두 xml 어트리뷰트로 처리가능하며 모든 ID값 (ExtJS에서 Ext.getCmp("id") )으로 처리된


참조사이트 : http://www.exttld.com/index.php

Post by 넥스트리소프트 데꾸벅(techbug)
, |
사용자 삽입 이미지
John Le Drew씨의 Simplicity PHP Application Framework은 규모가 크고 트래픽이 많은 사이트에 적합하다.

이번에 facelift된 그의 사이트는 Ajax UI Library인 extjs를 채택함으로써 좀더 Web2.0 애플리케이션의 면모를 갖추고 있다.
Ajax 인터페이스(extjs)를 이용한 관리자콘솔 및 stub controller, 데이타베이스 모델링까지 포함하고 있다.
소스를 함 뜯어볼 가치는 있는것 같네...




출처 : https://launchpad.net/simplicity/
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Extjs v2.02 릴리즈

Scripter/EXTJS / 2008. 2. 26. 09:29
Adobe AIR를 지원해주는 Extjs 2.02버전이 릴리즈 되었다.

Adobe AIR팀과 ExtJS개발팀의 협업으로 탄생한 2.02버전에서는 AIR API를 완벽지원하고 있다.
오래전 Jack Slocum의 블로그에서 봤던 간단한 태스크관리 샘플을 봤을때의 충격이 채가시지 않은채 발표된 이번 버전에서는 AIR API를 패키징하여 Ext.air 형태로 다음과 같이 배포된다.

- 네이티브 윈도우 관리하기
- AIR beta3에서 소개되었던 데이타베이스 동기화
- 네이티브 드래그엔 드롭과 클립보드 접근
- 사운드 새생
- 시스템 트레이에 AIR 애플리케이션 miniming하기(아래소스 참조)

var win = new Ext.air.NativeWindow({
    id: 'mainWindow',
    instance: window.nativeWindow,
 
    // System tray config
    minimizeToTray: true,
    trayIcon: 'ext-air/resources/icons/extlogo16.png',
    trayTip: 'Simple Tasks',
    trayMenu : [{
        text: 'Open Simple Tasks',
        handler: function(){
            win.activate();
        }
    }, '-', {
        text: 'Exit',
        handler: function(){
            air.NativeApplication.nativeApplication.exit();
        }
    }]
});

트리뷰(treeview) 콤보박스의 리스트트리 샘플
사용자 삽입 이미지

extjs listtree





















Custom Grid Columns
사용자 삽입 이미지



















Switch : 버튼 모음 테두리 (메타포어기능)
사용자 삽입 이미지









Ext V2.02 다운로드 : 다운로드하기(http://extjs.com/download)

데꾸벅 블로그에서 바로 다운로드 받기 :

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

Basic Concept

이번 장은 이전에 작성해놓은 기본레이아웃잡기 및 기본그리드기리기를 이용하여 레이아웃의 좌측영역(west region)에 그리드를 붙이는 작업을 진행한다.
이전장을 충분히 숙지하였다는 가정하에 Grid를 붙여보자

기본소스 :


<그리드 해부도>

사용자 삽입 이미지









Step 1.  왼쪽 기본 패널을 Grid패널로 변경

basicLayout_AttachedGrid.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicLayout_AttachedGrid.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

 

basicLayout_AttachedGrid.js

// 그리드 패널 클래스 시작
LeftArea = function(viewport){ //왼쪽 패널(LeftArea)을 정의한다.
this.viewport = viewport;

this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
// 왼쪽 패널의 config옵션을 정의한다.
LeftArea.superclass.constructor.call(this, {
region: 'west', // 굵게 표시된 부분은 기본레이아웃을 잡는 config옵션이다.
title: 'WEST',
collapsible: true,
collapsed: false,
width: 250,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5',
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, {
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
emptyText: 'No Record found'
})
});
};

//기본레이아웃에서는 Ext.Panel을 상속받았지만
//여기서는 그리드패널이므로 Ext.grid.GridPanel을 상속받음.
Ext.extend(LeftArea, Ext.grid.GridPanel, {
});


// 그리드 패널 클래스 끝



// 메인 레이아웃 클래스 시작
BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();
this.viewport = new Ext.Viewport({
layout: 'border',
items: [this.WestPanel = new LeftArea(this), this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});

this.viewport.doLayout();
this.viewport.syncSize();

// 좌측그리드의 데이타 스토어를 화면을 최초 그릴때 데이타를 로드한다.
this.WestPanel.store.load();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);
// 메인 레이아웃 클래스 끝

 기본레이아웃에서 달라진 부분은 왼쪽 패널(LeftArea)이  기본패널(Ext.Panel)에서 그리드패널(Ext.grid.GridPanel)로 바뀌었을 뿐이다. 

위소스중 기본패널의 config옵션( 굵게 표현된 부분)에 Grid 옵션이 더 추가되었을 뿐이다. 대신 왼쪽 그리드패널의 데이타스토어를 메인레이아웃에서 호출(this.WestPanel.store.load();)하였다.
Step2에서는 좌측 그리드패널 클래스를 별도의 파일로 추가하여 정리해보자

 

Step 2. 좌측그리드패널 클래스 별도 파일로 관리하기

basicLayout_AttachedGrid.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
//좌측 그리드 패널클래스 별도 파일로 분리, 반드시 main 레이아웃 클래스보다 먼저 호출
<script type="text/javascript" src="basicLayout_LeftGrid.js"></script>
<script type="text/javascript" src="basicLayout_AttachedGrid.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
    return {
        init: function(){
            Ext.QuickTips.init();
            this.viewport = new Ext.Viewport({
                layout: 'border',
                items: [this.WestPanel = new LeftArea(this),
                //좌측 그리드 클래스 호출
                this.CenterPanel = new Ext.Panel({
                    region: 'center',
                    title: 'CENTER',
                    layout: 'fit',
                    margins: '5 5 5 0',
                    html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                })]
            });
            this.viewport.doLayout();
            this.viewport.syncSize();
            //좌측 패널의 public method호출
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

basicLayout_LeftGrid.js

LeftArea = function(viewport){

    this.viewport = viewport;

    this.store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({
            url: 'basicGrid.json'
        }),
        sortInfo: {
            field: 'price',
            direction: "DESC"
        },
        reader: new Ext.data.JsonReader({
            root: 'testData'
        }, [{
            name: 'company',
            type: 'string',
            mapping: 'company'
        }, {
            name: 'price',
            type: 'float',
            mapping: 'price'
        }, {
            name: 'change',
            type: 'float',
            mapping: 'change'
        }, {
            name: 'pctChange',
            type: 'float',
            mapping: 'pctChange'
        }, {
            name: 'lastChange',
            type: 'date',
            dateFormat: 'n/j h:ia',
            mapping: 'lastChange'
        }])
    });

    LeftArea.superclass.constructor.call(this, {
        region: 'west',
        title: 'WEST',
        collapsible: true,
        collapsed: false,
        width: 250,
        split: true,
        layout: 'fit',
        margins: '5 0 5 5',
        cmargins: '5 5 5 5',
        store: this.store,
        columns: [{
            id: 'company',
            header: "Company",
            width: 160,
            sortable: true,
            dataIndex: 'company'
        }, {
            header: "Price",
            width: 75,
            sortable: true,
            renderer: 'usMoney',
            dataIndex: 'price'
        }, {
            header: "Change",
            width: 75,
            sortable: true,
            dataIndex: 'change'
        }, {
            header: "% Change",
            width: 75,
            sortable: true,
            dataIndex: 'pctChange'
        }, {
            header: "Last Updated",
            width: 85,
            sortable: true,
            renderer: Ext.util.Format.dateRenderer('m/d/Y'),
            dataIndex: 'lastChange'
        }],
        stripeRows: true,
        autoExpandColumn: 'company',
        loadMask: {
            msg: '데이타 로드중'
        },
        sm: new Ext.grid.RowSelectionModel({
            singleSelect: true
        }),
        view: new Ext.grid.GridView({
            forceFit: true,
            enableRowBody: true,
            emptyText: 'No Record found'
        })
    });
};

Ext.extend(LeftArea, Ext.grid.GridPanel, {
    //public Method 정의 부분
    loadGridDataSet: function(){
        this.store.load();
    }


});


위의 Step1과 변경된것이라고는 좌측그리드패널 클래스를 별도 파일로 분리하고 클래스내의 public 메쏘드를 메인레이아웃에서 호출한다는것이다.
그러나 이 러한 구조의 클래스는 항상 메인 레이아웃에서 특정 레이아웃일때만 사용된다 예를 들어  좌측그리드패널클래스(LeftArea)의 config option중에 레이아웃에 해당하는 값들이 이미 들어가 있기 때문에 다른 구조의 레이아웃에서는 이 클래스를 호출하여 사용할수 없다는 단점이 있다.

 

 

 

Step 3. 클래스 리팩토링

basicLayout_LeftGrid.js
LeftArea = function(viewport, config){

    this.viewport = viewport;
    Ext.apply(this, config);

    this.store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({
            url: 'basicGrid.json'
        }),
        sortInfo: {
            field: 'price',
            direction: "DESC"
        },
        reader: new Ext.data.JsonReader({
            root: 'testData'
        }, [{
            name: 'company',
            type: 'string',
            mapping: 'company'
        }, {
            name: 'price',
            type: 'float',
            mapping: 'price'
        }, {
            name: 'change',
            type: 'float',
            mapping: 'change'
        }, {
            name: 'pctChange',
            type: 'float',
            mapping: 'pctChange'
        }, {
            name: 'lastChange',
            type: 'date',
            dateFormat: 'n/j h:ia',
            mapping: 'lastChange'
        }])
    });
    // 레이아웃과 관련된 부분을 제외하고 순수 그리드 config option 부분만 사용
    LeftArea.superclass.constructor.call(this, {
        store: this.store,
        columns: [{
            id: 'company',
            header: "Company",
            width: 160,
            sortable: true,
            dataIndex: 'company'
        }, {
            header: "Price",
            width: 75,
            sortable: true,
            renderer: 'usMoney',
            dataIndex: 'price'
        }, {
            header: "Change",
            width: 75,
            sortable: true,
            dataIndex: 'change'
        }, {
            header: "% Change",
            width: 75,
            sortable: true,
            dataIndex: 'pctChange'
        }, {
            header: "Last Updated",
            width: 85,
            sortable: true,
            renderer: Ext.util.Format.dateRenderer('m/d/Y'),
            dataIndex: 'lastChange'
        }],
        stripeRows: true,
        autoExpandColumn: 'company',
        loadMask: {
            msg: '데이타 로드중'
        },
        sm: new Ext.grid.RowSelectionModel({
            singleSelect: true
        }),
        view: new Ext.grid.GridView({
            forceFit: true,
            enableRowBody: true,
            emptyText: 'No Record found'
        })
    });
};

Ext.extend(LeftArea, Ext.grid.GridPanel, {
    loadGridDataSet: function(){
        this.store.load();
    }
});



 

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();
//레이아웃과 관련된 내용을 메인레이아웃 클래스 밖으로 빼냄
this.WestPanel = new LeftArea(this, {
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5'
}), this.viewport = new Ext.Viewport({
layout: 'border',
items: [this.WestPanel, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

위 와 같이 작성되었을 경우는 별도 파일로 빼낸 BasicLayout_LeftGrid.js 파일은 다른 곳에서도 재사용할수 있다. 그러나 ExtJS Panel특성상 바로 Ext.apply()를 통하여 해당 config option을 LayoutContainer에 적용할수 없다. 위 소스를 실행 시켜 보면  왼쪽 판넬의 collapseMode가  잘못 적용되어 container 속성까지 변경되었다. 이럴 경우에는 NestedLayout Manager를 사용하여 다음과 같이 표현하면 된다.


basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
    return {
        init: function(){
            Ext.QuickTips.init();
            
            this.viewport = new Ext.Viewport({
                layout: 'border',
                items: [{
                    region: 'west', // 일반 Ext.panel로 설정하여 왼쪽
                    title: 'WEST',
                    collapsible: true,
                    collapsed: false,
                    width: 650,
                    split: true,
                    margins: '5 0 5 5',
                    cmargins: '5 5 5 5',
                    layout: 'border',
                    items: [ // 일반 Ext.panel에 Nested된 좌측패널
                        this.WestPanel = new LeftArea(this, {
                        region: 'center',
                        layout: 'fit',
                        border: false
                    })]
                }, this.CenterPanel = new Ext.Panel({
                    region: 'center',
                    title: 'CENTER',
                    layout: 'fit',
                    margins: '5 5 5 0',
                    html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                })]
            });
            this.viewport.doLayout();
            this.viewport.syncSize();
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

 

Step 4. 그리드 판넬에 새로고침 메뉴넣기

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

 위의 Toolbar 를 basicLayout_LeftGrid.js를 이용하여 붙일수도 있으나 사용하는곳 마다 툴바가 틀릴수 있으므로 되도록이면 좌측 그리드패널 클래스를 호출하는 메인 클래스에 붙이는것이 재사용성에 좋다. 넥스트리소프트의 경우는 모두 좌측그리드패널 클래스 안에 private로 선언되어 있다. 어느것이 좋을지는 사용하는곳마다 틀리므로 해당 상황을 보면서 붙여보도록 한다.

넥스트리의 경우는 모두 클래스안에 private method로 해당 클래스내에서만 사용하는 메쏘드를 두었으며 필요에 따라 외부클래스에서 호출하는 식으로 되어 있다. 취향에 따라 맞춰 하면 될듯... ^^

 

 

 

Step 5. 선택시 우측컨텐츠 판넬(cente region Basic fit layout)의 body 업데이트하기

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
//Row선택시 발생하는 이벤트를 this.updateCenter() 메쏘드에서 실행
this.WestPanel.getSelectionModel().on('rowselect', this.updateCenter, this);
},

// 선택된 Row의 데이타를 가져와서 오른쪽 center Region의 Body에 업데이트한다.
updateCenter: function(selectionModel, index, record){
// 선택된 Row의 데이타를 가져온다.
var data_company = record.get('company');
var data_price = record.get('price');
var data_change = record.get('change');
var data_pctChange = record.get('pctChange');
var data_lastChange = record.get('lastChange');
// update할 HTML생성
var updateHTML = "<table cellpadding=2 cellspacing=1 border=1 width='100%'>" +
"<tr><td>Company</td><td>" +
data_company +
"</td></tr>" +
"<tr><td>Price</td><td>" +
data_price +
"</td></tr>" +
"<tr><td>Change</td><td>" +
data_change +
"</td></tr>" +
"<tr><td>Percentage</td><td>" +
data_pctChange +
"</td></tr>" +
"<tr><td>Last</td><td>" +
data_lastChange +
"</td></tr>" +
"</table>";

Ext.get("_CONTENTS_AREA_").update(updateHTML); //update
},



//다른 방법으로 선택된 데이타 모두 가져오기
updateCenter1: function(selectionModel, index, record){
var updateHTML = "<table cellpadding=2 cellspacing=1 border=1 width='100%'>"
// 선택된 데이타의 컬럼을 모두 가져오기
var cmc = this.WestPanel.getColumnModel().getColumnCount();
for (var i = 0; i < cmc; i++) {
//선택된 데이타의 컬럼Head (그리드 헤더 가져오기)
var columnName = this.WestPanel.getColumnModel().getColumnHeader(i);
//그리드 헤더의 데이타 인덱스 코드 가져오기
var columnCode = this.WestPanel.getColumnModel().getDataIndex(i);
var updateHTML = updateHTML + "<tr><td>" + columnName + "</td><td>" + record.get(columnCode);
+"</td></tr>" //데이타 가져와서 업데이트할 HTML 셋팅하기
}
var updateHTML = updateHTML + "</table>";
Ext.get("_CONTENTS_AREA_").update(updateHTML);

}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

위 의 소스에서 updateCenter() 메쏘드는 this.WestPanel의 데이타 및 데이타인덱스 코드값을 모두 알고 있을 경우(대부분 소스까보면 다 나옴.. ㅡ.,ㅡ;) 사용하기 편리하고 아래의 updateCenter1() 메쏘드의 경우는 데이타 인덱스값을 모르거나 동적으로 필드값들이 셋팅 되는 경우에 상당히 유용하게 사용할수 있다. 넥스트리에서는 용어사전에 등록된 용어필드들을 자동으로 가져올때 사용되었던 소스이다.

 

 

Post by 넥스트리소프트 데꾸벅(techbug)
, |
eclipse로 extjs2.0 버전을 사용하고 싶으면 다음과 같이 하면된다.

extjs2.0 버전 플러그인 다운로드 : http://support.aptana.com/asap/browse/STU-547

사용자 삽입 이미지

extjs2.01 최신버전은 아래에서 다운로드 받으세요!
Post by 넥스트리소프트 데꾸벅(techbug)
, |

들어가며

본 포스팅은 사내(넥스트리소프트) 솔루션 개발시 가이드작성했던 문서를 다시 정리하여 올린것입니다.


기본개념

기본적인 Grid 사용법및 DataStore 및 RowSelectionModel에 대해 알아본다.
GridPanel을 이용하여 데이타스토어에 저장된 데이타를 가져오는 법과 기본적인 그리드 렌더링 방법, 각각의 Row에 대한 Model에서 Data를 축출하는 법에 대해서 기술한다.

아래소스다운로드



 

Step 1. Basic Array Grid

basicGrid.html

<html>
<head>
<title>Basic Grid</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicGrid.js"></script>
</head>
<body id="basicGridBody">
<div id="DIV_GRID_HERE"></div> <!-- Grid를 렌더링 할 위치 -->
</body>
</html>


 

basicGrid.js

BasicGridClass = function(){
    return {
        init: function(){
            //데이타스토어에 사용할 데이타
            var myData = [
                ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
                ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
                ['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
                ['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
                ['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
                ['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
                ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
                ['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
                ['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
                ['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
                ['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
                ['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
                ['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
                ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
                ['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
                ['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
                ['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
                ['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
                ['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
                ['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
                ['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
                ['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
                ['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
                ['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
                ['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
                ['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
                ['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
                ['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
                ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
            ];
                      
            this.store = new Ext.data.SimpleStore({
                fields: [{
                    name: 'company'
                }, //1번째 컬럼을 'company'로 정의
                {
                    name: 'price',
                    type: 'float'
                },//2번째 컬럼을 'price'로 정의하며 데이타 타입은 float으로 정의
                {
                    name: 'change',
                    type: 'float'
                }, {
                    name: 'pctChange',
                    type: 'float'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia'
                }]
            });
            //데이타 스토어 로드하기
            this.store.loadData(myData);
            
            //그리드 그리기
            this.grid = new Ext.grid.GridPanel({
                store: this.store, //그리드에 사용될 데이타 스토어 정의
                columns: [ //그리드 헤드 및 데이타 정제(ColumnModel 정의)
                {
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'pctChange'
                }, {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true, //Row마다 CSS class를 적용하고 싶을때 처리
                autoExpandColumn: 'company', //정의된 ColumnModel에서 자동으로 늘리고 싶은 컬럼
                loadMask: {
                    msg: '데이타 로드중'
                }, //그리드 로드시 화면로딩 indicator설정
                //그리드 선택모델(selectionModel)정의 : RowSelectionMode -> Row별로 처리하겠다.
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true //Row 하나만 선택가능하게 하기
                }),
                viewConfig: { //그리드의 Dataview 설정
                    forceFit: true //가로에 그리드의 크기를 맞춘다.
                },
                height: 350,
                width: 800,
                title: '기본기리드'
            });
            //그리드를 DIV_GRID_HERE 라는 ID값을 가진 객체에 렌더링한다.
            this.grid.render('DIV_GRID_HERE');
            //그리드 로드시 첫번째 Row를 자동으로 선택되게 한다.
            this.grid.getSelectionModel().selectFirstRow();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);


 

 

 

Step 2. How to use HttpProxy & ScriptTagProxy

Step1에서 배열데이타를 사용할때(Ext.data.SimpleStore)와 직접 통신하여 사용할때(Ext.data.Store)를 비교하면서 아래 소스를 보기 바란다.
Simplestore 는 기본적으로 네트웍을 사용하지 않으므로 Proxy를 사용하지 않는다. 그러나 Store, JsonStore, GroupingStore의 경우 Proxy를 사용하여 데이타의 위치를 지정할수 있다.

SimpleStore vs. (Json, Grouping) Store


SimpleStore
(Json, Grouping) Store
Proxy
없음(없을시 HttpProxy사용)
HttpProxy, ScriptTagProxy, MemoryProxy
Reader
ArrayReader
JsonReader, XmlReader, ArrayReader
Load Method
store.loadData(data);
store.load()
Extend
Ext.data.Store
Ext.data.Store

 

 

 

 

 

basicGrid.js

BasicGridClass = function(){
    return {
        init: function(){
            ////DataStore를 정의한다. Ext.data.Store
            this.store = new Ext.data.Store({
                // Data를 가져올 Proxy설정
                proxy: new Ext.data.HttpProxy({
                    url: 'basicGrid.json',
                    method: 'POST'
                }),
                // Client쪽에서 Sort할 경우 소트할 항목
                sortInfo: {
                    field: 'price',
                    direction: "DESC"
                },
                reader: new Ext.data.JsonReader({
                    root: 'testData'
                },
                [{
                    name: 'company'
                }, {
                    name: 'price',
                    type: 'float'
                }, {
                    name: 'change',
                    type: 'float'
                }, {
                    name: 'pctChange',
                    type: 'float'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia'
                }])
            });
            this.grid = new Ext.grid.GridPanel({
                store: this.store,
                columns: [{
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'pctChange'
                }, {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true,
                autoExpandColumn: 'company',
                loadMask: {
                    msg: '데이타 로드중'
                },
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true
                }),
                viewConfig: {
                    forceFit: true
                },
                height: 350,
                width: 800,
                title: '기본 그리드'
            });
           
            this.grid.render('DIV_GRID_HERE');
            this.grid.getSelectionModel().selectFirstRow();
            this.store.load(); //데이타를 로드한다.
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

  HttpProxy나 ScriptTagProxy로 서버 통신을 한다. ScriptTagProxy에 대한 고찰(?) 을 참조하라.

  • HttpProxy : 같은 도에인일 경우
  • ScriptTagProxy: 다른 도메인일 경우
  • MemoryProxy: 메모리에 있는 데이타

 

basicGrid.json
{
    testData: [{
        'company': '3m Co',
        'price': 71.72,
        'change': 0.02,
        'pctChange': 0.03,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Alcoa Inc',
        'price': 29.01,
        'change': 0.42,
        'pctChange': 1.47,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Altria Group Inc',
        'price': 83.81,
        'change': 0.28,
        'pctChange': 0.34,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'American Express Company',
        'price': 52.55,
        'change': 0.01,
        'pctChange': 0.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'American International Group, Inc.',
        'price': 64.13,
        'change': 0.31,
        'pctChange': 0.49,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'AT&T Inc.',
        'price': 31.61,
        'change': -0.48,
        'pctChange': -1.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Boeing Co.',
        'price': 75.43,
        'change': 0.53,
        'pctChange': 0.71,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Caterpillar Inc.',
        'price': 67.27,
        'change': 0.92,
        'pctChange': 1.39,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Citigroup,Inc.',
        'price': 49.37,
        'change': 0.02,
        'pctChange': 0.04,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'E.I. du Pont de Nemours and Company',
        'price': 40.48,
        'change': 0.51,
        'pctChange': 1.28,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Exxon Mobil Corp',
        'price': 68.1,
        'change': -0.43,
        'pctChange': -0.64,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'General Electric Company',
        'price': 34.14,
        'change': -0.08,
        'pctChange': -0.23,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'General Motors Corporation',
        'price': 30.27,
        'change': 1.09,
        'pctChange': 3.74,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Hewlett-Packard Co.',
        'price': 36.53,
        'change': -0.03,
        'pctChange': -0.08,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Honeywell Intl Inc',
        'price': 38.77,
        'change': 0.05,
        'pctChange': 0.13,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Intel Corporation',
        'price': 19.88,
        'change': 0.31,
        'pctChange': 1.58,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'International Business Machines',
        'price': 81.41,
        'change': 0.44,
        'pctChange': 0.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Johnson & Johnson',
        'price': 64.72,
        'change': 0.06,
        'pctChange': 0.09,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'JP Morgan & Chase & Co',
        'price': 45.73,
        'change': 0.07,
        'pctChange': 0.15,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'McDonald\'s Corporation',
        'price': 36.76,
        'change': 0.86,
        'pctChange': 2.40,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Merck & Co., Inc.',
        'price': 40.96,
        'change': 0.41,
        'pctChange': 1.01,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Microsoft Corporation',
        'price': 25.84,
        'change': 0.14,
        'pctChange': 0.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Pfizer Inc',
        'price': 27.96,
        'change': 0.4,
        'pctChange': 1.45,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Coca-Cola Company',
        'price': 45.07,
        'change': 0.26,
        'pctChange': 0.58,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Home Depot,Inc.',
        'price': 34.64,
        'change': 0.35,
        'pctChange': 1.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Procter & Gamble Company',
        'price': 61.91,
        'change': 0.01,
        'pctChange': 0.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'United Technologies Corporation',
        'price': 63.26,
        'change': 0.55,
        'pctChange': 0.88,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Verizon Communications',
        'price': 35.57,
        'change': 0.39,
        'pctChange': 1.11,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Wal-Mart Stores, Inc.',
        'price': 45.45,
        'change': 0.73,
        'pctChange': 1.63,
        'lastChange': '9/1 12:00am'
    }]
}

 

 

 

 

 

Step 3. Data-Mapping and Renderer

BasicGridClass = function(){
    return {
        init: function(){
            //데이타 스토어 정의
            this.store = new Ext.data.Store({
                proxy: new Ext.data.HttpProxy({
                    url: 'basicGrid.json',
                    method: 'POST'
                }),
                sortInfo: {
                    field: 'price',
                    direction: "DESC"
                },
                reader: new Ext.data.JsonReader({
                    root: 'testData'
                }, [{
                    name: 'company',
                    type: 'string',
                    mapping: 'company',
                    convert: this.convertCompany
                }, //응답받은 JSON의 company와 맵핑된다. 맵핑정보 변경을 converting한다.
                {
                    name: 'price',
                    type: 'float',
                    mapping: 'price'
                }, {
                    name: 'change',
                    type: 'float',
                    mapping: 'change'
                }, {
                    name: 'pctChange',
                    type: 'float',
                    mapping: 'pctChange'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia',
                    mapping: 'lastChange'
                }])
            });
            this.grid = new Ext.grid.GridPanel({
                store: this.store,
                columns: [{
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, // 맵핑된 company의 name을 dataIndex로 잡는다.
                {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    renderer: this.pctChange,
                    dataIndex: 'pctChange'
                }, // 화면에 렌더링할때  renderer를 이용해 칼라를 바꿔준다.
                {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true,
                autoExpandColumn: 'company',
                loadMask: {
                    msg: '데이타 로드중'
                },
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true
                }),
                viewConfig: {
                    forceFit: true
                },
                height: 350,
                width: 800,
                title: '기본 그리드'
            });
           
            this.grid.render('DIV_GRID_HERE');
            this.grid.getSelectionModel().selectFirstRow();
            this.store.load();
           
        },
        //화면에 렌더링할때 renderer에 의해서 칼라를 바꿔주느 메쏘드
        pctChange: function(val){
            if (val > 0) {
                return '<span>' + val + '%</span>';
            }
            else
                if (val < 0) {
                    return '<span>' + val + '%</span>';
                }
            return val;
        },
        // 데이타 맵핑할때 자식노드가 있을 경우 해당 자식노드로 변환하여 반환한다.
        convertCompany: function(value, p, record){
            return (value.name != undefined) ? value.name : value;
        }
       
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

위의 convert 와 renderer 옵션을 이용하여 여러가지 다양한 형태의 grid 생성이 가능하다. 또한 JsonReader의 root 노드또한 Json 표기 형식의 . (dot) 연산자로 해당 자식 노드를 가져올수 있다.

 

추가적으로 extjs의 API Doc에는 rederer에 대해서 다음과 같이 기술하고 있다.

rederer의 파라미터값들
* value : Object - 셀에 들어가는 데이타
* metadata : Object - 정의한 객체 메타데이타
* css : String : 테이블테그에서 td에 먹이는 css
* attr : String : HTML 애트리뷰트 정의
* record : Ext.data.record : 데이타가 압축된 레코드
* rowIndex : Number
* colIndex : Number
* store : Ext.data.Store

 

 

Step 4. DataStore Load & Loadexception Handling

 데이타스토어가 모두 로드가 완료됐을 경우 혹은 서버이상으로 로드가 되지 않았을 경우를 예를 들어 설명한다.

BasicGridClass = function(){
return {
init: function(){
//데이타 스토어 정의
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
this.grid = new Ext.grid.GridPanel({
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, {
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
ignoreAdd: true,
emptyText: 'No Record found'
}),
height: 350,
width: 800,
title: '기본 그리드'
});

this.grid.render('DIV_GRID_HERE');
//this.grid.getSelectionModel().selectFirstRow();
this.store.load();


this.grid.on('rowcontextmenu', function(grid, index, e){
alert('오른쪽 버튼 클릭');
}, this);

// Row에서 마우스 오른쪽 클릭시
this.grid.on('rowclick', function(grid, index, e){
alert('클릭');
}, this);

// Row 클릭시
this.grid.on('rowdblclick', function(grid, index, e){
alert('더블클릭');
}, this);

// Row 더블클릭시
this.gsm = this.grid.getSelectionModel();

// 데이타 스토어에서 데이타 로드 완료시 첫번째 Row 선택되게하기
this.store.on('load', this.gsm.selectFirstRow, this.gsm);
this.store.on('load', function(store, records, options){
alert('데이타로드완료')
}, this);

// 데이타 스토어에서 데이타 로드실패시 exception throw
this.store.on('loadexception', function(a, conn, resp){
//alert(resp.status.toString() +'\n'+ resp.statusText);
this.grid.emptyText = 'data load error';
}, this);

// Ext.data.Dataproxy 통신 에러시
this.store.proxy.on('loadexception', function(proxy, dataObj, callbackArgs, e){
if (Ext.gecko)
console.log(e);
});

}
}
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

 

 

 

Step 5. 선택된 Row에서 값 추출하기

BasicGridClass = function(){
return {
init: function(){
//데이타 스토어 정의
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
this.grid = new Ext.grid.GridPanel({
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, // DataIndex정의
{
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
//RowSelectionModel 이거나 CellSelectionModel일 경우만 데이타 가져오기
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
emptyText: 'No Record found'
}),
height: 350,
width: 800,
title: '기본 그리드'
});

this.grid.render('DIV_GRID_HERE');
this.store.load();
this.gsm = this.grid.getSelectionModel();
this.store.on('load', this.gsm.selectFirstRow, this.gsm);

// ① Row에서 오른쪽 마우스 클릭했을 경우
this.grid.on('rowcontextmenu', this.getRowData, this);
// ① Row 클릭했을 경우
this.grid.on('rowclick', this.getRowData, this);
// ① Row 더블클랙했을 경우
this.grid.on('rowdblclick', this.getRowData, this);
// ① Row가 선택됐을 경우
this.gsm.on('rowselect', this.rowSelect, this);
},

getRowData: function(thisGrid, rowIndex, eventObject){
// ② 클릭,컨텍스트,더블클릭시 해당 이벤트가 일어난 Row의 Record가져오기
var record = this.store.getAt(rowIndex) || this.gsm.getSelected();
// ③ 선택된 레코드에서 데이타 가져오기
alert(record.data.company + '\n' + record.get('company'));
},
// ④ rowSelectionModel에서 선택된 Row의 데이타 가져오기
rowSelect: function(selectionModel, index, record){
alert(record.data.company + '\n' + record.get('company'));
}

}
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);


  • ① : Ext.grid.GridPanel에 등록된 Event  사용법에 유의할것
    this.grid.on('rowclick',this.getRowData(), this );
    잘못된표현  - argument가 없이 ()로만 사용하였을 경우 closure로 인식하여 바로 실행된다.

    this.grid.on('rowclick',this.getRowData(grid,rowIdex,eventObject), this);
    정확한 표현 - argument가 있을시는 closure로 인식하지 않음

    this.grid.on('rowclick',this.getRowData, this);
    정확한 표현

    this.grid.on('rowclick',function(grid,rowIndex,eventObject){this.getRowData(grid,rowIndex,evnetObjt);},this);
    정확한 표현


  • ② 선택된 Row에서 Record를 가져오는 방법에는 위오 같이 두가지 방법이 있다. 
    데이터스토어.getAt(Row의 Index Number) - 데이타 스토어의 인덱스에서 가져오는 방법
    셀렉션 모델.getSelected() - RowSelectionModel이나 CellSelectionModel에서 가져오는 방법

  • ③ Record에서 해당 데이타셋 가져오기 : 아래의 가져올DataIndex는 Columns에서 정의한 DataIndex를 가져온다.
    레코드.data.가져올DataIndex텍스트 
    레코드.get('가져올DataIndex텍스트)

  • ④ rowSelectionModel일 경우 rowselect 이벤트를 이용하여 선택된 Row의 값을 가져올수 있다.  클릭이나 더블클릭, 컨텍스트 메뉴와 달리 사용자로 부터 이벤트를 받는것이 아니라 데이터 스토어가 최초 로드 되거나 하였을 경우 해당 이벤트를 바로 자아 처리해줄 로직이 있을 경우에는 상당히 유용하다. 만약 예를 들어 초기 데이타 로드시 첫번째 Row가 선택된지 선택된 Row의 상세정보를 오른쪽 패널에 보여줄 경우 선택된 Row의 정보를 사용자의 어떠한 액션 없이 해당 레코드의 값을 받아 올수 있다. 데이타 스토어의 'load'이벤트를 사용할때 첫번째 Row를 선택하여 해당 선택된 row의 데이타를 뽑아내는 방법으로도 가능하다.

 


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

기본적으로 ExtJS의 레이아웃을 잡을때 Center Region은  Hide/show 및 Collapse/Expand가 되지 않으나 해당 Center Region을 감추는 방법에 대해서 기술한다. (꼼수). 단, 해당 꼼수(^^)는 fullscreen이거나 전체 사이즈가 고정되어 있다고 전제하에 동작한다.  만약 좌측패널이 전체 화면으로 center region을 감췄을때 브라우저 사이즈를 줄이거나 한다면 다시 syncSize해줘야 한다.



basicLayout.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
// body container의 id(basicLayoutBody)를 이용하여 전체 화면사이즈를 가져온다.
</html>

 

basicLayout.js

LeftArea = function(viewport) {

    this.viewport = viewport;

    LeftArea.superclass.constructor.call(this, {
        region : 'west',
        title : 'WEST',
        width : 250,
        layout : 'fit',
        margins : '5 5 5 5',
        html : '좌측',
        tbar : [{
            text : 'CenterPanel 없애기',
            tooltip : {
                title : '전체화면',
                text : '화면을 전체/축소합니다.'
            },
            pressed : false,
            enableToggle : true,
            scope : this,
            toggleHandler : this.showHideCenterRegion
        }]
    })
};

Ext.extend(LeftArea, Ext.Panel, {
    // toggleHandler는 2개의 arguments를 가지고 있다. pressed는 클릭시 true , 그렇지 않을시 false반환
        showHideCenterRegion : function(btn, pressed) {
            if (pressed) {
                var fullWidth = this.viewport.getFullBody('width') - 10;
                this.setWidth(fullWidth);
                this.viewport.CenterPanel.hide();
                this.viewport.CenterPanel.ownerCt.doLayout();
            } else {
                this.setWidth(250);
                this.viewport.CenterPanel.show();
                this.viewport.CenterPanel.ownerCt.doLayout();
            }
        }

    });

BasicLayoutClass = function() {
    return {
        init : function() {
            Ext.QuickTips.init();
            this.viewport = new Ext.Viewport( {
                layout : 'border',
                items : [this.WestPanel = new LeftArea(this),
                        this.CenterPanel = new Ext.Panel( {
                            region : 'center',
                            title : 'CENTER',
                            layout : 'fit',
                            margins : '5 5 5 0',
                            html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                        })]
            });

            this.viewport.doLayout();
            this.viewport.syncSize();
        },

        getFullBody : function(wh) {
            if (wh == 'width')
                return Ext.get('basicLayoutBody').getWidth();
            else if (wh == 'height')
                return Ext.get('basicLayoutBody').getHeight();
            else
                return Ext.get('basicLayoutBody').getWidth();
        }

    }
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


Post by 넥스트리소프트 데꾸벅(techbug)
, |
Extjs를 사용하다가 각 그리드 row에 각각의 프로세스바를 구현하게 되었다.
각 grid의 node 마다 프로세스 클래스를 재선언하였더니 너무 페이지자체가 무거워져
taskMgr를 이용하여 CSS로 구현하기로 하였다.
아래 소스는 넥스트리 사내프로젝트에서 구현되었던 일부소스이다.


[Extjs 소스중 일부] 기밀이므로 요기 까지만... ^^
// 타스크매니저로 autorefresh한다.
this.showTask = Ext.TaskMgr.start({
    run: this.store.reload,
    scope: this.store,
    interval:1000,
    args:[{params:{'appId':appId, 'deployId':deployId}, add:false}]
});

// 배포상태를 가져오는것을 멈춘다.
 Ext.TaskMgr.stop(this.showTask);



// 데이타가 모두 로드되었을 경우 해당 데이타값을 비교하여 계속
// autoRefresh할것인가를 결정한다.
this.store.on('load',function(store,records,opts){
    this.loadCompleteStatus(store,records,opts);
},this);



/**
 * 데이타가 모두 로드되었을 경우 해당 데이타값을 비교하여 계속 autoRefresh할것인가를 결정한다.
 * this.store.on('load',fn) 에 해당하는 메쏘드 실제 그리드안의
 * record에 해당하는 내용들을 모두 update한다.
 */
loadCompleteStatus : function(store,records,opts){
    var totalSize = store.getCount();
    var totalStop = 0;


    for(var i=0; i < totalSize; i++){
        var record = store.getAt(i);
        var pid = '_PROGRESSING_'+record.data.instsId;
        var lastMessage = (record.data.lastMessage) ? record.data.lastMessage : '';
        var progressRate= (record.data.progressRate)? parseInt(record.data.progressRate) : 0;
        var curStop = (record.data.isStop) ? parseInt(record.data.isStop) : 0 ;
       
        totalStop = totalStop + curStop;
        // 성공+실패 횟수가 전체 갯수와 같을 경우 autorefresh를 멈춘다.
        if(totalStop == totalSize) this.stopDeployStatus();


    }// for end
}

/**
 * 각 인스턴스마다의 정보(인스턴스명, 인스턴스Id, IP,port)를 그리드Row에 모두 표현한다.
 */
rendererDeployStatus : function(value, metadata, record,rowIndex,colIndex,store) {
return String.format(
        '<div class="_DEPLOYSTATUS_ROW_"><b>{0}</b><span class="_INSTANCE_IP_">{1}</span>'
        +'<div class="_PROGRESSBAR_CLASS_">'
        +'<div class="_PROGRESSBAR_" style="background-position-x:{2}%">{2}% : {3}</div>'
        +'<div id="_PROGRESSING_{4}" class="_PROGRESSBAR_COM_" '
        +'style="top:0;width:{2}%">{2}% : {3}</div></div></div>',
        value
        ,'http://'+record.data.ip +':'+record.data.port
        , record.data.progressRate
        , record.data.lastMessage
        , record.data.instanceId
        );
},


스타일시트
/* Progress bar 만들기 시작*/
DIV._DEPLOYSTATUS_ROW_ { padding-left:20px; }
DIV._DEPLOYSTATUS_ROW_ B {
    display:block;
    color:#333;
}
DIV._DEPLOYSTATUS_ROW_ SPAN._INSTANCE_IP_ { color:#333; }
._PROGRESSBAR_CLASS_LEFT_ {text-align:left !important}
DIV._PROGRESSBAR_CLASS_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 !important;
    height:18px !important;
    overflow:hidden !important;
    margin:7px 20px 10px 0 !important;
    background : transparent url(../img/progressbgwhite.png) 0 0 repeat-x !important;
    border:#6593CF solid 1px !important;
    position:relative;
}

DIV._PROGRESSBAR_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 3px 0 10px !important;
    margin:0 !important;
    height:18px !important;
    line-height:18px !important;
    white-space:nowrap !important;
    overflow:hidden !important;
    font-family:'segoe ui',verdana,arial;
    color:#396095;
    background-image : url(../img/progressbgwhite.png) !important;
    background-repeat : no-repeat ;
    background-position-y : 0;
    border:0 !important;
}

DIV._PROGRESSBAR_COM_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 3px 0 10px !important;
    margin:0 !important;
    height:18px !important;
    line-height:18px !important;
    white-space:nowrap !important;
    overflow:hidden !important;
    font-family:'segoe ui',verdana,arial;
    color:white;
    background: url(../img/progressbarbg.png) 0 0 repeat-x !important;
    border:0 !important;
    position:absolute;left:0;
    z-index:10;
}




.failrow DIV._PROGRESSBAR_COM_ {
    background-image: url(../img/progressbarfail.png) !important;
    background-color:#EFF39B !important;
}

/*프로그레스바 만들기 끝 */








Post by 넥스트리소프트 데꾸벅(techbug)
, |
아래와 같은 상태진행(progress)바를 생성할때 사용합니다.

사용자 삽입 이미지














Ext.onReady(function() {

    var percent;
    var textfield;

    var testPanel = new Ext.Panel( {
        title : 'progressbar test',
        collapsible : true,
        renderTo : document.body,
        width : 600,
        height : 200,
        bodyStyle : 'padding:10px',
        html : '<div id="_progress_"></div>',
        tbar : [
                percent = new Ext.form.NumberField( {
                    id : '_1_',
                    maxValue : 100,
                    minValue : 0,
                    width : 50,
                    value : '20'
                }),
                textfield = new Ext.form.TextField( {
                    id : '_2_',
                    width : 400,
                    value : '다운로드: - Application already exists at path /bcf'
                }),
                '-',
                {
                    text : '변경',
                    handler : function() {
                        // 상태바의 값을 입력한 값으로 계산하여 뿌려준다.
                        pbar.updateProgress(parseInt(percent.getValue()) / 100,
                                percent.getValue() + '% - '
                                        + textfield.getValue());
                    }
                }]
    });
    // 실제 프로그래스바를 생성하는 컴포넌트
        var pbar = new Ext.ProgressBar( {
            text : 'Initializing...',
            autoWidth : false,
            width : '100%',
            cls : 'left-align',
            renderTo : '_progress_'
        });
    });
}

<body><div id="techbugbar" ></div>

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

특정 CSS Object 값에 이벤트를 걸어주는 방법에 대해서 소개한다.

아래의 소스에는 "menu"라는 id값을 가진 객체에 클릭이벤트를 걸어주는 소스이다.

 
        Ext.select('#menu').on('click', function(e){
            alert(e.target)
            text = Ext.get(e.target); //이벤트 타겟을 찾는다.
            text.puff(); //텍스트에 fade효과를 준다.
            e.stopEvent() //Event bubble효과를 막기위해서 event를 멈춘다.
        });

위의 소스는 어플리케이션 기본정보나 컴포넌트 기본정보에서 담당자 클릭시 나오는 컨텍스트 메뉴를 구현하는데 사용되었다.


사용자 삽입 이미지











사용자 삽입 이미지

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

와우~ 죽인다. Extjs와 AIR가 만나니 이런 종류의 윈도우 프로그램도 가능하구나..!!
Jack Slocum 이 작성한 AIR와 ExtJS를 이용한 Vista Gadget이다.

사용자 삽입 이미지

extjs.comAIR DOC설치후 아래 window.gadget파일을 다운로드 받은후 더블클릭한다.





AIR 설치하기 : http://extjs.com/playpen/air/docs.html
관련URL : http://jackslocum.com/blog/category/ext-js/
윈도우가젯 만들기 : MSDN

function changeDock(){
if(System.Gadget.docked){
System.Gadget.background = 'images/docked.png';
var bd = Ext.getBody();
bd.setSize(130, 250);
bd.addClass('docked');
cm.setHidden(1, true);
cm.setRenderer(0, renderTopicMini);
grid.setPosition(4, 4);
grid.setSize(121, 241);
toggleDetails(null, false);
}else{
System.Gadget.background = 'images/undocked2.png';
var bd = Ext.getBody();
bd.setSize(528, 383);
bd.removeClass('docked');
cm.setHidden(1, false);
cm.setRenderer(0, renderTopic);
grid.setPosition(14, 14);
grid.setSize(494, 349);
cm.setColumnWidth(0, 350);
toggleDetails(null, true);
}
}
 
System.Gadget.onUndock = changeDock;
System.Gadget.onDock = changeDock;



Post by 넥스트리소프트 데꾸벅(techbug)
, |
GWT-EXT2.0에 이어 .NET용 Ext버전이 출시됐다.

Ext Framework을 이용한 .NET 웹컨트롤 유닛인 Coolite Studio 는 Ext의 대부분의 기능을 GWT-EXT와 마찬가지로 Server-Side Ajax를 지원한다.

사용자 삽입 이미지



Post by 넥스트리소프트 데꾸벅(techbug)
, |
기본적인 에러핸들링에 대해서 설명한다.

GridPanel 의 loadexception을 이용한 ErrorHandling 방법

기본 그르드 패널에서 데이타 로드시 loadexception이벤트에 대해서 설명한다.

BasicGridClass = function() {
    return {
        init : function() {
            // 데이타 스토어 정의
            this.store = new Ext.data.Store( {
                proxy : new Ext.data.HttpProxy( {
                    url : 'basicGrid.json'
                }),
                sortInfo : {
                    field : 'price',
                    direction : "DESC"
                },
                reader : new Ext.data.JsonReader( {
                    root : 'testData'
                }, [ {
                    name : 'company',
                    type : 'string',
                    mapping : 'company'
                }, {
                    name : 'price',
                    type : 'float',
                    mapping : 'price'
                }, {
                    name : 'change',
                    type : 'float',
                    mapping : 'change'
                }, {
                    name : 'pctChange',
                    type : 'float',
                    mapping : 'pctChange'
                }, {
                    name : 'lastChange',
                    type : 'date',
                    dateFormat : 'n/j h:ia',
                    mapping : 'lastChange'
                }])
            });
            this.grid = new Ext.grid.GridPanel( {
                store : this.store,
                columns : [ {
                    id : 'company',
                    header : "Company",
                    width : 160,
                    sortable : true,
                    dataIndex : 'company'
                }, {
                    header : "Price",
                    width : 75,
                    sortable : true,
                    renderer : 'usMoney',
                    dataIndex : 'price'
                }, {
                    header : "Change",
                    width : 75,
                    sortable : true,
                    dataIndex : 'change'
                }, {
                    header : "% Change",
                    width : 75,
                    sortable : true,
                    dataIndex : 'pctChange'
                }, {
                    header : "Last Updated",
                    width : 85,
                    sortable : true,
                    renderer : Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex : 'lastChange'
                }],
                stripeRows : true,
                autoExpandColumn : 'company',
                loadMask : {
                    msg : '데이타 로드중'
                },
                sm : new Ext.grid.RowSelectionModel( {
                    singleSelect : true
                }),
                view : new Ext.grid.GridView( {
                    forceFit : true,
                    enableRowBody : true,
                    emptyText : 'No Record found'
                }),
                height : 350,
                width : 800,
                title : '기본 그리드'
            });

            this.grid.render('DIV_GRID_HERE');
            // this.grid.getSelectionModel().selectFirstRow();
            this.store.load();

            this.grid.on('rowcontextmenu', function(grid, index, e) {
                alert('오른쪽 버튼 클릭');
            }, this);
            this.grid.on('rowclick', function(grid, index, e) {
                alert('클릭');
            }, this);
            this.grid.on('rowdblclick', function(grid, index, e) {
                alert('더블클릭');
            }, this);
            this.gsm = this.grid.getSelectionModel();
            this.store.on('load', this.gsm.selectFirstRow, this.gsm);

            this.store.on('load', function(store, records, options) {

            }, this);

            this.store.on('loadexception',
                    function(a, conn, resp) {
                        if (resp.status == 200) {  ============> 혹은 resp.statusText를 사용
                            var jsonData = resp.responseText;
                            var result = (jsonData != undefined && jsonData
                                    .trim() != "") ? Ext.decode('(' + jsonData
                                    + ')') : null;
                            if (result != null && result.code != undefined
                                    && result.code.trim() != ''
                                    && result.message != undefined) {
                                this.showErrorStackHandler(result);
                            }
                        }
                    }, this);

            // 커넥션에서 문제가 일어났을 경우
            this.store.proxy.getConnection()
                    .on(
                            'requestcomplete',
                            function(conn, resp, options) {
                                var jsonData = resp.responseText;
                                var result = (jsonData != undefined && jsonData
                                        .trim() != "") ? Ext.decode('('
                                        + jsonData + ')') : null;
                                if (result != null && result.code != undefined
                                        && result.code.trim() != ''
                                        && result.message != undefined) {
                                    this.showErrorStackHandler(result);
                                }
                            }, this);

            this.store.on('load', function(store, records, opt) {
            });

        },

        showErrorStackHandler : function(result) {
            var code = (result.code != undefined) ? result.code : '';
            var mesg = (result.message != undefined) ? result.message : '';
            var insNm = (result.instanceName != undefined)
                    ? result.instanceName
                    : '';
            var errStack = (result.stack != undefined) ? result.stack : '';
            var occDate = (result.occurDateTime != undefined)
                    ? result.occurDateTime
                    : '';

            var markup = "<div><ul>" + "<li>에러코드 :  " + code + "</li>"
                    + "<li>에러메세지 : " + mesg + "</li>" + "<li>인스턴스명:  " + insNm
                    + "</li>" + "<li>발생시각:  " + occDate + "</li>"
                    + "<li>에러스택 :<br /> " + errStack + "</li>"
            "</ul></div>";

            var win = new Ext.Window( {
                title : 'Error',
                closable : true,
                width : 600,
                height : 350,
                layout : 'border',
                modal : true,
                items : [{
                    region : 'center',
                    title : '에러메세지',
                    border : false,
                    autoScroll : true,
                    html : markup
                }]
            });
            win.show(this);
        }

    }
}();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);


관련소스다운로드


실제 위의 소스중 loadexception에서 발생하는 resp.status=="200"이 FF에서는 잘 작동하나 IE에서 처리가 안될때는 다음과 같이 해준다.
그리고 옵션중 deferRowRender:false 로 설정하고
resp.status=="200" || resp.status=="12030" 로 처리해준다.
12030 ===> 'The connection with the server has been terminated'뜻으로 internal server error와 같다...
IE에서는 xhr.status 코드값이 틀리게 넘어온다.. ㅡ.,ㅡ;
ext-base.js 를 수정해야 하는 경우도 생길듯..

(참조URL : http://support.microsoft.com/default.aspx?scid=kb;EN-US;193625 )


   
Code Error Message and Description
----- -----------------------------
12001 ERROR_INTERNET_OUT_OF_HANDLES
No more handles could be generated at this time.

12002 ERROR_INTERNET_TIMEOUT
The request has timed out.

12003 ERROR_INTERNET_EXTENDED_ERROR
An extended error was returned from the server. This is
typically a string or buffer containing a verbose error
message. Call InternetGetLastResponseInfo to retrieve the
error text.

12004 ERROR_INTERNET_INTERNAL_ERROR
An internal error has occurred.

12005 ERROR_INTERNET_INVALID_URL
The URL is invalid.

12006 ERROR_INTERNET_UNRECOGNIZED_SCHEME
The URL scheme could not be recognized or is not supported.

12007 ERROR_INTERNET_NAME_NOT_RESOLVED
The server name could not be resolved.

12008 ERROR_INTERNET_PROTOCOL_NOT_FOUND
The requested protocol could not be located.

12009 ERROR_INTERNET_INVALID_OPTION
A request to InternetQueryOption or InternetSetOption
specified an invalid option value.

12010 ERROR_INTERNET_BAD_OPTION_LENGTH
The length of an option supplied to InternetQueryOption or
InternetSetOption is incorrect for the type of option
specified.

12011 ERROR_INTERNET_OPTION_NOT_SETTABLE
The request option cannot be set, only queried.

12012 ERROR_INTERNET_SHUTDOWN
The Win32 Internet function support is being shut down or
unloaded.

12013 ERROR_INTERNET_INCORRECT_USER_NAME
The request to connect and log on to an FTP server could
not be completed because the supplied user name is
incorrect.

12014 ERROR_INTERNET_INCORRECT_PASSWORD
The request to connect and log on to an FTP server could
not be completed because the supplied password is
incorrect.

12015 ERROR_INTERNET_LOGIN_FAILURE
The request to connect to and log on to an FTP server
failed.

12016 ERROR_INTERNET_INVALID_OPERATION
The requested operation is invalid.

12017 ERROR_INTERNET_OPERATION_CANCELLED
The operation was canceled, usually because the handle on
which the request was operating was closed before the
operation completed.

12018 ERROR_INTERNET_INCORRECT_HANDLE_TYPE
The type of handle supplied is incorrect for this
operation.

12019 ERROR_INTERNET_INCORRECT_HANDLE_STATE
The requested operation cannot be carried out because the
handle supplied is not in the correct state.

12020 ERROR_INTERNET_NOT_PROXY_REQUEST
The request cannot be made via a proxy.

12021 ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND
A required registry value could not be located.

12022 ERROR_INTERNET_BAD_REGISTRY_PARAMETER
A required registry value was located but is an incorrect
type or has an invalid value.

12023 ERROR_INTERNET_NO_DIRECT_ACCESS
Direct network access cannot be made at this time.

12024 ERROR_INTERNET_NO_CONTEXT
An asynchronous request could not be made because a zero
context value was supplied.

12025 ERROR_INTERNET_NO_CALLBACK
An asynchronous request could not be made because a
callback function has not been set.

12026 ERROR_INTERNET_REQUEST_PENDING
The required operation could not be completed because one
or more requests are pending.

12027 ERROR_INTERNET_INCORRECT_FORMAT
The format of the request is invalid.

12028 ERROR_INTERNET_ITEM_NOT_FOUND
The requested item could not be located.

12029 ERROR_INTERNET_CANNOT_CONNECT
The attempt to connect to the server failed.

12030 ERROR_INTERNET_CONNECTION_ABORTED
The connection with the server has been terminated.

12031 ERROR_INTERNET_CONNECTION_RESET
The connection with the server has been reset.

12032 ERROR_INTERNET_FORCE_RETRY
Calls for the Win32 Internet function to redo the request.

12033 ERROR_INTERNET_INVALID_PROXY_REQUEST
The request to the proxy was invalid.

12036 ERROR_INTERNET_HANDLE_EXISTS
The request failed because the handle already exists.

12037 ERROR_INTERNET_SEC_CERT_DATE_INVALID
SSL certificate date that was received from the server is
bad. The certificate is expired.

12038 ERROR_INTERNET_SEC_CERT_CN_INVALID
SSL certificate common name (host name field) is incorrect.
For example, if you entered www.server.com and the common
name on the certificate says www.different.com.

12039 ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
The application is moving from a non-SSL to an SSL
connection because of a redirect.

12040 ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR
The application is moving from an SSL to an non-SSL
connection because of a redirect.

12041 ERROR_INTERNET_MIXED_SECURITY
Indicates that the content is not entirely secure. Some of
the content being viewed may have come from unsecured
servers.

12042 ERROR_INTERNET_CHG_POST_IS_NON_SECURE
The application is posting and attempting to change
multiple lines of text on a server that is not secure.

12043 ERROR_INTERNET_POST_IS_NON_SECURE
The application is posting data to a server that is not
secure.

12110 ERROR_FTP_TRANSFER_IN_PROGRESS
The requested operation cannot be made on the FTP session
handle because an operation is already in progress.

12111 ERROR_FTP_DROPPED
The FTP operation was not completed because the session was
aborted.

12130 ERROR_GOPHER_PROTOCOL_ERROR
An error was detected while parsing data returned from the
gopher server.

12131 ERROR_GOPHER_NOT_FILE
The request must be made for a file locator.

12132 ERROR_GOPHER_DATA_ERROR
An error was detected while receiving data from the gopher
server.

12133 ERROR_GOPHER_END_OF_DATA
The end of the data has been reached.

12134 ERROR_GOPHER_INVALID_LOCATOR
The supplied locator is not valid.

12135 ERROR_GOPHER_INCORRECT_LOCATOR_TYPE
The type of the locator is not correct for this operation.

12136 ERROR_GOPHER_NOT_GOPHER_PLUS
The requested operation can only be made against a Gopher+
server or with a locator that specifies a Gopher+
operation.

12137 ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
The requested attribute could not be located.

12138 ERROR_GOPHER_UNKNOWN_LOCATOR
The locator type is unknown.

12150 ERROR_HTTP_HEADER_NOT_FOUND
The requested header could not be located.

12151 ERROR_HTTP_DOWNLEVEL_SERVER
The server did not return any headers.

12152 ERROR_HTTP_INVALID_SERVER_RESPONSE
The server response could not be parsed.

12153 ERROR_HTTP_INVALID_HEADER
The supplied header is invalid.

12154 ERROR_HTTP_INVALID_QUERY_REQUEST
The request made to HttpQueryInfo is invalid.

12155 ERROR_HTTP_HEADER_ALREADY_EXISTS
The header could not be added because it already exists.

12156 ERROR_HTTP_REDIRECT_FAILED
The redirection failed because either the scheme changed
(for example, HTTP to FTP) or all attempts made to redirect

failed (default is five attempts).


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

ExtJS에서는(비단, Extjs뿐만 아니라 기존 XHR과 마찬가지로)  같은 도메인일 경우는 HttpProxy를 사용하면 아무런 문제가 되지 않으나 서로 다른 도메인일 경우 ScriptTagProxy로 해당 URL의 Ajax통신을 할수 있게 잘(?) 만들어져 있다.기존 XHR(XMLHttpRequest)를 이용할 시에는 서버쪽에서 Proxy script를 만들어 사용해야 하나 jQuery나 ExtJS의 경우는 제공하는 Proxy를 사용하면 된다.

ScriptTagProxy로 가져온 Datastore파일을 JsonReader로 읽을 때에는 기존의 HttpProxy를 사용할때와 달리 다음과 같이 서버측에 callback 파라미터값을 던지고 아래와 같은 구조로된 Json을 받게 된다.

stcCallback1013(
{
Json형식
}
)

 ExtJS에서  ScriptTagProxy는 아래와 같은 로직으로 구현되어 있다.

boolean scriptTag = false;
String cb = request.getParameter("callback");
if (cb != null) {
scriptTag = true;
response.setContentType("text/javascript");
} else {
response.setContentType("application/x-json");
}
Writer out = response.getWriter();
if (scriptTag) {
out.write(cb + "(");
}
out.print(dataBlock.toJsonString());
if (scriptTag) {
out.write(");");
}


scriptTagProxy 와 Form Submit은  callback 이라는 파라미터 값을 함께 던지므로 서버상에서 callback 파라미터를 받게 만들어 줘야 한다. 그렇게 때문에 서버에서 별도의 작업을 해줘야 하는데 PHP와 JSP의 경우를 비교해서 기술한다.

 

String callback = request.getParameter("callback");
String jsonData = getJsonData();
if (callback != null) {
out.write(callback + "(");
}
out.write(jsonData);
if (callback != null) {
out.write(");");
}
 
$callBack = isset($_REQUEST["callback"]) ? $_REQUEST["callback"] : '';

if($callBack) {
echo $callBack . "(";
}
echo getJsonData();
if($callBack) {
echo ");";
}

Post by 넥스트리소프트 데꾸벅(techbug)
, |
스크롤바에 자바스크립트로 스킨을 입힌 사이트를 발견하다.
CSS로 색깔만 바꾸는것만 가능한줄 알았던 것이 이미지를 입힐수 있게 해주는 스크립트이다

사용자 삽입 이미지



















관련소스(flexcrolljs.zip)를 첨부하다.


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

자바스크립트로 구현한 3D 포토갤러리  (0) 2008.03.06
JsonSQL: JSON parser, SQL style  (0) 2008.03.06
Native CSS selectors with querySelector  (0) 2008.03.06
IE minimize  (2) 2008.03.06
Javascript Performance Stack  (2) 2008.03.03
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Basic Concept

BCF 에서는 Ext.Viewport를 이용하여 Layout을 잡는것을 기본으로 한다. Ext.Viewport는 브라우저의 Viewport(<body></body>)영역에 자동으로 document Body에 렌더링하는 유틸리티 컨테이너 클래스이다. 브라우저의 크기가 변경되었을때 자동으로 레이아웃을 재계산하여 full-screen으로 렌더링한다.
주의 :  Viewport는 document.body에 다른 어떤 container와 같이 렌더링 할수 없다.  페이지당 단 한개의 Viewport만 사용된다.

 

Region Concept














north
   west          east
   center  
     
     south






브 라우저에서 위와 같이 그리드를 갖추고 있다고 하면 Viewport에서는 north, west,east,south,center 라는 영역(Region)을 갖는 5개의 Panel이 자동으로 생성된다. 위의 영역중 Center를 제외하고는 모두 Collapse/Expand 및 Hide/show 가 가능하다. 레이아웃을 생성할때에는 반드시 Center Region이 하나 이상(Center Region이 TabPanel 일 경우 여러개 존재) 존재하여야 한다.

기본적으로 아래 소스에서 사용되어질 레이아웃 종류는 BorderLayout이다. 이전 장을 반드시 숙지할것

최종 소스 :  다운로드

 

 

Step 1 : 기본 틀 잡기

 각 소스는 이미 설명했던 0010. Basic Structure0020.Basic Format을 이용하여 재사용성을 고려하여 만든다.

basicLayout.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

basicLayout.js

//BasicLayout 기본 클래스를 정의한다.
BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',               //기본적으로 모든 viewport는 borderLayout으로 시작한다.
items : [
{
    region : 'north',    //상단 영역
    title : 'NORTH',    //상단 타이틀
    collapsible : true,    //상단 영역을 감추기 버튼 나타나기
    height : 200,        //상단 영역의 높이  (상/하단은 높이, 좌/우측은 너비)
    split : true,        //아래 영역과 splitting한다. (5px정도 차이를 둔다.)
    layout : 'fit',        //레이아웃 매니저는 fitLayout
    html : '상단'          //패널의 body HTML을 정의한다.
},
{
    region : 'south',
    title : 'SOUTH',
    collapsible : true,
    height : 200,
    split : true,
    layout : 'fit',
    html : '하단'
},
{
    region : 'east',
    title : 'EAST',
    collapsible : true,
    width : 250,
    split : true,
    layout : 'fit',
    html : '우측'

},
{
    region : 'west',
    title : 'WEST',
    collapsible : true,
    width : 250,
    split : true,
    layout : 'fit',
    html : '좌측'
},
{
    region : 'center',    //Center Region의 경우 Width,Height를 표시하지 않는다. (상/하, 좌/우 패널에서 모두 정의된 높이/너비)
    title : 'CENTER',
    layout : 'fit',
    html : '중간'
}
]
});
this.viewport.doLayout();    //viewport의 레이아웃을 그린다. (생략가능 : 되도록 작성 )
this.viewport.syncSize();    //viewport의 사이즈를 sync한다. (생략가능 : 되도록 작성 )

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);   

 

 

 

Step 2 : 상/하단 및 우측 판넬 없애기

basicLayout.js

BasicLayoutClass = function() {
return {
init : function() {

this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
// Step 1에서 정의된 좌측 패널 : 
//Step1에서는 Default 값인 new Ext.Panel() 이 생략됨
this.WestPanel = new Ext.Panel({
region : 'west',
title : 'WEST',
collapsible : true,
//초기 로딩시 collapse된 상태에서 보여진다.
collapsed : true,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측'
}),
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '중간'
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);



Step3 : 좌측 패널을 별도의 Class로 분리하여 빼내기

basicLayout.js

LeftArea = function(){
//기존 좌측 판넬의 config option을 그래도 호출하여 사용
LeftArea.superclass.constructor.call(this,{
region : 'west',
title : 'WEST',
collapsible : true,
collapsed:false,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측'
});
};

Ext.extend(LeftArea, Ext.Panel,{
//Ext.Panel을 상속받아 사용하였다, GridPanel일 경우는
//Ext.grid.GridPane을 사용하듯이 여러 Panel정의하여 사용가능
//Left Area의 Public,Private,Privileged 메쏘드를 정의한다.
});



BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
// LeftArea Class로 별도로 선언
this.WestPanel= new LeftArea(),
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


 

Step 4 : 좌측 패널에 ToolBar 붙여 오른쪽 패널의 Body를 Update하기

basicLayout.js

// 메인 클래스인 BasicLayoutClass를 인자로 넘겨받음 this.WestPanel = new LeftArea(this)
LeftArea = function(viewport){       

// 인자로 받은 private변수를 public 변수로 변환
this.viewport = viewport;   
 // LeftArea에서만 사용가능한 private 변수
var privateNum = 0;       

LeftArea.superclass.constructor.call(this,{
region : 'west',
title : 'WEST',
collapsible : true,
collapsed:false,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측',
// 툴바 붙이기 TopToolbar의 경우 tbar, BottomToolbar의 경우 bbar로 표시
tbar : [           
// 툴바 안의 액션버튼 붙이기 new Ext.Action({})으로 표기가 생략
{                 
text:'클릭하세요',
scope:this,    //scope는 handler에서 사용할 이벤트영역 : this는 LeftArea 클래스를 가르킴               
handler:function(){
privateNum++;// LeftArea 클래스의 private변수
this.viewport.updateBody(privateNum);// this.viewport로 BasicLayoutClass.viewport로도 가능
}
}
]
})
};

Ext.extend(LeftArea, Ext.Panel,{
//Left Area의 Public,Private,Privileged 메쏘드를 정의한다.
});


BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
this.WestPanel= new LeftArea(this),    // 클래스 호출시 BasicLayoutClass를 인자로 넘겨줌
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>',
tbar : [
this.firstMenu = new Ext.Action({    // 좌측 판넬에서 정의했던 툴바를 선언하는 다른 방식
text:'좌측패널 보였다/안보였다',
scope:this,
handler:function(){
if(this.WestPanel.isVisible()){        // 좌측패널의 보였다/안보였다
this.WestPanel.collapse(true);    // this.WestPanel로 좌측패널을 컨트롤
}else {
this.WestPanel.expand(true);
}
}
})
]
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();
},

// 좌측 패널클래스 new LeftArea에서 직접 호출하는 Public Method
updateBody : function(privateNum){
Ext.get('_CONTENTS_AREA_').update('좌측판넬 클릭시 '+privateNum+'번 변했네요!');
}

}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);





Post by 넥스트리소프트 데꾸벅(techbug)
, |
이 포스팅은 넥스트리에서 내부프로젝트를 진행할때 표준문서로 삼았던 자료입니다.
현재 내용이 표준이 아니므로 참조하실때 유의하시기 바랍니다.

기본 문서구조 만들기

아래 내용은 [1원팁]윈도우 로딩시 스크립트 실행에 잠시 소개했던 내용이다.

방법 1 : 클래스 상속 및 복잡한 페이지에 용이하다.

BasicInfo_Main = function(){
   return {
      init: {
          내용~
      }
   }

}(); 
Ext.EventManager.onDocumentReady(BasicInfo_Main.init, BasicInfo_Main, true);

ext에서 클래스 상속은 다음과 같이 정의한다.
Ext.MyClass = function() { Ext.MyClass.superclass.constructor.call(this); ..... }; Ext.extend(Ext.validProd, Ext.util.Observable, { functionName:function (v) { ............... return v; } }); var valid = new Ext.MyClass();

방법 2 : 간단한 페이지에 사용하기

Ext.onReady(function(){
내용~
}

현재 BCF(factoryware:가칭) Project에서는 방법1을 사용하기로 한다.

 

문서 구조틀

기본문서구조

//메인 클래스에서 사용된 서브 클래스를 정의한다.
왼쪽판넬클래스 = function(){
this.변수;

this.public메쏘드 = functon(){
내용~~
};

//==== 서브클래스 옵션을 상속받은 Ext.panel 옵션을 호출한다.
왼쪽판넬클래스.superclass.constructor.call(this, {
왼쪽판넬클래스의 config 옵션모음~
});

왼쪽판넬클래스 이벤트 걸기~

};

//서브클래스의 속성을 Ext.panel 을 상속받아 사용한다.
Ext.extend(왼쪽판넬클래스, Ext.panel, {
왼쪽판넬 클래스의_private메쏘드_01(){
내용~
}
});


//기본 클래스를 정의한다. 메인 클래스
기본클래스 = function(){
var 로컬변수1;
var 로컬변수2;
return{
//기본클래스가 호출될때 실행되는 첫번째 메쏘드
init:function(){
this.변수,this.변수;

로컬변수1 = new 왼쪽클래스(); //왼쪽 클래스 호출

기본레이아웃 그리기 viewport설정;

(function(){ 페이지 로딩시 사용될 메쏘드 호출 })();
객체.on('이벤트',페이지로딩시 이벤트걸 메쏘드호출);

private메쏘드_01(); //메쏘드 실행

},


private메쏘드_01 : function(){
내용~
},

private메쏘드_02 : function(){
function 내장메쏘드(){
내장메쏘드내용
}
}

};
}();
Ext.EventManager.onDocumentReady(기본클래스.init, 기본클래스,true);
//화면이 로딩될때 기본클래스.init 메쏘드를 먼저 호출한다.

 

 현재 BCF(factoryware)의 기본구조의 위와 같이 사용될 Class와 메인클래스를 별도의 파일로 관리하여 해당 클래스를 호출한다.
 메인클래스를 제외환 대부분의 클래스(위에서는 "왼쪽클래스")에서는 Private메쏘드를 사용하려면 확장클래스 (왼쪽클래스.superclass.constructor.call(this,{});)를 사용하여야 하며,
 확장을 위해서 Ext.extend(왼쪽클래스,Ext.Panel)을 이용하여 "왼쪽클래스" Ext.Panel을 상속받는것임을 알수 있다.

 


Post by 넥스트리소프트 데꾸벅(techbug)
, |
이 포스팅은 넥스트리에서 내부프로젝트를 진행할때 표준문서로 삼았던 자료입니다.
현재 내용이 표준이 아니므로 참조하실때 유의하시기 바랍니ㅏㄷ.

1. HTML 구조


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Component Basic Information</title>
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" />        <!-- extjs stylesheet    -->
<link rel="stylesheet" type="text/css" href="../common/style/css-all.css" />            <!-- bcf stylesheet      -->
<link rel="stylesheet" type="text/css" href="../common/style/component.css" />            <!-- bcf stylesheet      -->
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>            <!-- extjs script        -->
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>                <!-- extjs script        -->
<script type="text/javascript" src="../extjs/adapter/ext/localXHR.js"></script>            <!-- extjs Extended      -->
<script type="text/javascript" src="../extjs/adapter/ext/RowExpander.js"></script>        <!-- extjs Extended      -->
<script type="text/javascript" src="../scripts/global.js"></script>                        <!-- bcf global variable -->
<script type="text/javascript" src="../scripts/requestUrl.js"></script>                    <!-- Url Handling        -->
<script type="text/javascript" src="./scripts/versionRegisterWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/versionModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactRegisterWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/componentModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactListGrid.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/basicInfoTopPanel.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/basicInfo.js"></script>                    <!-- current page script -->
</head>
<body>
<!-- Real Body Container Start -->
<div id="div_basicInformation_Container">

기본 내용이 들어간다.

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


 

HTML 작성규칙 :

  1. 반드시 웹표준에 맞게 구조화 되게 작성한다.
  2. DOCTYPE을 반드시 명기할것. (XHTML1.0 Trasitional DTD)
  3. 모든 HTML Element(TAG)들은 소문자로 표현한다. (XHTML1.0 Spec)
  4. HTML Element들의 properties와 attribute들은 최소화한다.
  5. ExtJS의 스타일은 <link /> 태그로 작성한다.
    예외 : factoryware(가칭)의 스타일은 @import를 이용한 include방식으로 작성한다 ( 일괄적인 수정용이)
  6. <script></script>태그의 type은 반드시 작성해 주며 위와 같은 순서대로 작성한다.
    ExtJS Framework JS > ExtJS Extension Module > Global Variables > 현재페이지에 필요한 Javascript resources

 

2. StyleSheet의 구조


@import url("layout.css");    /* Page Layout */
@import url("openpage.css"); /* OpenPage Layout */
/* Elements */
@import url("font.css"); /* Font */
@import url("form.css"); /* Form */
@import url("link.css"); /* Link */
@import url("table.css"); /* table */


 body {
    padding:0;
    margin:0;
    width:100%;
    height:100%;
}


DIV#_DIV_SEARCH_RESULT_ {
    font-family: segoe ui,tahoma,arial;
    font-size:12px;
    width:100%;
    height:100%;
    overflow:auto;
}

DIV#_DIV_SEARCH_RESULT_ H3{
    border:#DDD dotted 1px;
    padding:5px;
    margin:5px;
    height:200px;
    text-align:center;
    font-weight:normal;
    font-family:segoe ui,Dotumn,tahoma

}

 


 

 

 

CSS 작성규칙 :

  1. 기본적으로 ExtJS의 모든 화면 구성은 <div></div> <table></table>을 사용하여 작성되며 모두 x-prefix로 시작된다.

  2. inline Style은 최대한 자제한다.
    예외사항 : ExtJS의 component들은 대부분 position:relative로 작성되기 때문에 IE6, IE7 Bug에 의한 부분은 배제된다.
    또한 Javascript로 ExtJS의 모듈로 들어가는 부분은 제외된다.

  3. ExtJS의 스타일을 제외한 부분은 되도록이면 @import를 이용하여 작성한다.
    (예외 : eclipse css validator에서는 @import를 invalid로 인식하여 예외적으로 사용, 그러나 사용상, 웹표준에 어긋나는 표현은 아님!)

  4. Block Label Element와 Inline Lable Element는 적절히 조화롭게 사용하되 ExtJS의 특징상 Block Lable 요소중에서 부득이하게 네비게이션을 Table로 사용하는 경우가 발생하므로 이경우는 예외로 처리한다.

  5. factoryware는 IE6이상, Firefox2.0 을 기본 브라우저로 하며 IE7.0 , Firefox3.0, Opera9.0 호환모드로 작성한다.
    IE6 Hack, IE7 Hack 작성시 ExtJS의 기본 스타일을 반드시 지켜야 한다.

  6. selector를 사용하려면 Id 값은 해당 화면에서 반드시 하나만 존재해야 하며 같은 스타일을 유지하려면 되도록 class selector를 사용한다.
    (사용예 : <div id="_DIV_SEARCH_RESULT_" class="link_color  x-btn-test"></div> )

  7. Id selector를 사용할 경우 반드시 HTML element를 붙여준다. (예외적으로 전체적으로 적용할때는 제외한다.)
    (사용예 : DIV#_DIV_SEARCH_RESULT_ {내용} )

  8. class selector는 쪼개어 multiple로 작성한다.
    (사용예 : <div class="first_class  second_class third_class"></div> )

  9. 명명규칙 :
    - camel 표기법이 아닌 _ (under-line)을 이용한 명명법을 쓴다. (Javascript에서 사용시 한눈에 보기 쉽게 작성
    - Id 값은 되도록 대문자로 작성한다.
    - class값은 소문자로 작성한다.


 

 

 

Javascript 명명규칙
Edit section

주석처리


XHTML validaion처리를 위하여 주석은 아래와 같이 작성한다.

 <style type="text/css">
/*<![CDATA[*/

/*]]>*/
</style>

<script type="text/javascript">
//<![CDATA[

//]]>
</script>

 

 


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

Extjs Sample :: Viewport를 이용한 기본레이아웃잡기  (3) 2008.02.18
Extjs Sample :: 기본문서구조만들기  (0) 2008.02.18
Extjs Qtips 사용하기  (0) 2008.02.18
Extjs Template 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Post by 넥스트리소프트 데꾸벅(techbug)
, |

HTML작업을 하다 보면 Tooltip을 사용할 일이 많아진다.
예를 들어

<img src="이미지 주소" alt="이미지설명"  title="이미지설명" />

<a href="" title="링크설명">

<table summary="테이블에 대한 간략한 소개">


 와 같이 사용하는데 사용자 마음대로 원하는 형태로 나오기가 어렵다는 단점이 있다. 또한 일정시간이 지나면 자동으로 없어지므로 사용하기가 까다롭다.

단 이미지가 없을 경우 엑박(엑스박스)을 없애기 이미지에 대한 내용이 들어가게 해주는 기능이 있으나 이럴경우 ExtJS를 사용하여 다음과 같이 작성해 줄수 있다.

 MARKUP

<div id="아이디" ext:qtitle="툴팀 타이틀" ext:qtip="내용HTML">

 JAVAScript (ExtJS)

Ext.QuickTips.init();
Ext.QuickTips.getQuickTip();

 과 같이 적어주면 자동으로 Ext.Tooltip() 과 같은 효과를 볼수 있다.

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

Extjs Sample :: 기본문서구조만들기  (0) 2008.02.18
Extjs Sample :: 기본문서구조  (0) 2008.02.18
Extjs Template 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Extjs 기본 DomQuery  (0) 2008.02.18
Post by 넥스트리소프트 데꾸벅(techbug)
, |

머리글
Edit section

Ext UI 를 다루다 보면 중간 중간 객체에 문자열을 입력을 할 때 {0}, {id} 와 같은 형태로 중간에 들어가 있는 것을 볼 수 있다.
이런 것은 대부분 내부적으로 Ext.Template 클래스를 사용한다. 그래서 실제 템플릿은 두고 변하는 부분만 변수화 시켜서 대입하는 방식으로 많은 문자열이 쓰일 때 편하게 작업 할 수 있다.
이번에는 Ext.Template 클래스에 대해서 알아보자..


기본적인 Template 사용 방법

var str = "<html><head>{head}</head><body>{body}</body></html>";
var tp = new Ext.Template(str);

위와 같은 형태로 template 객 체를 생성한다.
template에 적용될 변수는 {변수} 와 같은 형태로 입력된다.
나중에 다른 문자열로 대체될 부분이다.

속도를 좀 더 빠르게 하고 싶다면

tp.compile();  


해서 미리 컴파일 해두면 파싱 작업 없이 바로 바로 텍스트를 수정 할 수 있다.

Element에 템플릿 텍스트 넣기

그렇다면 이제 실제 element에 template로 작성한 문자열을 넣어보도록 하자 .

var tp = new Ext.Template(
     "<div id='{div_id}'>",
         "<span id='{span_id}' class='{cls}'>{content}</span>",
     "</div>"
);
tp.append('아이디', {div_id : 'top', span_id : 'content',
cls : 'icon', content : '안녕'});



위와 같은 코드를 실행하면

<div id='top'><span id='content' class='icon'>안녕</span></div>


과 같은 형태로 문자열이 만들어 지고 이것은 '아이디' element의 내용에 추가되어진다.
추가되어질때는 html 형태 그대로 추가된다.



Template 테스트 바로 얻어오기

    var tp = new Ext.Template('텍스트{name}');
    var str = tp.applyTempate({name:'홍길동'});

변환된 값을 바로 문자열로 리턴해줍니다.





Template 텍스트의 몇가지 속성들

숫자 형태로 위치 지정하기

위의 예제들은 단순히 {id} 와 같은 형태로 문자열 형태로  변수를 지정하였습니다.
하지만 {0}, {1} 과 같은 숫자 형태로 변환할 수 있습니다.

var str = "안녕하세요, 전 {0} 입니다. 그동안 안녕하셨죠? {1}씨? ";
var tp = new Ext.Template(str);
tp.append('아이디', { name1: '꺽정', name2 : '길동' });


옵션에 대해서 0, 1 과 같은 순서 형태로도 지원을 할 수 있습니다.

변수 변환할 때 함수 사용하기

var str = "안녕하세요, 전 {name1:stripTags} 입니다. 그동안 안녕하셨죠? {name2:trim}씨? ";
var tp = new Ext.Template(str);
tp.append('아이디', { name1: '꺽정', name2 : '길동' });   

변수명 뒤에 실행할 함수들이 : 를 구분으로 지정이 됩니다.
그러면 각각의 변수에 해당하는 값은 함수에 적용이 되어진 값으로 변환이 되서 실제 텍스트로 바뀝니다.  여기서 사용되는 함수들은 모두  Ext.util.Format 에 있는 함수들입니다.

       {변수명[:함수명(매개변수,...)]}


 위의 형식과 같은 형태로 지정 해주시면 됩니다. 
Ext.util.Format 을 보시면 각각의 메소드들이 처음 매개변수로 string을 포함하고 있는데요 이것은 템플릿으로 변환되는 텍스트 이기 때문에 실제로 템플릿 상에서 함수를 사용 할 때는 두번째 매개변수 부터 적어주시면 됩니다. 만약 두번째 매개 변수가 없다면 적지 않으셔도 됩니다.

 
ex){name1:ellipsis(2)}
- Ext.util.Format.ellipsis(String value, Number length) ...
{name1:trim}
- Ext.util.Format.trim(Strint value)  ....






Template로 변환된 텍스트 넣는 위치 지정하기

현재까지 알아 본 것은 append  메소드로만 넣는 것을 봤습니다.

 

append

:지정된 element 안의 내용에서 가장 뒤에 추가합니다. 

ex)  <div id='test'>aaa</div>
tp.append('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 aaa 뒤에 들어가게 됩니다.

 

insertAfter

: 지정된 element 의 뒤에 추가합니다.

ex)  <div id='test'>aaa</div>
tp.insertAfter('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 <div id='test'>aaa</div>~ 뒤에 들어갑니다.

 

insertBefore

: 지정된 element 의 뒤에 추가합니다.

ex)  <div id='test'>aaa</div>
tp.insertBefore('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 ~ <div id='test'>aaa</div> 앞에 들어갑니다.

 

insertFirst

: 지정된 element의 하위 노드들 중에 첫번째 요소로 들어갑니다.

ex)
<div id='test'>
<span id='a'></span>
<span id='b'></span>
<span id='c'></span>
</div>
tp.insertFirst('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이

<div id='test'>
~ // 여기에 들어갑니다.
<span id='a'></span>
<span id='b'></span>
<span id='c'></span>
</div>

 

overwrite
Edit section

: 지정된 element의 내용을 전부 교체합니다.

ex)
<div id='test'>aaa</div>
tp.overwrite('test', ...) 
와 같은 형태로 하면 tp에 있는 내용들이 <div id='test'>~</div> 안에 있는 내용이 모두 바뀝니다.

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

Extjs Sample :: 기본문서구조  (0) 2008.02.18
Extjs Qtips 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Extjs 기본 DomQuery  (0) 2008.02.18
Extjs 초보자를 위한 애플리케이션 레이아웃(2.02)  (8) 2008.02.17
Post by 넥스트리소프트 데꾸벅(techbug)
, |