블로그 이미지

카테고리

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

최근에 올라온 글

최근에 달린 댓글

http://adomas.org/javascript-mouse-wheel/


Mouse wheel programming in JavaScript

Quick links: source · test page · long test page (with scrollbars) · compatibility

Though many people still find this page useful, there have been some changes in the browsers since last update of this page, and I generally consider some information here to be outdated. However, I suggest using MooTools or other general Javascript frameworks that provide portable and maintainable basis for writing Javascript code.

Web applications are becoming more and more like “normal” desktop applications. Of course, they are more and more functional, but smooth user interface acts the primary role. So we have drag and drop, autocompletition, and much more. Many of those nice features got possible only with help of AJAX.

This page, however, is not about AJAX (or any other buzzword). It is about rather simple user input method -- mouse wheel. I believe it would now be difficult to find a mouse without wheel. Vast majority of users are used to the wheel as a control for scrolling in lists, zooming etc. Rather few web applications, however, make smart use of mouse wheel. This page is to provide you with general information about handling mouse wheel-generated events in JavaScript programming language.

Annotated code

Below is annotated javascript code, which should explain the magic behind mouse wheel generated events. There is also plain version for copy and paste.

/** This is high-level function.
* It must react to delta being more/less than zero.
*/
function handle(delta) {
if (delta < 0)
…;
else
…;
}

/** Event handler for mouse wheel event.
*/
function wheel(event){
var delta = 0;
if (!event) /* For IE. */
event = window.event;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta/120;
/** In Opera 9, delta differs in sign as compared to IE.
*/
if (window.opera)
delta = -delta;
} else if (event.detail) { /** Mozilla case. */
/** In Mozilla, sign of delta is different than in IE.
* Also, delta is multiple of 3.
*/
delta = -event.detail/3;
}
/** If delta is nonzero, handle it.
* Basically, delta is now positive if wheel was scrolled up,
* and negative, if wheel was scrolled down.
*/
if (delta)
handle(delta);
/** Prevent default actions caused by mouse wheel.
* That might be ugly, but we handle scrolls somehow
* anyway, so don't bother here..
*/
if (event.preventDefault)
event.preventDefault();
event.returnValue = false;
}

/** Initialization code.
* If you use your own event management code, change it as required.
*/
if (window.addEventListener)
/** DOMMouseScroll is for mozilla. */
window.addEventListener('DOMMouseScroll', wheel, false);
/** IE/Opera. */
window.onmousewheel = document.onmousewheel = wheel;

Handler function

Deltas pictured. Negative means down, positive up.

In the code above, there is “handle” function which is, of course, meant to be written by you. As you have noticed, it takes on parameter, delta. It is called so, because mouse wheel has no absolute system, and we only capture scrolling deltas, that is wheel angle changes.

Practically, you only have to watch for positive and negative values of delta. These cases are pictured on the left.

If delta is positive, wheel was scrolled up. Otherwise, it was scrolled down.

Note that in applications, scrolling up usually means page is scrolled down etc.

You might wonder, what will the actual values of delta be. In fact, the code above is adjusted in such a way that you should get -1 or +1 in 99% of all cases. Though, for instance, I have seen ±3 in Firefox if you scroll very fast. Someone in Digg reported delta of 14, and Geoffrey Kruse was able to get 76 on his Powerbook trackpad. :-) Of course, that depends on sensivity that you've set for your mouse. There is also subtlety in Firefox (at least): if you start scrolling the wheel fast and then push the right mouse button for the menu, large value of delta is reported, e.g. 30.

Robert Gerlach writes about his experience on Safari, MacOS X: “For a single turn its +-0.1, if you roll it faster, the values become bigger. This is because of Mac OS' mouse/wheel-acceleration. You move/turn it one time, it moves/scrolls 1 pixel. Then you move/turn it three times fast and it moves/turns 30 pixel” He also writes about Camino, which is based on Gecko, the same engine that Firefox uses: “much like Safari (+- 0.3 to +-Infinity), which astonishes me, since it is based on the same engine as Firefox, which only shows +-2.666666 despite the speed.”

You are also welcome to see test page and also long test page, i.e. with scrollbars – that shows how preventing default behaviour works.

Compatability

I have written first version of this code in January 2006. Back then, Firefox and Internet Explorer 6 were only supported browsers. Older Internet Explorer version do not work. Thanks to Kane Barton for reporting that this works since Firefox 1.0.7 (so possibly since 1.0).

I would like to thank Jean-Luc Milot for contributing notes on how to make the code work properly in Opera 9. That version of Opera is relatively new (as of July 2006); previous versions of Opera do not support mouse wheel. I am also grateful to Юрій Чайковський (Yurij Chaikovsky) for pointing out that in newer versions of Opera (at least from 9.20 and up) you no longer have to invert the sign of delta. So the behaviour is the same as in Internet Explorer. I am a bit worried about compatability with older versions of Opera (though Opera forcibly recommends its users to update once new version is out) but after some time passes I will remove the conditional code that was written specifically for Opera.

Also thanks to Andrew Shearer, Dorian and Geoffrey Kruse for pointing out that the code works in Safari. Andrew mentions version 2.0.4, but I am not sure about earlier versions. Markus Rothenbacher wrote me telling that Konqueror (at least till version 3.5.4) does not have support for mouse wheel.

Preventing default behaviour code comes from phpspot.org and Johan Sundström. You can see how their code works in long test page (i.e. with scrollbars). Still, it works in IE/Firefox. It should also work in Opera since version 9.02. (Thanks to Jarkko Rantavuori for pointing this out.)

It is useful to note, however, that Opera, Safari and Firefox include automated upgrade systems that prompt users to upgrade to newer versions of the browser once it gets released, so you can be prepared to the fact that more and more internet users will have browsers for which this mouse wheel code works.

Mouse wheel currently works on: (with earliest version known to work)

  • Internet Explorer 6
  • Firefox 1.0
  • Opera 9.
  • Safari.

Unsupported browsers: (no existing version supports mouse wheel)

  • Konqueror
  • OmniWeb
  • iCab

Usability

(Few “don't”s.)

  • Don't make wheel act unexpectedly. Users know that wheel scrolls the list of files. If there is list of files in your web application, use it. Some map programs use wheel for zooming (and so does Google Maps). So use it.
  • Don't force users to depend on having wheel working. Just as it is bad practice to rely on JavaScript.
  • Try to get rid of global scrollbars, because mixing them with this code might cause frustration to the users.

Real world examples

If you know more practical and interesting examples, please mail me a link.

Related stuff

Hope this was useful to you. You may drop me a note if it was.

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

[출처 : PHPSchool]

쩝..IE7에서 보안이 강화되면서 미리보기 기능이 구현안돼서 헤매고 당기다 아지트에서 발견하다...

소스 :



<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=euc-kr">
<style type="text/css">
#previewPhoto {
 width:100%;
 height:100%;
 position:absolute;
 z-index:1;
 filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale);
}
#previewPhotoGara {
 width:115px;
 height:150px;
 filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale);
}
</style>
<script type="text/JavaScript">
<!--
function showPhoto(src) {
 if(src.match(/\.(gif|jpg|jpeg|png)$/i)) {
 // 복사용 개체에 그림 삽입하고 실제 크기 구하기
  var obj = document.getElementById("previewPhotoGara");
  obj.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod=image)";
  var w = obj.offsetWidth;
  var h = obj.offsetHeight;
  /*if(w > 115) { // 가로 길이가 115 이상일 경우만 축소하기
   var x_axis = w / 115; // X축의 축소 비율 구하기
   var y_axis = parseInt(h / x_axis); // X축이 들어든 만큼 Y축의 크기를 축소한다.
  } else {
   var y_axis = h;
  }
  // 썸네일 컨테이너의 크기 조정
  document.getElementById("previewPhotoBox").style.width = '115px';
  document.getElementById("previewPhotoBox").style.height = y_axis + 'px';
  */

  // 썸네일에 그림 삽입하고 이상유무 첵~
  document.getElementById("previewPhoto").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod=scale)";
 } else {
  alert("이미지 파일만 올려주세요");

 }
}
function killPhoto() {
 if(!confirm("사진을 취소하시겠습니까?")) return;
 document.getElementById("filePhoto").outerHTML = document.getElementById("filePhoto").outerHTML;
 document.getElementById("previewPhoto").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='noPhoto.gif',sizingMethod=scale)";
}
//-->
</script>
</head>
<body>


<DIV style="overflow:hidden; width:115px; height:150px"><!-- 썸네일 크롭핑 -->
<DIV id=previewPhotoBox style="width:115px; height:150px"><!-- 썸네일 컨테이너 -->
<DIV id=previewPhoto><!-- 썸네일 -->
</DIV>
</DIV>
</DIV>

<DIV style="overflow:hidden; width:1px; height:1px">
<DIV id=previewPhotoGara></DIV>
</DIV>

<input type=hidden id=verifyPhoto value=""><!-- 업로드 파일이 이미지 파일(jpg,gif)인지 최종판단 -->

<input type=file name=filePhoto id=filePhoto onchange="showPhoto(this.value)">
<input type=button value="사진취소" onclick="killPhoto()">

</body>
</html>

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

ExtJS 2.1 릴리즈

Scripter/EXTJS / 2008. 4. 22. 09:55

프로젝트가 바빠서 한동안 들리지 않았더니만...
extjs 2.1 이 릴리즈됐다.

많은 변화가 있었는데 가장 큰 변화라면 라이센스정책이 기존의 LPGL에서 3가지로 구분되어 나뉘어졌다. YUI에서 출발한 extjs는 아닐거라고 생각했는데 유료로 전환해 버렸네~
우선 가장 이번 2.1버전에서 달라진 점(Release Note)을 찾아본다면 아래와 같다.

  1. 라이센스 정책 변화
  2. Full REST 지원
  3. Ext.StatusBar 컴포넌트 추가 (IE7,FF에서 보안정책때문에 애먹어서 구현하고 있었는데.. ㅠ.,ㅠ)
  4. 컴포넌트 Config옵션 Remote Loading지원
  5. Grid 필터링 추가
  6. Layout Browser Sample 추가
  7. Spotlight 기능추가
  8. 아이콘 팝업 추가
  9. 슬라이드기능 추가
  10. validation기능 추가(향상이라고 해야 하나?)
  11. Card Panel Layout추가 (Wizard기능)
  12. Markup re-Rendering기능 향상

라이센스정책
많은 개발자들의 활발한 활동과 커뮤니티의 활성화로 Ext가 안정화 되면서 Ext개발자와 공헌자들이 계속 개발을 할수 있게 하기위해 라이센스 정책을 변경함 ( 뭐~ 다들 일케 말하더라~ )  여기서 말하는 듀얼 라이센스 정책이라는것은 개발된 애플리케이션의 소스를 공개하지 않는다면 commercial License이고 공개한다면 Open Source가 된다는 거다.. ㅡ.,ㅡ;
둘다 Open Source이긴 하지만.. 돈있으면 소스공개하지 말고 commercial로 쓰고 돈없으면 소스 공개해서 OpenSource로 까발리라는 얘기다~~

1. Commercial License :
 Quid Pro Quo 원칙에 근거하여 배포할 목적으로 애플리케이션을 개발한다면 적정한 수 만큼의 라이센스를 구입해야 함. (듀얼 라이센스정책)
 - 구매한 소스에 대해서는 상업용으로 전환 가능.
  - GPL에 의해 소스를 수정할수 있음.
2. Open Source
 기존의 LGPL에서 GPL에 따른 라이센스 정책
3. OEM / Reseller License




샘플보기 : http://www.extjs.com/deploy/dev/examples/samples.html


헉... Firebug를 켠 상태로 들어갔더니만 이런 메세제가 ㅡ.,ㅡ; firebug detecting기능을..

사용자 삽입 이미지





그리드 필터링 기능 : 언제가 사용자중에 이 기능을 extesion으로 올렸던것이 추가되었다.

사용자 삽입 이미지






Sportlight기능 : 설치하기나 step으로 나눈것 설명할때 편하겠군효~~
사용자 삽입 이미지






Drag & Drop 기능향상

사용자 삽입 이미지









슬라이드 기능 (기존 scriptaculous사이트에서 보던 예제를 extjs로... ㅡ.,ㅡ;)


사용자 삽입 이미지






StatusBar 기능
사용자 삽입 이미지






흠.. StatusBar기능 괜찮네~ 이것도 사용자 extension이였던 기능이였는데..
사용자 삽입 이미지







개발시 가장 까다로웠던 validation check기능이 많이 추가되었다.
사용자 삽입 이미지







CardLayout을 이용한 Wizard기능 추가
사용자 삽입 이미지






흠... 이건.. 추가라고 할것도 없이 사용자 편의성을 위해 아이콘추가 ㅡ.,ㅡ;
사용자 삽입 이미지







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

Extjs 3.0 Roadmap  (10) 2008.11.12
Ext2.2 Release  (11) 2008.08.07
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |

100라인 Ajax Wrapper

Scripter/AJAX / 2008. 4. 19. 15:13



다른 분들은 어떨지 모르나 UI 개발자인 데꾸벅의 경우는 프로젝트를 할때 마다 느끼는 거지만 어떤 Ajax framework를 쓸지 고민을 많이 하는 편이다.
그냥 규모가 좀 되는곳이라면 jQuery나 prototype을.. 그리고 내부 애플리케이션이라면 extjs를... 혹은 작더라도 struts2를 쓸때는 dojo를...
근데 문제가 되는것은 아주 작은 소규모 프로젝트의 경우는 이러한 ajax framework나 libraries를 사용하기가 좀 그래서 단순 XHR Wrapper를 만들어 사용한다



[데꾸벅이 사용하는 Ajax Wrapper 01]

function createHttp() {
 try {
  return new XMLHttpRequest();
 }
 catch (e) {
  var objectNames = ["MSXML2.XMLHTTP.5.0",
"MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
  for (var i = 0; i < objectNames.length; i ++) {
   try {
    return new ActiveXObject(objectNames[i]);
    break;
   }
   catch (e) {
   }
  }
  return null;
 }
}


var loading = false;
function getResponse(uri,content) {
 try {
  loading = true
  var body = document.body;
  var oHttp = createHttp();
  if(uri.indexOf('?') ==-1) aux = '?';
  else aux = '&';
  oHttp.open("POST", uri + aux+"time=" + (new Date()).getTime(), false);
  if(content == undefined) {
   content = '';
  } else {
   oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  }
  oHttp.send(content);
  result = new Array();
  if(isSafari || isOpera) {
    resultNodes = oHttp.responseXML.firstChild.childNodes;
    for(var i=0; i';

  return false;
 }
}

function requestHttp(uri) {
 try{
  var oHttp = createHttp();
  oHttp.open("GET", uri + "&time=" + (new Date()).getTime(), false);
  oHttp.send("");
  if(isSafari || isOpera) {
   var returnValue = oHttp.responseXML.firstChild.firstChild.nextSibling.firstChild.nodeValue;
   delete oHttp;
   return returnValue;
  } else {
   var returnValue = oHttp.responseXML.selectSingleNode("/response/error").text;
   delete oHttp;
   return returnValue;
  }
 } catch (e) {
  window.status = e.messge;
 }
}

function requestHttpText(uri) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.send("");
 var returnValue = oHttp.responseText;
 delete oHttp;
 return returnValue;
}

function requestHttpXml(uri) {
 var oHttp = createHttp();
 oHttp.open("GET", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.send("");
 var returnValue = oHttp.responseXML;
 delete oHttp;
 return returnValue;
}

function requestPost(uri, content) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 oHttp.send(content+ "&time=" + (new Date()).getTime());
 var returnValue = oHttp.responseXML.selectSingleNode("/response/error").text;
 delete oHttp;
 return returnValue;
}

function requestPostText(uri, content) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 oHttp.send(content);
 var returnValue = oHttp.responseText;
 delete oHttp;
 return returnValue;
}


function setRequestBody(elementName, elementValue, boundary){
 var body = "";
 body += "--" + boundary + "\r\n";
 body += "Content-Disposition: form-data; name=\"" + elementName + "\"" + "\r\n\r\n";
 body += elementValue + "\r\n";
 return body;
}



[모 프로젝트에서 사용했던 예~ ]

/**
 * HTTP 혹은 HTTPS 방식의 통신을 제공하는 클래스객체를 생성합니다.
 * @class EnHttp
 *
 */
function EnHttp(){

 this.dialBack ="";    //컴포넌트와 컨테이너 인증에 사용합니다.
 this.extraInfo ="";    //Url의 추가 정보를 지정합니다.
 this.host = "127.0.0.1";  //호스트 이름을 지정합니다.
 this.method = "POST";   //메쏘드(HTTP의 Verb)를 지정합니다.
 this.optionalHeaders = "";  //HTTP요청의 추가 헤더를 지정합니다.
 this.password = "";    //패스워드를 지정합니다.
 this.port = "80";    //포트를 지정합니다.
 this.postData ="";    //POST, PUT 요청에 사용되는 데이터를 지정합니다.
 this.protocol ="HTTP";   //프로토콜을 지정합니다.
 this.proxy ="";     //프록시가 설정된 환경인 경우, 프록시설정 정보를 지정합니다.
 this.referer = "";    //HTTP요청의 Referer를 지정합니다.
 this.responseContents="";  //응답 내용 문자열을 전달합니다.
 this.responseHeaders ="";  //응답헤더 문자열을 전달합니다.
 this.responseStatusCode =""; //응답상태코드를 문자열로 전달합니다 (예: "404")
 this.responseStatusText =""; //응답상태를 문자열로 전달합니다.(예: "Not Found")
 this.ssl = false;    //SSL 사용여부를 지정합니다.
 this.timeout = 0;    //HTTP요청의 timeout을 지정합니다.
 this.urlPath="";    //경로를 지정합니다.
 this.user ="";     //사용자 이름을 지정합니다.
 this.version ="";    //HTTP버전 문자열을 지정합니다.
 this.url = "";     //전체 URL을 지정 합니다.


 /**
  * 실제 HTTP요청을 서버에 전송합니다.
  * @return true/false
  */
 this.SendRequest = function(){
  this.url = this.protocol + "://"+ this.host + ":" + this.port + this.urlPath;

  var xmlhttp = createHttp();
  xmlhttp.open(this.method, this.url,false);

  var headers = this.optionalHeaders.split("\r\n");
  for(var i=0; i < headers.length; i++){
   var oneHeaders = headers[i].split(":");
   xmlhttp.setRequestHeader(oneHeaders[0],oneHeaders[1]);
  }


  var varResponseStatusCode = "";
  var varResponseHeaders = "";
  var varResponseContents = "";

     //도착하는 이벤트 핸들러 걸어주고
     xmlhttp.onreadystatechange = function() {
         if (xmlhttp.readyState==4) {
    varResponseStatusCode = xmlhttp.status;
    varResponseHeaders = xmlhttp.getAllResponseHeaders();
             if (xmlhttp.status==200) {
     varResponseContents = xmlhttp.responseText;
    }
         }
     }
     xmlhttp.send(this.postData);
  this.responseStatusCode = varResponseStatusCode;
  this.responseHeaders = varResponseHeaders;
  this.responseContents = varResponseContents;
 }


 /**
  * 쿠키를 읽어옵니다.
  * @param {Object} url - 쿠키를 읽어올 Url주소
  * @return Cookie
  */
 this.getCookie = function(url){

 }

 /**
  * 쿠키를 설정합니다.
  * @param {Object} url- 쿠키를 설정할 Url주소.
  * @param {Object} cookie- 설정할 쿠키.
  * @return ture@false
  */
 this.setCookie = function(url,cookie){

 }


 /**
  * 파일을 form 형태로 업로드합니다.
  * @param {Object} file- 업로드할 파일명(경로).
  * @param {Object} url- 업로드할 경로.
  */
 this.upload = function(file,url){

}
}


Kris Zyp's 블로그에 Future Ajax Wrapper라는 포스트에서 cross-browser와 cross-domain을 지원하는 100라인 짜리 ajax wrapper 소스를 선보였다.

W3C에서 제안한 cross-site acess 를 사용하는 대신에 IE8의 XDR(XDomainRequest)와 새로운 cross-site request를 추가하였다고 한다. 이번에 출시된 IE8은 표준을 모두 따른다고 하더니만....   또다시 IE8용으로 분기를 해줘야 한다.. ㅡ.,ㅡ;



function doRequest(method,url,async,onLoad,onProgress) {
    var xhr;
    if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
         if (url.match(/^https:/) && !onProgress) {
            loadUsingScriptTag(url); 

            return;
	}
        xhr = new XDomainRequest;
	if (!url.match(/^http:/)) {
            url = absoluteUrl(location.href,url);

          }
	if (!(method == “GET” || method == “POST”)) {
	    url += “&method=” + method;
	    method = “POST”;
	}
	function xdrLoad() {
	   if (xhr.contentType.match(/\/xml/)){
		var dom = new ActiveXObject(”Microsoft.XMLDOM”);
		dom.async = false;
		dom.loadXML(xhr.responseText,200);
		onLoad(dom);
	   }
	   else {
	   	onLoad(xhr.responseText,200);
	   }
	}
	if (async === false) {
	    var loaded;
	    xhr.onload = function() {
		loaded = true;
		xdrLoad();
	    }
            xhr.open(method,url);
	    xhr.send(null);
	    while(!loaded) {
		alert(”결과를 기다리는 중입니다.”);
	    }
 	    return;
	}
	else {	// do an asynchronous request with XDomainRequest
            xhr.onload = xdrLoad;
            xhr.open(method,url);
            xhr.onprogress = onProgress;
	}
    }

    else {
        xhr = new XMLHttpRequest; 
        xhr.open(method,url,async);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 3) 
                onProgress(xhr.responseText);
            if (xhr.readyState == 4) // finished
                onLoad(xhr.responseText,xhr.status);
        }
    }
    xhr.send(null); 


	function absoluteUrl : function(baseUrl, relativeUrl) {
		if (relativeUrl.match(/\w+:\/\//))
			return relativeUrl;
		if (relativeUrl.charAt(0)==’/') {
			baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
			return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
		}
		baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);
		if (relativeUrl == ‘.’)
			return baseUrl;
		while (relativeUrl.substring(0,3) == ‘../’) {
			baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
			relativeUrl = relativeUrl.substring(3);
		}
		return baseUrl + relativeUrl;
	}
	function loadUsingScriptTag(url) {
		… do JSONP here if we want
	}
}

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

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |
소위 Mark of the Web이라고 불리우는 ActiveX 보안창을 끄기 위해서 매일 도구>옵션에서 체크를 해줬는데 근본적인 문제 해결은 아닌것 같다.

실제로 모 프로젝트에서 X-internet솔루션중에 해당 HTML소스를 사용자 로컬에 내려받아서 HTML을 내려받는 서버부하를 줄였던 기억이 난다.

그럴경우 대부분 로컬에서 스크립트를 실행하게 되는데 실제 프로젝트에서는 ActiveX를 설치해서 작업하였다. 빈대잡으려다 초가삼간 태운다고 아래코드 한줄이면 해결할수 있었는데...
Windows XP SP2에서
<!-- saved from url=(0013)about:internet -->


위 코드를 실제 javascript가 포함된 HTML안에 넣어주면 로컬영역이 아니라 인터넷 영역으로 설정되어 보안설정이 해제된다. HTML뿐만 아니라 flash에서도 적용된다.




[본문인용:http://www.adobe.com/kr/support/flash/ts/documents/xpsp2.htm]


Microsoft에서 제시한 해결 방법은 .html 파일에 해결 코드 한 줄을 삽입하는 것입니다. 이 코드를 삽입하면 파일이 로컬 컴퓨터 영역 대신 인터넷 영역으로 설정되어 로컬 컴퓨터 차단이 해제됩니다. Microsoft에서 Mark of the Web이라고 하는 이 코드는 다음과 같습니다.

<!-- saved from url=(0013)about:internet -->

JavaScript 또는 Flash 내용이 포함된 파일을 로컬에서 미리 볼 때의 보안 메시지

액티브 콘텐트(예: JavaScript 또는 Flash)가 포함된 파일을 하드 드라이브에서 로컬로 미리 볼 경우 Internet Explorer의 노란색 정보 표시줄에서 다음과 같은 보안 메시지가 나타납니다.

보안을 위해 Internet Explorer가 이 파일에서 사용자의 컴퓨터를 액세스할 수 있는 액티브 컨텐트를 표시하는 것을 차단했습니다. 옵션을 보려면 여기를 클릭하십시오...

참고: 이 보안 메시지는 하드 드라이브의 파일(예: C:\myFiles\myFile.htm)을 직접 볼 경우에만 나타납니다. http:// 프로토콜을 사용하여 로컬 웹 서버(http://localhost/myFile.htm)나 원격 웹 서버(http://www.macromedia.com/myFile.htm)에서 페이지를 볼 경우에는 이 메시지가 나타나지 않습니다.

이 보안 메시지가 나타나지 않도록 하는 방법에는 두 가지가 있습니다.

  • 웹 페이지를 만드는 Flash 개발자가 SWF 파일을 로컬에서 재생할 때 메시지가 표시됩니다.(TechNote 19480)의 설명대로 Mark of the Web 코드를 파일에 추가하여 이 경고 메시지가 나타나지 않도록 할 수 있습니다.
  • 로컬에서 파일을 테스트하는 Flash 개발자의 경우 페이지에 Mark of the Web 코드를 추가하는 대신 다음 단계에 따라 로컬에서 액티브 콘텐트를 허용할 수 있습니다. 웹 사이트 방문자는 http:// 프로토콜을 통해 웹 페이지를 보게 되므로 Internet Explorer에서 이 변경 작업을 수행할 필요가 없습니다.
    1. 로컬 워크스테이션의 Internet Explorer에서 "도구" > "인터넷 옵션"을 선택합니다.
    2. "고급" 탭을 선택한 다음 "보안" 섹션으로 스크롤합니다.
    3. "[내 컴퓨터]에 있는 파일에서 액티브 콘텐트가 실행되는 것을 허용"을 선택합니다.
    4. "확인"을 선택합니다.









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

RESTFul 한 ExtJS

Scripter/EXTJS / 2008. 4. 16. 19:49


오늘따라 많이 포스팅한다는 느낌이... ( 데꾸벅 무리하다... ㅡ.,ㅡ; )
사실 뒤에 앉아 있는 울 겸댕이 윤부장의 친구분이 이 블로그를 보고 계신다기에 팁으로 올려본다.

RESTFul 한 Web application을 개발하기 위하여 부단히 노력하고 계신분들을 위한 포스트~
크헬헬~


먼저 이곳을 먼저 보세요.. : RESTful Web Services (번역중~ 크헬헬)



이제껏 RESFul하다고 하면서 개발할때 사실 2.0이전 버전에서는 모든 데이타를 parameter값들에 의해서 일일이 encodeURIComponent까지 친절히 적어주면서 작업을 하였으나 2.0 버전부터는 REST를 Full 지원하게 되면서 상당히 작업하기가 편해진것은 사실이다.

Request를 보낼때 다음과 같이 보내면 된다.....

    Ext.Ajax.request({
        url: 'process.json',
        method:'delete',
        jsonData: {foo:'bar'},   //보낼 json타입을 파라미터 값이아니라 json객체로 ..
        headers: {
            'Content-Type': 'application/json-rpc' // application/json이 아님에 주의
        }
    });


단, 아직은 완전한 REST 같지 않아서 다음과 같이 오버라이드해야 한다.


Ext.override(Ext.form.Action.Submit, {
    run : function(){
        var o = this.options;
        var method = this.getMethod();
        var isGet = method == 'GET';
        if(o.clientValidation === false || this.form.isValid()){
            Ext.Ajax.request(Ext.apply(this.createCallback(o), {
                form:this.form.el.dom,
                url:this.getUrl(isGet),
                method: method,
                params:!isGet ? this.getParams() : null,
                isUpload: this.form.fileUpload
            }));
        }else if (o.clientValidation !== false){ // client validation failed
            this.failureType = Ext.form.Action.CLIENT_INVALID;
            this.form.afterAction(this, false);
        }
    }
});

Ext.lib.Ajax.request = function(method, uri, cb, data, options) {
    if(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){
            if (!hs || !hs['Content-Type']){
                this.initHeader('Content-Type', 'text/xml', false);
            }
            method = (method ? method : (options.method ? options.method : 'POST'));
            data = options.xmlData;
        }else if(options.jsonData){
            if (!hs || !hs['Content-Type']){
                this.initHeader('Content-Type', 'application/json', false);
            }
            method = (method ? method : (options.method ? options.method : 'POST'));
            data = typeof options.jsonData == 'object' ? Ext.encode(options.jsonData) : options.jsonData;
        }
    }
   
    return this.asyncRequest(method, uri, cb, data);
};

Ext.lib.Ajax.asyncRequest = function(method, uri, callback, postData) {
    var o = this.getConnectionObject();
    if (!o) {
        return null;
    }
    else {
        o.conn.open(method, uri, true);
       
        if (this.useDefaultXhrHeader) {
            if (!this.defaultHeaders['X-Requested-With']) {
                this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
            }
        }
       
        if(postData && this.useDefaultHeader && !this.headers['Content-Type']){
            this.initHeader('Content-Type', this.defaultPostHeader);
        }
       
        if (this.hasDefaultHeaders || this.hasHeaders) {
            this.setHeader(o);
        }
       
        this.handleReadyState(o, callback);
        o.conn.send(postData || null);
       
        return o;
    }
};

참고 : extjs.com 포럼글 중에서





 

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

Ext2.2 Release  (11) 2008.08.07
ExtJS 2.1 릴리즈  (4) 2008.04.22
Extjs Qtips 사용하기  (2) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
Post by 넥스트리소프트 데꾸벅(techbug)
, |

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

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

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

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

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

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

 MARKUP

<div id="아이디" ext:qtitle="툴팀 타이틀" ext:qtip="데꾸벅<br />머리아포">

 JAVAScript (ExtJS)

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

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


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

ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
ExtJS 로드맵  (0) 2008.04.01
Post by 넥스트리소프트 데꾸벅(techbug)
, |

JSON은 XML보다 더 심플하게 작성하는 방법에 대한 아이디어가 화장실에서 문득.. ㅡ.,ㅡ;
XML보다 좀더 구조화되고 심플하게 작성!!

개념은 일반적으로 Data를 구조화하기 위해 필요한 태그나 엘리먼트들을 줄이면 상당히 적은양의 데이타로 빠르게 통신할수 있을텐데라는 생각에서 출발한다.
Http통신이 원래는 RESTFul한 사상에서 출발해서 지금은 대부분 잘못쓰고 있는것처럼, JS(simple)ON 철학이 이런것에서 출발한것인데 잘못 쓰고 있다는 생각이... 아주 문득 들었다.

기존 JSON :

{
    "users": [
        {"first": "Homer",
         "last": "Simpson"},
        {"first": "Hank",
         "last": "Hill"},
        {"first": "Peter",
         "last": "Griffin"}
    ],
    "books": [
        {"title":"JavaScript",
         "author": "Flanagan",
         "year": 2006},
        {"title": "Cascading Style Sheets",
         "author": "Meyer",
         "year": 2004},
        {"title": "The C Programming Lanaguage",
         "author": "Kernighan",
         "year": 1988},
        {"title": "The Scheme Programming Language",
         "author": "Dybvig",
         "year": 2003},
        {"title": "Design Patterns",
         "author": "The Gang of Four",
         "year": 1995}

    ]

}

위와 같이 first,last 라는 attribute값들이 들어가는데 이것만 별도로 처리하여 배열식으로 처리한다면...

다이어트된 JSON :

{
    "users": {
        "cols": ["first", "last"],
        "rows": [["Homer", "Simpson"],
                 ["Hank", "Hill"],
                 ["Peter", "Griffin"]]
    },
    "books": {
        "cols": ["title", "author", "year"],
        "rows": [["JavaScript", "Flanagan", 2006],
                 ["Cascading Style Sheets", "Meyer", 2004],
                 ["The C Programming Language", "Kernighan", 1988],
                 ["The Scheme Programming Language", "Dybvig", 2003],
                 ["Design Patterns", "Gang of Four", 1995]]
    }
}


반복되는 키들을 줄임으로써 리소스를 상당량 줄일수 있겠다.문제는 서버상에서 JSON 생성할때랑 프리젠테이션 레이어에서 처리해줄때 약간 신경을 더 써야 한다는거...

흠.. Extjs에서는 어케 하지? ㅡ.,ㅡ;

Post by 넥스트리소프트 데꾸벅(techbug)
, |
사내 위키에 올렸던 글을 다시 정리하여 포스팅하다!
기본 레이아웃 viewport에 각 Region마다 서로다른 패널(panel)을 붙여봅니다.

Basic Concept

각 각의 패널(Panel)에 대하여 알아본다. 기본적인 레이아웃은 이미 앞장에서 설명했던 소스를 이용하여 각각의 Region에 서로 다른 Panel을 붙여본다. 서로다른 패널들이 레이아웃에서 어떻게 붙고(append)되고 자동으로 생성되며(Create) 삭제(remove and destroy)되는지에 대해서 알아본다. 최종적으로 재사용할 컴포넌트모듈을 작성하여 적용하는 방법(Step4)을 소개한다.
각 패널들에 대한 상세한 옵션에 대해서는 장차 설명하기로 하고 여기에서는 기본적인 레이아웃에 패널을 넣는 방식에 대해서 알아본다.

최종소스 :







Step 1.  Basic Layout and BoxComponent

우선 이전에 포스팅했던 기본레이아웃 그리기를 준비한다.

[basicLayout.html]

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="../../../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]

// 좌측 패널 (region : west ) 클래스
WestArea
= function() {
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  layout : 'fit',
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  html : '좌측'
 })
};
Ext.extend(WestArea, Ext.Panel, {});

// 우측 패널 (region: east) 클래스
EastArea
= function() {
 EastArea.superclass.constructor.call(this, {
  region : 'east',
  title : 'EAST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  layout : 'fit',
  margins : '5 5 5 0',
  cmargins : '5 5 5 5',
  html : '우측'
 })
};
Ext.extend(EastArea, Ext.Panel, {});

// 중앙 컨텐츠 패널 (region : center) 클래스
CenterArea
= function() {
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : 'CENTER',
  layout : 'fit',
  margins : '5 0 5 0',
  html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
 })
};
Ext.extend(CenterArea, Ext.Panel, {});

// 메인 클래스
BasicLayoutClass = function() {
 return {
  init : function() {
   Ext.QuickTips.init();
   this.viewport = new Ext.Viewport( {
    layout : 'border',
    items : [this.WestPanel = new WestArea(),
      this.EastPanel = new EastArea(),
      this.CenterPanel = new CenterArea()]
   });

   this.viewport.doLayout();
   this.viewport.syncSize();
  }
 }
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


 위와 같이 각각의 region마다 별도의 파일로 클래스를 관리한다.

  • WestArea.js
  • EastArea.js
  • CenterArea.js
  • basicLayout.js

사용자 삽입 이미지










Step 2.  Tab Panel - center regionEdit section

중앙컨텐츠 패널(center region : CenterArea.js )에 여러개의 TabPanel을 넣어보자

CenterArea = function(viewport) {
 this.viewport = viewport;
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : false,
  // TabPanel일 경우 Nested된 Panel의 타이틀이 영역을 차지하므로 title은 항상 false가 되어야함.
  margins : '5 0 5 0',
  deferredRender : false, // CotainerLayout이 로드될때 defer없이 렌더링한다.
  tabPosition : 'top', // Tab이 패널의 위쪽에 위치하게 한다. ( 'top'/'bottom'
  // 두가지를 지원하며 scrolling TabPanel은 'top'만
  // 지원한다.)
  activeTab : 0, // 처음 로드(렌더링)될때 첫번째(tab index : 0 ) 탭이 activate되게 한다.
  // 기본 Panel을 사용하거나 다른Panel이 Nested 될수 있다.
  items : [this.FirstPanel = new Ext.Panel( {
   id : 'CENTER_TAB_01',
   title : '첫번째 탭',
   autoScroll : true,
   html : "첫번째"
  }), this.SecondPanel = new Ext.Panel( {
   id : 'CENTER_TAB_02',
   title : '두번째 탭',
   autoScroll : true,
   html : "두번째"

  }), this.ThirdPanel = new Ext.Panel( {
   id : 'CENTER_TAB_03',
   title : '세번째 탭',
   autoScroll : true,
   closable : true,
   html : "세번째"
  })]
 })
  // activate - 탭이 활성화 될때 일어나는(fire)되는 이벤트
 this.SecondPanel.on('activate', function(panel) {
  alert(panel.title + '이 활성화 되었습니다.' + panel.getId());
 }, this);

 // deactivate - 탭이 비활성화될때 일어나는 이벤트
 this.SecondPanel.on('deactivate', function(panel) {
  alert(panel.title + '이 비활성화 되었습니다.' + panel.getId());
 }, this);

 // beforedestory - 탭이 닫히기전에 일어나는 이벤트
 this.ThirdPanel.on('beforedestroy', function(panel) {
  alert(panel.title + '이 닫혔습니다.');
 }, this);
};
Ext.extend(CenterArea, Ext.TabPanel, {});

탭패널의 경우 한 화면에 여러개의 패널(Panel)이 필요할 경우 유용하며 연관된 다를 데이타를 사용할때 상당히 유용하다. 기본적인 event는 위에서는 3가지만 열거했지만 위 3가지 만으로도 충분히 다양한 효과를 낼수 있을 것이다. 또한 각 탭 마다 다양한 패널을 추가할수 있으며 기본 ContainerLayout도 추가할수 있으므로 여러가지형태의 레이아웃을 잡을때 상당히 유용하다.
예를 들어 이전장에서 기본레이아웃에 그리드를 추가하는 방법을 배웠는데 첫번째 탭패널에 그리드 레이아웃을 넣어 해당 레이아웃을 테스트 해보기 바란다. 또한 각 탭이 activate될때 그리드의 데이타스토어를 다시 로드할수 있도록 이벤트를 걸어주거나 다른 액션을 취할수 있다.

참고: Ext.TabPanel은 Ext.layout.CardLayout을 사용하므로 deferredRender의 경우 Ext.layout.CardLayout의 deferredRender를 참조한다.

사용자 삽입 이미지



위의 소스중 탭패널이 바로 borderLayout에 속한(region:center)일 경우에는 title 이 나타나지 않는데 기본 Layout에 Nested된 TabPanel일 경우는 기본 Layout의 region에 title을 넣으면 된다. 실제로 작업을 하다보면 위의 방법보다 아래방법을 많이 사용하게 된다. 탭패널안의 탭패널이 들어갈 경우도 발생할수 있으므로 Nested된 패널로 기본 cotainerLayout을 잡은후 넣는 방법이 좋다.

CenterArea = function(viewport) {
 this.viewport = viewport;
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : 'CENTER',
  layout : 'fit',
  margins : '5 0 5 0',
  items : [ // Nested된 TabPanel
   new Ext.TabPanel( {
    deferredRender : false,
    tabPosition : 'bottom',
    activeTab : 0,
    border : false,
    enableTabScroll : true,
    items : [this.FirstPanel = new Ext.Panel( {
     id : 'CENTER_TAB_01',
     title : '첫번째 탭',
     autoScroll : true,
     html : "첫번째"
    }), this.SecondPanel = new Ext.Panel( {
     id : 'CENTER_TAB_02',
     title : '두번째 탭',
     autoScroll : true,
     html : "두번째"

    }), this.ThirdPanel = new Ext.Panel( {
     id : 'CENTER_TAB_03',
     title : '세번째 탭',
     autoScroll : true,
     closable : true,
     html : "세번째"
    })]
   })]
  })
};
Ext.extend(CenterArea, Ext.Panel, {}); //기본 ContainerLayout

  위 소스를 응용하면 탭안의 탭패널, 그안의 탭패널을 넣는 방식으로 계단식 탭패널이 가능하다.

사용자 삽입 이미지








Step 3. Accordian Panel and Tree Panel - west regionEdit section

좌측패널(WestArea.js, west region)에 Accordian Panel을 붙여본다. 각각의 패널에는 tree Panel 및 여러 Panel이 올수 있다.

WestArea = function(viewport){
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this,{
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed:false,
  width : 300,
  minSize:100,
  split : true,
  margins:'5 0 5 5',
  cmargins : '5 5 5 5',
  // Accordion Layout을 사용
  layout:'accordion',
  // Accordion Layout의 config option
  layoutConfig:{
   // 각각의 패널 처음 로드시 collapse먼저 하겠는지 확인
   collapseFirst:false,    
   // 패널 로드시 animated되게 할것인지
   animate:true,      
   // 타이틀을 눌렀을때 collapse할것인지
   titleCollapse: true,    
   // 각각의 패널중 activate될때 맨 처음으로 오게 할것인지
   activeOnTop: false,    
   // 패널의 내용에 상관없이 패널의 높이를 owerContainer의 높이와 맞출것인지 처리
   fill:true      
  },
  items: [
   this.FirstPanel = new Ext.Panel({
    title:'첫번째',
    border:false,
    html:'<p>트리가 들어갈 곳입니다.</p>'
   }),
   this.SecondPanel = new Ext.Panel({
    title:'두번째',
    border:false,
    html:'<p>두번째 패널의 내용이 들어갑니다.</p>'
   })
  ]
 })
};
Ext.extend(WestArea, Ext.Panel,{ });

accordion Layout으로 바꿨으니 이제 각각의 패널에 알맞은(?) 패널들을 넣어보자. 우선 첫번째 패널에 TreePanel을 넣어보자

WestArea = function(viewport) {
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  layout : 'accordion',
  layoutConfig : {
   collapseFirst : false,
   animate : true,
   titleCollapse : true,
   activeOnTop : false,
   fill : true
  },
  items : [this.FirstPanel = new Ext.tree.TreePanel( {
   title : '첫번째',
   border : false,
   layout : 'fit',
   loader : new Ext.tree.TreeLoader( {
    dataUrl : 'menu.js',
    baseParams : {}
   }),
   rootVisible : true,
   lines : true,
   autoScroll : true,
   root : new Ext.tree.AsyncTreeNode( {
    id : '_MENU_PANEL_',
    text : '트리메뉴의 Root',
    draggable : false,
    expanded : true
   })
  }), this.SecondPanel = new Ext.Panel( {
   title : '두번째',
   border : false,
   html : '<p>두번째 패널의 내용이 들어갑니다.</p>'
  })]
 })
};
Ext.extend(WestArea, Ext.Panel, {});

사용자 삽입 이미지







Step 4. Common Modules

이미 Step1,2,3 에서 접해봐서 알겠지만 각각의 패널에는 기본 ContainerLayout만 있으면 어떤 패널이던지 가져다 붙일수 있다는것을 알았을 것이다.
그럼 각각의 공통적으로 사용되는 패널의 경우 공통모듈화 하여 사용자가 정의한 모듈로써 사용하게 된다면 어느 패널에서든지 마음대로 붙였다 떼었다 할수 있을 것이다.


[basicLayout.html]

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="../../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../../adapter/ext/localXHR.js"></script>
<script type="text/javascript" src="../../../ext-all.js"></script>
<script type="text/javascript" src="Modules.js"></script>
<script type="text/javascript" src="WestArea.js"></script>
<script type="text/javascript" src="EaseArea.js"></script>
<script type="text/javascript" src="CenterArea.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

[Modules.js]

Ext.namespace("Ext.techbug");

// 공통으로 사용되는 트리메뉴
Ext.techbug.TreeMenu = function(config) {
 Ext.apply(this, config);
 Ext.techbug.TreeMenu.superclass.constructor.call(this, {
  loader : new Ext.tree.TreeLoader( {
   dataUrl : 'menu.js',
   baseParams : {}
  }),
  rootVisible : true,
  lines : true,
  autoScroll : true,
  root : new Ext.tree.AsyncTreeNode( {
   id : '_MENU_PANEL_',
   text : '트리메뉴의 Root',
   draggable : false,
   expanded : true
  })
 })
};
Ext.extend(Ext.techbug.TreeMenu, Ext.tree.TreePanel, {});

[WestArea.js]

WestArea = function(viewport) {
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  layout : 'accordion',
  layoutConfig : {
   collapseFirst : false,
   animate : true,
   titleCollapse : true,
   activeOnTop : false,
   fill : true
  },
  items : [
  // 공통모듈로 사용하는 트리메뉴로 변경
    this.FirstPanel = new Ext.techbug.TreeMenu( {
     title : '첫번째',
     border : false,
     layout : 'fit'
    }),
this.SecondPanel = new Ext.Panel( {
     title : '두번째',
     border : false,
     html : '<p>두번째 패널의 내용이 들어갑니다.</p>'
    })]
 })
};
Ext.extend(WestArea, Ext.Panel, {});







 

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

RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Post by 넥스트리소프트 데꾸벅(techbug)
, |

IE전용으로 프로젝트를 진행하다 보니 Jscript를 사용할 일이 생겼다.
Ajax를 이용하지 않고 Jscript만 사용한다면 아래 방법도 괜찮을것 같다.

<html xmlns:ie>
<head>
<meta http-equiv="content-type" content="text/html; charset=EUC-KR">
<style type="text/css">
<!--
BODY { font-size:12px; }
-->
</style>

<script language="javascript">
<!--
var loaded, content, processlist;
var urls1 = "a.html";
var urls2 = new Array("b.html", "c.html", "d.html");

function onDownloadDone(html){
 content += html;
 if (++loaded < processlist.length) CONTENT.startDownload(processlist[loaded], onDownloadDone);
 else CONTENT.innerHTML = content;
}

function loadHTML(toSource){
 loaded = 0;
 content = "";
 
 processlist = new Array();
 for (var i=0; i<arguments.length; i++) {
  processlist = processlist.concat(arguments[i]);
 }
 
 CONTENT.startDownload(processlist[0], onDownloadDone);
}
-->
</script>
</head>

<body>

사용법 예제 :
<button onclick="loadHTML(urls1);"> 1</button>
<button onclick="loadHTML(urls2);">2</button>
<button onclick="loadHTML(urls1, urls2);">3</button>
<button onclick="loadHTML('c.html');">4</button>
<button onclick="loadHTML('c.html', 'd.html');">5</button>
<button onclick="loadHTML('c.html', urls1, 'd.html', urls2, urls2, urls1);">6</button>

<hr />
<!-- 불러온 코드가 삽입될 곳 -->
<ie:download id="CONTENT" style="behavior:url(#default#download);" />
</body>
</html>


 

Post by 넥스트리소프트 데꾸벅(techbug)
, |
사내(넥스트리)위키에 올렸던 자료를 다시 정리하여 포스팅한다.

일반그리드와 달리 에디터그리드의 경우 해당 Cell에 해당하는 값을 변경할수 있다. 각각의 선택된 셀값과 추가하는 방법 및 해당 값들을 셋팅하는 방법, ActionComplete후 dirtyFlag를 삭제하는 방법등에 대해서 알아보자! 에디터 그리드는 이미 이전장에서 사용되었던 기본그리드를 사용하였으므로 반드시 이전장을 참조하도록 한다. 추가적으로 FormPanel에 대하여 미리 알아 보도록 한다.

최종소스 :

사용자 삽입 이미지


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>
</body>
</html>

basicGrid.js

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'
                }, this.myRecordObj = Ext.data.Record.create([ {
                    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.EditorGridPanel( {
                store : this.store,
                columns : [ {
                    id : 'company',
                    header : "Company",
                    width : 160,
                    sortable : true,
                    dataIndex : 'company',
                    editor : new Ext.form.TextField( {
                        allowBlank : true
                    })
                }, {
                    header : "Price",
                    width : 75,
                    sortable : true,
                    renderer : 'usMoney',
                    dataIndex : 'price',
                    editor : new Ext.form.NumberField( {
                        allowBlank : false,
                        allowNegative : false
                    })
                }, {
                    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',
                    editor : new Ext.form.DateField( {
                        allowBlank : false,
                        minValue : '08/21/00',
                        disabledDays : [0, 2, 6],
                        disabledDaysText : '이날짜는 선택안되요'
                    })
                }

                ],
                stripeRows : true,
                autoExpandColumn : 'company',
                loadMask : {
                    msg : '데이타 로드중'
                },
                clicksToEdit : 1, // Edit하기 위해 Cell을 클릭해야 하는 횟수 (최대2, 최소1)
                sm : new Ext.grid.RowSelectionModel( {
                    singleSelect : true
                }),
                view : new Ext.grid.GridView( {
                    forceFit : true,
                    enableRowBody : true,
                    emptyText : 'No Record found'
                }),
                height : 350,
                width : 1024,
                title : '기본 그리드',
                tbar : [
                        {
                            text : '저장',
                            scope : this,
                            handler : this.saveAllChanges
                        },
                        '-',
                        {
                            text : '추가',
                            scope : this,
                            handler : this.addRecord
                        },
                        '-',
                        {
                            text : '삭제',
                            scope : this,
                            handler : function() {
                                var selectedKeys = this.grid.selModel.selections.keys;
                                if (selectedKeys.length > 0) {
                                    Ext.MessageBox.confirm('확인',
                                            '정말 삭제할래요? 삭제하면 복구안돼요..',
                                            this.deleteRecord, this);
                                } else {
                                    alert('삭제할 Row를 선택하세요');
                                }

                            }
                        }, '-', {
                            text : '새로고침',
                            scope : this,
                            handler : function() {
                                this.store.reload()
                            }
                        }]
            });

            this.grid.render('DIV_GRID_HERE');
            this.store.load();
            this.grid.on('afteredit', this.afterCellEditEventHandler, this); // Cell을
            // 변경후
            // 일어나는
            // 이벤트
        },

        // CEll변경후 일어나는 이벤트
        afterCellEditEventHandler : function(editEvent) { // Cell을 변경후 일어나는
            // 이벤트
            var gridField = editEvent.field;
            var gridValue = editEvent.value;
            editEvent.record.set('company', editEvent.record.get('company')
                    + '변경필트:' + gridField + '변경된값' + gridValue);
            this.updateCell(editEvent);
        },

        // 삭제하는 루틴
        deleteRecord : function(btn) {
            if (btn == 'yes') {
                var selectedRows = this.grid.selModel.selections.items;
                var selectedKeys = this.grid.selModel.selections.keys;
                var selectedRecord = this.grid.selModel.getSelected();
                alert(selectedRecord.get('company') + '\n' + selectedRows
                        + '\n' + selectedKeys + '\n지우는 Ajax Call 후에 다시 리로드한다.');
                this.store.reload();
            }
        },

        // 새로운 Row를 추가하기
        addRecord : function(btn) {
            var r = new this.myRecordObj( {
                // this.myRecordObj는
                // this.store.reader의 Record Field를
                // 말함.
                company : '새 회사명',
                price : 0.00,
                change : 0.00,
                pctChange : 0.00,
                lastChange : new Date(),
                newRecord : 'yes', // Update할때 트리거로 이용하기 위해서
                id : 0
            });
            this.grid.stopEditing();
            this.store.insert(0, r);
            this.grid.startEditing(0, 0); // 추가할 rowIndex, colIndex 위치
        },

        // 수정된 Cell만 저장하기
        updateCell : function(editEvent) {
            var isNewRecord = editEvent.record.data.newRecord;
            // 업데이트할때 사용될
            // 트리거('yes'면 새로운 레코드)
            /** ** AJAX 통신 모듈 들어가는곳 ***** */

            // DB를 성공적으로 업데이트 후 처리할 루틴
            if (isNewRecord == 'yes') {
                // 저장한후 새로운 레코드가 아님을 표시
                editEvent.record.set('newRecord', 'no');
                // isDirty Flag 없애기
                this.store.commitChanges();
            } else {
                this.store.commitChanges();
            }

            // 잘못됐을경우는 this.sotre.rejectChanges(); 로 리복한다.
            this.store.rejectChanges();

        },

        //변경된것들 모두 저장하기
        saveAllChanges : function() {
            var modifyRecords = this.store.getModifiedRecords(); //수정된 레코드 모두 찾기
            var modifyLen = modifyRecords.length;
            if (modifyLen > 0) {
                for (var i = 0;i < modifyLen; i++) {
                    /* 각 Row마다 AJAX Call을 하거나 배열로 모두 넘기는 Ajax Call Method가 들어간다.*/
                    var modifyCompany = modifyRecords[i].get('company')
                }
            }

        }

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






 






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

Extjs Qtips 사용하기  (2) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Post by 넥스트리소프트 데꾸벅(techbug)
, |
COMET관련 사이트를 정리해 봅니다.
뇌입어 블로그(블로그.뇌입어.컴/데꾸벅)에 있던 내용을 다시 옮깁니다.
2008년 후반기 부터는 COMET이 수면위로 올라올것 같네.. ㅡ.,ㅡ;

사용자 삽입 이미지




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

ExtJS 로드맵

Scripter/EXTJS / 2008. 4. 1. 22:58
매일 프로젝트를 수행하면서 Ext의 새로운 버전이 나올때마다 현재 작업버전과 호환이 될까 노심초사하면서 테스팅하던 때가 있었는데 이번에 Extjs의 Roadmap이 공개가 되었다.

올 겨울에 나올 Ext3.0은 chart기능과 Comet기능까지 추가 된다니 기대가 되긴하는데 프레임웍 자체가 너무 방대해지는건 아닐까 걱정해 본다... (스크립트언어인데..방대해지면.. 문제가... ㅡ.,ㅡ;)


ExtJS의 로드맵 --------------------------------------------
2008년의 목표는 2.x 버전의 새로운 컴포넌트 추가와 현재 몇몇 기능을 향상하는 선이다.
앞으로 3.0에서 보는봐와 같이  챠트와, Comet(실시간 통신-Ajax는 Reqeust에 의한 Response방식인데 비해 Coment은 일반 어플리케이션처럼 릴레이션을 물고 있다 :데꾸벅 주) 을 추가하여 큰그림을 그리고 있다.... 이하 생략...원문참조


2.1 (2008 봄)

  • RESTFul 지원 (그리 RESTFul 하지 않던데.. ㅡ.,ㅡ;)
  • 원격컴포넌트 로딩
  • 전통적인 비Ajax적인 Submit 지원클래스
  • 다른 JS라이브러리 Adapter 업그레이드
  • AIR 플랫폼 지원추가
  • 그리드 필터링
  • 체크박스/라이오 그룹
  • 상태바

2.2 (2008 여름)

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

3.0 (2008 겨울/2009 초)

  • Ext 이벤트 등록모델 업데이트
  • 플래쉬 챠트 API ( Gwt-Ext에는 구글 챠트를 이용하던데..)
  • Comet/Bayeux 지원
  • 통합된 client-server 데이타 바인딩/marshaling for Ext components
  • Browser history support (흠.. window.event.hash값을 이용한걸까? 기대되네..)
  • ListView
  • Section 508(장애자 지원정책:웹접근성) 기능향상


여튼 3.0이 기대되는건 사실이자나...  ^^ 



Post by 넥스트리소프트 데꾸벅(techbug)
, |
Ajax 관련 프로젝트를 진행하다 보면 HTTP Request/Response 및 헤더정보를 읽어오는 경우가 종종 발생한다. 그러나 Firefox의 경우 Firebug가 있어서 개발하기 상당히 편한데....
여튼... Fiddler를 Firefox에 붙여보기나 하자

넥스트리 사내 위키에 포스팅했던걸 다시 정리해서 올려본다
(어차피 데꾸벅이 쓴글이라.. ㅡ.,ㅡ;)




[Fiddler에 Json Formatter붙이기 ]

XML은 기존의 훌륭한 Formatter와 개발툴들이 많이 있으나(가량, XMLSpy와 같이..) JSON에서는 해당하는 포맷터를 찾기 힘들었다.
Visualizer같은것이 있다면 좋을텐데 라는 생각에 검색을 보니 아래와 같이 좋은놈(?)이 있었다.. Search is life ㅡ.,ㅡ;

Formatter
http://curiousconcept.com/jsonformatter

Visualizer
http://www.codeplex.com/JsonViewer/Project/ProjectRss.aspx (이넘은 Fiddler Plugin까지 있다.)
http://www.epocalipse.com/blog/2007/06/03/json-viewer/

 

이놈은 StandAlone형태, Visual Studio Plug-in, Fiddler에서 Plug-in형식으로 볼수 있게 해준다 
Fiddler에서 사용하는 방법은 아래와 같다.

Fiddler2 (http://www.fiddler2.com) 를 설치하고 해당 디렉토리의 fiddler.exe.config파일을 열어서 아래와 같이 추가한다.

<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="1" />
 <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <probing privatePath="Inspectors" />
 </assemblyBinding>
  </runtime>
</configuration>

과 같이 설정후 Inspectors  디렉토리에 다운로드 받은 dll파일을 추가한다. 

FireFox에서의 사용
FireFox에서 Fiddler를 사용하려면 설정의 고급->네트워크->연결->프록시설정에서 아래의 프록시 자동설정주소에서
file:///C:/사용자문서위치/Documents/Fiddler2/Scripts/BrowserPAC.js 을 입력후 새로고침한후 실행하면 된다. (아래 다시 설명.. ㅡ.,ㅡ;)

참고적으로 IE7 에서 실행이 안되거나 Fiddler2에서 폰트가 깨져나올때
Tools > Fiddler Options 에서 Enable IPv6 항목을 uncheck 해주면 된다.

 만일 localhost가 잡히지 않는다면 Rules > Customize Rules(Ctrl+R)에서  다음과 같이 추가한다.
    static function OnBeforeRequest(oSession: Session)
    {
        if (oSession.host.toLowerCase() == "localhost") { oSession.host = "127.0.0.1"; }
       if (!(oSession.host == "127.0.0.1" || oSession.host == "127.0.0.1:8080" || oSession.host.toLowerCase() == "localhost" || oSession.host.toLowerCase() == "localhost:8080")) { oSession["ui-hide"] = "true"; }

을 추가한다.  첫번째 줄은  로컬호스트를 캡쳐할수 있게 하는것이고 두번째줄은 로컬호스트를 제외한 건에 대해서는 보여주지 말라는 얘기이다.





[FireFox에서 Fiddler사용하기]

Fiddler는 브라우저와 서버와의 HTTP 통신 상태를 보여주는 HTTP Debugging proxy이다. Fiddler에 대해서는 다음을 참조한다. (http://www.fiddlertool.com)
Fiddler를 사용할 경우 IE나 Opera 또는 그 외의 HTTP 통신은 모두 잡는다. 그런데 유독 firefox의 HTTP 통신은 잡지 못한다.

다음과 같은 방법으로 firefox에서도 fiddler를 사용할 수 있다.

1. Fiddler를 설치하고 실행한다.

2. "내 문서" 폴더 밑에 보면 "Fiddler2"라는 폴더가 있다. 그 하위 폴더에 Scripts라는 폴더가 있으며 이 디렉토리 경로를 복사한다.

3. Firefox를 열어 "도구 > 설정" 메뉴에서 "고급"을 선택한다.

4. 고급메뉴에서 "네트워크" 탭의 연결의 "설정" 버튼을 선택하여 "프록시 자동 설정 주소"를 선택하고 위에서 디렉토리 경로를 붙여넣고 마지막에 "BrowserPAC.js" 파일을 추가한다.
    예)  C:\Documents and Settings\stan\My Documents\Fiddler2\Scripts\BrowserPAC.js

사용자 삽입 이미지

5. "새로고침"을 클릭하고 "확인" 버튼을 클릭한다.

6. Firefox를 restart하면 firefox의 HTTP 통신이 fiddler에 잡힌다.



[HTTP Tracing Tools]
참고적으로 Fiddler 말고 다른 형태의 HTTP Hooking을 하려면 다음과 같은 도구들이 있다. 참조하시길..
받기 귀찮은 분들은 여기서.. 쿨럭~













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

JSON에 대한 작은 단상 : JSON Diet  (1) 2008.04.16
HTML include하기  (0) 2008.04.15
Javascript and jQuery Talk  (0) 2008.03.29
XHTML1일때 스크롤링시 문제점  (0) 2008.03.28
자바스크립트로 루비프로그래밍을?  (2) 2008.03.28
Post by 넥스트리소프트 데꾸벅(techbug)
, |
PHP스쿨의 질문내용중에 패스워드 미터에 대해 물어보던 내용이 있어서 일전에 꼬불쳐 두었던 URL을 정리하여 올린다.
사용자 삽입 이미지


아래 소스는 ExtJS의 User extension Plugin 이다


[HTML]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Ext.ux.PasswordMeter</title>
<link rel="stylesheet" type="text/css" href="../ext-1.1.1/resources/css/ext-all.css" />

<script type="text/javascript" src="../ext-1.1.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../ext-1.1.1/ext-all.js"></script>
		<script type="text/javascript" src="Ext.ux.PasswordMeter.js"></script>
<script type="text/javascript" src="passwordmeter.js"></script>
<link rel="stylesheet" type="text/css" href="passwordmeter.css" />

<!-- Common Styles for the examples -->
<link rel="stylesheet" type="text/css" href="../ext-1.1.1/examples/examples.css" />
</head>
<body>


<div style="width:300px;">
<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>
    <div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc">
<h3 style="margin-bottom:5px;">Simple Form</h3>
<div id="form-ct">

</div>
</div></div></div>
<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>
</div>
<div class="x-form-clear"></div>
</body>
</html>





[Javascript]

Ext.onReady(function(){

Ext.QuickTips.init();

// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

/*
* ================ Simple form =======================
*/
var simple = new Ext.form.Form({
labelWidth: 75, // label settings here cascade unless overridden
url:'save-form.php'
});
simple.add(
new Ext.form.TextField({
fieldLabel: 'First Name',
name: 'first',
width:175,
allowBlank:false
}),

new Ext.form.TextField({
fieldLabel: 'Last Name',
name: 'last',
width:175
}),

new Ext.form.TextField({
fieldLabel: 'Company',
name: 'company',
width:175
}),

new Ext.form.TextField({
fieldLabel: 'Email',
name: 'email',
vtype:'email',
width:175
}),
new Ext.ux.PasswordMeter({
fieldLabel: 'Password',
name: 'password',
width:175
})
);

simple.addButton('Save');
simple.addButton('Cancel');

simple.render('form-ct');
});



[JQuery로 만든 Password Meter ]

<html>
<head>
<title>Jquery</title>
<style type="text/css">
<!--
.password {
font-size : 12px;
border : 1px solid #cc9933;
width : 200px;
font-family : arial, sans-serif;
}
.pstrength-minchar {
font-size : 10px;
}
-->
</style>
</head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.pstrength-min.1.2.js"></script>
<script type="text/javascript">
$(function() {
$('.password').pstrength();
});
</script>
<body>
<INPUT class="password" type=password name="Password">
</body>
<html>



[참고 사이트]
http://justwild.us/examples/password/
http://www.codeandcoffee.com/2007/07/16/how-to-make-a-password-strength-meter-like-google-v20/
http://testcases.pagebakers.com/PasswordMeter/
http://www.passwordmeter.com/
http://ajaxorized.com/?p=14
http://simplythebest.net/scripts/ajax/ajax_password_strength.html
http://phiras.wordpress.com/2007/04/08/password-strength-meter-a-jquery-plugin/








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

ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
ExtJS 로드맵  (0) 2008.04.01
Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Extjs를 이용한 간단한 form submit  (0) 2008.03.14
Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈  (0) 2008.03.07
Post by 넥스트리소프트 데꾸벅(techbug)
, |



jQuery의 창시자 John Resig의 자바스크립트 강의 동영상이 있어 포스팅한다.

Javascript2 에 대한 내용도 있어 상당히 유용한 동영상이다.

John의 동영상은 구글비디오에서 볼수 있다.

Post by 넥스트리소프트 데꾸벅(techbug)
, |
YouTube API 와 Extjs를 이용한 Youtube player extension이 공개되었다.

데모페이지 : http://www.siteartwork.de/youtubeplayer_demo/
공식페이지 : http://www.siteartwork.de/youtubeplayer/
사용자 삽입 이미지

일전에 LiveGrid를 공개했던 Thorsten Suckow-Homberg가 공개한 YouTube Player는 개발자들이 쉽게 Youtube API를 이용하여 Ext application에 embed하도록 하였다.
간단한 기능으로는
  • 버퍼링 상태
  • 플레이백 슬라이더
  • 음소거
  • 볼륨컨트롤
등 기존 미디어플레이어의 기능을 많이 내포하고 있다.
당연히 오픈소스이므로 꽁짜다.. ^^

다운로드 :


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

ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Extjs를 이용한 간단한 form submit  (0) 2008.03.14
Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈  (0) 2008.03.07
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Post by 넥스트리소프트 데꾸벅(techbug)
, |
제목은 거창하게 달았지만 데꾸벅만 몰랐던 것일수도 있다.
대부분 SI관련 프로젝트는 quirks mode(스펠링이 맞나? 흠..)에서만 작업을 하기 때문에
XHTML transitional이나 strict모드에서는 잘 작업을 하지 않아서 잘못된것임에도 인지못하였던것일 수도 있다.

DOCTYPE이 아래와 같이 XHTML transitional 모드일때
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

[ 슬라이딩 메뉴 소스 ]
function slidingBars(objid,starttop,bottommargin){
    var obj=document.getElementById(objid);
    if(obj !=null && obj !=undefined){
        var ntop;
        var maxtop;
        var speed=10;
        var move=function(){
            ntop=starttop+document.body.scrollTop;
            maxtop=document.body.scrollHeight-(bottommargin+obj.offsetHeight);

            if(maxtop < ntop) ntop=maxtop;
            movevalue = Math.floor((ntop-obj.offsetTop)/speed);
            obj.style.top=(obj.offsetTop+movevalue)+'px';
            setTimeout(move,10);
        }
        move();
    }
}
window.onload=function(){
    slidingBars('slidingbar',35,100);
}

<div id="slidingbar">포지션:엡솔루트;지-인덱스:1인 레이어</div>


와 같이 늘쌍 써오던것이 Opera를 제외한 IE6,7,8 및 FF2.0에서 적용이 되질 않았다.
혹, XHTML같은 경우 테이블의 height가 100%가 지정되지 않기때문에 이런 문제일수도 있을것 같아 검색하다 보니 다음과 같은 글을 발견하였다.

http://forums.digitalpoint.com/archive/index.php/documentbodyscrolltop-in-ie/t-11965.html
http://forums.mozilla.or.kr/viewtopic.php?t=2382&sid=442d133080a29e669f7ca4357cdf3415

참조들 하심 되겠고....
말하고자 하는 바는 사실 우리가 늘쌍 써 왔던 document.body.가 아니라 document.html.scrollTop해도 적용이 되긴 하나 정확한 표현은 document.documentElement 라는 사실이다.
(위 소스에서 수정해줘야 해요~~ ^^)

documentElement 속성값은 문서의 최상위노드(root) 참조를 나타내는 개체이다.
이 속성은 읽기전용이며 디폴트값은 없다.
가장 전형적인 최상위 노느는 html 개체이다.


Post by 넥스트리소프트 데꾸벅(techbug)
, |
스크립트로 루비프로그래밍을 만든다?? 기발한 생각보다는 별놈이 다 있다는 생각이..

원본사이트 : HotRuby

아래그림은 루비로 실행된 FlashAS3 이다.

사용자 삽입 이미지

아래 파일을 다운로드 받아 다음과 같이 사용한다.



<html>
<head>
    <title>Block - HotRuby - Ruby on JavaScript</title>
    <link href="../css/prettify.css" type="text/css" rel="stylesheet" />
    <script type="text/javascript" src="../js/prettify.js"></script>
    <script type="text/javascript" src="../js/ext-base.js"></script>
    <script type="text/javascript" src="../js/HotRuby.js"></script>
   
    <script type="text/ruby">
class Hoge
    def add_msg &block
        block.yield "is"
    end
end
   
class Foo
    NAME = ' - William Shakespeare'
    def main
        pre = "Action"
        @space = " "
        Hoge.new.add_msg do |msg|
            fuga = "eloquence"
            puts pre + @space + msg + @space + fuga + NAME
        end
    end
end
   
Foo.new.main
    </script>
</head>
<body onload="prettyPrint(); new HotRuby().runFromScriptTag('/compileRuby.cgi')">
    <h1>Block - HotRuby(Ruby on JavaScript)</h1>
    <div>Result:</div>
    <div id="debug" class="result"></div>
    <br />




[compileRuby.cgi 내용]
require 'json'
require 'cgi'

OutputCompileOption = {
  :peephole_optimization    =>true,
  :inline_const_cache       =>false,
  :specialized_instruction  =>false,
  :operands_unification     =>false,
  :instructions_unification =>false,
  :stack_caching            =>false,
}

cgi = CGI.new

puts "Content-type: text/plain\n\n"
puts VM::InstructionSequence.compile(cgi['src'], "src", 1, OutputCompileOption).to_a.to_json








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

원본출처 : John Resig's Blog


사용자 삽입 이미지

IE8이 출시된지 2주일이 지난 지금 이전 브라우저에서 정상적으로 동작되던 스크립트들이 IE8에서는 안되는 경우가 있다. 예를 들면 XMLHttpRequest를 통한 전송부분이나 <object>.getElementsByTagName("*")부분일 것이다.







브라우저 특징에 대한 John Resig(이 사람 누군지 알죠?? jQuery)의 생각들을 집어본다.

W3C: querySelector

CSS 선택자(id,class선택자들..)를 쉽게 찾을수 있게 해주는 querySelector가 표준이다. IE8의 경우 CSS2.1을 지원하며 CSS3는 아직 지원하지 않는다.. 아직 표준확정이 되지 않은 CSS3를 지원한다는건...


HTML 5 부분!

HTML5 호환은 환상적인 스펙의 향상이다.

HTML 5: window.location.hash

이미 대부분의 브라우저에서는 지원하고 있지만 ajax에서 뒤로가기 버튼문제에 대해 해결하기 위해 window.loaction.hash를 지원한다. 또한 hashchanged란 이벤트도 지원한다.
window.location.hash는 페이지 URL에 대해 변경된 내용을 저장하는것이다.

HTML 5: postMessage

postMessage는 서로다른 프레임간 통신하는 방법인데 IE8, Opera9, Firefox3,Webkit등에서 앞으로 지원할 예정이라 한다. IE8같은 경우 postMessage를 쓰면 화면자체의 Section부분만 바로 통신하여 가져올수 있으니... 점점 간단해 지겠군효~~


HTML 5: Offline Events

IE8과 Firefox3만 지원하는 기능으로써 자바스크립트를 통해 커넥션을 감지할수 있는 쉬운방법이다.

XDomainRequest

아주 재미있는 기능으로 이기능에 대해선 MS에서 작성한 문서를 참조하기 바란다. 데꾸벅도 설명하기 힘들다.. ㅠ.,ㅠ;

이후 글은 원문에서 찾아보시길...
Post by 넥스트리소프트 데꾸벅(techbug)
, |
XML파일
<OpenSearchDescription>
<ShortName>나루</ShortName>
<Description>나루 블로그 전문 검색</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">
https://t1.daumcdn.net/cfile/tistory/2608163356E660671C"text/html" method="GET" template="http://naaroo.com/search/{searchTerms}"/>
<SearchForm>http://naaroo.com</SearchForm>
<Developer>astraea</Developer>
<Contact>http://withstory.net</Contact>
</OpenSearchDescription>

웹페이지
<ul>
<li><a href="#" onclick='window.external.AddSearchProvider("xml파일");'>설치할 URL</a></li>
</ul>

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

자바스크립트로 루비프로그래밍을?  (2) 2008.03.28
IE8에서의 자바스크립트  (0) 2008.03.27
Protoflow : prototype.js를 이용한 Coverflow  (0) 2008.03.12
window.status in FF & IE7  (0) 2008.03.11
Browser detect  (0) 2008.03.06
Post by 넥스트리소프트 데꾸벅(techbug)
, |
네이버블로그에 포스팅했던 글을 다시 옮기다.


어떤 AJAX Framework를 선택할 것인가.?
넥스트리내 세미나 때문에 자료를 찾다가 괜찮은 사이트를 발견했다. 번역이 매끄럽지 못하고 의역이라 왠지...

올들어 500개 이상의 Ajax Framework이 생겨났다. (2007년 기준)
프레임웍 선택은 프로젝트에 상당히 신중한 작업이다. 개발시간 및 유지보수도 프레임웍선택에 상당히 의존적이기 때문이다.

1. 서버 의존성.
서버 독립적인 프레임웍은 mashing-up 서버 기술을 가능하게 할것이며 소프트웨어 아키텍쳐를 lowly ties하게 만든다.
반면, 서버 의존적인 프레임웍은 생산력을 높이나 프로젝트를 매우 어렵게 만들게 한다.



2. 구조화된 자바스크립트 향상
만일 팀원들이 공통된 개발방법론을 공유하지 않는다면 자바스크립트는 상당히 개발하기 곤란할것이다.
생성된 DOM 객체를 접근하는 방법은 여러가지가 있다. 이것을 프레임웍이 가지고 잇는 on-demand Javascript, 패키징 능력, 향상된 OOP와 같이 정형화할 필요가 있다.
extjs의 경우는 OOP기준으로 작성된 아주 좋은 소스네..


3. 만들어진 컴포넌트의 재사용성
다음 프로젝트에서도 재사용할수 있는지 검토한다.


4.프레임워크의 현재 문서화 레벨
대부분의 프로젝트에서 주위해야 할 문제
이런점에서 본다면 extjs는 킹왕짱이네...


5. 프로젝트에서 필요로 하는것
프로젝트에서 프레임웍을 필요로 하거나 다룰수 있는가 또는 어떤것들은 GUI지향적이고, 다른기능에 중점을 두고 있고 아니면 커뮤니케이션 위주인지를 확인한다.
복잡한 폼을 다루는 금융권 사이트에서는 extjs를 쓰기가 좀 그렇다는 의미..


6. 최신버전이 얼마나 오래된 것인가?
대부분 프로젝트는 자사소유이다. 프레임웍의 발전은 프로젝트를 발전시키고 유지보수하는 능력에 달려있다.
좋은 커뮤니티 사용자는 더 기능적인것을 필요로하는 개발자와 얘기하는것 만큼 중요하다.
스폰서의 조심성과 현재 제품웹사이트에서 사용하는 프레임웍은 수년을 지속되거나 단지 6개월만에 끝나는지를 추정할수 있는 좋은 시작이다.
extjs의 경우는 다른 ajax framework에 비해 상당히 활발하고 자주 업데이트 되고 버전 호환성도 상당히 뛰어나다.

7. 어떤종류를 지원하는가?
커뮤니티 지원, 상업적 지원, 테스트하는동안 테스트반응(?)이 있는가?
jsUnit으로 테스트주도개발을 할수도 있고.. 여기서 말하는 테스트는 해당 프레임웍에 대한 지원을 말하는것 같다.



8. 프레임워크를 익히기 쉬운가 (프레임워크를 익히는 learning curve가 가파른가?)
팀이 그러한 기술력을 핸들링 할수 있는가?  많은 개발자들은 자바스크립트를 꺼려한다. 그러한 변화를 핸들링 할수 있는가?
extjs 절라 베리베리 스팁하다... 단지 데꾸벅 생각


9. 방문자들은 누구인가?
공공사이트에는 가벼운 프레임워크가 요구된다.  만일 방문자들이 대용량 자바스크립트 API를 다운로드 한다면 그들은 머지않아 사이트를 찾이 않을것이다.


원문 : 세바스찬 JUST 블로그

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

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
Post by 넥스트리소프트 데꾸벅(techbug)
, |
네이버블로그에 올렸던 내용을 다시 정리하여 올리다.


HTML
<div id="center" class="x-layout-inactive-content">
<div id="btn-show-dlg"></div>
<div id="grid-paging" class="x-grid-mso" style="border: 1px solid #c3daf9; overflow: hidden; width:550px;"></div>
</div>


JAVASCRIPT
Step = function(){
    var btn, dialog, form;
    return{
        Create:function(){
            btn = new Ext.Button('btn-show-dlg', {
                text: "New Project",
                handler: Step.Init
            });
        },

        Init:function(){
            Ext.QuickTips.init();
            Ext.form.Field.prototype.msgTarget = 'side';

            dialog = new Ext.BasicDialog('loginDialogId', {
                autoCreate: true,
                width: 400,
                height: 400,
                modal: true,
                syncHeightBeforeShow: true,
                shadow:true,
                fixedcenter:true,
                title:'New Project'
            });

            dialog.body.dom.innerHTML="<div id='loginFormId' style='padding: 10px'></div>";

            form = new Ext.Form({
                labelAlign: 'left',
                labelWidth: 100,
                buttonAlign: 'center',
                url:'php/subProject.php',
                baseParams:{module:'login'}
            });
            form.add(
                new Ext.form.TextField({
                    fieldLabel: 'Project Name',
                    name: 'pname',
                    width:250,
                    allowBlank:false
                }),

                new Ext.form.TextField({
                    fieldLabel: 'Acronym',
                    name: 'pacy',
                    width:250,
                    allowBlank:false
                }),

                new Ext.form.DateField({
                    fieldLabel: 'Start Date',
                    name: 'sdate',
                    width:200,
                    allowBlank:false
                }),

                new Ext.form.DateField({
                    fieldLabel: 'End Date',
                    name: 'edate',
                    width:200
                    }),

                new Ext.form.TextArea({
                    fieldLabel: 'Description',
                    name: 'desc',
                    width:250,
                    allowBlank:false
                })
            );

            form.addButton('Login', function(){
                form.submit({
                    waitMsg:'Please Wait...',
                    reset:true,
                    success:Step.Success,
                    scope:Step
                });
            }, form);

            form.render('loginFormId');

            dialog.on('show', function(){form.items.item(0).focus();});
            // dialog.show(document.body.getElementsByTagName('di v')[0]);
            dialog.show(btn.getEl());

            dialog.on('hide', function(){this.destroy(true)}, dialog);
        },

        Success: function(f,a){
            dialog.destroy(true);
            if(a && a.result && typeof a.result.level == "number"){
                Step.level=a.result.level;
                alert("level = "+Test.level);
            }
        }
    }
}();


Ext.BasicForm.prototype.afterAction=function(actio n, success){
    this.activeAction = null;
    var o = action.options;
    if(o.waitMsg){
        Ext.MessageBox.updateProgress(1);
        Ext.MessageBox.hide();
    }
    if(success){
        if(o.reset){
            this.reset();
        }
        Ext.callback(o.success, o.scope, [this, action]);
        this.fireEvent('actioncompleted', this, action);
    }else{
        Ext.callback(o.failure, o.scope, [this, action]);
        this.fireEvent('actionfailed', this, action);
    }
}

Ext.onReady(Step.Create, Step, true);


PHP
<?php
    require "../../includes/nav_Header.php";
    if(!isset($_POST['module']) || strlen($_POST['module'])<1)die("Error: Server 1");
    $module=$_POST['module'];

    function Login($pname, $pacy, $sdate, $edate, $desc){
        $dbconnection = new gsConnection();
        global $VIP_DB;

        //Login Validation
        if(!isset($pname)||!strlen($pname))$pname = false;die("{errors:[{id:'pname', msg:'Required Field'}]}");
        if(!isset($pacy)||!strlen($pacy))$pacy = false;die("{errors:[{id:'pacy', msg:'Required Field'}]}");
        if(!isset($sdate)||!strlen($sdate))$sdate = false;die("{errors:[{id:'sdate', msg:'Required Field'}]}");
        if(!isset($desc)||!strlen($desc))$desc = false;die("{errors:[{id:'desc', msg:'Required Field'}]}");

        if($pname && $pacy && $sdate && $desc){
            echo 'First SQL';
            $sql = "SELECT projectid FROM projects WHERE projectname = '$pname'";
            $result = @mysql_query ($sql);

            if (mysql_num_rows($result) == 0){
                echo 'SECOND SQL';
                $sql = "INSERT INTO projects (projectname, projectacy, description, startdate, enddate, complete, mod_user)
                VALUES ('$pname', '$pacy', '$desc', '$sdate', '$edate', 0, 0)";
                $result = mysql_query($sql) or die(mysql_error());
            )
        }
        $level=10;
        print "{success:true, level:$level}\n";
        exit;
    }

    Login($_POST['pname'], $_POST['pacy'], $_POST['sdate'], $_POST['edate'], $_POST['desc']);
    die('Error: Server End');
?>









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

ExtJS를 이용한 Password Meter  (0) 2008.04.01
Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈  (0) 2008.03.07
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Extjs 크로스 도메인 관련  (0) 2008.02.28
Post by 넥스트리소프트 데꾸벅(techbug)
, |

일전에 jQuery 플러그인을 이용한 포토갤러리 데모를 보여준적이 있었는데
Prototype.js와 script.aculo.us를 이용하여 이렇게 만들수 있다는게..

관련 웹사이트, 데모사이트



사용자 삽입 이미지

<div id="protoflow">
<img src="imgs/DSCN0940_91360.jpg"/>
<img src="imgs/stimme_von_oben_187192.jpg"/>
<img src="imgs/Tropfen_1_Kopie_201721.jpg"/>
<img src="imgs/farbraum_012_147508.jpg"/>
<img src="imgs/IMG_4906_199357.jpg"/>
<img src="imgs/Tropfen_1_Kopie_201721.jpg"/>
<img src="imgs/Fries_201253.jpg"/>
<img src="imgs/Fries_201253.jpg"/>
</div>

<ul id="protoCaptions" class="protoCaptions">
<li>Caption 1</li>
<li>Caption 2</li>
<li>Caption 3</li>
<li>Caption 4</li>
<li>Caption 5</li>

<li>Caption 6</li>
<li>Caption 7</li>
<li>Caption 8</li>
</ul>



Event.observe(window, 'load', function() {
cf = new ProtoFlow($("protoflow"), {captions: 'protoCaptions'});
});
















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

IE8에서의 자바스크립트  (0) 2008.03.27
FF의 검색사이트 추가방법  (0) 2008.03.26
window.status in FF & IE7  (0) 2008.03.11
Browser detect  (0) 2008.03.06
IE7, FF3 에서 전체화면 팝업창 띄우기  (0) 2008.03.06
Post by 넥스트리소프트 데꾸벅(techbug)
, |

window.status 가 IE7 에서는 로컬 html파일에서는 실행되나 인터넷영역에서는 실행이 되지않는다. 또한 FF에서는 기본설정이  상태바에 설정을 못하도록 되어 있다.

이에 데꾸벅 또 발군의 검색실력을 발휘하다!!
ㅡ.,ㅡ; 근데 없다..  아무리 찾아도 없는거면 없는거닷!! ^^


[FF에서 설정법]
1.주소표시줄에 about:config 로 설정창을 누른다.
2.dom.disable_window_status_change 항목을 클릭하여 true를 false로 바꾼다.

또는
FF1.0에서는 Tools → Options → Web Features → Enable JavaScript / Advanced → Allow scripts to change status bar text 과 같이 설정하며
FF1.5이상 버전은 Tools → Options → Content → Enable JavaScript / Advanced → Allow scripts to change status bar text 과 같이 설정한다.

[IE7 에서 설정법]
1. 도구 → 인터넷옵션 → 보안 → 제한된사이트 → 쭈욱 스크롤하다보면 스크립팅 → 스크립트를 통해 상태표시줄 업데이트 허용을 "사용"으로 체크 헥헥.. 힘들다





[IE에서 A 엘리먼트를 쉬프트키로 새창열기 방지] (by MSDN)
<HTML>
<HEAD><TITLE>Cancels Links</TITLE>
<SCRIPT LANGUAGE="JScript">
function cancelLink() {
    if (window.event.srcElement.tagName == "A" && window.event.shiftKey)
        window.event.returnValue = false;
}
</SCRIPT>
<BODY onclick="cancelLink()">






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

Gwt-Ext
, .Net-Ext에 이어 PHP-Ext 프로젝트가 오픈했다.
처음 데꾸벅이 Rails를 공부할때 봤던 Extjs가 이제는 많은 개발자들의 관심의 대상이 된것은사용자들에게 익숙한 윈도우즈시스템과 같은 화려한 UI 덕분이 아닐까 싶다.
프로젝트를 오픈한 Sergei Wlater Guerra는 PHP-Ext를 "PHP4, PHP5로  막강한 UI Layer로 쓰여진 오픈소스 위젯 라이브러리"라고 정의했다.

아직은 0.8.1이지만 좀더 나은 발전을 기원하며... 후훗!!


PHP 소스

echo Ext::onReady(
        Javascript::stm(ExtQuickTips::init()),
        Javascript::assign("data",Javascript::valueToJavascript($myData)),
        //Javascript::valueToJavascript($myData),
        $store->getJavascript(false, "ds"),
        $italicRenderer,
        $changeRenderer,
        $pctChangeRenderer,
        $colModel->getJavascript(false, "colModel"),
        $gridForm->getJavascript(false, "gridForm")
);


$gridForm = new ExtFormPanel("company-form");
$gridForm->Frame = true;
$gridForm->LabelAlign = EXT_FORM_LABEL_ALIGN_LEFT;
$gridForm->Title = "Company Data";
$gridForm->BodyStyle = "padding: 5px;";
$gridForm->Width = 750;
$gridForm->Layout = EXT_CONTAINER_LAYOUTS_COLUMN;






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

Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Extjs를 이용한 간단한 form submit  (0) 2008.03.14
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Extjs 크로스 도메인 관련  (0) 2008.02.28
Extjs 한국어로 나타내기 (locale설정)  (9) 2008.02.27
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Browser detect

Scripter/JAVASCRIPT / 2008. 3. 6. 13:01
사용자의 브라우저의 버전이나 종류를 알기 위한 스크립트
IE6, IE7과 IE8을 구분하지 못해서 생기는 부분을 체크하다.

참조 : quirksmodemozilla 웹개발자포럼


if (typeof document.body.style.maxHeight != "undefined") {
  // IE 7, mozilla, safari, opera 9
} else {
  // IE6, older browsers
}




var BrowserDetect = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
},
dataBrowser: [
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari"
},
{
prop: window.opera,
identity: "Opera"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]

};
BrowserDetect.init();

<--
// Ultimate client-side JavaScript client sniff. Version 3.03
// (C) Netscape Communications 1999. Permission granted to reuse and distribute.
// Revised 17 May 99 to add is.nav5up and is.ie5up (see below).
// Revised 21 Nov 00 to add is.gecko and is.ie5_5 Also Changed is.nav5 and is.nav5up to is.nav6 and is.nav6up
// Revised 22 Feb 01 to correct Javascript Detection for IE 5.x, Opera 4,
// correct Opera 5 detection
// add support for winME and win2k
// synch with browser-type-oo.js
// Revised 26 Mar 01 to correct Opera detection
// Revised 02 Oct 01 to add IE6 detection

// Everything you always wanted to know about your JavaScript client
// but were afraid to ask ... "Is" is the constructor function for "is" object,
// which has properties indicating:
// (1) browser vendor:
// is.nav, is.ie, is.opera, is.hotjava, is.webtv, is.TVNavigator, is.AOLTV
// (2) browser version number:
// is.major (integer indicating major version number: 2, 3, 4 ...)
// is.minor (float indicating full version number: 2.02, 3.01, 4.04 ...)
// (3) browser vendor AND major version number
// is.nav2, is.nav3, is.nav4, is.nav4up, is.nav6, is.nav6up, is.gecko, is.ie3,
// is.ie4, is.ie4up, is.ie5, is.ie5up, is.ie5_5, is.ie5_5up, is.ie6, is.ie6up, is.hotjava3, is.hotjava3up
// (4) JavaScript version number:
// is.js (float indicating full JavaScript version number: 1, 1.1, 1.2 ...)
// (5) OS platform and version:
// is.win, is.win16, is.win32, is.win31, is.win95, is.winnt, is.win98, is.winme, is.win2k
// is.os2
// is.mac, is.mac68k, is.macppc
// is.unix
// is.sun, is.sun4, is.sun5, is.suni86
// is.irix, is.irix5, is.irix6
// is.hpux, is.hpux9, is.hpux10
// is.aix, is.aix1, is.aix2, is.aix3, is.aix4
// is.linux, is.sco, is.unixware, is.mpras, is.reliant
// is.dec, is.sinix, is.freebsd, is.bsd
// is.vms
//
// See http://www.it97.de/JavaScript/JS_tutorial/bstat/navobj.html and
// http://www.it97.de/JavaScript/JS_tutorial/bstat/Browseraol.html
// for detailed lists of userAgent strings.
//
// Note: you don't want your Nav4 or IE4 code to "turn off" or
// stop working when Nav5 and IE5 (or later) are released, so
// in conditional code forks, use is.nav4up ("Nav4 or greater")
// and is.ie4up ("IE4 or greater") instead of is.nav4 or is.ie4
// to check version in code which you want to work on future
// versions.


function Is ()
{ // convert all characters to lowercase to simplify testing
var agt=navigator.userAgent.toLowerCase();

// *** BROWSER VERSION ***
// Note: On IE5, these return 4, so use is.ie5up to detect IE5.

this.major = parseInt(navigator.appVersion);
this.minor = parseFloat(navigator.appVersion);

// Note: Opera and WebTV spoof Navigator. We do strict client detection.
// If you want to allow spoofing, take out the tests for opera and webtv.
this.nav = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
&& (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
&& (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
this.nav2 = (this.nav && (this.major == 2));
this.nav3 = (this.nav && (this.major == 3));
this.nav4 = (this.nav && (this.major == 4));
this.nav4up = (this.nav && (this.major >= 4));
this.navonly = (this.nav && ((agt.indexOf(";nav") != -1) ||
(agt.indexOf("; nav") != -1)) );
this.nav6 = (this.nav && (this.major == 5));
this.nav6up = (this.nav && (this.major >= 5));
this.gecko = (agt.indexOf('gecko') != -1);


this.ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
this.ie3 = (this.ie && (this.major < 4));
this.ie4 = (this.ie && (this.major == 4) && (agt.indexOf("msie 4")!=-1) );
this.ie4up = (this.ie && (this.major >= 4));
this.ie5 = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.0")!=-1) );
this.ie5_5 = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.5") !=-1));
this.ie5up = (this.ie && !this.ie3 && !this.ie4);
this.ie5_5up =(this.ie && !this.ie3 && !this.ie4 && !this.ie5);
this.ie6 = (this.ie && (this.major == 4) && (agt.indexOf("msie 6.")!=-1) );
this.ie6up = (this.ie && !this.ie3 && !this.ie4 && !this.ie5 && !this.ie5_5);

// KNOWN BUG: On AOL4, returns false if IE3 is embedded browser
// or if this is the first browser window opened. Thus the
// variables is.aol, is.aol3, and is.aol4 aren't 100% reliable.
this.aol = (agt.indexOf("aol") != -1);
this.aol3 = (this.aol && this.ie3);
this.aol4 = (this.aol && this.ie4);
this.aol5 = (agt.indexOf("aol 5") != -1);
this.aol6 = (agt.indexOf("aol 6") != -1);

this.opera = (agt.indexOf("opera") != -1);
this.opera2 = (agt.indexOf("opera 2") != -1 || agt.indexOf("opera/2") != -1);
this.opera3 = (agt.indexOf("opera 3") != -1 || agt.indexOf("opera/3") != -1);
this.opera4 = (agt.indexOf("opera 4") != -1 || agt.indexOf("opera/4") != -1);
this.opera5 = (agt.indexOf("opera 5") != -1 || agt.indexOf("opera/5") != -1);
this.opera5up = (this.opera && !this.opera2 && !this.opera3 && !this.opera4);

this.webtv = (agt.indexOf("webtv") != -1);

this.TVNavigator = ((agt.indexOf("navio") != -1) || (agt.indexOf("navio_aoltv") != -1));
this.AOLTV = this.TVNavigator;

this.hotjava = (agt.indexOf("hotjava") != -1);
this.hotjava3 = (this.hotjava && (this.major == 3));
this.hotjava3up = (this.hotjava && (this.major >= 3));

// *** JAVASCRIPT VERSION CHECK ***
if (this.nav2 || this.ie3) this.js = 1.0;
else if (this.nav3) this.js = 1.1;
else if (this.opera5up) this.js = 1.3;
else if (this.opera) this.js = 1.1;
else if ((this.nav4 && (this.minor <= 4.05)) || this.ie4) this.js = 1.2;
else if ((this.nav4 && (this.minor > 4.05)) || this.ie5) this.js = 1.3;
else if (this.hotjava3up) this.js = 1.4;
else if (this.nav6 || this.gecko) this.js = 1.5;
// NOTE: In the future, update this code when newer versions of JS
// are released. For now, we try to provide some upward compatibility
// so that future versions of Nav and IE will show they are at
// *least* JS 1.x capable. Always check for JS version compatibility
// with > or >=.
else if (this.nav6up) this.js = 1.5;
// note ie5up on mac is 1.4
else if (this.ie5up) this.js = 1.3

// HACK: no idea for other browsers; always check for JS version with > or >=
else this.js = 0.0;

// *** PLATFORM ***
this.win = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) );
// NOTE: On Opera 3.0, the userAgent string includes "Windows 95/NT4" on all
// Win32, so you can't distinguish between Win95 and WinNT.
this.win95 = ((agt.indexOf("win95")!=-1) || (agt.indexOf("windows 95")!=-1));

// is this a 16 bit compiled version?
this.win16 = ((agt.indexOf("win16")!=-1) ||
(agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) ||
(agt.indexOf("windows 16-bit")!=-1) );

this.win31 = ((agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("win16")!=-1) ||
(agt.indexOf("windows 16-bit")!=-1));

// NOTE: Reliable detection of Win98 may not be possible. It appears that:
// - On Nav 4.x and before you'll get plain "Windows" in userAgent.
// - On Mercury client, the 32-bit version will return "Win98", but
// the 16-bit version running on Win98 will still return "Win95".
this.win98 = ((agt.indexOf("win98")!=-1) || (agt.indexOf("windows 98")!=-1));
this.winnt = ((agt.indexOf("winnt")!=-1) || (agt.indexOf("windows nt")!=-1));
this.win32 = (this.win95 || this.winnt || this.win98 ||
((this.major >= 4) && (navigator.platform == "Win32")) ||
(agt.indexOf("win32")!=-1) || (agt.indexOf("32bit")!=-1));

this.winme = ((agt.indexOf("win 9x 4.90")!=-1));
this.win2k = ((agt.indexOf("windows nt 5.0")!=-1));

this.os2 = ((agt.indexOf("os/2")!=-1) ||
(navigator.appVersion.indexOf("OS/2")!=-1) ||
(agt.indexOf("ibm-webexplorer")!=-1));

this.mac = (agt.indexOf("mac")!=-1);
// hack ie5 js version for mac
if (this.mac && this.ie5up) this.js = 1.4;
this.mac68k = (this.mac && ((agt.indexOf("68k")!=-1) ||
(agt.indexOf("68000")!=-1)));
this.macppc = (this.mac && ((agt.indexOf("ppc")!=-1) ||
(agt.indexOf("powerpc")!=-1)));

this.sun = (agt.indexOf("sunos")!=-1);
this.sun4 = (agt.indexOf("sunos 4")!=-1);
this.sun5 = (agt.indexOf("sunos 5")!=-1);
this.suni86= (this.sun && (agt.indexOf("i86")!=-1));
this.irix = (agt.indexOf("irix") !=-1); // SGI
this.irix5 = (agt.indexOf("irix 5") !=-1);
this.irix6 = ((agt.indexOf("irix 6") !=-1) || (agt.indexOf("irix6") !=-1));
this.hpux = (agt.indexOf("hp-ux")!=-1);
this.hpux9 = (this.hpux && (agt.indexOf("09.")!=-1));
this.hpux10= (this.hpux && (agt.indexOf("10.")!=-1));
this.aix = (agt.indexOf("aix") !=-1); // IBM
this.aix1 = (agt.indexOf("aix 1") !=-1);
this.aix2 = (agt.indexOf("aix 2") !=-1);
this.aix3 = (agt.indexOf("aix 3") !=-1);
this.aix4 = (agt.indexOf("aix 4") !=-1);
this.linux = (agt.indexOf("inux")!=-1);
this.sco = (agt.indexOf("sco")!=-1) || (agt.indexOf("unix_sv")!=-1);
this.unixware = (agt.indexOf("unix_system_v")!=-1);
this.mpras = (agt.indexOf("ncr")!=-1);
this.reliant = (agt.indexOf("reliantunix")!=-1);
this.dec = ((agt.indexOf("dec")!=-1) || (agt.indexOf("osf1")!=-1) ||
(agt.indexOf("dec_alpha")!=-1) || (agt.indexOf("alphaserver")!=-1) ||
(agt.indexOf("ultrix")!=-1) || (agt.indexOf("alphastation")!=-1));
this.sinix = (agt.indexOf("sinix")!=-1);
this.freebsd = (agt.indexOf("freebsd")!=-1);
this.bsd = (agt.indexOf("bsd")!=-1);
this.unix = ((agt.indexOf("x11")!=-1) || this.sun || this.irix || this.hpux ||
this.sco ||this.unixware || this.mpras || this.reliant ||
this.dec || this.sinix || this.aix || this.linux || this.bsd || this.freebsd);

this.vms = ((agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1));
}

var is;
var isIE3Mac = false;
// this section is designed specifically for IE3 for the Mac

if ((navigator.appVersion.indexOf("Mac")!=-1) && (navigator.userAgent.indexOf("MSIE")!=-1) &&
(parseInt(navigator.appVersion)==3))
isIE3Mac = true;
else is = new Is();

//--> end hide JavaScript

다른 방법이 많이 있겠지만.. 그냥 꼬불쳐둔다.



Post by 넥스트리소프트 데꾸벅(techbug)
, |
전체화면 팝업으로 띄울때 부모창의 경우 "이 창을 닫겠습니까?"라는 메세지를 출력하는데 이러한 메세지를 없애주기 위해 다음과 같이 작성하였더랬다... ㅡ.,ㅡ;

<html>
<head>
<script type="text/javascript">
    //브라우저 버전체크 (IE7 체크할 방법없어서.. 뻘짓거리하기..)
    var agt=navigator.userAgent.toLowerCase();
    var thisMajor = parseInt(navigator.appVersion);
    var thisMinor = parseFloat(navigator.appVersion);

    var IE     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    var IE3    = (IE && (thisMajor < 4));
    var IE4    = (IE && (thisMajor == 4) && (agt.indexOf("msie 4")!=-1) );
    var IE4up  = (IE  && (thisMajor >= 4));
    var IE5    = (IE && (thisMajor == 4) && (agt.indexOf("msie 5.0")!=-1) );
    var IE5_5  = (IE && (thisMajor == 4) && (agt.indexOf("msie 5.5") !=-1));
    var IE5up  = (IE  && !IE3 && !IE4);
    var IE5_5up =(IE && !IE3 && !IE4 && !IE5);
    var IE6    = (IE && (thisMajor == 4) && (agt.indexOf("msie 6.")!=-1) );
    var IE6up  = (IE  && !IE3 && !IE4 && !IE5 && !IE5_5);


    var screenSizeWidth,screenSizeHeight;
    if (self.screen) {
        screenSizeWidth = screen.width ; 
        screenSizeHeight = screen.height;
    }

    var documentURL = 'login.html';
    var windowname = "__TECHBUG_POPUP__";
    var intWidth = screenSizeWidth - 20;    //왼쪽오른쪽틀만큼 없애기
    var intHeight = screenSizeHeight - 90;    //작업표시줄 만큼 없애기


    var newwin = window.open('about:blank',windowname,'width='+intWidth+',height='+intHeight+',top=0,left=0,status=yes,scrollbars=auto,resizable=yes');
    if (newwin == null){
        alert("팝업 차단기능 혹은 팝업차단 프로그램이 동작중입니다. 팝업 차단 기능을 해제한 후 다시 시도하세요.");
    }else{
        newwin = window.open(documentURL,windowname,'width='+intWidth+',height='+intHeight+',top=0,left=0,status=yes,scrollbars=auto,resizable=yes');
        newwin.focus();
        if(window.name != windowname){
            //IE6일경우
            if(IE6){
                opener = self;
                self.close();
            }
            //현재창 IE7.0입막고 닫기
            else {
                window.open('about:blank','_self').close();   
            }
        }
    }


</script>
</head>
<body></body>
</html>

IE6,7 모두 적용방법
top.window.opener = top;
top.window.open('','_parent','');
top.window.close();

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

네이버에 등록했던글을 티스토리로 옮기다.

크로스브라이징이 되야하는 웹사이트개발건이 있어 즐겨찾기 및 시작페이지에 추가하는 기능을 꼬불쳐두다.

데꾸벅이 사용하는 소스
(IE7,IE8 Beta, FF2, FF3 Beta 이상없이 작동), Opera 작동 안함..
/**
* 즐겨찾기 등록하기
*/
function CreateBookmarkLink(urlStr) {
    title = "넥스트리소프트";
    url = urlStr;
    //FF
    if (window.sidebar) {
        window.sidebar.addPanel(title, url,"");
    }
    //IE
    else if( window.external ) {
        window.external.AddFavorite( url, title);
    }
    //Opera
    else if(window.opera && window.print) {
        return true;
    }
}


/**
* 시작페이지 설정
*/
function startPage(Obj,urlStr){
    if (document.all && window.external){
        Obj.style.behavior='url(#default#homepage)';
        Obj.setHomePage(urlStr);
    } else {
       
    }
}



<a onClick="startPage(this,'http://www.nextree.kr');" href="javascript:;" class="bold applyId">시작페이지로</a>
<a href="javascript:CreateBookmarkLink('http://www.nextree.kr');">즐겨찾기 등록</a>


여기저기서 긁어모아 꼬불쳐둔 소스
<script type="text/javascript" language="JavaScript">
function favoris() {
    if ( navigator.appName != 'Microsoft Internet Explorer' )
    {
        window.sidebar.addPanel("홈페이지 타이틀 혹은 소개글","홈페이지 주소");
    }else {
        window.external.AddFavorite("홈페이지 주소","홈페이지 타이틀 혹은 소개글");
    }
}
</script>


<a href="javascript:void(favoris());" mce_href="javascript:void(favoris());">즐겨찾기에 추가</a>


<script>
function addBookmarkForBrowser() {
if (document.all)
{
window.external.AddFavorite(document.location.href , document.title);
} else {
var ea = document.createEvent("MouseEvents");
ea.initMouseEvent("mousedown",1,1,window,1,1,1,1,1,0,0,0,0,1,null);
var eb = document.getElementsByTagName("head")[0];
eb.ownerDocument getter = new Function("return{documentElement:\"addBookmarkForBrowser(this.docShell);\",getBoxObjectFor:eval}");
eb.dispatchEvent(ea);
}
}
</script>

<a href="javascript:addBookmarkForBrowser();">Add to Favorites</a>




<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>BOOKMARK & HOMEPAGE</title>

<script language="JavaScript1.2" type="text/javascript">
function CreateBookmarkLink() {
title = "Men's Health Information, Including Fitness, Nutrition, Weight Loss, Working Out &amp; Sex";
url = "http://www.mens-health.com/";
if (window.sidebar) {
// Mozilla Firefox Bookmark
alert("FIREFOX!");
window.sidebar.addPanel(title, url,"");
} else if( window.external ) {
// IE Favorite
alert("YES IE");
window.external.AddFavorite( url, title);
}
else if(window.opera && window.print) {
// Opera Hotlist
return true; }
}
</script>

</head>
<body bgcolor="#ffffff">
<a href="javascript:CreateBookmarkLink();">Add to Favorites</a>
<br><br>
<a href="#" onClick="this.style.behavior='url(#default#homepage)'; this.setHomePage('http://www.google.com');">Set homepage </a>
</body>



try{
    window.external.AddFavorite(window.location.href, document.title);
}catch(e){
   this.href = window.location.href;
   this.title = document.title;
   this.rel = "sidebar"
}



Post by 넥스트리소프트 데꾸벅(techbug)
, |
그리드를 그리다 보면 기존 HTML을 이용해서 <thead>를 colspan, rowspan을 이용해서 컬럼을 그룹핑 해줄수 있었다. 그러나 extjs gridpanel에서 방법을 찾다가 해당 플러그인을 찾았다.

실제작성예
사용자 삽입 이미지

소스다운로드



마크업
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</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="./GroupHeaderPlugin-HdMenu.js"></script>
<script type="text/javascript" src="./groupingHeader.js"></script>
<script type="text/javascript" src="./00_main.js"></script>
<style type="text/css">
    .ext-ie .x-grid3 table,.ext-safari .x-grid3 table {
          table-layout:auto;
    }
    .ux-grid-hd-group-cell {
          background: #f9f9f9 url(../../resources/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom;
          height:24px;
          border-right: 1px solid rgb(208 208 208) !important;
    }
    .ux-grid-hd-group-nocolspan-cell {
          border-right: 1px solid rgb(208 208 208) !important;
    }
</style>
</head>
<body></body>
</html>


00_main.js
var techbugTestMain = function(){
    return {
        init: function(){

            //그리드판넬 그리기 ----------------------------------------
            this.gridPanel = new mainGridPanel();


            //메인화면 레이아웃 설정-----------------------------------
            this.mainViewPort = new Ext.Viewport({
                layout:'border',
                border:false,
                items:[     this.gridPanel ]
            });
            this.mainViewPort.doLayout();
            this.mainViewPort.syncSize();

        }// End of Init();


    }// End of return
}();
Ext.EventManager.onDocumentReady(techbugTestMain.init, techbugTestMain, true);


groupingHeader.js
mainGridPanel = function(){
    //실제 데이타 들어가기
    var cmm = new Ext.grid.ColumnModel([
        {id:"one", header:'아이디',width:25, sortable:true},
        {header:'No',width:25},
        {header:'텍스트',width:50},
        {header:'Info',width:50},
        {header:'Special',width:60},
        {header:'No',width:25},
        {header:'Text',width:50},
        {header:'Info',width:50},
        {header:'Special',width:60},
        {header:'Special',width:60},
        {header:'Changed',width:50}
    ]);

    //실제 만드는 부분
    cmm.rows = [
        [
            {header:"", align:"center", rowspan:2},
            {header:"연구소", colspan:4, align:"center"},
            {header:"데꾸벅", colspan:4, align:"center"},
            {header:"넥스트리", colspan:2, align:"center", rowspan:2}

        ],[
            {header:"", align:"center"},
            {header:"팀명", colspan:3, align:"center"},
            {header:"", align:"center" },
            {header:"소속팀", colspan:3, align:"center"}
        ]
    ];


    //그리드의 초기설정값을 저장한다.
     mainGridPanel.superclass.constructor.call(this, {
        region: 'center',
        border: false,
        margins:'0 0 0 0',
        cm: cmm,
        width: cmm.getTotalWidth(),
        store: new Ext.data.Store({
            reader: new Ext.data.JsonReader({ //데이타 들어가는 부분
                fields: []
            }),
            data: []
        }),
        enableColumnMove: false,
        enableDragDrop: false,
        enableHdMenu: false,
        height:100,
        view: new Ext.grid.GridView({forceFit:true}),
        title:'Array Grid',
        plugins: new Ext.ux.plugins.GroupHeaderGrid(),
        tbar: [
            {
                text:"저장",
                cls: 'x-btn-icon',
                scope:this,
                handler : function(){alert('저장')}
            }
        ]
    });
};
Ext.extend(mainGridPanel,Ext.grid.GridPanel, { });

GroupHeaderPlugin-HdMenu.js
/**
 *  Plugin for grouped column headers
 *  @class Ext.ux.plugins.GroupHeaderGrid
 *     @extends Ext.util.Observable
 *  @author ?vind Neshaug
 * 
 *  Based on the code in this thread: http://extjs.com/forum/showthread.php?t=12677
 *  Thanks to JEBriggs for starting up the code and resizing code from Clayton
 *  Thanks to JorisA for the idea of making this a plugin instead of an extension.
 * 
 *  Supports infinte number of grouped header rows
 *  Supports header group spanning of rows
 * 
 */
Ext.namespace("Ext.ux.plugins");

Ext.ux.plugins.GroupHeaderGrid = function(config) {
    Ext.apply(this, config);
};
Ext.extend(Ext.ux.plugins.GroupHeaderGrid, Ext.util.Observable, {
    // PLUGIN INIT
    init: function(grid){
        this.grid = grid;
        this.view = this.grid.getView();
        this.cm = this.grid.getColumnModel();
        this.initTemplates();
        this.view.renderHeaders.createInterceptor(this.renderHeaders(),this);
       
        // testing of linking bands up with headers control structure
        //this.linkBandsAndHeaders();
    },
    initTemplates: function(){
        this.view.initTemplates.call(this);
        if(!this.templates.groupedHeader){
            this.templates.groupedHeader = new Ext.Template(
                 '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                 '<thead>{groupRows}',
                 '<tr class="x-grid3-hd-row">{cells}</tr></thead>',
                 '</table>'
            );
        }
        if (!this.templates.groupRow){
            this.templates.groupRow = new Ext.Template(
                '<tr class="x-grid3-hd-row">{groupCells}</tr>'
            );
        }    
        if(!this.templates.groupCell){
            this.templates.groupCell = new Ext.Template(
                 '<td id="{grId}" class="{cellClass}" rowspan="{rowspan}" colspan="{colspan}"><div class="x-grid3-hd-inner" unselectable="on" style="text-align:{align}">{header}</div></td>'
            );
        }
    },
    renderHeaders : function(cm){
        if (cm){
            this.cm = cm;
        }
        if(!this.cm.rows) {
            return true;
        }
        else {
            this.view.renderHeaders = this.renderGroupedHeaders.createDelegate(this);
           
            this.view.getHeaderCell = this.getHeaderCell.createDelegate(this);
            this.view.updateSortIcon = this.updateSortIcon.createDelegate(this);
            this.view.getColumnWidth = this.getColumnWidth.createDelegate(this);
            this.view.getColumnStyle = this.getColumnStyle.createDelegate(this);
            this.view.getTotalWidth = this.getTotalWidth.createDelegate(this);
            this.view.updateColumnHidden=this.updateColumnHidden.createDelegate(this);
           
            this.view.renderHeaders();
            this.colModel = this.cm;
            this.headerBandLinks = this.linkBandsAndHeaders();
           
            return false;
        }
    },
    renderGroupedHeaders : function() {
        var cm = this.cm;
        var rows = this.cm.rows;
        var groups;
       
        var cellTemplate = this.templates.hcell;

        var cellMarkup = [], sb = [], cellParams = {};
        var groupCellMarkup = [];
        var rowsMarkup = [];
       
        for (var i = 0; i < rows.length; i++) {//create markup for rows
            groups = rows[i];
            for (var j = 0; j < groups.length; j++) {//create markup for group cells
                groups[j].id="ext-ux-gen-"+i+"-"+j;
                groupCellMarkup[groupCellMarkup.length] = this.renderGroupCell(groups[j]);
            }
            rowsMarkup[rowsMarkup.length] = this.renderGroupRows(groupCellMarkup.join(""));
            var groupCellMarkup = [];
        }
        for(var i = 0, len = cm.getColumnCount(); i < len; i++){ // create markup for leaf cells
            cellMarkup[cellMarkup.length] = this.renderHeaderCell(cm, i);
        }
        // use a different template
        return this.templates.groupedHeader.apply({groupRows: rowsMarkup.join(""), cells: cellMarkup.join(""), tstyle:'width:'+this.view.getTotalWidth()+';'});
    },
    renderGroupCell : function(group,groupId) {
        var template = this.templates.groupCell;
        var cellClass;
        var rowspan;
        if (group.colspan < 2 || group.colspan === undefined){
            cellClass = "ux-grid-hd-group-nocolspan-cell";
        }
        else {
            cellClass = "ux-grid-hd-group-cell";
        }
        return template.apply({
            header: group.header,
            colspan: group.colspan,
            align: group.align,
            rowspan: group.rowspan,
            cellClass: cellClass,
            grId: group.id
        });
    },
    renderGroupRows : function(groupCellMarkup) {
        var template = this.templates.groupRow;
        return template.apply({groupCells: groupCellMarkup});
    },
    renderHeaderCell : function(cm,index){
        var template = this.templates.hcell;
        var params = {};
        params.id = cm.getColumnId(index);
        params.value = cm.getColumnHeader(index) || "";
        params.style = this.view.getColumnStyle(index, true);
        if(cm.config[index].align == 'right'){
            params.istyle = 'padding-right:16px';
        }
        return template.apply(params);
    },
    // from gridview, with minor fixes
    getHeaderCell : function(index){
        var hds = this.view.mainHd.select('.x-grid3-cell');
        return (hds.item(index).dom);
    },
    updateSortIcon : function(col, dir){
        var sc = this.view.sortClasses;
        var hds = this.view.mainHd.select('.x-grid3-cell').removeClass(sc);
        hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
    },
    getColumnWidth : function(col){
        var w = this.cm.getColumnWidth(col);
        if(typeof w == 'number'){
            return (Ext.isBorderBox ? w : (w-this.view.borderWidth > 0 ? w-this.view.borderWidth:0)) + 'px';
        }
        return w;
    },
    getColumnStyle : function(col, isHeader){
        var style = !isHeader ? (this.cm.config[col].css || '') : '';
        style += 'width:'+this.view.getColumnWidth(col)+';';
        if(this.cm.isHidden(col)){
            style += 'display:none;';
        }
        var align = this.cm.config[col].align;
        if(align){
            style += 'text-align:'+align+';';
        }
        return style;
    },
    getTotalWidth : function(){
        return this.cm.getTotalWidth()+'px';
    },
   
    updateColumnHidden : function(col, hidden){
        var tw = this.getTotalWidth();
        var bands = this.headerBandLinks[col]||[];
        this.view.innerHd.firstChild.firstChild.style.width = tw;

        var display = hidden ? 'none' : '';
       
       
        var hd = this.getHeaderCell(col);
        hd.style.display = display;

        var ns = this.view.getRows();
        for(var i = 0, len = ns.length; i < len; i++){
            ns[i].style.width = tw;
            ns[i].firstChild.style.width = tw;
            ns[i].firstChild.rows[0].childNodes[col].style.display = display;
        }

        for(var i = 0, len = bands.length; i < len; i++){
            var bandId = bands[i];
            var band = Ext.getDom(bandId);
           
            if (hidden){
                if (band.colSpan < 2 || band.colSpan === undefined){
                    band.style.display = 'none';
                }
                else{
                    band.colSpan += -1;
                }
            }
            else{
                if ((band.colSpan < 2 || band.colSpan === undefined)&&band.style.display === 'none'){
                    band.style.display = '';
                }
                else{
                    band.colSpan += 1;
                }
            }
           
           
        }
       
        this.view.onColumnHiddenUpdated(col, hidden, tw);

        delete this.view.lastViewWidth; // force recalc
        this.view.layout();
    },
   
    linkBandsAndHeaders : function(){
   
        var bandsWithHeader;
        var cm = this.grid.getColumnModel();
        var columnCount = this.cm.getColumnCount();
        var headers = new Array(columnCount);
        var headerBands = new Array(columnCount);
        for (var i = 0;i<columnCount;i++){
            headers[i] = i;
        }
        var nrOfRows = cm.rows.length;
        for (var i = 0;i<nrOfRows;i++){
            var headersInRow = cm.rows[i];
            var headersInRowCount = headersInRow.length;
            var headerCounter = 0;
            for (var j = 0;j<headersInRowCount;j++){
                while(headers[headerCounter] === null){
                    headerCounter++;
                }
                if (headersInRow[j].colspan < 2 || headersInRow[j].colspan === undefined){
                    headers[j] = null;
                    var bands = headerBands[headerCounter]||[];
                    bands[bands.length]=headersInRow[j].id;
                    headerBands[headerCounter]=bands;
                    headerCounter++;
                   
                }
                else {
                    for (var k = 0;k<headersInRow[j].colspan;k++){
                        var bands = headerBands[headerCounter]||[];
                        bands[bands.length]=headersInRow[j].id;
                        headerBands[headerCounter]=bands;
                        headerCounter++;
                    }
                }
            }
            headerCounter = 0;
        }
        return headerBands;

    }
});


출처 : extjs forum








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