/* * */ /* * @(#) du_base.js * build version : 0.9.0 $Revision: 13634 $ * * Copyright ⓒ LG CNS, Inc. All rights reserved. * * Do Not Erase This Comment!!! (이 주석문을 지우지 말것) * * DevOn Rich UI Framework를 실제 프로젝트에 사용하는 경우 DevOn Rich UI 개발담당자에게 * 프로젝트 정식명칭, 담당자 연락처(Email)등을 mail로 알려야 한다. * * 소스를 변경하여 사용하는 경우 DevOn Rich UI 개발담당자에게 * 변경된 소스 전체와 변경된 사항을 알려야 한다. * 저작자는 제공된 소스가 유용하다고 판단되는 경우 해당 사항을 반영할 수 있다. * 중요한 Idea를 제공하였다고 판단되는 경우 협의하에 저자 List에 반영할 수 있다. * * (주의!) 원저자의 허락없이 재배포 할 수 없으며 * LG CNS 외부로의 유출을 하여서는 안 된다. */ /** * DU 객체는 네임스페이스, 상속, 로깅을 포함하는 utility이다. * @module core * @namespace * @title DU Global */ if (typeof DU == "undefined" || !DU) { /** * DU는 global namespace object 이다.. * 만약 DU가 이미 정의되어 있으면, 기존의 DU object는 정의된 namespace들이 보존되도록 * overwrite 않을 것이다. * @class DU * @static */ var DU = {}; } (function(){ var userAgent = navigator.userAgent.toLowerCase(); // API 작성기때문에 주석을 가장 아래로 옮겼음. DU.browser = { version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1], opera: /opera/.test( userAgent ), chrome:/chrome/.test( userAgent ), webkit:/webkit/.test(userAgent), safari: !/chrome/.test( userAgent ) && /safari/.test( userAgent ), safari2 : /safari/.test( userAgent ) && /applewebkit\/4/.test( userAgent ), safari3 : /safari/.test( userAgent ) && /version\/3/.test( userAgent ), safari4 : /safari/.test( userAgent ) && /version\/4/.test( userAgent ), safari5 : /safari/.test( userAgent ) && /version\/5/.test( userAgent ), msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ), msie6: /msie/.test( userAgent ) && /msie 6/.test( userAgent ), msie7: /msie/.test( userAgent ) && /msie 7/.test( userAgent ), msie8: /msie/.test( userAgent ) && /msie 8/.test( userAgent ), mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ), gecko : !/webkit/.test(userAgent) && /gecko/.test(userAgent), gecko2 : !/webkit/.test(userAgent) && /gecko/.test(userAgent) && /rv:1\.8/.test(userAgent), gecko3 : !/webkit/.test(userAgent) && /gecko/.test(userAgent) && /rv:1\.9/.test(userAgent), iPhone: false // 소스 추가해야함. }; DU.platform = { window: /window/.test( userAgent ) || /win32/.test( userAgent ), mac : /macintosh/.test( userAgent ), air : /adobeair/.test( userAgent ), linux : /linux/.test( userAgent ) }; // remove css image flicker if(DU.browser.msie6){ try{ document.execCommand("BackgroundImageCache", false, true); }catch(e){} } /** * @description https가 적용되었는지 확인하는 속성 * @property isSecure * @public * @static * @type {Boolean} */ DU.isSecure = (window.location.href.toLowerCase().indexOf("https") === 0) ? true : false; /** * @description css의 compatMode가 CSS1Compat인지 확인하는 속성 * @property isStrict * @public * @static * @type {Boolean} */ DU.isStrict = document.compatMode == "CSS1Compat"; /** * @description Border가 box 모델이 적용되는지 확인하는 속성 * @property isBorderBox * @public * @static * @type {Boolean} */ DU.isBorderBox = DU.browser.msie && !DU.isStrict; /** * namespace를 지정하고 만약 존재하지 않는 경우 생성하고 반환한다. *

	 * DU.namespace("property.package");

	 * DU.namespace("DU.property.package");

	 * 
* DU.property나 DU.property.package 중에 하나를 생성한다. * * 패키지 네이밍은 조심하여야 한다. 예약어는 어떤 브라우저에서는 작동하고 * 또 다른데서는 작동하지 않을 수 있다. 예를 들어, 다음은 Safari에서 실패할 것이다: *

	 * DU.namespace("really.long.nested.namespace");

	 * 
* 이 fail은 "long"이 ECMAScript에서 앞으로 예약어가 될 것이기 때문이다. * * @method namespace * @static * @param {String} 생성할 1-n namespace들의 argument들 * @return {Object} 생성된 마지막 namespace object의 reference */ DU.namespace = function() { var a=arguments, o=null, i, j, d; for (i=0; icallback은 LCustomEvent이며, signature는 다음과 같다:

*

type <string>, args <array>, customobject <object>

*

onReady events에 대해서 fire argument들은 없으며, signature는 다음과 같다:

*

"onReady", [], obj

* * * @method onReady * * @param {function} p_fn element가 발견되었을때 실행할 함수. * @param {object} p_obj p_fn에 대한 parameter로 다시 전달하는 부가적인 object * @param {boolean|object} p_scope 만약 true로 설정하면, p_fn은 p_onj의 scope에서 * 실행을 하며, object로 설정할 경우 그 object의 scope에서 실행한다. * * @static */ DU.onReady = function(p_fn, p_obj, p_override){ DU.util.LEvent.onDOMReady(p_fn, p_obj, p_override); } /** * onAvaliable과 같은 방식으로 실행되지만, 추가적으로 * 사용가능한 element의 content를 수정하기 위한 안전 여부를 결정하기 위하여, * sibling element들의 상태를 체크한다. * *

callback은 하나의 parameter로 실행된다: * custom object parameter를 제공하는 경우.

* * @method onContentReady * * @param {string} p_id 찾을 element의 id. * @param {function} p_fn element가 준비되었을때 실행할 함수. * @param {object} p_obj p_fn에 대한 parameter로 다시 전달하는 부가적인 object * @param {boolean|object} p_scope 만약 true로 설정하면, p_fn은 p_onj의 scope에서 * 실행을 하며, object로 설정할 경우 그 object의 scope에서 실행한다. * * @static */ DU.onContentReady = function(p_id, p_fn, p_obj, p_override) { DU.util.LEvent.onContentReady(p_id, p_fn, p_obj, p_override); } /** * Ajax를 호출한다. * * @method ajax * @static * @param {object} config config : url(String), method(String/optional), success(Function), failure(Function/optional), params(Object/optional) * @return {object} connection object를 반환. */ DU.ajax = function(config) { config = DU.applyIf(config, { method: 'GET', failure: DU.emptyFn }); return DU.LConnect.asyncRequest(config.method, config.url, { success: config.success, failure: config.failure }, config.params, config); } /** * 유일한 id들을 생성한다. 만약 element가 이미 id를 가지고 있으면, 그것은 변하지 않는다. * @method id * @param {Mixed} el (optional) id가 생성될 element * @param {String} prefix (optional) Id prefix (기본 "ext-gen") * @return {String} 생성된 Id. */ DU.id = function(el, prefix){ return DU.util.LDom.generateId(el, prefix); } /** * 주어진 CSS selector에 기반한 node들의 집합을 조회한다. * @method select * * @param {string} selector 테스트 할 CSS LDomSelector. * @param {HTMLElement | String} root optional query로 부터 시작할 id나 HTMLElement. 기본은 LDomSelector.document. * @param {Boolean} firstOnly optional 처음 일치하는 값만 반환할지에 대한 여부. * @return {DU.LElementList} 주어진 selector와 일치하는 node들의 array. * @static */ DU.select = function (selector, root, firstOnly) { var n = DU.util.LDomSelector.query(selector, root, firstOnly); var element = []; if(DU.isArray(n)) { DU.each(n, function(child) { element.push(DU.get(child)); }); } else { if(!DU.isEmpty(n)) element.push(DU.get(n)); } return new DU.LElementList(element); } /** * 주어진 CSS selector에 기반한 node들의 집합을 조회한다. * @method query * * @param {string} selector node를 상대로 테스트할 CSS LDomSelector. * @param {HTMLElement | String} root optional query로 부터 시작할 id나 HTMLElement. 기본은 LDomSelector.document. * @param {Boolean} firstOnly optional 처음 일치하는 값만 반환할지에 대한 여부. * @return {DU.LElementList} 주어진 selector와 일치하는 node들의 array. * @static */ DU.query = function (selector, root, firstOnly) { return DU.util.LDomSelector.query(selector, root, firstOnly); } /** * items 정보에 해당되는 객체를 Function으로 호출하는 메소드 * @method each * @static * @param {Array} items Array 배열 * @param {Function} func Array 배열 * @param {Object} scope Array 배열 * @return void */ DU.each = function(items, func, scope) { return DU.util.LArray.each(items, func, scope); } /** * object나 array를 표현하는 간단한 문자열을 반환한다. * object들의 다른 타입은 처리되지 않고 반환될 것이다. * Array들은 색인될 것으로 예상된다. * 연관 배열에 대한 object 표기법을 사용한다. * @method dump * @static * @since 2.3.0 * @param o {Object} dump 할 object * @param d {int} child object를 탐색할 깊이(deep), 기본적으로 3 * @return {String} dump 결과 */ DU.dump = function(o, d) { var i,len,s=[],OBJ="{...}",FUN="f(){...}", COMMA=', ', ARROW=' => '; // Cast non-objects to string // Skip dates because the std toString is what we want // Skip HTMLElement-like objects because trying to dump // an element will cause an unhandled exception in FF 2.x if (!DU.isObject(o)) { return o + ""; } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) { return o; } else if (DU.isFunction(o)) { return FUN; } // dig into child objects the depth specifed. Default 3 d = (DU.isNumber(d)) ? d : 3; // arrays [1, 2, 3] if (DU.isArray(o)) { s.push("["); for (i=0,len=o.length;i 0) ? DU.dump(o[i], d-1) : OBJ); } else { s.push(o[i]); } s.push(COMMA); } if (s.length > 1) { s.pop(); } s.push("]"); // objects {k1 => v1, k2 => v2} } else { s.push("{"); for (i in o) { if (DU.hasOwnProperty(o, i)) { s.push(i + ARROW); if (DU.isObject(o[i])) { s.push((d > 0) ? DU.dump(o[i], d-1) : OBJ); } else { s.push(o[i]); } s.push(COMMA); } } if (s.length > 1) { s.pop(); } s.push("}"); } return s.join(""); } /** * object instance에 property를 추가할지에 대한 여부를 결정한다. * 만약 property가 object에 존재하지 않거나 prototype으로부터 * 상속된 경우에는 false를 반환한다. * 이 abstraction은 Safari 1.3.x 버전에 대해 hasOwnProperty를 사용 가능하도록 제공한다. * property가 같은 값으로 instance와 prototype 양쪽에 추가될때 * DU.hasOwnProperty와 Object.prototype.hasOwnProperty 사이엔 차이점이 있다: *

	     * var A = function() {};

	     * A.prototype.foo = 'foo';

	     * var a = new A();

	     * a.foo = 'foo';

	     * alert(a.hasOwnProperty('foo')); // true

	     * alert(DU.hasOwnProperty(a, 'foo')); // false when using fallback

	     * 
* @method hasOwnProperty * @static * @param {Object} o 테스트될 object * @param prop {string} 테스트할 property의 이름 * @return {boolean} 결과 */ DU.hasOwnProperty = (Object.prototype.hasOwnProperty) ? function(o, prop) { return o && o.hasOwnProperty(prop); } : function(o, prop) { return !DU.isUndefined(o[prop]) && o.constructor.prototype[prop] !== o[prop]; }; DU.augment = DU.applyPrototype; /** * DU.env는 DU 라이브러리나 브라우징 환경에 대해 알려진 항목들을 추적하는데 사용된다. * @class DU.env * @static */ DU.env = DU.env || { /** * 보고된 모든 DU 모듈들에 대한 버전 정보를 유지. * @property modules * @type Object[] */ modules: [], /** * DU 모듈이이 보고될 때마다 실행되어야 할 함수들의 목록. * @property listeners * @type Function[] */ listeners: [] }; /** * DU 모듈이 로딩될때마다 실행되어야 할 함수에 대한 reference. * parameter로서 이 함수들은 모듈에 대한 version 정보를 받는다. * version 데이터 구조에 대한 설명은 * DU.env.getVersion을 참고한다. * @property listener * @type Function * @static * @default undefined */ /** * 만약 라이브러리가 window.onload 이후에 동적으로 로딩될 경우에는 true를 설정한다. * 기본값은 false. * @property injecting * @type boolean * @static * @default undefined */ /** * yui 컴포넌트와 그것들의 dependancy들을 동적으로 로딩하기 위하여 * yuiloader 컴포넌트에 지시한다. * 동적인 로딩에 대한 더 자세한 정보는 yuiloader documentation을 참고한다. * @property load * @static * @default undefined * @see yuiloader */ /** * 라이브러리에서 제공된 locale의 사용에 적용하도록 강제한다. * @property locale * @type string * @static * @default undefined */ /** * 명시된 모듈들에 대한 version 데이터를 반환한다: *
*
name:
모듈의 이름
*
version:
사용중인 버전
*
build:
사용중인 빌드 번호
*
versions:
등록된 모든 버전들
*
builds:
등록된 모든 빌드들
*
mainClass:
현재 버전과 빌드가 stamp된 object. * 만약 mainClass.VERSION != version 이거나, mainClass.BUILD != build 이거나 * 라이브러리 조각의 여러 버전들이 로딩되어 있으면 잠재적으로 * 이슈가 야기 될 수 있다.
*
* * @method getVersion * @static * @param {String} name 모듈의 이름(event, slider 등) * @return {Object} version 정보 */ DU.env.getVersion = function(name) { return DU.env.modules[name] || null; }; DU.env._id_counter = DU.env._id_counter || 0; /** * id에 해당되는 LElement 객체를 리턴한다. * @method $E * @static * @param {String} id * @return {DU.LElement} LElement객체 */ window.$E = function(id) { return DU.get(id); }; /** * selector에 맞는 LElementList 객체를 리턴한다. * @method $S * @static * @param {String} selector * @return {DU.LElementList} LElementList객체 */ window.$S = function(selector) { return DU.select(selector); }; /** * browser 정보 * @namespace DU * @class browser * @static */ /** * @description browser 버전 정보 * @property version * @public * @static * @type {String} */ /** * @description safari 여부 * @property safari * @public * @static * @type {String} */ /** * @description opera 여부 * @property opera * @public * @static * @type {String} */ /** * @description msie 여부 * @property msie * @public * @static * @type {String} */ /** * @description mozilla 여부 * @property mozilla * @public * @static * @type {String} */ /** * platform 정보 * @namespace DU * @class platform * @static */ /** * @description window 여부 * @property window * @public * @static * @type {String} */ /** * @description mac 여부 * @property mac * @public * @static * @type {String} */ })(); DU.namespace("DU.util"); /** * Static Array 클래스는 Array 형식의 데이터를 처리하는 helper 함수들을 제공한다. * @module util * @namespace DU.util * @requires DU * @class LArray * @static */ DU.util.LArray = { /** * QueryString형 문자로 리턴한다. * @method serialize * @param {Array} params Array 배열 * @return {String} QueryString형 문자열 id=ddd&pwd=ccc */ serialize : function(params) { var buf = []; for (var key in params) { if (typeof params[key] != 'function') { buf.push(encodeURIComponent(key), '=', encodeURIComponent(params[key]), '&'); } } delete buf[buf.length - 1]; params = buf.join(''); return params; }, /** * items 정보에 해당되는 객체를 Function으로 호출하는 메소드 * @method each * @param {Array} items Array 배열 * @param {Function} func Array 배열 * @param {Object} scope Array 배열 * @return void */ each : function(items, func, scope) { var newItems = [].concat(items); var max = items.length; for(var i = 0 ; i < max; i++) { if(func.call(scope || window, items[i], i, max) == false) break; } }, /** * items 배열에서 item이 몇번째인지를 리턴하는 메소드 * @method indexOf * @param {Array} items Array 배열 * @param {Object} item Array 배열 * @param {Int} i 시작 위치 * @return {Int} */ indexOf : function(items, item, i) { i || (i = 0); var length = items.length; if (i < 0) i = length + i; for (; i < length; i++) if (items[i] === item) return i; return -1; }, /** * items 배열에서 item이 존재하는지 여부를 리턴하는 메소드 * @method contains * @param {Array} items Array 배열 * @param {Object} item Array 배열 * @return {Boolean} */ contains : function(items, item) { return (this.indexOf(items, item) >= 0) ? true : false; } }; /** * The static String class provides helper functions to deal with data of type * Number. * static 문자열 클래스는 number 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module core * @title DU Global * @namespace prototype * @class String * @static */ /** * @description 문자열 앞뒤 공백 제거 * @method trim * @return {String} 공백 제거된 문자열 */ String.prototype.trim = function() { return DU.util.LString.trim(this); } /** * @description 문자열의 왼쪽부터 특정 문자를 주어진 갯수만큼 붙여넣는다. * @method lPad * @param {String} pad padding할 문자 * @param {Int} r 붙이는 갯수 * @return {String} 결과 문자열 */ String.prototype.lPad = function(pad, r) { return DU.util.LString.lPad(this, pad, r); } /** * @description 문자열의 오른쪽부터 특정 문자를 주어진 갯수만큼 붙여넣는다. * @method rPad * @param {String} pad padding할 문자 * @param {Int} r 붙이는 갯수 * @return {String} 결과 문자열 */ String.prototype.rPad = function(pad, r) { return DU.util.LString.rPad(this, pad, r); } /** * @description 문자열을 주어진 format에 따라 Date 객체로 변환 * @method toDate * @param {Object|String} oConfig, format or oConfig.format/oConfig.locale * @return {Date} oDate */ String.prototype.toDate = function(oConfig) { oConfig = typeof oConfig == 'string' ? { format:oConfig } : oConfig; return DU.util.LString.toDate(this, oConfig); } /** * @description 입력된 xml 문자열을 xml document object model로 변환해서 return * @method toXml * @return {object} xml dom */ String.prototype.toXml = function() { return DU.util.LString.toXml(this); } /** * @description static 문자열 클래스는 문자열 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module util * @title LString * @requires DU */ DU.namespace("DU.util"); (function(){ /** * @description LString * @namespace DU.util * @class LString */ DU.util.LString = { /** * @description 문자열 앞뒤 공백 제거 * @method trim * @param {String} s 문자열 * @return {String} 공백 제거된 문자열 */ trim: function(s){ return s.replace(/^\s+/, '').replace(/\s+$/, ''); }, /** * @description 문자열의 왼쪽부터 특정 문자를 주어진 갯수만큼 붙여넣는다. * @method lPad * @param {String} x 작업 대상 문자열 * @param {String} pad padding할 문자 * @param {Int} r 붙이는 갯수 * @return {String} 결과 문자열 */ lPad: function(x, pad, r){ x += ''; r = r || x.length + 1; for (; x.length < r && r > 1; ) { x = pad.toString() + x; } return x.toString(); }, /** * @description 문자열의 오른쪽부터 특정 문자를 주어진 갯수만큼 붙여넣는다. * @method rPad * @param {String} x 작업 대상 문자열 * @param {String} pad padding할 문자 * @param {Int} r 붙이는 갯수 * @return {String} 결과 문자열 */ rPad: function(x, pad, r){ x += ''; r = r || x.length + 1; for (; x.length < r && r > 1; ) { x = x + pad.toString(); } return x.toString(); }, /** * @description 문자열을 주어진 format에 따라 Date 객체로 변환 * @method toDate * @param {String} sDate * @param {Object} oConfig, oConfig.format/oConfig.locale * @return {Date} oDate */ toDate: function(sDate, oConfig){ return DU.util.LDate.parse(sDate, oConfig); }, /** * @description 입력된 xml 문자열을 xml document object model로 변환해서 return * @method toXml * @param {String} text * @return {object} xml dom */ toXml: function(text){ var xmlDoc = null; if (window.DOMParser) { var parser = new DOMParser(); xmlDoc = parser.parseFromString(text, "text/xml"); } else // Internet Explorer { xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(text); } return xmlDoc; } }; DU.util.LString.format = /^((\w|[\-\.])+)@((\w|[\-\.])+)\.([A-Za-z]+)$/ })(); /** * static 문자열 클래스는 object 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module util * @title LObject * @namespace DU.util * @class LObject * @static */ DU.namespace("DU.util"); DU.util.LObject = { /** * @description 객체를 복사하는 메소드 * @method clone * @public * @param {Object} o 복사하고자 하는 원본 객체 * @param literal {Boolean} (optional) literal 객체 여부 * @param fn {Functdion} (optional) Function 객체. * @return {Object} 복사된 객체 */ clone: function(o, literal, fn){ var loop = 0; var cloneFn = function(o, literal, fn) { if(literal) return [].concat(o); else { if(!DU.isValue(o)) { return o; } var copy = {}; if(fn && fn.apply(this, o) === true) { copy = o; } else if(DU.isFunction(o)) { copy = o; } else if(DU.isArray(o)) { var array = []; for(var i=0,len=o.length;i 10) { continue; } loop++; array[i] = DU.util.LObject.clone(o[i], literal, fn); } copy = array; } else if(DU.isObject(o)) { for (var x in o){ if(DU.hasOwnProperty(o, x)) { if(DU.isValue(o[x]) && DU.isObject(o[x]) || DU.isArray(o[x])) { if (loop > 10) { continue; } loop++; copy[x] = cloneFn(o[x], literal, fn); } else { copy[x] = o[x]; } } } } else { copy = o; } return copy; } } return cloneFn(o, literal, fn); }, /** * QueryString형 문자로 리턴한다. * @method serialize * @param {Array} params Array 배열 * @return {String} QueryString형 문자열 id=ddd&pwd=ccc */ serialize : function(params) { var buf = []; for (var key in params) { if (typeof params[key] != 'function') { buf.push(encodeURIComponent(key), '=', encodeURIComponent(params[key]), '&'); } } delete buf[buf.length - 1]; params = buf.join(''); return params; } }; (function(){ /** * Number type의 데이터를 다루는데 도움이 되는 function들을 제공하는 static Date 클래스 * @module core * @title DU Global * @namespace prototype * @class Date * @static */ /** * 자바스크립트의 내장 객체인 Date 객체에 format 메소드를 추가한다. format 메소드는 Date 객체가 가진 날짜를 * 지정된 포멧의 스트링으로 변환한다. *

 *     var dateStr = new Date().format("YYYYMMDD");

 *

 *     참고 : Date 오브젝트 생성자들 - dateObj = new Date()

 *                                   - dateObj = new Date(dateVal)

 *                                   - dateObj = new Date(year, month, date[, hours[, minutes[, seconds[,ms]]]])

 * 
* 위의 예에서 오늘날짜가 2002년 3월 5일이라면 dateStr의 값은 "20020305"가 된다. * default pattern은 "YYYYMMDD"이다. * @method format * @param {String|Object} oConfig pattern optional 변환하고자 하는 패턴 스트링이나 Config객체. (default : YYYYMMDD) * @return : Date를 표현하는 변환된 String. * @author : 임재현 */ Date.prototype.format = function(oConfig){ oConfig = typeof oConfig == 'string' ? { format: oConfig } : oConfig; return DU.util.LDate.format(this, oConfig); } /** * 해당 instance에 지정된 시간량을 추가한다. * Day "D", Week "W", Year "Y", Month "M", Hour "H", Minute "m", Second "S", Milisecond "s" * @method add * @param {Date} date 추가적으로 실행될 JavaScript Date object * @param {String} field 추가적인 실행에 사용되는 field constant * @param {Number} amount 날짜에 추가하기 위한 unit들의 number(field constant에서 측정된) * @return {Date} Date object의 결과 */ Date.prototype.add = function(field, amount) { return DU.util.LDate.add(this, field, amount); } /** * @description 현재 날짜를 대상날짜와 format 형식에 맞게 비교한다. config를 주지 않을경우 %x(yyyy-mm-dd)로 비교한다. * @method equals * @param {Date} date 비교 대상 date 객체 * @param {Object} config [optional] format등 옵션 * @return {boolean} */ Date.prototype.equals = function(d, config) { return DU.util.LDate.equals(this, d, config); } /** * 시간을 제외한 날짜를 비교하여 현재 날짜와의 차이를 일자로 리턴한다. * @method compareTo * @param {Date} date 비교 날짜 객체 * @return {int} */ Date.prototype.compareTo = function(compareDate){ var date1 = this.clone(); date1.setHours(0); date1.setMinutes(0); date1.setSeconds(0); compareDate.setHours(0); compareDate.setMinutes(0); compareDate.setSeconds(0); return parseInt((date1 - compareDate) / 1000 / 24 / 60 / 60, 10); } /** * @description 현재 날짜의 마지막 일자를 Date형으로 리턴한다. * @method getDayInMonth * @return {Date} */ Date.prototype.getDayInMonth = function() { return new Date(this.getFullYear(), this.getMonth(), DU.util.LDate.getDayInMonth(this.getMonth())); } /** * @description 현재 날짜를 복사하여 리턴한다. * @method clone * @return {Date} */ Date.prototype.clone = function() { return new Date(this.getFullYear(), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds()); } })(); (function(){ /** * @module util * @title DU Global */ var xPad = function(x, pad, r){ r = r || 10; for (; parseInt(x, 10) < r && r > 1; r /= 10) { x = pad.toString() + x; } return x.toString(); }; /** * Date type의 데이터를 다루는데 도움이 되는 function을 제공하는 static Date 클래스 * @requires DU * @class LDate * @static */ var Dt = { /***************************************LDateMath에서 가져온 function 시작***************************************/ /** * representing Day 상수 field * @property DAY * @static * @final * @type String */ DAY: "D", /** * representing Week 상수 field * @property WEEK * @static * @final * @type String */ WEEK: "W", /** * representing Year 상수 field * @property YEAR * @static * @final * @type String */ YEAR: "Y", /** * representing Month 상수 field * @property MONTH * @static * @final * @type String */ MONTH: "M", /** * representing Hour 상수 field * @property HOUR * @static * @final * @type String */ HOUR:"H", /** * representing Minute 상수 field * @property MINUTE * @static * @final * @type String */ MINUTE : "m", /** * representing Second 상수 field * @property SECOND * @static * @final * @type String */ SECOND : "S", /** * representing Milisecond 상수 field * @property MILISECOND * @static * @final * @type String */ MILLISECOND : "s", /** * representing one day, in milliseconds 상수 field * @property ONE_DAY_MS * @static * @final * @type Number */ ONE_DAY_MS: 1000 * 60 * 60 * 24, /** * Constant field representing the date in first week of January * which identifies the first week of the year. * 한해의 첫주를 식별하게 하는 1월의 첫째주에 대한 date를 표시하는 상수 field *

* 미국에서는 1월 1일이 보통 한주의 일요일 시작을 기반으로 사용된다. * 유럽에서 넓게 사용되는 ISO 8601 은 한주의 월요일 시작을 기반으로한 1월 4일을 사용한다. *

* @property WEEK_ONE_JAN_DATE * @static * @type Number */ WEEK_ONE_JAN_DATE: 1, /** * 해당 instance에 특정 시간량을 추가한다. * @method add * @param {Date} date 추가적으로 실행될 JavaScript Date object * @param {String} field 추가적인 실행에 사용되는 field constant * @param {Number} amount 날짜에 추가하기 위한 unit들의 number(field constant에서 측정된) * @return {Date} Date object의 결과 */ add: function(date, field, amount) { var d = new Date(date.getTime()); switch (field) { case this.MONTH: var newMonth = date.getMonth() + amount; var years = 0; if (newMonth < 0) { while (newMonth < 0) { newMonth += 12; years -= 1; } } else if (newMonth > 11) { while (newMonth > 11) { newMonth -= 12; years += 1; } } d.setMonth(newMonth); d.setFullYear(date.getFullYear() + years); break; case this.DAY: this._addDays(d, amount); break; case this.YEAR: d.setFullYear(date.getFullYear() + amount); break; case this.WEEK: this._addDays(d, (amount * 7)); break; case this.HOUR: date.setHours(date.getHours()+amount); d = date; break; case this.MINUTE: date.setMinutes(date.getMinutes()+ amount); d = date; break; case this.SECOND: date.setSeconds(date.getSeconds()+ amount); d = date; break; case this.MILLISECOND: date.setMilliseconds(date.getMilliseconds()+ amount); d = date; break; } return d; }, /** * Date.setDate(n)의 n값이 -128 미만이거나 127보다 클때, * Safari 2 (webkit < 420) 버그에 대해 담당 하는 private helper method. *

* 해결 접근 및 문제 원인은 다음에서 찾을 수 있다.: * http://brianary.blogspot.com/2006/03/safari-date-bug.html *

* @method _addDays * @param {Date} d JavaScript date object * @param {Number} nDays date object에 추가할 날짜들의 number(음수 가능) * @private */ _addDays: function(d, nDays) { if (DU.browser.webkit && DU.browser.webkit < 420) { if (nDays < 0) { // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127) for (var min = -128; nDays < min; nDays -= min) { d.setDate(d.getDate() + min); } } else { // Ensure we don't go above 96 + 31 = 127 for (var max = 96; nDays > max; nDays -= max) { d.setDate(d.getDate() + max); } } // nDays should be remainder between -128 and 96 } d.setDate(d.getDate() + nDays); }, /***************************************LDateMath에서 가져온 function 끝***************************************/ formats: { a: function (d, l) { return l.a[d.getDay()]; }, A: function (d, l) { return l.A[d.getDay()]; }, b: function (d, l) { return l.b[d.getMonth()]; }, B: function (d, l) { return l.B[d.getMonth()]; }, C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); }, d: ['getDate', '0'], e: ['getDate', ' '], g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); }, G: function (d) { var y = d.getFullYear(); var V = parseInt(Dt.formats.V(d), 10); var W = parseInt(Dt.formats.W(d), 10); if(W > V) { y++; } else if(W===0 && V>=52) { y--; } return y; }, H: ['getHours', '0'], I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); }, j: function (d) { var gmd_1 = new Date('' + d.getFullYear() + '/1/1 GMT'); var gmdate = new Date('' + d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate() + ' GMT'); var ms = gmdate - gmd_1; var doy = parseInt(ms/60000/60/24, 10)+1; return xPad(doy, 0, 100); }, k: ['getHours', ' '], l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, ' '); }, m: function (d) { return xPad(d.getMonth()+1, 0); }, M: ['getMinutes', '0'], p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; }, P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; }, s: function (d, l) { return parseInt(d.getTime()/1000, 10); }, S: ['getSeconds', '0'], u: function (d) { var dow = d.getDay(); return dow===0?7:dow; }, U: function (d) { var doy = parseInt(Dt.formats.j(d), 10); var rdow = 6-d.getDay(); var woy = parseInt((doy+rdow)/7, 10); return xPad(woy, 0); }, V: function (d) { var woy = parseInt(Dt.formats.W(d), 10); var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); // First week is 01 and not 00 as in the case of %U and %W, // so we add 1 to the final result except if day 1 of the year // is a Monday (then %W returns 01). // We also need to subtract 1 if the day 1 of the year is // Friday-Sunday, so the resulting equation becomes: var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); if(idow === 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) { idow = 1; } else if(idow === 0) { idow = Dt.formats.V(new Date('' + (d.getFullYear()-1) + '/12/31')); } return xPad(idow, 0); }, w: 'getDay', W: function (d) { var doy = parseInt(Dt.formats.j(d), 10); var rdow = 7-Dt.formats.u(d); var woy = parseInt((doy+rdow)/7, 10); return xPad(woy, 0, 10); }, y: function (d) { return xPad(d.getFullYear()%100, 0); }, Y: 'getFullYear', z: function (d) { var o = d.getTimezoneOffset(); var H = xPad(parseInt(Math.abs(o/60), 10), 0); var M = xPad(Math.abs(o%60), 0); return (o>0?'-':'+') + H + M; }, Z: function (d) { var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, '$2').replace(/[a-z ]/g, ''); if(tz.length > 4) { tz = Dt.formats.z(d); } return tz; }, '%': function (d) { return '%'; } }, aggregates: { c: 'locale', D: '%m/%d/%y', F: '%Y-%m-%d', h: '%b', n: '\n', q: 'locale',//숫자형 날짜형식 short Q: 'locale',//숫자형 날짜형식 long r: 'locale', R: '%H:%M', t: '\t', T: '%H:%M:%S', x: 'locale', X: 'locale' //'+': '%a %b %e %T %Z %Y' }, /** * 내장된 JavaScript Date를 가져오고 사용자에게 표시할 문자열로 formating 처리. * * @method format * @param oDate {Date} Date. * @param {Object} oConfig (Optional) 부가적인 configuration 값: * @return {String} 표시할 Formatted date. */ format : function (oDate, oConfig) { oConfig = oConfig || {}; if(!(oDate instanceof Date) && DU.isString(oDate)) { // 바꾸고자 하는 값이 Date 객체가 아니라 문자열인 경우에 Date 객체로 변환한다. oDate = DU.util.LDate.parse(oDate,{locale:oConfig.locale}); } var sLocale = oConfig.locale; var format = oConfig.format; format = Dt.getFormat(format, sLocale); if(!(oDate instanceof Date)) { return DU.isValue(oDate) ? oDate : ""; } // Be backwards compatible, support strings that are // exactly equal to YYYY/MM/LDD, LDD/MM/YYYY and MM/LDD/YYYY if(format === 'YYYY/MM/LDD') { format = '%Y/%m/%d'; } else if(format === 'LDD/MM/YYYY') { format = '%d/%m/%Y'; } else if(format === 'MM/LDD/YYYY') { format = '%m/%d/%Y'; } // end backwards compatibility block var aLocale = Dt.getLocale(sLocale); var replace_aggs = function (m0, m1) { var f = Dt.aggregates[m1]; return (f === 'locale' ? aLocale[m1] : f); }; // First replace aggregates (run in a loop because an agg may be made up of other aggs) while(format.match(/%[cDFhnrRtTxX]/)) { format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs); } var replace_formats = function (m0, m1) { var f = Dt.formats[m1]; if(typeof f === 'string') { // string => built in date function return oDate[f](); } else if(typeof f === 'function') { // function => our own function return f.call(oDate, oDate, aLocale); } else if(typeof f === 'object' && typeof f[0] === 'string') { // built in function with padding return xPad(oDate[f[0]](), f[1]); } else { return m1; } }; // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a) var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats); replace_aggs = replace_formats = undefined; return str; }, /** * @description 기본 local 정보 등을 가져온다. * @param {String} sLocale * @return DU.util.LDateLocale */ getLocale : function (sLocale){ sLocale = sLocale || ((DU.getConfig) ? DU.getConfig().getFirst("$.core.defaultLocale") : "ko"); // Make sure we have a definition for the requested locale, or default to ko. /* if(!(sLocale in DU.util.LDateLocale)) { if(sLocale.replace(/-[a-zA-Z]+$/, '') in DU.util.LDateLocale) { sLocale = sLocale.replace(/-[a-zA-Z]+$/, ''); } else { sLocale = "ko"; } } */ return DU.util.LDateLocale.getInstance(sLocale); }, /** * @description format 문자열 return, 입력값 없을 경우 default return * @param {String} format * @param {String} locale * @return string */ getFormat : function(format, locale){ format = format || "%x"; if(format == "%x" || format == "%q" || format == "%Q" || format == "%T" || format == "%R" || format == "%r") { var aLocale = Dt.getLocale(locale); for(var f in Dt.aggregates) { if(Dt.aggregates[f] != 'locale') { aLocale[f] = Dt.aggregates[f]; } } format = aLocale[format.replace('%','')]; } return format; }, /** * @description 두 날짜를 format 형식에 맞게 비교한다. config를 주지 않을경우 %x(yyyy-mm-dd)로 비교한다. * @method equals * @param {Date} date1 대상 date 객체 * @param {Date} date2 비교 대상 date 객체 * @param {Object} config [optional] format등 옵션 * @return {boolean} */ equals : function(d1, d2, config) { config = config || { format:'%x' }; return (d1.format(config.format) == d2.format(config.format)); }, /** * @description inx월에 해당되는 마지막 날짜 * @method getDayInMonth * @param {Int} inx * @return {Int} */ getDayInMonth: function(inx){ return DU.util.LDate.DAYS_IN_MONTH[inx]; } }; /** * format 문자열에 기반한 문자열로부터 세부 시간 정보를 parsing 하는 * strptime에 대한 부분적인 implementation. *

* This implementation largely takes its cue from the documentation for Python's * time module, as documented at * http://docs.python.org/lib/module-Dt.html; with the exception of seconds * formatting, which is restricted to the range [00,59] rather than [00,61]. *

* 지원되는 formatting directive들: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DirectiveMeaning
%bLocale의 단축된 월 이름.
%BLocale의 전체 월 이름.
%d[01,31]의 십진수로된 월의 날짜.
%H[00,23]의 십진수로된 시간(24시간제).
%I[00,12]의 십진수로된 시간(12시간제).
%m[01,12]의 십진수로된 월.
%M[00,59]의 십진수로된 분.
%p * Locale의 AM이나 PM 표시(시간을 분류하기 위해 %I directive 가 * 사용되는 경우에 시간 출력 field에만 영향을 미친다.) *
%S[00,59]의 십진수로된 초.
%y[00,99]의 십진수로된 세기값이 없는 년도.
%Y십진수로된 세기값을 포함한 년도.
%%"%" 문자 literal .
* * @param {String} format 특정 formatting directive 문자열. * @param {Object} locale 해당 parser를 생성하기 위해 사용되는 locale object. * @constructor */ Dt.Parser = function(format, locale) { /** * 주어진 original formatting 문자열 * * @type String */ this.format = format; // Normalise whitespace before further processing format = format.split(/(?:\s|%t|%n)+/).join(" "); var pattern = []; var expected = []; var c; for (var i = 0, l = format.length; i < l; i++) { c = format.charAt(i); if (c == "%") { if (i == l - 1) { throw new Error("strptime format ends with raw %"); } c = format.charAt(++i); var directiveType = typeof Dt.Parser.DIRECTIVE_PATTERNS[c]; if (directiveType == "undefined") { throw new Error("strptime format contains a bad directive: '%" + c + "'") } else if (directiveType == "function") { pattern[pattern.length] = Dt.Parser.DIRECTIVE_PATTERNS[c](locale); } else { pattern[pattern.length] = Dt.Parser.DIRECTIVE_PATTERNS[c]; } expected = expected.concat(Dt.Parser.EXPECTED_DATA_TYPES[c]); } else { pattern[pattern.length] = c; } } /** * 해당 parser를 생성하기 위해 사용되는 locale object. * * @type Object */ this.locale = locale; /** * 해당 parser가 parsing을 위해 생성한 format을 위해 생성된 정규표헌식. * * @type RegExp */ this.regexp = new RegExp("^" + pattern.join("") + "$"); /** * 순서에 따라 일차하는 자리를 차지할 것으로 예상되는 * 해당 paeser의 regexp에 의해 일치될 예상 directives code의 list * * @type Array */ this.expected = expected; }; /** * directive에 해당하는 데이터, 혹은 locale에 의존하는 directive의 경우에나 * locale을 가지고 정규표현식 패턴 조각을 생성하는 함수를 * 캡쳐할 정규표현식 패턴 조각에 directive code들을 맵핑. * * @type Object */ Dt.Parser.DIRECTIVE_PATTERNS = { "a": function(l) { return "(" + l.a.join("|") + ")"; }, // Locale's abbreviated month name "b": function(l) { return "(" + l.b.join("|") + ")"; }, // Locale's full month name "B": function(l) { return "(" + l.B.join("|") + ")"; }, // Locale's equivalent of either AM or PM. "p": function(l) { return "(" + l.AM + "|" + l.PM + ")"; }, "d": "(\\d\\d?)", // Day of the month as a decimal number [01,31] "H": "(\\d\\d?)", // Hour (24-hour clock) as a decimal number [00,23] "I": "(\\d\\d?)", // Hour (12-hour clock) as a decimal number [01,12] "m": "(\\d\\d?)", // Month as a decimal number [01,12] "M": "(\\d\\d?)", // Minute as a decimal number [00,59] "S": "(\\d\\d?)", // Second as a decimal number [00,59] "y": "(\\d\\d?)", // Year without century as a decimal number [00,99] "Y": "(\\d\\d\\d\\d)", // Year with century as a decimal number "%": "%" // A literal "%" character }; /** * 각 directive에 대해 예상되는 캡쳐된 directive code들을 directive code들에 맵핑. * - 여러 데이터 항목들을 포함할 수 있는 어떤 directive들로 지정한 목록. * * @type Object */ Dt.Parser.EXPECTED_DATA_TYPES = { "b": ["b"], "B": ["B"], "d": ["d"], "H": ["H"], "I": ["I"], "m": ["m"], "M": ["M"], "p": ["p"], "S": ["S"], "y": ["y"], "Y": ["Y"], "%": [] }; Dt.Parser.prototype = { /* * Attempts to extract date and time details from the given input. *

* Time fields in this method's result are as follows: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
IndexRepresentsValues
0Year(for example, 1993)
1Monthrange [1,12]
2Dayrange [1,31]
3Hourrange [0,23]
4Minuterange [0,59]
5Secondrange [0,59]
6Day of week (not implemented - always 0)range [0,6], Monday is 0
7Day of year (not implemented - always 1)range [1,366]
8Daylight savings flag (not implemented - always -1)0, 1 or -1
* * @param {String} input the time string to be parsed. * * @return a list of 9 integers, each corresponding to a time field. * @type Array */ parse: function(input) { var matches = this.regexp.exec(input); if (matches === null) { return false; //throw new Error("Time data did not match format: data=" + input + // ", format=" + this.format); } // Collect matches in an object under properties corresponding to their // data types. var data = {}; for (var i = 1, l = matches.length; i < l; i++) { data[this.expected[i -1]] = matches[i]; } // Default values for when more accurate values cannot be inferred var time = [1900, 1, 1, 0, 0, 0, 0, 1, -1]; // Extract year if (typeof data["Y"] != "undefined") { var year = parseInt(data["Y"], 10); if (year < 1900) { //throw new Error("Year is out of range: " + year); return false; } time[0] = year; } else if (typeof data["y"] != "undefined") { var year = parseInt(data["y"], 10); if (year < 68) { year = 2000 + year; } else if (year < 100) { year = 1900 + year; } time[0] = year; } // Extract month if (typeof data["m"] != "undefined") { var month = parseInt(data["m"], 10); if (month < 1 || month > 12) { //throw new Error("Month is out of range: " + month); return false; } time[1] = month; } else if (typeof data["B"] != "undefined") { time[1] = this._indexOf(data["B"], this.locale.B) + 1; } else if (typeof data["b"] != "undefined") { time[1] = this._indexOf(data["b"], this.locale.b) + 1; } // Extract day of month if (typeof data["d"] != "undefined") { var day = parseInt(data["d"], 10); if (day < 1 || day > 31) { //throw new Error("Day is out of range: " + day); return false; } time[2] = day; } // Extract hour if (typeof data["H"] != "undefined") { var hour = parseInt(data["H"], 10); if (hour > 23) { //throw new Error("Hour is out of range: " + hour); return false; } time[3] = hour; } else if (typeof data["I"] != "undefined") { var hour = parseInt(data["I"], 10); if (hour < 1 || hour > 12) { //throw new Error("Hour is out of range: " + hour); return false; } // If we don't get any more information, we'll assume this time is // a.m. - 12 a.m. is midnight. if (hour == 12) { hour = 0; } time[3] = hour; if (typeof data["p"] != "undefined") { if (data["p"] == this.locale.PM) { // We've already handled the midnight special case, so it's // safe to bump the time by 12 hours without further checks. time[3] = time[3] + 12; } } } // Extract minute if (typeof data["M"] != "undefined") { var minute = parseInt(data["M"], 10); if (minute > 59) { //throw new Error("Minute is out of range: " + minute); return false; } time[4] = minute; } // Extract seconds if (typeof data["S"] != "undefined") { var second = parseInt(data["S"], 10); if (second > 59) { //throw new Error("Second is out of range: " + second); return false; } time[5] = second; } // Validate day of month var day = time[2], month = time[1], year = time[0]; if (((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) || (month == 2 && day > ((year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? 29 : 28))) { //throw new Error("Day " + day + " is out of range for month " + month); return false; } return new Date(time[0],time[1]-1,time[2],time[3],time[4],time[5]); }, _indexOf: function(item, list) { for (var i = 0, l = list.length; i < l; i++) { if (item === list[i]) { return i; } } return -1; } }; /** * @description 지정한 format의 문자열을 date로 변환 * @param {String} sDate * @param {Object} oConfig * @return {Date} */ Dt.parse = function(sDate, oConfig) { sDate = DU.util.LString.trim(sDate); oConfig = oConfig || {}; var format = oConfig.format; if(!format) { //입력용의 길이를 검사해서 입력날짜 길이와 같이면 해당 것으로 변환 var tempDate = Dt.format(new Date(),{locale:oConfig.locale,format:"%q"}); if(sDate.length == tempDate.length) format = "%q"; else format = '%Q'; } var locale = oConfig.locale; return new Dt.Parser(Dt.getFormat(format,locale), Dt.getLocale(locale)).parse(sDate); }; /* //strftime, strptime 구현시 아래와 같이 하면 된다. Dt.strptime = function(sDate, format, locale) { return new Dt.Parser(format, Dt.getLocale(locale)).parse(sDate); }; Dt.strftime = function(oDate, format, locale){ return Dt.format(oDate,{format:format,locale:locale}); }; */ DU.namespace("DU.util"); DU.util.LDate = Dt; DU.util.LDate.DAYS_IN_MONTH = [31,28,31,30,31,30,31,31,30,31,30,31]; })(); /** * static 문자열 클래스는 number 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module util * @title DU Global * @static */ DU.namespace("DU.util"); /** * The static Function class provides helper functions to deal with data of type Function. * statice 함수 클래스는 함수 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * * @namespace DU.util * @requires DU * @class LFunction * @static */ DU.util.LFunction = { /** * @description Function의 Delegate를 생성한다. * @method createDelegate * @param {Function} fn function 객체 * @param {Object} obj 수행할 오브젝트 * @param {Object} args 파라미터 * @param {boolean} appendArgs 추가 파라미터를 붙일지 여부 * @return {Function} Function 객체 */ createDelegate : function(fn, obj, args, appendArgs) { var method = fn; return function() { var callargs = args || arguments; if (appendArgs) { callargs = Array.prototype.concat.apply(arguments, args); } return method.apply(obj || window, callargs); } }, /** * @description Delegate를 생성하여 수행하는데 millis초값 만큼 시간이 지난후에 fn을 수행한다. * @method defer * @param {Function} fn function 객체 * @param {Int} millis 몇초후에 수행할지 값 (천분의 1초) * @param {Object} obj 수행할 오브젝트 * @param {Object} args 파라미터 * @param {boolean} appendArgs 추가 파라미터를 붙일지 여부 * @return {Function} Function 객체 */ defer : function(fn, millis, obj, args, appendArgs){ var fn = DU.util.LFunction.createDelegate(fn, obj, args, appendArgs); if(millis > 0){ return setTimeout(fn, millis); } fn(); return 0; }, /** * @description fcn을 수행하여 결과에 따라 fn을 대신 수행하는 Interceptor 메소드 * @method createInterceptor * @param {Function} fn function 객체 * @param {Function} fcn fn 의 해당되는 기능을 수할하지 여부를 판단하는 Function * @param {Object} scope 수행할 오브젝트 * @return {Function} Function 객체 */ createInterceptor : function(fn, fcn, scope){ var method = fn; return !DU.isFunction(fcn) ? this : function() { var me = this, args = arguments; fcn.target = me; fcn.method = method; return (fcn.apply(scope || me || window, args) !== false) ? method.apply(me || window, args) : null; }; } }; /** * Json 문자열을 파싱하는 method를 제공하고 Json 문자열로 object들을 변환한다. * @module util * @title Utility * @namespace DU.util * @requires DU */ /** * Json 문자열을 파싱하는 method를 제공하고 Json 문자열로 object들을 변환한다. * @class LJson * @static */ DU.util.LJson = (function(){ var useHasOwn = !!{}.hasOwnProperty; var lPad = DU.util.LString.lPad; var m = { "\b": '\\b', "\t": '\\t', "\n": '\\n', "\f": '\\f', "\r": '\\r', '"' : '\\"', "\\": '\\\\' }; var encodeString = function(s){ if (/["\\\x00-\x1f]/.test(s)) { return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) { var c = m[b]; if(c){ return c; } c = b.charCodeAt(); return "\\u00" + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + s + '"'; }; var encodeArray = function(o){ var a = ["["], b, i, l = o.length, v; for (i = 0; i < l; i += 1) { v = o[i]; switch (typeof v) { case "undefined": case "function": case "unknown": break; default: if (b) { a.push(','); } a.push(v === null ? "null" : DU.util.LJson.encode(v)); b = true; } } a.push("]"); return a.join(""); }; // Return the public API return { /** * object, array 그외 다른 값을 인코딩한다. * @method encodeDate * @static * @param {Mixed} o 인코딩할 변수 * @return {String} The Json string */ encodeDate: function(o){ return '"' + o.getFullYear() + "-" + lPad(o.getMonth() + 1, '0') + "-" + lPad(o.getDate(), '0') + "T" + lPad(o.getHours(), '0') + ":" + lPad(o.getMinutes(), '0') + ":" + lPad(o.getSeconds(), '0') + '"'; }, /** * object, array 그외 다른 값을 인코딩한다. * @method encode * @static * @param {Mixed} o 인코딩할 변수 * @return {String} The Json string */ encode: function(o){ if(typeof o == "undefined" || o === null){ return "null"; }else if(DU.isArray(o)){ return encodeArray(o); }else if(DU.util.LDate.isDate(o)){ return DU.util.LJson.encodeDate(o); }else if(typeof o == "string"){ return encodeString(o); }else if(typeof o == "number"){ return isFinite(o) ? String(o) : "null"; }else if(typeof o == "boolean"){ return String(o); }else { var a = ["{"], b, i, v; for (i in o) { if(!useHasOwn || o.hasOwnProperty(i)) { v = o[i]; switch (typeof v) { case "undefined": case "function": case "unknown": break; default: if(b){ a.push(','); } a.push(this.encode(i), ":", v === null ? "null" : this.encode(v)); b = true; } } } a.push("}"); return a.join(""); } }, /** * object에 Json 문자열을 디코딩한다. Json이 유효하지 않을 경우 * safe 옵션이 설정되어 있지 않다면 이 함수는 SyntaxError를 발생시킨다. * @method decode * @static * @param {String} json The Json string * @param {Boolean} safe (optional) true로 설정하고 Json이 잘못된 경우 null이 반환된다. * @return {Object} 결과 object */ decode: function(json, safe){ var fn = function(){ return eval("(" + json + ')'); }; if(safe){ try{ return fn(); }catch(e){ return null; } }else{ return fn(); } }, /** * Json을 XPath형식으로 정보를 얻어오는 메소드 *


         * alert(config.get("$.core.defaultLocale"));

         * 
XPath JsonPath Description
/ $ 루트 object/element
. @ 현재 object/element
/ . or [] child 연산자
.. n/a parent 연산자
// .. recursive descent. JsonPath는 E4X로 부터 이런 문법을 빌려왔다.
* * 와일드카드. 모든 object/element들은 그것들의 이름을 고려하지 않는다.
@ n/a attribute 액세스. Json 구조는 attribute들을 가지지 않는다.
[] [] subscript 연산자. XPath는 predicates와 element collection들의 반복을 위해 이것을 사용한다. Javascript와 Json에서 이것은 native array 연산자이다.
| [,] 노드 집합들의 조합에 XPath 결과들의 Union 연산자. JsonPath는 집합으로 대체적인 이름이나 array 인덱스들이 가능하다.
n/a [start:end:step] ES4에서 빌려온 array slice 연산자.
[] ?() 스크립트 표현 필터를 적용.
n/a () underlying 스크립트 엔진을 사용한 스크립트 표현.
() n/a Xpath 에서의 그룹핑
* @method jsonPath * @static * @param obj {Object} Json Object * @param expr {Object} Date의 문자열 serialization * @param arg {Object} 규칙 * @return {Object} */ jsonPath: function(obj, expr, arg){ var P = { resultType: arg && arg.resultType || "VALUE", result: [], normalize: function(expr){ var subx = []; return expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0, $1){ return "[#" + (subx.push($1) - 1) + "]"; }).replace(/'?\.'?|\['?/g, ";").replace(/;;;|;;/g, ";..;").replace(/;$|'?\]|'$/g, "").replace(/#([0-9]+)/g, function($0, $1){ return subx[$1]; }); }, asPath: function(path){ var x = path.split(";"), p = "$"; for (var i = 1, n = x.length; i < n; i++) p += /^[0-9*]+$/.test(x[i]) ? ("[" + x[i] + "]") : ("['" + x[i] + "']"); return p; }, store: function(p, v){ if (p) P.result[P.result.length] = P.resultType == "PATH" ? P.asPath(p) : v; return !!p; }, trace: function(expr, val, path){ if (expr) { var x = expr.split(";"), loc = x.shift(); x = x.join(";"); if (val && val.hasOwnProperty(loc)) P.trace(x, val[loc], path + ";" + loc); else if (loc === "*") P.walk(loc, x, val, path, function(m, l, x, v, p){ P.trace(m + ";" + x, v, p); }); else if (loc === "..") { P.trace(x, val, path); P.walk(loc, x, val, path, function(m, l, x, v, p){ typeof v[m] === "object" && P.trace("..;" + x, v[m], p + ";" + m); }); } else if (/,/.test(loc)) { // [name1,name2,...] for (var s = loc.split(/'?,'?/), i = 0, n = s.length; i < n; i++) P.trace(s[i] + ";" + x, val, path); } else if (/^\(.*?\)$/.test(loc)) // [(expr)] P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(";") + 1)) + ";" + x, val, path); else if (/^\?\(.*?\)$/.test(loc)) // [?(expr)] P.walk(loc, x, val, path, function(m, l, x, v, p){ if (P.eval(l.replace(/^\?\((.*?)\)$/, "$1"), v[m], m)) P.trace(m + ";" + x, v, p); }); else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step] phyton slice syntax P.slice(loc, x, val, path); } else P.store(path, val); }, walk: function(loc, expr, val, path, f){ if (val instanceof Array) { for (var i = 0, n = val.length; i < n; i++) if (i in val) f(i, loc, expr, val, path); } else if (typeof val === "object") { for (var m in val) if (val.hasOwnProperty(m)) f(m, loc, expr, val, path); } }, slice: function(loc, expr, val, path){ if (val instanceof Array) { var len = val.length, start = 0, end = len, step = 1; loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function($0, $1, $2, $3){ start = parseInt($1 || start); end = parseInt($2 || end); step = parseInt($3 || step); }); start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start); end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end); for (var i = start; i < end; i += step) P.trace(i + ";" + expr, val, path); } }, eval: function(x, _v, _vname){ try { return $ && _v && eval(x.replace(/@/g, "_v")); } catch (e) { throw new SyntaxError("jsonPath: " + e.message + ": " + x.replace(/@/g, "_v").replace(/\^/g, "_a")); } } }; var $ = obj; if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) { P.trace(P.normalize(expr).replace(/^\$;/, ""), obj, "$"); return P.result.length ? P.result : false; } } }; })(); /** * dom 모듈은 DOM element들을 처리하기 위해 도움을 주는 method들을 제공한다. * @module util * @title LDom Utility * @namespace DU.util * @requires DU */ (function() { var Y = DU.util, // internal shorthand getStyle, // for load time browser branching setStyle, // ditto propertyCache = {}, // for faster hyphen converts reClassNameCache = {}, // cache regexes for className document = window.document; // cache for faster lookups DU.env._id_counter = DU.env._id_counter || 0; // for use with generateId (global to save state if Dom is overwritten) // brower detection var isOpera = DU.browser.opera, isSafari = DU.browser.webkit, isGecko = DU.browser.gecko, isIE = DU.browser.msie; // regex cache var patterns = { HYPHEN: /(-[a-z])/i, // to normalize get/setStyle ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards, OP_SCROLL:/^(?:inline|table-row)$/i }; var testElement = function(node, method) { return node && node.nodeType == 1 && ( !method || method(node) ); }; var toCamel = function(property) { if ( !patterns.HYPHEN.test(property) ) { return property; // no hyphens } if (propertyCache[property]) { // already converted return propertyCache[property]; } var converted = property; while( patterns.HYPHEN.exec(converted) ) { converted = converted.replace(RegExp.$1, RegExp.$1.substr(1).toUpperCase()); } propertyCache[property] = converted; return converted; //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug }; var getClassRegEx = function(className) { var re = reClassNameCache[className]; if (!re) { re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)'); reClassNameCache[className] = re; } return re; }; // branching at load instead of runtime if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method getStyle = function(el, property) { var value = null; if (property == 'float') { // fix reserved word property = 'cssFloat'; } var computed = el.ownerDocument.defaultView.getComputedStyle(el, ''); if (computed) { // test computed before touching for safari value = computed[toCamel(property)]; } return el.style[property] || value; }; } else if (document.documentElement.currentStyle && isIE) { // IE method getStyle = function(el, property) { switch( toCamel(property) ) { case 'opacity' :// IE opacity uses filter var val = 100; try { // will error if no DXImageTransform val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity; } catch(e) { try { // make sure its in the document val = el.filters('alpha').opacity; } catch(e) { } } return val / 100; case 'float': // fix reserved word property = 'styleFloat'; // fall through default: // test currentStyle before touching var value = el.currentStyle ? el.currentStyle[property] : null; return ( el.style[property] || value ); } }; } else { // default to inline only getStyle = function(el, property) { return el.style[property]; }; } if (isIE) { setStyle = function(el, property, val) { switch (property) { case 'opacity': if ( DU.isString(el.style.filter) ) { // in case not appended el.style.filter = 'alpha(opacity=' + val * 100 + ')'; if (!el.currentStyle || !el.currentStyle.hasLayout) { el.style.zoom = 1; // when no layout or cant tell } } break; case 'float': property = 'styleFloat'; case 'cursor': val = val == 'col-resize' ? (DU.browser.webkit ? 'e-resize' : 'col-resize') : val; default: el.style[property] = val; } }; } else { setStyle = function(el, property, val) { if (property == 'float') { property = 'cssFloat'; } el.style[property] = val; }; } /** * DOM element들을 위해 도움을 주는 method들을 제공한다. * @namespace DU.util * @class LDom */ DU.util.LDom = { /** * HTMLElement refrence를 반환한다. * @static * @method get * @param {String | HTMLElement |Array} el DOM reference, 실제 DOM reference, * 혹은 ID들이나 HTMLElement들의 Array를 가져오기 위한 * ID 로서 사용하기 위한 문자열 Accepts * @return {HTMLElement | Array} HTML element나 HTMLElement들의 array에 대한 DOM reference */ get: function(el) { if (el) { // GridView가 ScrollingGridView 일경우 el이 slider이면 nodeType 에러가 발생함. 원인 확인 필요 if (el.nodeType || el.item) { // Node, or NodeList return el; } if (typeof el === 'string') { // id return document.getElementById(el); } if ('length' in el) { // array-like var c = []; for (var i = 0, len = el.length; i < len; ++i) { c[c.length] = Y.LDom.get(el[i]); } return c; } return el; // some other object, just pass it back } return null; }, /** * currentStyle과 ComputedStyle의 일반화(Normalize). * @static * @method getStyle * @param {String | HTMLElement |Array} el ID로서 사용할 실제 DOM reference, 혹은 ID들이나 * HTMLElement들의 Array의 문자열 Accepts * @param {String} property 값이 반환될 style property. * @return {String | Array} element를 위한 style property의 현재값. */ getStyle: function(el, property) { property = toCamel(property); var f = function(element) { return getStyle(element, property); }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * Wrapper for setting style properties of HTMLElements. * 최신 브라우저을 통해 "opacity"를 일반화(Normalize). * @static * @method setStyle * @param {String | HTMLElement |Array} el ID로서 사용할 실제 DOM reference, 혹은 ID들이나 * HTMLElement들의 Array의 문자열 Accepts * @param {String} property 설정될 style property. * @param {String} val 주어진 property에 적용될 값. */ setStyle: function(el, property, val) { property = toCamel(property); var f = function(element) { setStyle(element, property, val); }; Y.LDom.batch(el, f, Y.LDom, true); }, /** * element에 style specification을 적용한다. * @param {String/HTMLElement} el style이 적용될 element * @param {String/Object/Function} styles 스타일 지정 문자열, 예: "width:100px", * form의 {width:"100px"} object, * specification을 반환하는 함수 */ applyStyles : function(el, styles){ if(styles){ el = DU.get(el); if(typeof styles == "string"){ var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi; var matches; while ((matches = re.exec(styles)) != null){ el.setStyle(matches[1], matches[2]); } }else if (typeof styles == "object"){ for (var style in styles){ el.setStyle(style, styles[style]); } }else if (typeof styles == "function"){ DU.util.LDom.applyStyles(el, styles.call()); } } }, /** * 주어진 class의 HTMLElement들의 array를 반환한다. * 최적화된 성능을 위해서 가능한한 태그 및 또는 root node를 포함한다. * Note: 이 callback(node들의 추가/삭제 등)에서의 collection 변경 같은, * live collecttion에 반하는 운영 method는 부작용을 갖는다. * native "getElementsByTagName" method와 마찬가지로, * 대신 node array 반환을 반복해야 한다. * @static * @method getElementsByClassName * @param {String} className 일치하는 class 이름 * @param {String} tag (optional) collect될 element들의 태그 이름 * @param {String | HTMLElement} root (optional) 시작지점으로 사용할 HTMLElement나 ID * @param {Function} apply (optional) 발견했을때 각 element에 적용할 함수 * @return {Array} 주어진 class이름을 가진 element들의 array */ getElementsByClassName: function(className, tag, root, apply) { className = DU.trim(className); tag = tag || '*'; root = (root) ? Y.LDom.get(root) : null || document; if (!root) { return []; } var nodes = [], elements = root.getElementsByTagName(tag), re = getClassRegEx(className); for (var i = 0, len = elements.length; i < len; ++i) { if ( re.test(elements[i].className) ) { nodes[nodes.length] = elements[i]; if (apply) { apply.call(elements[i], elements[i]); } } } return nodes; }, /** * HTMLElement가 주어진 class 이름을 가졌는지에 대한 여부. * @static * @method hasClass * @param {String | HTMLElement | Array} el test할 element나 collection * @param {String} className 검색할 class 이름 * @return {Boolean | Array} boolean값이나 boolean값의 array */ hasClass: function(el, className) { var re = getClassRegEx(className); var f = function(el) { return re.test(el.className); }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * 주어진 element나 element들의 collection에 class 이름을 추가한다. * @static * @method addClass * @param {String | HTMLElement | Array} el class를 추가할 element나 collection * @param {String} className class attribute에 추가할 class 이름 * @return {Boolean | Array} pass/fail의 boolean 값이나 boolean값의 array */ addClass: function(el, className) { var f = function(el) { if (this.hasClass(el, className)) { return false; // already present } el.className = DU.trim([el.className, className].join(' ')); return true; }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * Removes a class name from a given element or collection of elements. * 주어진 element나 element들의 collection으로부터 class 이름을 삭제한다. * @static * @method removeClass * @param {String | HTMLElement | Array} el class를 삭제할 element나 collection * @param {String} className class attribute에 삭제할 class 이름 * @return {Boolean | Array} pass/fail의 boolean 값이나 boolean값의 array */ removeClass: function(el, className) { var ret = []; var cnList = DU.isArray(className) ? className : [className]; DU.util.LArray.each(cnList, function(cn) { var re = getClassRegEx(cn); var f = function(el) { var ret = false, current = el.className; if (cn && current && this.hasClass(el, cn)) { el.className = current.replace(re, ' '); if ( this.hasClass(el, cn) ) { // in case of multiple adjacent this.removeClass(el, cn); } el.className = DU.trim(el.className); // remove any trailing spaces if (el.className === '') { // remove class attribute if empty var attr = (el.hasAttribute) ? 'class' : 'className'; el.removeAttribute(attr); } ret = true; } return ret; }; ret.push(Y.LDom.batch(el, f, Y.LDom, true)); }, this); return ret.length == 1 ? ret[0]: ret; }, /** * ID를 반환하고, 만약 제공된 경우 "el" element로 적용된다. * @static * @method generateId * @param {String | HTMLElement | Array} el (optional) ID를 추가할 optional element의 array * (하나라도 이미 존재한다면, ID는 추가되지 않는다.) * @param {String} prefix (optional) 사용할 optional perfix(기본은 "L-gen") * @return {String | Array} 생성된 ID나, 생성된 ID들의 array * (아니면, element에 이미 존재할 경우 original ID) */ generateId: function(el, prefix) { prefix = prefix || 'L-gen'; var f = function(el) { if (el && el.id) { // do not override existing ID return el.id; } var id = prefix + DU.env._id_counter++; if (el) { el.id = id; } return id; }; // batch fails when no element, so just generate and return single ID return Y.LDom.batch(el, f, Y.LDom, true) || f.apply(Y.LDom, arguments); }, /** * HTMLElement가 DOM 계층구조에서 다른 HTML element의 ancestor 인지에 대한 여부 * @static * @method isAncestor * @param {String | HTMLElement} haystack 가능한 ancestor * @param {String | HTMLElement} needle 가능한 descendent * @return {Boolean} haystack이 needle의 ancestor인지에 대한 여부 */ isAncestor: function(haystack, needle) { haystack = Y.LDom.get(haystack); needle = Y.LDom.get(needle); var ret = false; if ( (haystack && needle) && (haystack.nodeType && needle.nodeType) ) { if (haystack.contains && haystack !== needle) { // contains returns true when equal ret = haystack.contains(needle); } else if (haystack.compareDocumentPosition) { // gecko ret = !!(haystack.compareDocumentPosition(needle) & 16); } } else { } return ret; }, /** * HTMLElement가 현재 document에 존재하는지에 대한 여부 * @static * @method inDocument * @param {String | HTMLElement} el 검색할 element * @return {Boolean} element가 현재 document에 존재하는지에 대한 여부 */ inDocument: function(el) { return this.isAncestor(document.documentElement, el); }, /** * Collection/Array의 각 항목에 대해 제공되는 method를 실행한다. * method는 첫번째 인자로 element를, 두번째로 method(el, o) 같은 optional 인자를 가지고 호출된다. * @static * @method batch * @param {String | HTMLElement | Array} el (optional) method가 적용할 element나 element들의 array * @param {Function} method element로 적용할 method * @param {Any} o (optional) 제공될 method로 전달할 optional arg * @param {Boolean} override (optional) "o"와 "method"의 scope를 override할지에 대한 여부 * @return {Any | Array} 제공된 method로 부터의 반환값(들) */ batch: function(el, method, o, override) { el = (el && (el.tagName || el.item)) ? el : Y.LDom.get(el); // skip get() when possible if (!el || !method) { return false; } var scope = (override) ? o : window; if (el.tagName || el.length === undefined) { // element or not array-like return method.call(scope, el, o); } var collection = []; for (var i = 0, len = el.length; i < len; ++i) { collection[collection.length] = method.call(scope, el[i], o); } return collection; }, /** * test method로 전달할 HTMLElement childNode들의 array를 반환한다. * @static * @method getChildrenBy * @param {HTMLElement} node 시작될 HTMLElemwnt * @param {Function} method 그것의 유일한 인자값으로 test되는 node를 받는 * children test에 사용되는 boolean 함수 * @return {Array} HTMLElement들의 static array */ getChildrenBy: function(node, method) { var child = Y.LDom.getFirstChildBy(node, method); var children = child ? [child] : []; Y.LDom.getNextSiblingBy(child, function(node) { if ( !method || method(node) ) { children[children.length] = node; } return false; // fail test to collect all children }); return children; }, /** * HTMLElement childNode들의 array를 반환한다. * @static * @method getChildren * @param {String | HTMLElement} node 시작점으로 사용할 HTMLElement나 ID * @return {Array} HTMLElement들의 static array */ getChildren: function(node) { node = Y.LDom.get(node); if (!node) { } return Y.LDom.getChildrenBy(node); }, /** * test method로 전달할 처음 HTMLElement child를 반환한다. * @static * @method getFirstChildBy * @param {HTMLElement} node 시작점으로 사용할 HTMLElement * @param {Function} method 그것의 유일한 인자값으로 test되는 node를 받는 * children test에 사용되는 boolean 함수 * @return {Object} HTMLElement나 발견되지 않았을 경우엔 null */ getFirstChildBy: function(node, method) { var child = ( testElement(node.firstChild, method) ) ? node.firstChild : null; return child || Y.LDom.getNextSiblingBy(node.firstChild, method); }, /** * boolean method로 전달할 다음 형제 HTMLElement를 반환한다. * 성능상의 이유로, ID들은 허용되지 않으며, argument validation은 생략된다. * @static * @method getNextSiblingBy * @param {HTMLElement} node 시작점으로 사용할 HTMLElement * @param {Function} method 그것의 유일한 인자값으로 test되는 sibling node를 받는 * siblings test에 사용되는 boolean 함수 * @return {Object} HTMLElement나 발견되지 않았을 경우엔 null */ getNextSiblingBy: function(node, method) { while (node) { node = node.nextSibling; if ( testElement(node, method) ) { return node; } } return null; }, /** * reference node의 이전 sibling으로 새 node를 삽입한다. * @static * @method insertBefore * @param {String | HTMLElement} newNode 삽입될 node * @param {String | HTMLElement} referenceNode 새로운 node 이전에 삽입할 node * @return {HTMLElement} 삽입된 node(만약 삽입이 실패되면 null) */ insertBefore: function(newNode, referenceNode) { newNode = Y.LDom.get(newNode); referenceNode = Y.LDom.get(referenceNode); if (!newNode || !referenceNode || !referenceNode.parentNode) { return null; } return referenceNode.parentNode.insertBefore(newNode, referenceNode); }, /** * reference node의 다음 sibling으로 새 node를 삽입한다. * @static * @method insertAfter * @param {String | HTMLElement} newNode 삽입될 node * @param {String | HTMLElement} referenceNode 새로운 node 이후에 삽입할 node * @return {HTMLElement} 삽입된 node(만약 삽입이 실패되면 null) */ insertAfter: function(newNode, referenceNode) { newNode = Y.LDom.get(newNode); referenceNode = Y.LDom.get(referenceNode); if (!newNode || !referenceNode || !referenceNode.parentNode) { return null; } if (referenceNode.nextSibling) { return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } else { return referenceNode.parentNode.appendChild(newNode); } }, /** * @description document로부터 DOM node를 삭제한다. body node는 전달될 경우 무시될 것이다. * @method removeNode * @param {HTMLElement} node 삭제할 node * @return {void} */ removeNode : DU.browser.msie ? function(){ var d; return function(n){ if(n && n.tagName != 'BODY'){ d = d || document.createElement('div'); d.appendChild(n); d.innerHTML = ''; } } }() : function(n){ if(n && n.parentNode && n.tagName != 'BODY'){ n.parentNode.removeChild(n); } }, /** * @description 전달된 simple selector의 match를 위한 현재 node와 parent node를 찾는다.(예: div.some-class or span:first-child) * @method findParent * @param {HTMLElement} node The node * @param {String} selector test를 위한 simple selector * @param {Number/Mixed} maxDepth (optional) element나 number로서 검색하기 위한 depth max값 * (defaults to 10 || document.body) * @return {HTMLElement} 매치되는 DOM node(매치되는 값을 찾지 못하면 null) */ findParent : function(dom, simpleSelector, maxDepth){ var p = dom, b = document.body, depth = 0, dq = DU.util.LDomSelector, stopEl; maxDepth = maxDepth || 50; if(typeof maxDepth != "number"){ stopEl = DU.getDom(maxDepth); maxDepth = 10; } while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){ if(dq.test(p, simpleSelector)){ return p; } depth++; p = p.parentNode; } return null; }, /** * @description Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child) * @description 전달된 simple selector의 match를 위한 parent node들을 찾는다.(예: div.some-class or span:first-child) * @method findParentNode * @param {HTMLElement} node The node * @param {String} selector test를 위한 simple selector * @param {Number/Mixed} maxDepth (optional) element나 number로서 검색하기 위한 depth max값 * (defaults to 10 || document.body) * @return {HTMLElement} 매치되는 DOM node(매치되는 값을 찾지 못하면 null) */ findParentNode : function(dom, simpleSelector, maxDepth){ return this.findParent(dom.parentNode, simpleSelector, maxDepth); }, /** * 범위 객체안에 입력 field의 value값들을 object형으로 만들어서 리턴한다. * @method getValues * @param {selector} selector selector 문자열 * @param {String|HTMLElement} id 범위 객체 * @return {Object} * @static */ getValues : function(selector, id) { var obj = {}; var el = DU.get(id); var children = DU.util.LDomSelector.query(selector, el.dom); DU.util.LArray.each(children, function(child) { var f = DU.get(child); var name = f.dom.name || f.dom.id; if(f.hasClass('empty')) return true; var value = f.get("value"); if (f.dom.type == 'checkbox' || f.dom.type == 'radio') { if (/LRadio/.test(f.dom.declaredClass)) { if (value !== false) { obj[name] = value; } } else { var ary = obj[name]; if (!ary) { ary = []; obj[name] = ary; } if (value !== false) { ary.push(value); } } } else { obj[name] = value; } }, this); return obj; } }; })(); /** * Event 유틸리티는 event 시스템들을 만들기 위한 DOM Event들과 도구들을 관리하는 * 유틸리티들을 제공한다. * * @module util * @title Event Utility * @namespace DU.util * @requires DU */ /** * event가 발생할때 사용되기 위한 subscriber 정보를 저장한다. * @param {Function} fn 실행할 함수 * @param {Object} obj event가 발생할때 전달될 object * @param {boolean} override true인 경우 If true, 전달된 obj는 listener의 실행 범위가 된다. * @class LSubscriber * @constructor */ DU.util.LSubscriber = function(fn, obj, override) { /** * event가 발생할때 실행될 callback * @property fn * @type function */ this.fn = fn; /** * callback으로 전달되는 부가적인 custom object * the event fires * @property obj * @type object */ this.obj = DU.isUndefined(obj) ? null : obj; /** * event listener에 대한 기본적인 실행 scope는 event가 생성될때 * 정의된다(일반적으로 event에 포함된 object). * override를 true로 설정함으로 인하여 실행 scope는 subscriber로 인해 * 전달되는 custom object가 된다. * override가 object인 경우 해당 object는 scope가 된다. * @property override * @type boolean|object */ this.override = override; }; /** * 해당 listener에 대한 실행 scope를 반환한다. * override가 true로 설정되어 있을 경우 custom obj가 scope가 될 것이다. * override가 object인 경우, 그것은 scope가 되며, * 그렇지 않은 경우에는 기본 scope가 사용될 것이다. * @method getScope * @param {Object} defaultScope 해당 listener가 override 되지 않은 경우 사용할 scope. */ DU.util.LSubscriber.prototype.getScope = function(defaultScope) { if (this.override) { if (this.override === true) { return this.obj; } else { return this.override; } } return defaultScope; }; /** * fn과 obj가 해당 object들의 property들과 일치하는 경우 true를 반환한다. * 정확한 subscriber 일치를 위하여 unsubscribe method에 의해 사용된다. * * @method contains * @param {Function} fn 실행할 함수 * @param {Object} obj event가 발생할때 전달될 object * @return {boolean} 제공된 argument들이 해당 subscriber의 signature와 일치하는 경우 true. */ DU.util.LSubscriber.prototype.contains = function(fn, obj) { if (obj) { return (this.fn == fn && this.obj == obj); } else { return (this.fn == fn); } }; /** * @method toString */ DU.util.LSubscriber.prototype.toString = function() { return "Subscriber { obj: " + this.obj + ", override: " + (this.override || "no") + " }"; }; /** * Event 유틸리티는 event 시스템들을 만들기 위한 DOM Event들과 도구들을 관리하는 * 유틸리티들을 제공한다. * * @module util * @title Event Utility * @namespace DU.util * @requires DU */ /** * LEventProvider는 event들이 이름으로 subscribe되거나 발생시키는 것이 * 가능하게 하는 인터페이스에서 CustomEvent들을 wrapping하기 위한 * Du.argument와 함께 사용되도록 디자인 된다. * 이것은 아직 만들어지지 않았거나, 전혀 만들어지지 않을 event에 * subscribe 하기 위한 코드 구현을 가능하게 한다. * * @Class LEventProvider */ DU.util.LEventProvider = function() { }; DU.util.LEventProvider.prototype = { /** * custom event들의 private 저장공간 * @property __DU_events * @type Object[] * @private */ __DU_events: null, /** * custom event subsctiber들의 private 저장공간 * @property __DU_subscribers * @type Object[] * @private */ __DU_subscribers: null, /** * event 타입에 의해 LCustomEvent에 subscribe * * @method on * @param p_type {string} 타입이나 event의 이름 * @param p_fn {function} event가 발생할때 실행할 함수 * @param p_obj {Object} event가 발생할때 전달되는 object * @param p_override {boolean} true일 경우, obj가 listener의 실행 scope가 된다. */ on: function(p_type, p_fn, p_obj, p_override) { this.__DU_events = this.__DU_events || {}; var ce = this.__DU_events[p_type]; if (ce) { ce.on(p_fn, p_obj, p_override); } else { this.__DU_subscribers = this.__DU_subscribers || {}; var subs = this.__DU_subscribers; if (!subs[p_type]) { subs[p_type] = []; } subs[p_type].push( { fn: p_fn, obj: p_obj, override: p_override } ); } }, /** * 특정 event로 부터 하나 혹은 그 이상의 listener들을 unsubscribe 한다. * @method unOn * @param p_type {string} 타입, 혹은 event의 이름. * 타입을 명시하지 않은 경우, 모든 host된 event들로부터 listener를 * 제거하기 위해 시도할 것이다. * @param p_fn {Function} unsubscribe 하기 위한 subsctibe된 함수. * 제공되지 않는 경우 모든 subscriber가 삭제될 것이다. * @param p_obj {Object} subscribe하기 위해 전달되는 custom object. * 이것은 옵션이지만, 만약 제공되면 같은 여러 개의 listener들을 * 명확하게 하는데 사용될 것이다.(예들 들어, prototype에 존재하는 * 함수를 사용하여 많은 object를 subscribe 하는 것을 들수 있다.) * @return {boolean} subscriber가 발견되고 detach 된 경우 true. */ unOn: function(p_type, p_fn, p_obj) { this.__DU_events = this.__DU_events || {}; var evts = this.__DU_events; if (p_type) { var ce = evts[p_type]; if (ce) { return ce.unOn(p_fn, p_obj); } } else { var ret = true; for (var i in evts) { if (DU.hasOwnProperty(evts, i)) { ret = ret && evts[i].unOn(p_fn, p_obj); } } return ret; } return false; }, /** * 특정 event로부터 모든 listener들을 제거한다. * event type이 명시되지 않은 경우, host된 custom event들로부터의 * 모든 listener들이 삭제될 것이다. * @method unOnAll * @param p_type {string} 타입 혹은 event의 이름 */ unOnAll: function(p_type) { return this.unOn(p_type); }, /** * 명시된 타입의 새로운 custom event를 생성한다. * 이미 존재하는 이름의 custom event일 경우 그것은 재생성 되지 않을 것이다. * 두 경우 모두 custom event가 반환된다. * * @method createEvent * * @param p_type {string} 타입 혹은 event의 이름 * @param p_config {object} optional config params. 유효한 속성은 다음과 같다: * *
    *
  • * scope: 기본적인 실행 scope를 정의한다. 정의되지 않은 경우 * 기본 scope는 해당 instance가 될 것이다. *
  • *
  • * silent: true인 경우, custome event는 로그메세지를 생성하지 않는다. * 이것은 기본적으로 false 이다. *
  • *
  • * onSubscribeCallback: event가 새로운 subscriber를 가졌을때 실행할 * callback을 명시한다. 이것은 event의 생성이전에 존재하는 경우 * 각각의 큐에 저장된 subscriber를 위해 즉시 발생한다. *
  • *
* * @return {LCustomEvent} the custom event * */ createEvent: function(p_type, p_config) { this.__DU_events = this.__DU_events || {}; var opts = p_config || {}; var events = this.__DU_events; if (events[p_type]) { } else { var scope = opts.scope || this; var silent = (opts.silent); var signature = opts.signature || DU.util.LCustomEvent.FLAT; var ce = new DU.util.LCustomEvent(p_type, scope, silent, signature); events[p_type] = ce; if (opts.onSubscribeCallback) { ce.subscribeEvent.on(opts.onSubscribeCallback); } this.__DU_subscribers = this.__DU_subscribers || {}; var qs = this.__DU_subscribers[p_type]; if (qs) { for (var i=0; i *
  • fire()는 첫번째 argument로 실행
  • *
  • subscribe() method로 전달된 custom object
  • * * @method fireEvent * @param p_type {string} 타입 혹은 event의 이름 * @param arguments {Object*} handler로 전달할 parameter들의 임의의 집합. * @return {boolean} LCustomEvent.fire부터의 리턴값 * */ fireEvent: function(p_type, arg1, arg2, etc) { this.__DU_events = this.__DU_events || {}; var ce = this.__DU_events[p_type]; if (!ce) { return null; } var args = []; for (var i=1; i *
  • DU.util.LCustomEvent.LIST: *
      *
    • param1: event name
    • *
    • param2: 발생시키기 위해 전송되는 argument들의 array
    • *
    • param3: subscriber에 의해 제공되는 custom object
    • *
    *
  • *
  • DU.util.LCustomEvent.FLAT *
      *
    • param1: 발생시키기 위해여 전달되는 첫번째 argument. * 만약 여러 parameter들을 전달하고자 한다면, array나 object literal을 사용한다.
    • *
    • param2: subscriber에 의해 제공되는 custom object
    • *
    *
  • * * @property signature * @type int */ this.signature = signature || DU.util.LCustomEvent.LIST; /** * 해당 event를 위한 subscriber들 * @property subscribers * @type Subscriber[] */ this.subscribers = []; if (!this.silent) { } var onsubscribeType = "_DUCEOnSubscribe"; // Only add subscribe events for events that are not generated by // LCustomEvent if (type !== onsubscribeType) { /** * Csutom event들은 event에 새로운 subscriber가 있을때 마다 발생되는 * custom event를 제공한다. * 그리고 새로운 subscriber를 가지고 이미 fire된 * non-repeating 이벤트가 있는 경우를 제어하는 기회를 제공한다. * * @event subscribeEvent * @type DU.util.LCustomEvent * @param {Function} fn 실행할 function * @param {Object} obj event가 발생할때 전달될 object An object to be passed along when the event fires * @param {boolean|Object} override 만약 true면, 전달된 obj가 listener의 실행 scope가 된다. * 만약 object면, 그 object가 실행 scope가 된다. */ this.subscribeEvent = new DU.util.LCustomEvent(onsubscribeType, this, true); } /** * exception이 발생했을때 subscriber 스택의 나머지를 실행 가능하게 하기 위하여 * subscriber exception들이 잡힌다. 가장 최근의 exception이 이 propery에 저장된다. * @property lastError * @type Error */ this.lastError = null; }; /** * subscriber listener signature 상수. * LIST type은 세가지 parameter들을 반환한다: event type, 발생할때 전달되는 args의 array, * 그리고 optional custom object. * @property DU.util.LCustomEvent.LIST * @static * @type int */ DU.util.LCustomEvent.LIST = 0; /** * subscriber listener signature 상수. * FLAT type은 두가지 parameter들을 반환한다: 발생할때 전달되는 첫번째 argument와 * optional custom object * @property DU.util.LCustomEvent.FLAT * @static * @type int */ DU.util.LCustomEvent.FLAT = 1; DU.util.LCustomEvent.prototype = { /** * Subscribes the caller to this event * 이 event로의 caller 를 명시한다. * @method on * @param {Function} fn 실행할 function * @param {Object} obj event 발생시에 전달될 object * @param {boolean|Object} override 만약 true면, 전달된 obj가 listener의 실행 scope가 된다. * 만약 object면, 그 object가 실행 scope가 된다. */ on: function(fn, obj, override) { if (!fn) { throw new Error("Invalid callback for subscriber to '" + this.type + "'"); } if (this.subscribeEvent) { this.subscribeEvent.fire(fn, obj, override); } this.subscribers.push( new DU.util.LSubscriber(fn, obj, override) ); }, /** * unOns subscribers. * @method unOn * @param {Function} fn 삭제하기 위하여 명시된 function, 만약 제공되지 않으면, * 모든 function이 삭제될 것이다. * @param {Object} obj subscribe로 전달된 custom object. * 이것은 option 이지만, 만약 명확한 여러 listener들에서 사용되게 * 제공되는 것은 모두 같을 것이다. * (e.g., prototype상에서 존재하는 function을 사용하는 * 많은 object를 명시한다.) * @return {boolean} subscriber가 있고 detach 되어 있으면 true. */ unOn: function(fn, obj) { if (!fn) { return this.unOnAll(); } var found = false; for (var i=0, len=this.subscribers.length; i *
  • event의 type
  • *
  • array로서 실행된 모든 arguments fire()
  • *
  • (만약 있다면,) subscribe() method로 전달된 custom object
  • * * @method fire * @param {Object*} arguments handler로 전달할 parameter들의 임의 set * @return {boolean} 만약 subscriber가 false 를 반환하면 false 이고 아니면 true */ fire: function() { this.lastError = null; var errors = [], len=this.subscribers.length; if (!len && this.silent) { return true; } var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false; if (!this.silent) { } // make a copy of the subscribers so that there are // no index problems if one subscriber removes another. var subs = this.subscribers.slice(), throwErrors = DU.util.LEvent.throwErrors; for (i=0; i 0) { param = args[0]; } try { ret = s.fn.call(scope, param, s.obj); } catch(e) { this.lastError = e; // errors.push(e); if (throwErrors) { throw e; } } } else { try { ret = s.fn.call(scope, this.type, args, s.obj); } catch(ex) { this.lastError = ex; if (throwErrors) { throw ex; } } } if (false === ret) { if (!this.silent) { } break; // return false; } } } return (ret !== false); }, /** * 모든 listener들을 삭제한다. * @method unOnAll * @return {int} unsubscribe 된 listener의 개수 */ unOnAll: function() { for (var i=this.subscribers.length-1; i>-1; i--) { this._delete(i); } this.subscribers=[]; return i; }, /** * @method _delete * @private */ _delete: function(index) { var s = this.subscribers[index]; if (s) { delete s.fn; delete s.obj; } // this.subscribers[index]=null; this.subscribers.splice(index, 1); }, /** * @method toString */ toString: function() { return "LCustomEvent: " + "'" + this.type + "', " + "scope: " + this.scope; } }; /** * Event 유틸리티는 event 시스템들을 만들기 위한 DOM Event들과 도구들을 관리하는 * 유틸리티들을 제공한다. * * @module util * @title LEvent Utility * @namespace DU.util * @requires DU */ // The first instance of Event will win if it is loaded more than once. // @TODO this needs to be changed so that only the state data that needs to // be preserved is kept, while methods are overwritten/added as needed. // This means that the module pattern can't be used. if (!DU.util.LEvent) { /** * event 유틸리티는 event listener들과 event cleansing을 추가하거나 삭제하는 * 함수들을 제공한다. 이것은 또한 unload event 동안 등록되는 listener들을 * 제거하려고 시도한다. * * @class LEvent * @static */ DU.util.LEvent = function() { /** * onload event가 발생한 후에 True * @property loadComplete * @type boolean * @static * @private */ var loadComplete = false; /** * Wrapping 된 listener들의 캐시 * @property listeners * @type array * @static * @private */ var listeners = []; /** * 모든 event들이 detach 되기 전에 발생되는 사용자 정의 unload 함수 * @property unloadListeners * @type array * @static * @private */ var unloadListeners = []; /** * Safari에서 DOM2 event들의 이슈 해결을 위한 DOM0 event handler들의 캐시 * @property legacyEvents * @static * @private */ var legacyEvents = []; /** * DOM0 event들에 대한 listener 스택 * @property legacyHandlers * @static * @private */ var legacyHandlers = []; /** * window.onload 이후에 polling 할 횟수. 이 숫자는 페이지 로딩 이후에 * 추가적인 late-bound handler들이 요청될 경우 증가한다. * @property retryCount * @static * @private */ var retryCount = 0; /** * onAvailable listeners * @property onAvailStack * @static * @private */ var onAvailStack = []; /** * legacy event들에 대한 Lookup 테이블 * @property legacyMap * @static * @private */ var legacyMap = []; /** * 자동 id 생성에 대한 count * @property counter * @static * @private */ var counter = 0; /** * webkit/safari에 대한 일반화된 keycode들 * @property webkitKeymap * @type {int: int} * @private * @static * @final */ var webkitKeymap = { 63232: 38, // up 63233: 40, // down 63234: 37, // left 63235: 39, // right 63276: 33, // page up 63277: 34, // page down 25: 9 // SHIFT-TAB (Safari provides a different key code in // this case, even though the shiftKey modifier is set) }; // String constants used by the addFocusListener and removeFocusListener methods var _FOCUS = DU.browser.msie ? "focusin" : "focus"; var _BLUR = DU.browser.msie ? "focusout" : "blur"; return { /** * document가 로딩되고 event가 요청되는 시점에서 DOM에 있지 않은 * element들을 찾기 위한 횟수. * 기본값은 2000@amp;20 ms 이며, 40초 혹은 모든 outstanding handler들이 * 바운딩 되는(둘중 먼저 오는) 동안 polling 될 것이다. * @property POLL_RETRYS * @type int * @static * @final */ POLL_RETRYS: 2000, /** * millisecond 단위의 polling 간격 * @property POLL_INTERVAL * @type int * @static * @final */ POLL_INTERVAL: 20, /** * 바인드할 element, 정수형 상수 * @property EL * @type int * @static * @final */ EL: 0, /** * event의 타입, 정수형 상수 * @property TYPE * @type int * @static * @final */ TYPE: 1, /** * 실행할 함수, 정수형 상수 * @property FN * @type int * @static * @final */ FN: 2, /** * scope 수정 및 정리를 위한 함수 wrapping, 정수형 상수 * @property WFN * @type int * @static * @final */ WFN: 3, /** * callback에의 parameter로서 반환될 사용자에 의해 전달되는 object, 정수형 상수. * listener들을 unload 하기 위해 명시된다. * @property UNLOAD_OBJ * @type int * @static * @final */ UNLOAD_OBJ: 3, /** * event에 등록하는 element나 listener에 의해 전달되는 custom object의 * 범위 조정, 정수형 상수 * @property ADJ_SCOPE * @type int * @static * @final */ ADJ_SCOPE: 4, /** * addListener로 전달되는 original obj * @property OBJ * @type int * @static * @final */ OBJ: 5, /** * addListener로 전달되는 original scope parameter * @property OVERRIDE * @type int * @static * @final */ OVERRIDE: 6, /** * addListener로 전달되는 original capture parameter * @property CAPTURE * @type int * @static * @final */ CAPTURE: 7, /** * addListener/removeListener는 예기치 않은 시나리오에서 에러를 throw 할수 있다. * 이런 에러들은 억제되며, hethod는 false를 반환하고, 해당 property를 설정한다. * @property lastError * @static * @type Error */ lastError: null, /** * Safari 브라우저 인식 * @property isSafari * @private * @static * @deprecated use DU.browser.webkit */ isSafari: DU.browser.webkit, /** * webkit 버전 * @property webkit * @type string * @private * @static * @deprecated use DU.browser.webkit */ webkit: DU.browser.webkit, /** * IE 브라우저 인식 * @property isIE * @private * @static * @deprecated use DU.browser.msie */ isIE: DU.browser.msie, /** * poll handle * @property _interval * @static * @private */ _interval: null, /** * document readystate poll handle * @property _dri * @static * @private */ _dri: null, /** * document가 처음으로 사용할 수 있을때 True * @property DOMReady * @type boolean * @static */ DOMReady: false, /** * custom event들의 subscriber에 의해 전달된 에러들이 캐치되고, * 에러 제세지는 debug 콘솔에 출력된다. * 이 property를 true로 설정할 경우, 에러를 re-throw 할 것이다. * @property throwErrors * @type boolean * @default false */ throwErrors: true, /** * @method startInterval * @static * @private */ startInterval: function() { if (!this._interval) { var self = this; var callback = function() { self._tryPreloadAttach(); }; this._interval = setInterval(callback, this.POLL_INTERVAL); } }, /** * 제공된 id의 항목이 발견됐을 때 제공된 callback을 실행한다. * 이것은 페이지 로딩시 가능한 빨리 동작을 실행하는데 * 사용될 수 있음을 의미한다. * 이것을 초기 페이지 로딩 이후에 사용할 경우, element에 대한 * 고정된 시간으로 polling을 할 것이다. * polling 횟수와 주기는 config 가능하다. * 기본적으로 10초 동안 polling 할 것이다. *

    callback은 하나의 parameter를 가지고 실행된다: * custom object parameter를 제공하는 경우.

    * * @method onAvailable * * @param {string||string[]} p_id element의 id나, 찾아야 할 id들의 array * @param {function} p_fn element를 찾았을때, 실행할 함수. * @param {object} p_obj p_fn의 parameter로서 전달될 optional object. * @param {boolean|object} p_override true가 설정될 경우, p_fn은 p_obj의 scope로 * 실행할 것이며, object가 설정될 경우, 해당 object의 scope로 * 실행할 것이다. * @param checkContent {boolean} child 노드의 준비성을 체크한다.(onContentReady) * @static */ onAvailable: function(p_id, p_fn, p_obj, p_override, checkContent) { var a = (DU.isString(p_id)) ? [p_id] : p_id; for (var i=0; icallback은 하나의 parameter를 가지고 실행된다: * custom object parameter를 제공하는 경우.

    * * @method onContentReady * * @param {string} p_id 찾아야 할 element의 id * @param {function} p_fn element가 준비되었을때 실행할 함수 * @param {object} p_obj p_fn의 parameter로서 전달될 optional object * @param {boolean|object} p_override true가 설정될 경우, p_fn은 p_obj의 scope로 * 실행할 것이며, object가 설정될 경우, 해당 object의 scope로 * 실행할 것이다. * * @static */ onContentReady: function(p_id, p_fn, p_obj, p_override) { this.onAvailable(p_id, p_fn, p_obj, p_override, true); }, /** * DOM이 처음으로 사용가능할 때 제공된 callback을 실행한다. * 이것은 DOMReady event가 발생된 이후에 호출될 경우 즉시 실행될 것이다. * @todo DOMContentReady event는 스크립트가 페이지에 동적으로 삽입된 경우 * 발생하지 않을 것이다. * 이것은 DOMReady custom event가 library가 삽입될때 FireFox나 Opera 브라우저에서 * 절대 발생하지 않음을 의미한다. * 이것은 Safari 브라우저에서는 발생할 것이며, IE의 구현에서는 * 지연된 스크립트가 사용가능하지 않은 경우 발생하도록 고려할 수 있다. * 우리는 이것이 모든 브라우저에서 동일하게 작동하게 하고 싶다. * 스크립트가 included inline 대신에 삽입되었을 때 확인할 방법이 있는가? * window onload event가 첨부된 listener를 가지지 않고 발생할수 있을지 * 에 대해서 알 수 있는 방법이 있는가? * *

    callback은 LCustomEvent이며, signature는 다음과 같다:

    *

    type <string>, args <array>, customobject <object>

    *

    DOMReady event들에 대해, 발생 argument들은 없으며, signature는 다음과 같다:

    *

    "DOMReady", [], obj

    * * * @method onDOMReady * * @param {function} p_fn element를 찾았을 때 실행할 함수 * @param {object} p_obj p_fn의 parameter로서 전달될 optional object * @param {boolean|object} p_scope true가 설정될 경우, p_fn은 p_obj의 scope로 * 실행할 것이며, object가 설정될 경우, 해당 object의 scope로 * 실행할 것이다. * * @static */ onDOMReady: function(p_fn, p_obj, p_override) { if (this.DOMReady) { setTimeout(function() { var s = window; if (p_override) { if (p_override === true) { s = p_obj; } else { s = p_override; } } p_fn.call(s, "DOMReady", [], p_obj); }, 0); } else { this.DOMReadyEvent.on(p_fn, p_obj, p_override); } }, /** * event handlet를 추가한다. * * @method _addListener * * @param {String|HTMLElement|Array|NodeList} el listener에 할당할 id나 * element reference 혹은 id나 element들의 collection. * @param {String} sType 추가할 event의 타입 * @param {Function} fn event가 수행하는 method * @param {Object} obj handler에 parameter로서 전달될 임의의 object * @param {Boolean|object} override true일 결우, obj가 listener의 실행 scope가 된다. * object일 경우, object가 실행 scope가 된다. * @param {boolen} capture capture or bubble phase * @return {Boolean} action이 성공이거나 defred일 경우 true, * 하나 혹은 그 이상의 element들이 첨부된 listener를 가질 * 수 없었거나 operation에서 exception이 발생했을 경우 false. * @private * @static */ _addListener: function(el, sType, fn, obj, override, capture) { if (!fn || !fn.call) { return false; } // The el argument can be an array of elements or element ids. if ( this._isValidCollection(el)) { var ok = true; for (var i=0,len=el.length; i-1; i--) { ok = ( this._removeListener(el[i], sType, fn, capture) && ok ); } return ok; } if (!fn || !fn.call) { //return false; return this.purgeElement(el, false, sType); } if ("unload" == sType) { for (i=unloadListeners.length-1; i>-1; i--) { li = unloadListeners[i]; if (li && li[0] == el && li[1] == sType && li[2] == fn) { unloadListeners.splice(i, 1); // unloadListeners[i]=null; return true; } } return false; } var cacheItem = null; // The index is a hidden parameter; needed to remove it from // the method signature because it was tempting users to // try and take advantage of it, which is not possible. var index = arguments[4]; if ("undefined" === typeof index) { index = this._getCacheIndex(el, sType, fn); } if (index >= 0) { cacheItem = listeners[index]; } if (!el || !cacheItem) { return false; } if (this.useLegacyEvent(el, sType)) { var legacyIndex = this.getLegacyIndex(el, sType); var llist = legacyHandlers[legacyIndex]; if (llist) { for (i=0, len=llist.length; i 0 && onAvailStack.length > 0); } // onAvailable var notAvail = []; var executeItem = function (el, item) { var scope = el; if (item.override) { if (item.override === true) { scope = item.obj; } else { scope = item.override; } } item.fn.call(scope, item.obj); }; var i, len, item, el, ready=[]; // onAvailable onContentReady for (i=0, len=onAvailStack.length; i-1; i--) { item = onAvailStack[i]; if (!item || !item.id) { onAvailStack.splice(i, 1); } } this.startInterval(); } else { clearInterval(this._interval); this._interval = null; } this.locked = false; }, /** * addListener를 통해 주어진 element에 연결된 모든 listener를 제거한다. * 부가적으로 노드의 chidren도 제거될 수 있다. * 또한 삭제될 event의 특정 타입을 명시할 수 있다. * @method purgeElement * @param {HTMLElement} el 제거할 element * @param {boolean} recurse element의 children을 재귀적으로 제거한다. * 주의해서 사용해야 한다. * @param {string} sType 제거할 listener의 optional 타입. * 남겨둘 경우, 모든 listener들이 삭제될 것이다. * @static */ purgeElement: function(el, recurse, sType) { var oEl = (DU.isString(el)) ? this.getEl(el) : el; var elListeners = this.getListeners(oEl, sType), i, len; if (elListeners) { for (i=elListeners.length-1; i>-1; i--) { var l = elListeners[i]; this._removeListener(oEl, l.type, l.fn, l.capture); } } if (recurse && oEl && oEl.childNodes) { for (i=0,len=oEl.childNodes.length; i 0) { // 2.5.0 listeners are removed for all browsers again. FireFox preserves // at least some listeners between page refreshes, potentially causing // errors during page load (mouseover listeners firing before they // should if the user moves the mouse at the correct moment). if (listeners) { for (j=listeners.length-1; j>-1; j--) { l = listeners[j]; if (l) { EU._removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], l[EU.CAPTURE], j); } } l=null; } legacyEvents = null; EU._simpleRemove(window, "unload", EU._unload); }, /** * scrollLeft를 반환한다. * @method _getScrollLeft * @static * @private */ _getScrollLeft: function() { return this._getScroll()[1]; }, /** * scrollTop을 반환한다. * @method _getScrollTop * @static * @private */ _getScrollTop: function() { return this._getScroll()[0]; }, /** * scrollTop과 scrollLeft를 반환한다. * Internet Explorer 브라우저에서 pageX와 pageY를 계산하기 위하여 사용된다. * @method _getScroll * @static * @private */ _getScroll: function() { var dd = document.documentElement, db = document.body; if (dd && (dd.scrollTop || dd.scrollLeft)) { return [dd.scrollTop, dd.scrollLeft]; } else if (db) { return [db.scrollTop, db.scrollLeft]; } else { return [0, 0]; } }, /** * 하위 호환성을 위해 복원된 LCustomEvent의 구버전에서 사용된다. * @method regCE * @private * @static * @deprecated 하위 호환성을 위해 아직 존재 */ regCE: function() { // does nothing }, /** * 캐싱, cleanup, scope 조정 등을 거치지 않고 직접 DOM event를 추가한다. * * @method _simpleAdd * @param {HTMLElement} el handler에 바인드할 element * @param {string} sType event handler의 타입 * @param {function} fn 수행할 callback * @param {boolen} capture capture or bubble phase * @static * @private */ _simpleAdd: function () { if (window.addEventListener) { return function(el, sType, fn, capture) { if (sType == 'mouseenter') { fn = DU.util.LFunction.createInterceptor(fn, checkRelatedTarget); el.addEventListener('mouseover', fn, (capture)); } else if (sType == 'mouseleave') { fn = DU.util.LFunction.createInterceptor(fn, checkRelatedTarget); el.addEventListener('mouseout', fn, (capture)); } else { el.addEventListener(sType, fn, (capture)); } }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, fn); }; } else { return function(){}; } }(), /** * 기본적인 listener 삭제 * * @method _simpleRemove * @param {HTMLElement} el handler에 바인드될 element * @param {string} sType event handler의 타입 * @param {function} fn 수행할 callback * @param {boolen} capture capture or bubble phase * @static * @private */ _simpleRemove: function() { if (window.removeEventListener) { return function (el, sType, fn, capture) { if (sType == 'mouseenter') { sType = 'mouseover' } else if (sType == 'mouseleave') { sType = 'mouseout' } el.removeEventListener(sType, fn, (capture)); }; } else if (window.detachEvent) { return function (el, sType, fn) { el.detachEvent("on" + sType, fn); }; } else { return function(){}; } }() }; function checkRelatedTarget(e) { var related = e.relatedTarget, isXulEl = Object.prototype.toString.apply(related) == '[object XULElement]'; if (!related) return false; return (!isXulEl && related != this && this.tag != 'document' && !elContains(this, related)); } function elContains(parent, child) { while(child) { if(child === parent) { return true; } try { child = child.parentNode; } catch(e) { // In FF if you mouseout an text input element // thats inside a div sometimes it randomly throws // Permission denied to get property HTMLDivElement.parentNode // See https://bugzilla.mozilla.org/show_bug.cgi?id=208427 return false; } if(child && (child.nodeType != 1)) { child = null; } } return false; } }(); (function() { var EU = DU.util.LEvent; /** * DU.util.LEvent.on는 addListener를 위한 alias이다. * @method on * @see addListener * @static */ EU.on = EU.addListener; /** * DU.util.LEvent.onFocus는 addFocusListener 위한 alias이다. * @method onFocus * @see addFocusListener * @static */ EU.onFocus = EU.addFocusListener; /** * DU.util.LEvent.onBlur is an alias for addBlurListener * DU.util.LEvent.onBlur는 addBlurListener 위한 alias이다. * @method onBlur * @see addBlurListener * @static */ EU.onBlur = EU.addBlurListener; /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller */ // Internet Explorer: use the readyState of a defered script. // This isolates what appears to be a safe moment to manipulate // the DOM prior to when the document's readyState suggests // it is safe to do so. if (EU.isIE) { if (self !== self.top) { document.onreadystatechange = function(){ if (document.readyState == 'complete') { document.onreadystatechange = null; EU._ready(); } }; } else { // Process onAvailable/onContentReady items when the // DOM is ready. DU.util.LEvent.onDOMReady( DU.util.LEvent._tryPreloadAttach, DU.util.LEvent, true); var n = document.createElement('p'); EU._dri = setInterval(function() { try { // throws an error if doc is not ready n.doScroll('left'); clearInterval(EU._dri); EU._dri = null; EU._ready(); n = null; } catch (ex) { } }, EU.POLL_INTERVAL); } // The document's readyState in Safari currently will // change to loaded/complete before images are loaded. } else if (EU.webkit && EU.webkit < 525) { EU._dri = setInterval(function() { var rs=document.readyState; if ("loaded" == rs || "complete" == rs) { clearInterval(EU._dri); EU._dri = null; EU._ready(); } }, EU.POLL_INTERVAL); // FireFox and Opera: These browsers provide a event for this // moment. The latest WebKit releases now support this event. } else { EU._simpleAdd(document, "DOMContentLoaded", EU._ready); } ///////////////////////////////////////////////////////////// EU._simpleAdd(window, "load", EU._load); EU._simpleAdd(window, "unload", EU._unload); EU._tryPreloadAttach(); })(); } /** * Connection Manager는 XMLHttpRequest object로의 단순화된 인터페이스를 제공한다. * 그리고 Connnection Manager는 XMLHttpRequest의 cross-browser의 인스턴스화, * 양방향의 상태값과 서버 response에 대한 처리, * 사용자가 만든 미리 정의된 callback으로의 결과 반환 등을 처리한다. * * @namespace DU * @module core * @requires DU * @requires event */ /** * Connection Manager singleton 패턴은 비동기화 transaction들을 * 만들고 관리하기 위한 method를 제공한다. * * @namespace DU * @class LConnect */ DU.LConnect = { /** * @description XMLHttpRequest를 위한 MSFT ActiveX id들의 Array * @property _msxml_progid * @private * @static * @type array */ _msxml_progid:[ 'Microsoft.XMLHTTP', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP' ], /** * @description HTTP header의 object literal * @property _http_header * @private * @static * @type object */ _http_headers:{}, /** * @description HTTP header들이 설정되어 있는지 결정한다. * @property _has_http_headers * @private * @static * @type boolean */ _has_http_headers:false, /** * @description POST transaction들을 위한 client들의 HTTP header 전송시에 * 'application/x-www-form-urlencoded' content-type을 기본 header로 추가할 것인지에 대해 결정한다. * @property _use_default_post_header * @private * @static * @type boolean */ _use_default_post_header:true, /** * @description POST transaction에서 사용되는 기본 header * @property _default_post_header * @private * @static * @type boolean */ _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8', /** * @description HTML form 사용을 포함하는 transaction에서 사용되는 기본 header * @property _default_form_header * @private * @static * @type boolean */ _default_form_header:'application/x-www-form-urlencoded', /** * @description 각 transaction에 'X-Requested-With: XMLHttpRequest'을 기본 header로 추가할지 결정한다. * @property _use_default_xhr_header * @private * @static * @type boolean */ _use_default_xhr_header:true, /** * @description "X-Requested-With"에 대한 기본 header 값. * 이것은 기본적으로 DU Connection Manager에 의해 만들어진 request를 확인하기 위한 각각의 트랜잭션과 함께 전송된다. * @property _default_xhr_header * @private * @static * @type boolean */ _default_xhr_header:'XMLHttpRequest', /** * @description 각 transaction에 대해 custom, default header를 설정할지 결정한다. * @property _has_default_headers * @private * @static * @type boolean */ _has_default_headers:true, /** * @description 각 transaction에 대해 custom, default header를 설정할지 결정한다. * @property _has_default_header * @private * @static * @type boolean */ _default_headers:{}, /** * @description HTML form에 의해 데이터가 submit 되어야할지 결정하기 위해 * setForm()에 의해 변경되는 property. * @property _isFormSubmit * @private * @static * @type boolean */ _isFormSubmit:false, /** * @description 파일업로드로 추측되는지 결정하기 위해 serForm()에 의해 변경되는 property. * @property _isFileUpload * @private * @static * @type boolean */ _isFileUpload:false, /** * @description 원하는 action이 파일업로드일 경우, HTML form node로의 참조를 설정하기 위해 * serForm()에 의해 변경되는 property. * @property _formNode * @private * @static * @type object */ _formNode:null, /** * @description 각 transaction에 대한 HTML form 데이터를 설정하기 위해 * serForm()에 의해 변경되는 property. * @property _sFormData * @private * @static * @type string */ _sFormData:null, /** * @description handleReadyState 상태에서 polling 메커니즘으로의 polling 참조 collection. * @property _poll * @private * @static * @type object */ _poll:{}, /** * @description 정의된 timeout value를 가지고 있는 각 transaction callback을 위한 timeput값의 queue * @property _timeOut * @private * @static * @type object */ _timeOut:{}, /** * @description transaction의 XHR readyState를 결정하기 위한 시도를 할 때 * HandleReadyState 상태를 위한 milliseconds 단위의 polling 빈도수 * 기본값은 50 milliseconds 이다. * @property _polling_interval * @private * @static * @type int */ _polling_interval:50, /** * @description 각 transaction에 대한 transaction id의 증가가 되는 transaction counter. * @property _transaction_id * @private * @static * @type int */ _transaction_id:0, /** * @description DU.util.LEvent가 가능하고, HTML form에서 여러 개의 submit 버튼이 존재할 경우 * "clicked" submit 버튼의 name-value pair에 대한 트랙들. * @property _submitElementValue * @private * @static * @type string */ _submitElementValue:null, /** * @description DU.util.LEvent가 가능한지 결정해서 true or false를 반환한다. * 만약 true면, eventlistener는 "Submit"의 target 타입으로 결정한 * 클릭 event를 trap 하기 위한 dcoument level에 바인딩 된다. * 이 listener는 HTML form에서 여러 submit 버튼의 클릭된 "Submit" 값을 * 결정하기 위한 setForm()을 활성화 할 것이다. * @property _hasSubmitListener * @private * @static */ _hasSubmitListener:(function() { if(DU.util.LEvent){ DU.util.LEvent.addListener( document, 'click', function(e){ var obj = DU.util.LEvent.getTarget(e); if(obj.nodeName.toLowerCase() == 'input' && (obj.type && obj.type.toLowerCase() == 'submit')){ DU.LConnect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value); } }); return true; } return false; })(), /** * @description transaction의 시작에서 발생시키는 custom event * @property startEvent * @private * @static * @type LCustomEvent */ startEvent: new DU.util.LCustomEvent('start'), /** * @description transaction rseponse가 완료되었을때 발생시키는 custom event * @property completeEvent * @private * @static * @type LCustomEvent */ completeEvent: new DU.util.LCustomEvent('complete'), /** * @description handleTransactionResponse() method가 HTTP 2xx range에서 * response를 결정할 때 발생시키는 custom enevt * @property successEvent * @private * @static * @type LCustomEvent */ successEvent: new DU.util.LCustomEvent('success'), /** * @description handleTransactionResponse() method가 HTTP 4xx/5xx range에서 * response를 결정할 때 발생시키는 custom enevt * @property failureEvent * @private * @static * @type LCustomEvent */ failureEvent: new DU.util.LCustomEvent('failure'), /** * @description handleTransactionResponse() method가 HTTP 4xx/5xx range에서 * response를 결정할 때 발생시키는 custom enevt * @property uploadEvent * @private * @static * @type LCustomEvent */ uploadEvent: new DU.util.LCustomEvent('upload'), /** * @description transaction이 성공적으로 aborted 됐을 때 발생시키는 custom event. * @property abortEvent * @private * @static * @type LCustomEvent */ abortEvent: new DU.util.LCustomEvent('abort'), /** * @description callback custom event들을 특정 event name에 mapping하는 참조 테이블 * @property _customEvents * @private * @static * @type object */ _customEvents: { onStart:['startEvent', 'start'], onComplete:['completeEvent', 'complete'], onSuccess:['successEvent', 'success'], onFailure:['failureEvent', 'failure'], onUpload:['uploadEvent', 'upload'], onAbort:['abortEvent', 'abort'] }, /** * @description 기존 xml_progid array에 ActiveX id를 추가하기 위한 member. * 새로 소개된 ActiveX id event에서 이것은 내부적인 코드 수정없이 추가될 수 있다. * @method setProgId * @public * @static * @param {string} id XHR object를 초기화하기 위하여 추가될 ActiveX id * @return void */ setProgId:function(id) { this._msxml_progid.unshift(id); }, /** * @description 기본 POST header를 override 하기 위한 member. * @method setDefaultPostHeader * @public * @static * @param {boolean} b default header를 설정하고 사용함. - true or false * @return void */ setDefaultPostHeader:function(b) { if(typeof b == 'string'){ this._default_post_header = b; } else if(typeof b == 'boolean'){ this._use_default_post_header = b; } }, /** * @description 기본 transaction header를 override 하기 위한 member. * @method setDefaultXhrHeader * @public * @static * @param {boolean} b default header를 설정하고 사용함. - true or false * @return void */ setDefaultXhrHeader:function(b) { if(typeof b == 'string'){ this._default_xhr_header = b; } else{ this._use_default_xhr_header = b; } }, /** * @description 기본 polling 간격을 변경하기 위한 member. * @method setPollingInterval * @public * @static * @param {int} i milliseconds 단위의 polling 간격 * @return void */ setPollingInterval:function(i) { if(typeof i == 'number' && isFinite(i)){ this._polling_interval = i; } }, /** * @description XMLHttpRequest object를 인스턴스화 하고, 2개의 property와 같이 반환한다: * XMLHttpRequest instance와 transaction id. * @method createXhrObject * @private * @static * @param {int} transactionId 해당 transaction을 위하여 transaction id를 포함하고 있는 property * @return object */ createXhrObject:function(transactionId) { var obj,http; try { // Instantiates XMLHttpRequest in non-IE browsers and assigns to http. http = new XMLHttpRequest(); // Object literal with http and tId properties obj = { conn:http, tId:transactionId }; } catch(e) { for(var i=0; i -1) { opt = oElement.options[oElement.selectedIndex]; data[item++] = oName + encodeURIComponent( (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text); } break; case 'select-multiple': if (oElement.selectedIndex > -1) { for(j=oElement.selectedIndex, jlen=oElement.options.length; j'); // IE will throw a security exception in an SSL environment if the // iframe source is undefined. if(typeof secureUri == 'boolean'){ io.src = 'javascript:false'; } } else{ io = document.createElement('iframe'); io.id = frameId; io.name = frameId; } io.style.position = 'absolute'; io.style.top = '-1000px'; io.style.left = '-1000px'; document.body.appendChild(io); }, /** * @description POST 데이터를 파싱하고, 각 key-value에 대한 hidden form element들을 생성하여, * HTML form object에 그것들을 붙인다. * @method appendPostData * @private * @static * @param {string} postData HTTP POST 데이터 * @return {array} hidden field들의 formElements collection */ appendPostData:function(postData) { var formElements = [], postMessage = postData.split('&'), i, delimitPos; for(i=0; i < postMessage.length; i++){ delimitPos = postMessage[i].indexOf('='); if(delimitPos != -1){ formElements[i] = document.createElement('input'); formElements[i].type = 'hidden'; formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos)); formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1)); this._formNode.appendChild(formElements[i]); } } return formElements; }, /** * @description transaction을 용이하게 하기 위한 createFrame에서 만든 iframe을 사용한 * 파일들과 첨부를 포함하는 HTML form을 업로드 한다. * @method uploadFile * @private * @static * @param {int} id The transaction id. * @param {object} callback 사용자 정의된 callback object * @param {string} uri 완전히 검증된 resource 경로 * @param {string} postData HTML form이외에 submit될 POST 데이터 * @return {void} */ uploadFile:function(o, callback, uri, postData){ // Each iframe has an id prefix of "yuiIO" followed // by the unique transaction id. var frameId = 'yuiIO' + o.tId, uploadEncoding = 'multipart/form-data', io = document.getElementById(frameId), oConn = this, args = (callback && callback.argument)?callback.argument:null, oElements,i,prop,obj; // Track original HTML form attribute values. var rawFormAttributes = { action:this._formNode.getAttribute('action'), method:this._formNode.getAttribute('method'), target:this._formNode.getAttribute('target') }; // Initialize the HTML form properties in case they are // not defined in the HTML form. this._formNode.setAttribute('action', uri); this._formNode.setAttribute('method', 'POST'); this._formNode.setAttribute('target', frameId); if(DU.browser.msie){ // IE does not respect property enctype for HTML forms. // Instead it uses the property - "encoding". this._formNode.setAttribute('encoding', uploadEncoding); } else{ this._formNode.setAttribute('enctype', uploadEncoding); } if(postData){ oElements = this.appendPostData(postData); } // Start file upload. this._formNode.submit(); // Fire global custom event -- startEvent this.startEvent.fire(o, args); if(o.startEvent){ // Fire transaction custom event -- startEvent o.startEvent.fire(o, args); } // Start polling if a callback is present and the timeout // property has been defined. if(callback && callback.timeout){ this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout); } // Remove HTML elements created by appendPostData if(oElements && oElements.length > 0){ for(i=0; i < oElements.length; i++){ this._formNode.removeChild(oElements[i]); } } // Restore HTML form attributes to their original // values prior to file upload. for(prop in rawFormAttributes){ if(DU.hasOwnProperty(rawFormAttributes, prop)){ if(rawFormAttributes[prop]){ this._formNode.setAttribute(prop, rawFormAttributes[prop]); } else{ this._formNode.removeAttribute(prop); } } } // Reset HTML form state properties. this.resetFormState(); // Create the upload callback handler that fires when the iframe // receives the load event. Subsequently, the event handler is detached // and the iframe removed from the document. var uploadCallback = function() { if(callback && callback.timeout){ window.clearTimeout(oConn._timeOut[o.tId]); delete oConn._timeOut[o.tId]; } // Fire global custom event -- completeEvent oConn.completeEvent.fire(o, args); if(o.completeEvent){ // Fire transaction custom event -- completeEvent o.completeEvent.fire(o, args); } obj = { tId : o.tId, argument : callback.argument }; try { // responseText and responseXML will be populated with the same data from the iframe. // Since the HTTP headers cannot be read from the iframe obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent; obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document; } catch(e){} if(callback && callback.upload){ if(!callback.scope){ callback.upload(obj); } else{ callback.upload.apply(callback.scope, [obj]); } } // Fire global custom event -- uploadEvent oConn.uploadEvent.fire(obj); if(o.uploadEvent){ // Fire transaction custom event -- uploadEvent o.uploadEvent.fire(obj); } DU.util.LEvent.removeListener(io, "load", uploadCallback); setTimeout( function(){ document.body.removeChild(io); oConn.releaseObject(o); }, 100); }; // Bind the onload handler to the iframe to detect the file upload response. DU.util.LEvent.addListener(io, "load", uploadCallback); }, /** * @description readyState 4 상태에 도달하지 못했을 경우 transaction을 종료시키기 위한 method. * @method abort * @public * @static * @param {object} o asyncRequest에 의해 반환되는 connection object * @param {object} callback 사용자 정의된 callback object * @param {string} isTimeout callback timeout의 결과 abort 경우를 나타내기 위한 bollean값 * @return {boolean} */ abort:function(o, callback, isTimeout) { var abortStatus; var args = (callback && callback.argument)?callback.argument:null; if(o && o.conn){ if(this.isCallInProgress(o)){ // Issue abort request o.conn.abort(); window.clearInterval(this._poll[o.tId]); delete this._poll[o.tId]; if(isTimeout){ window.clearTimeout(this._timeOut[o.tId]); delete this._timeOut[o.tId]; } abortStatus = true; } } else if(o && o.isUpload === true){ var frameId = 'yuiIO' + o.tId; var io = document.getElementById(frameId); if(io){ // Remove all listeners on the iframe prior to // its destruction. DU.util.LEvent.removeListener(io, "load"); // Destroy the iframe facilitating the transaction. document.body.removeChild(io); if(isTimeout){ window.clearTimeout(this._timeOut[o.tId]); delete this._timeOut[o.tId]; } abortStatus = true; } } else{ abortStatus = false; } if(abortStatus === true){ // Fire global custom event -- abortEvent this.abortEvent.fire(o, args); if(o.abortEvent){ // Fire transaction custom event -- abortEvent o.abortEvent.fire(o, args); } this.handleTransactionResponse(o, callback, true); } return abortStatus; }, /** * @description transaction이 아직 처리중인지에 대해서 결정한다. * @method isCallInProgress * @public * @static * @param {object} o asyncRequest에 의해 반환되는 connection object * @return {boolean} */ isCallInProgress:function(o) { // if the XHR object assigned to the transaction has not been dereferenced, // then check its readyState status. Otherwise, return false. if(o && o.conn){ return o.conn.readyState !== 4 && o.conn.readyState !== 0; } else if(o && o.isUpload === true){ var frameId = 'yuiIO' + o.tId; return document.getElementById(frameId)?true:false; } else{ return false; } }, /** * @description transaction이 완료된 후의 connection object와 XHR instance에 대한 역참조 * @method releaseObject * @private * @static * @param {object} o The connection object * @return {void} */ releaseObject:function(o) { if(o && o.conn){ //dereference the XHR instance. o.conn = null; //dereference the connection object. o = null; } } }; (function(){ /** * Element는 event listener들의 추가, dom method들의 사용, attribute들의 관리 등을 * 쉽게 하는 wrapper object를 제공한다. * @module util * @namespace DU * @requires dom, event * @beta */ /** * DU.util.LAttribute instance를 제공하고 관리한다. * @namespace DU.util * @class LAttributeProvider * @uses DU.util.LEventProvider */ DU.util.LAttributeProvider = function(){ }; DU.util.LAttributeProvider.prototype = { /** * attribute config의 key-value map * @property _configs * @protected (may be used by subclasses and augmentors) * @private * @type {Object} */ _configs: null, /** * attribute의 현재 값을 반환한다. * @method get * @param {String} key 반환될 attribute의 값 * @return {Any} attribute의 현재 값 */ get: function(key){ this._configs = this._configs || {}; var config = this._configs[key]; if (!config || !this._configs.hasOwnProperty(key)) { return undefined; } return config.value; }, /** * config의 값을 설정한다. * @method set * @param {String} key attribute의 이름 * @param {Any} value attribute에 적용할 값 * @param {Boolean} silent change event들의 억제 여부 * @return {Boolean} 값의 설정 여부 */ set: function(key, value, silent){ this._configs = this._configs || {}; var config = this._configs[key]; if (!config) { return false; } return config.setValue(value, silent); }, /** * attribute 이름들에 대한 array를 반환한다. * @method getAttributeKeys * @return {Array} attribute 이름들의 array */ getAttributeKeys: function(){ this._configs = this._configs; var keys = []; var config; for (var key in this._configs) { config = this._configs[key]; if (DU.hasOwnProperty(this._configs, key) && !DU.isUndefined(config)) { keys[keys.length] = key; } } return keys; }, /** * 여러 attribute의 값을 설정한다. * @method setAttributes * @param {Object} map attribute들의 key-value map * @param {Boolean} silent change event들의 억제 여부 */ setAttributes: function(map, silent){ for (var key in map) { if (DU.hasOwnProperty(map, key)) { this.set(key, map[key], silent); } } }, /** * 특정 attribute의 값을 초기값으로 재설정한다. * @method resetValue * @param {String} key attribute의 이름 * @param {Boolean} silent change event들의 억제 여부 * @return {Boolean} 값의 설정 여부 */ resetValue: function(key, silent){ this._configs = this._configs || {}; if (this._configs[key]) { this.set(key, this._configs[key]._initialConfig.value, silent); return true; } return false; }, /** * attribute의 값을 현재값으로 설정한다. * @method refresh * @param {String | Array} key refresh할 attribute * @param {Boolean} silent change event들의 억제 여부 */ refresh: function(key, silent){ this._configs = this._configs || {}; var configs = this._configs; key = ((DU.isString(key)) ? [key] : key) || this.getAttributeKeys(); for (var i = 0, len = key.length; i < len; ++i) { if (configs.hasOwnProperty(key[i])) { this._configs[key[i]].refresh(silent); } } }, /** * LAttributeProvider instance에 attribute를 추가한다. * @method register * @param {String} key attribute의 이름 * @param {Object} map attribute의 property를 포함하고 있는 key-value map * @deprecated Use setAttributeConfig */ register: function(key, map){ this.setAttributeConfig(key, map); }, /** * attribute의 property들을 반환한다. * @method getAttributeConfig * @param {String} key attribute의 이름 * @private * @return {object} attribute의 property들을 모두 포함하는 key-value map */ getAttributeConfig: function(key){ this._configs = this._configs || {}; var config = this._configs[key] || {}; var map = {}; // returning a copy to prevent overrides for (key in config) { if (DU.hasOwnProperty(config, key)) { map[key] = config[key]; } } return map; }, /** * attribute insatance의 property들을 업데이트 하거나 설정한다. * @method setAttributeConfig * @param {String} key attribute의 이름 * @param {Object} map attribute property들의 key-value map * @param {Boolean} init 초기 설정 적용 여부 */ setAttributeConfig: function(key, map, init){ this._configs = this._configs || {}; map = map || {}; if (!this._configs[key]) { map.name = key; this._configs[key] = this.createAttribute(map); } else { this._configs[key].configure(map, init); } }, /** * attribute insatance의 property들을 업데이트 하거나 설정한다. * @method configureAttribute * @param {String} key attribute의 이름 * @param {Object} map attribute property들의 key-value map * @param {Boolean} init 초기 설정 적용 여부 * @deprecated Use setAttributeConfig */ configureAttribute: function(key, map, init){ this.setAttributeConfig(key, map, init); }, /** * attribute를 초기 config로 재설정한다. * @method resetAttributeConfig * @param {String} key attribute의 이름 * @private */ resetAttributeConfig: function(key){ this._configs = this._configs || {}; this._configs[key].resetConfig(); }, // wrapper for LEventProvider.on // to create events on the fly on: function(type, callback){ this._events = this._events || {}; if (!(type in this._events)) { this._events[type] = this.createEvent(type); } DU.util.LEventProvider.prototype.on.apply(this, arguments); }, on: function(){ this.on.apply(this, arguments); }, addListener: function(){ this.on.apply(this, arguments); }, /** * attribute의 beforeChange event를 발생시킨다. * @method fireBeforeChangeEvent * @param {String} key attribute의 이름 * @param {Obj} e handler들에게 전달할 event object */ fireBeforeChangeEvent: function(e){ var type = 'before'; type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change'; e.type = type; return this.fireEvent(e.type, e); }, /** * attribute의 change event를 발생시킨다. * @method fireChangeEvent * @param {String} key attribute의 이름 * @param {Obj} e handler들에게 전달할 event object */ fireChangeEvent: function(e){ e.type += 'Change'; return this.fireEvent(e.type, e); }, createAttribute: function(map){ return new DU.util.LAttribute(map, this); } }; DU.augment(DU.util.LAttributeProvider, DU.util.LEventProvider); })(); /** * Element는 event listener들의 추가, dom method들의 사용, attribute들의 관리 등을 * 쉽게 하는 wrapper object를 제공한다. * @module util * @namespace DU * @requires dom, event * @beta */ /** * LAttribute의 configuration을 제공한다. * @namespace DU.util * @class LAttribute * @constructor * @param hash {Object} The initial LAttribute. * @param {DU.util.LAttributeProvider} LAttribute instance의 owner. */ DU.util.LAttribute = function(hash, owner) { if (owner) { this.owner = owner; this.configure(hash, true); } }; DU.util.LAttribute.prototype = { /** * attribute의 이름 * @property name * @type String */ name: undefined, /** * attribute의 값 * @property value * @type String */ value: null, /** * attribute의 owner * @property owner * @type DU.util.LAttributeProvider */ owner: null, /** * attribute의 read only 여부 * @property readOnly * @type Boolean */ readOnly: false, /** * atrribute의 written once 여부 * @property writeOnce * @type Boolean */ writeOnce: false, /** * attribute의 초기화 configuration * @private * @property _initialConfig * @type Object */ _initialConfig: null, /** * attribute의 값 설정 여부 * @private * @property _written * @type Boolean */ _written: false, /** * attribute의 값을 설정할때 사용하는 method. * method는 오직 인자로만 새 값을 받는다. * @property method * @type Function */ method: null, /** * attribute의 값을 설정할때 사용하는 validator * @property validator * @type Function * @return Boolean */ validator: null, /** * attribute의 현재 값을 받는다. * @method getValue * @return {any} attribute의 현재값 */ getValue: function() { return this.value; }, /** * attribute의 값을 설정하고 beforeChange와 change event를 발생시킨다. * @method setValue * @param {Any} value attribute에 적용되는 값 * @param {Boolean} silent 만약 true면, change event를 발생시키지 않는다. * @return {Boolean} 값의 설정 여부 */ setValue: function(value, silent) { var beforeRetVal; var owner = this.owner; var name = this.name; var event = { type: name, prevValue: this.getValue(), newValue: value }; if (this.readOnly || ( this.writeOnce && this._written) ) { return false; // write not allowed } if (this.validator && !this.validator.call(owner, value) ) { return false; // invalid value } if (!silent) { beforeRetVal = owner.fireBeforeChangeEvent(event); if (beforeRetVal === false) { return false; } } if (this.method) { this.method.call(owner, value); } this.value = value; this._written = true; event.type = name; if (!silent) { this.owner.fireChangeEvent(event); } return true; }, /** * attribute의 property를 config 할 수 있다. * @method configure * @param {Object} map A key-value map of Attribute properties. * @param {Boolean} init config 초기화에 대한 여부 */ configure: function(map, init) { map = map || {}; this._written = false; // reset writeOnce this._initialConfig = this._initialConfig || {}; for (var key in map) { if ( map.hasOwnProperty(key) ) { this[key] = map[key]; if (init) { this._initialConfig[key] = map[key]; } } } }, /** * Resets the value to the initial config value. * 초기 config 값을 재설정한다. * @method resetValue * @return {Boolean} 값의 설정 여부 */ resetValue: function() { return this.setValue(this._initialConfig.value); }, /** * 초기 config 상태로 attribute config를 재설정 한다. * @method resetConfig */ resetConfig: function() { this.configure(this._initialConfig); }, /** * 현재 값으로 값을 재설정한다. * 값들이 실제 property와 동기화 되지 않았을 경우에 유용하다. * @method refresh * @return {Boolean} 값의 설정 여부 */ refresh: function(silent) { this.setValue(this.value, silent); } }; (function() { // internal shorthand var Dom = DU.util.LDom, LAttributeProvider = DU.util.LAttributeProvider; /** * Element는 event listener들을 쉽게 추가하고, dom method들을 사용하거나, * sttribute를 관리하는 wrapper object를 제공한다. * @module core * @namespace DU * @requires dom, event * @beta */ /** * Element는 event listener들을 쉽게 추가하고, dom method들을 사용하거나, * sttribute를 관리하는 wrapper object를 제공한다. *
    sample : /sample/core/element-sample.html * @class LElement * @uses DU.util.LAttributeProvider * @constructor * @param {HTMLElement | String} el LElement를 표현하는 html element * @param map {Object} 초기 config 이름과 값들의 key-value map */ DU.LElement = function(el, map) { if (arguments.length) { this.init(el, map); } /** * @description dom 객체 * @property dom * @public * @type {Object} */ this.dom = typeof el == "string" ? document.getElementById(el) : el; /** * @description dom 객체의 id * @property id * @public * @type {String} */ this.id = this.get('id') || DU.id(this.dom); if(El.cache[this.id]) { return El.cache[this.id]; } }; DU.LElement.prototype = { /** * @description Element instance에 의해 지원되는 Dom event들. * @property DOM_EVENTS * @type Object */ DOM_EVENTS: null, /** * @description visibilityMode가 true면 visibility에 설정 false거나 없으면 display에 설정한다. * @property visibilityMode * @private * @type {Boolean} */ visibilityMode : true, /** * 해당 element가 border box를 사용하는 경우 다양한 css rule/브라우저들을 테스트한다. * @return {Boolean} */ isBorderBox : function(){ return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || DU.isBorderBox; }, /** * @description CSS Selector로 child 객체를 배열로 리턴한다. * @method select * @param {String} selector CSS selector 문자열 * @param {Boolean} firstOnly 찾은 객체의 무조건 첫번째 객체를 리턴한다. * @return {DU.LElementList} DU.LElementList 객체 리턴 */ select : function(selector, firstOnly) { var n = DU.util.LDomSelector.query(selector, this.dom, firstOnly); return this.createElementList(n); }, /** * @description CSS Selector로 child 객체를 배열로 리턴한다. * @method query * @param {String} selector CSS selector 문자열 * @param {Boolean} firstOnly 찾은 객체의 무조건 첫번째 객체를 리턴한다. * @return {Array} Array 객체 리턴 */ query : function(selector, firstOnly) { return DU.util.LDomSelector.query(selector, this.dom, firstOnly); }, /** * @description CSS Selector로 현재 node중 selector로 지정된 child node만 배열로 리턴한다. * @method filter * @param {String} selector CSS selector 문자열 * @return {Array} Array 객체 리턴 */ filter : function(selector) { var nodes = this.query('*'); return this.createElementList(DU.util.LDomSelector.filter(nodes, selector)); }, /** * @description list에 해당되는 배열을 LElement 개체의 배열인 LElementList객체로 만들어 리턴한다. * @method createElementList * @param {Array} list list 객체 * @return {DU.LElementList} */ createElementList : function(list) { var element = []; if(DU.isArray(list)) { DU.each(list, function(child) { element.push(DU.get(child)); }); } else { element.push(DU.get(list)); } return new DU.LElementList(element); }, /** * @description Dom객체의 value값을 리턴한다. * @method getValue * @return {Object} 객체에 들어 있는 값 */ getValue : function() { return this.dom.value; }, /** * @description Dom객체의 value값을 저장한다. * @method setValue * @param {Object} value 저장할 결과값 * @return void */ setValue : function(value) { this.dom.value = value; }, /** * @description HTMLElement method를 위한 Wrapper. * @method getElementsByTagName * @param {String} tag collect 할 tag 이름 * @return {HTMLCollection} DOM element들의 collection. */ getElementsByTagName: function(tag) { return this.get('element').getElementsByTagName(tag); }, /** * @description HTMLElement method를 위한 Wrapper. * @method hasChildNodes * @return {Boolean} element가 child node들을 가지고 있는지에 대한 여부 */ hasChildNodes: function() { return this.get('element').hasChildNodes(); }, /** * @description element의 구체적인 attribute들을 등록한다.. * @method initAttributes * @private * @param {Object} map 초기 attribute config들의 key-value map */ initAttributes: function(map) { }, /** * @description 주어진 event에 대해 listener를 추가한다. * 이것은 DOM이나 customEvent listener들이 될 수 있다. * frieEvent를 통해 발생한 어떤 event들도 listen될 수 있다. * 모든 handler들은 event object를 받는다. * @method addListener * @param {String} type listen될 event의 이름 * @param {Function} fn event가 발생할때 호출할 handler * @param {Any} obj handler에게 전달할 변수 * @param {Object} scope handler의 scope를 위해 사용할 object */ addListener: function(type, fn, obj, scope) { var el = this.get('element') || this.get('id'); scope = scope || this; var self = this; if (!this._events[type]) { // create on the fly if (el && this.DOM_EVENTS[type]) { DU.util.LEvent.addListener(el, type, function(e) { if (e.srcElement && !e.target) { // supplement IE with target e.target = e.srcElement; } self.fireEvent(type, e); }, obj, scope); } this.createEvent(type, this); } return DU.util.LEventProvider.prototype.on.apply(this, arguments); // notify via customEvent }, /** * @description addListener를 위한 alias * @method on * @param {String} type listen될 event의 이름 * @param {Function} fn event가 발생할때 호출할 handler * @param {Any} obj handler에게 전달할 변수 * @param {Object} scope handler의 scope를 위해 사용할 object */ on: function() { return this.addListener.apply(this, arguments); }, /** * @description event listener를 삭제한다. * @method removeListener * @param {String} type listen될 event의 이름 * @param {Function} fn event가 발생할때 호출할 handler */ removeListener: function(type, fn) { return this.unOn.apply(this, arguments); }, /** * @description event listener를 삭제한다. * @method unOn * @param {String} type listen될 event의 이름 * @param {Function} fn event가 발생할때 호출할 handler */ unOn: function(type, fn) { return DU.util.LEventProvider.prototype.unOn.apply(this, arguments); // notify via customEvent }, /** * @description event listener를 삭제한다. * @method unOnAll */ unOnAll: function() { return DU.util.LEventProvider.prototype.unOnAll.apply(this, arguments); // notify via customEvent }, /** * @description Dom method를 위한 Wrapper. * @method addClass * @param {String} className 추가할 className * @return {DU.LElement} this */ addClass: function(className) { Dom.addClass(this.get('element'), className); return this; }, /** * @description Dom method를 위한 Wrapper. * @method hasClass * @param {String} className The className to add * @return {Boolean} element가 class name을 가지고 있는지에 대한 여부 */ hasClass: function(className) { return Dom.hasClass(this.get('element'), className); }, /** * @description Dom method를 위한 Wrapper. * @method removeClass * @param {String|Array} className 삭제할 className * @return {DU.LElement} this */ removeClass: function(className) { var cnList = DU.isArray(className) ? className : [className]; DU.util.LArray.each(cnList, function(cn) { Dom.removeClass(this.get('element'), cn); }, this); return this; }, /** * @description Dom method를 위한 Wrapper. * @method getElementsByClassName * @param {String} className collect할 className * @param {String} tag (optional) class name과 함께 사용할 태그 * @return {Array} HTMLElement들의 array */ getElementsByClassName: function(className, tag) { return Dom.getElementsByClassName(className, tag, this.get('element') ); }, /** * @description Dom method를 위한 Wrapper. * @method setStyle * @param {String} property 설정할 style property * @param {String} value style property에 적용할 값 */ setStyle: function(property, value) { var el = this.get('element'); if (!el) { return this._queue[this._queue.length] = ['setStyle', arguments]; } return Dom.setStyle(el, property, value); // TODO: always queuing? }, /** * @description Dom method를 위한 Wrapper. * @method getStyle * @param {String} property 조회할 style property * @return {String} property의 현재값 */ getStyle: function(property) { return Dom.getStyle(this.get('element'), property); }, /** * @description queue set 호출을 적용한다. * @method fireQueue */ fireQueue: function() { var queue = this._queue; for (var i = 0, len = queue.length; i < len; ++i) { this[queue[i][0]].apply(this, queue[i][1]); } }, /** * @description attribute의 현재 value를 반환한다. * @method get * @param {String} key value가 반환될 attribute. * @return {Any} attribute의 현재 value. */ get: function(key) { var configs = this._configs || {}; var el = configs.element; // avoid loop due to 'element' if (el && !configs[key] && !DU.isUndefined(el.value[key]) ) { return el.value[key]; } return LAttributeProvider.prototype.get.call(this, key); }, /** * @description config의 값을 설정한다. * @method set * @param {String} key attribute의 이름 * @param {Any} value attribute에 적용할 값 * @param {Boolean} silent change event들에 대한 억제 여부 * @return {Boolean} 값이 설정되었는지에 대한 여부 */ set: function(key, value, silent) { var el = this.get('element'); if (!el) { this._queue[this._queue.length] = ['set', arguments]; if (this._configs[key]) { this._configs[key].value = value; // so "get" works while queueing } return; } // set it on the element if not configured and is an HTML attribute if ( !this._configs[key] && !DU.isUndefined(el[key]) ) { _registerHTMLAttr.call(this, key); } return LAttributeProvider.prototype.set.apply(this, arguments); }, /** * @description attribute instance의 property들을 설정하거나 업데이트한다. * @method setAttributeConfig * @param {String} key attribute의 이름. * @param {Object} map attribute propwerty들의 key-value map * @param {Boolean} init 해당 attribute가 초기 config가 되어야할지에 대한 여부. * @return {void} */ setAttributeConfig: function(key, map, init) { var el = this.get('element'); if (el && !this._configs[key] && !DU.isUndefined(el[key]) ) { _registerHTMLAttr.call(this, key, map); } else { LAttributeProvider.prototype.setAttributeConfig.apply(this, arguments); } this._configOrder.push(key); }, /** * @description attribute의 property들을 반환한다. * @method getAttributeConfig * @param {String} key attribute의 이름 * @private * @return {object} 모든 attribute의 property들을 포함하고 있는 key-value map. */ getAttributeKeys: function() { var el = this.get('element'); var keys = LAttributeProvider.prototype.getAttributeKeys.call(this); //add any unconfigured element keys for (var key in el) { if (!this._configs[key]) { keys[key] = keys[key] || el[key]; } } return keys; }, createEvent: function(type, scope) { this._events[type] = true; LAttributeProvider.prototype.createEvent.apply(this, arguments); }, init: function(el, attr) { _initElement.apply(this, arguments); }, /** * @description 전달된 simple selector의 match를 위한 현재 node와 parent node를 찾는다.(예: div.some-class or span:first-child) * @method findParent * @param {String} selector test를 위한 simple selector * @param {Number/Mixed} maxDepth (optional) element나 number로서 검색하기 위한 depth max값 * (defaults to 10 || document.body) * @return {HTMLElement} 매치되는 DOM node(매치되는 값을 찾지 못하면 null) */ findParent : function(simpleSelector, maxDepth){ return DU.get(Dom.findParent(this.dom, simpleSelector, maxDepth)); }, /** * @description 전달된 simple selector의 match를 위한 parent node들을 찾는다.(예: div.some-class or span:first-child) * @method findParentNode * @param {String} selector test를 위한 simple selector * @param {Number/Mixed} maxDepth (optional) element나 number로서 검색하기 위한 depth max값 * (defaults to 10 || document.body) * @param {Boolean} returnEl (optional) DOM node 대신 DU.LElement object를 반환하기 위해서는 True * @return {HTMLElement} 매치되는 DOM node(매치되는 값을 찾지 못하면 null) */ findParentNode : function(simpleSelector, maxDepth, returnEl){ return Dom.findParentNode(this.dom, simpleSelector, maxDepth, returnEl); }, matchNode : function(dir, start, selector, returnDom){ var n = this.dom[start]; while(n){ if(n.nodeType == 1 && (!selector || DU.util.LDomSelector.test(n, selector))){ return !returnDom ? DU.get(n) : n; } n = n[dir]; } return null; }, /** * @description 해당 element에 대한 parent node를 가져오고, 부가적으로 일치하는 selector를 찾는다. * @method parent * @param {String} selector (optional) 전달된 simple selector와 일치하는 parent node를 찾는다. * @param {Boolean} returnDom (optional) DU.LElement 대신 raw dom node를 반환하기 위해서는 True * @return {DU.LElement/HTMLElement} parent node 혹은 null */ parent : function(selector, returnDom){ return this.matchNode('parentNode', 'parentNode', selector, returnDom); }, /** * @description HTMLElement method를 위한 Wrapper. * @method insertBefore * @param {HTMLElement} element 삽입할 HTMLElement * @param {HTMLElement} before element 이전에 삽입할 HTMLElement * @return {DU.LElement} this */ insertBefore: function(element) { DU.util.LDom.insertBefore(element, this.dom); return this; }, /** * @description reference node의 다음 sibling으로서 새로운 node를 삽입한다. * @method insertAfter * @param {String | HTMLElement} newNode 삽입될 node * @param {String | HTMLElement} referenceNode 새로운 node 다음에 삽입할 node * @return {DU.LElement} this */ insertAfter: function(newNode) { DU.util.LDom.insertAfter(newNode, this.dom); return this; }, /** * @description DOM으로 부터 해당 element를 삭제하고 캐시로부터 그것을 지운다. * @method remove * @return {void} */ remove : function(){ this.removeNode(); delete El.cache[this.dom.id]; }, /** * @description document로 부터 DOM node를 삭제한다. body node는 전달될 경우 무시될 것이다. * @method removeNode * @param {HTMLElement} node 삭제할 node * @return {void} */ removeNode : function(){ return Dom.removeNode(this.dom); }, /** * @description HTMLElement method를 위한 Wrapper. * @method appendChild * @param {DU.LElement || HTMLElement} child 추가할 element. * @return {HTMLElement} 추가된 DOM element. */ appendChild: function(child) { child = child.get ? child.get('element') : child; child = child.dom ? child.dom : child; this.get('element').appendChild(child); return this; }, /** * @description element의 height offset을 반환한다. * @method getHeight * @public * @param {Boolean} contentHeight (optional) height의 border, padding의 음수값을 가져오려면 True * @return {Number} element의 height */ getHeight : function(contentHeight){ var h = this.dom.offsetHeight || 0; h = !contentHeight ? h : h - this.getBorderWidth("tb") - this.getPadding("tb"); return h < 0 ? 0 : h; }, /** * @description element의 width offset을 반환한다. * @method getWidth * @public * @param {Boolean} contentWidth (optional) width의 border, padding의 음수값을 가져오려면 True * @return {Number} element의 width */ getWidth : function(contentWidth){ var w = this.dom.offsetWidth || 0; w = !contentWidth ? w : w - this.getBorderWidth("lr") - this.getPadding("lr"); return w < 0 ? 0 : w; }, // private (needs to be called => addStyles.call(this, sides, styles)) addStyles : function(sides, styles){ var val = 0; DU.each(sides.match(/\w/g), function(s) { if (s = parseInt(this.getStyle(styles[s]), 10)) { val += Math.abs(s); } }, this); return val; }, /** * @description 특정 side에 대한 border의 width를 가져온다. * @method getBorderWidth * @public * @param {String} side 여러값들을 추가하기 위해 t, l, r, b나 이런 것들의 어떤 조합도 가능하다. * 예를 들어, 'lr'을 전달하면 left width + the border right width를 가져올 것이다. * @return {Number} 추가적으로 전달된 side들의 width */ getBorderWidth : function(side){ return this.addStyles.call(this, side, DU.LElement.borders); }, /** * @description 특정 side에 대한 padding의 width를 가져온다. * @method getPadding * @public * @param {String} side 여러값들을 추가하기 위해 t, l, r, b나 이런 것들의 어떤 조합도 가능하다. * 예를 들어, 'lr'을 전달하면 left width + the border right width를 가져올 것이다. * @return {Number} 추가적으로 전달된 side들의 padding */ getPadding : function(side){ return this.addStyles.call(this, side, DU.LElement.paddings); }, getAnimation : function(anim, domId) { var currAnim = null; if(typeof anim == 'object' && anim.type) { var animation = eval('DU.animation.' + anim.type); try{ currAnim = new animation(domId, anim, anim.duration, anim.method); if(anim.callback) currAnim.onComplete.on(anim.callback, DU.get(domId), true); } catch(e) {} } return currAnim; }, /** * @description visMode가 true면 visibility에 설정 false거나 없으면 display에 설정한다. * @method setVisibilityMode * @param {Boolean} visMode visibility로 설정할지 display로 설정할지 결정하는 값 * @return {DU.LElement} this */ setVisibilityMode : function(visMode) { this.visibilityMode = visMode; return this; }, /** * @description 객체를 보이게 설정하는 메소드 * @method show * @public * @param {Boolean|DU.animation.LAnim} anim (optional) Animation 여부를 설정한다. Boolean값이면 디폴트 animation을 실행하고 객체면 해당 객체에 설정된 animation을 수행한다. * @return {DU.LElement} this */ show : function(anim) { if(this.dom) { var width = this.dom.ordWidth || this.getWidth(true); if(anim) { if(anim === true) anim = { width: { from:0, to: width } }; anim = DU.applyIf(anim, { type: 'LAnim' }); } var currAnim = this.getAnimation(anim, this.dom.id); if(currAnim != null) { currAnim.onComplete.on(DU.util.LFunction.createDelegate(function() { this._show(); }, this)); currAnim.animate(); } else this._show(); } return this; }, _show: function() { this.visibilityMode ? this.removeClass('L-hide-visibility') : this.removeClass('L-hide-display'); }, /** * @description 객체를 안보이게 설정하는 메소드 * @method hide * @public * * @param {Boolean|DU.animation.LAnim} anim (optional) Animation 여부를 설정한다. Boolean값이면 디폴트 animation을 실행하고 객체면 해당 객체에 설정된 animation을 수행한다. * @return {DU.LElement} this */ hide : function(anim) { if(this.dom) { var width = this.getWidth(true); this.dom.ordWidth = width; if(anim) { if(anim === true) anim = { width: { from:width, to: 0 } }; anim = DU.applyIf(anim, { type: 'LAnim' }); } var currAnim = this.getAnimation(anim, this.dom.id); if(currAnim != null) { currAnim.onComplete.on(DU.util.LFunction.createDelegate(function() { this._hide(); }, this)); currAnim.animate(); } else { this._hide(); } } return this; }, _hide : function() { this.visibilityMode ? this.addClass('L-hide-visibility') : this.addClass('L-hide-display'); }, /** * @description 객체가 보이는지 여부를 확인하는 메소드 * @method isShow * @public * @return {void} */ isShow : function() { if(this.dom) { return (this.dom.style.visibility === 'hidden' || this.dom.style.display === 'none')? false : true; } else { return false; } }, /** * @description innerHTML에 html 내용을 채워넣는 메소드 * @method html * @public * @param {String} html innerHTML 넣은 HTML 문자열 * @return {DU.LElement} this */ html : function(html) { if(this.dom) this.dom.innerHTML = html; return this; }, /** * @description innerHTML에 html 내용을 리턴하는 메소드 * @method getHtml * @public * @return {String} html 내용 */ getHtml : function() { if(this.dom) return this.dom.innerHTML; else return ''; }, /** * @description 출력상태가 show이면 hide로 hide면 show로 변경한다. * @method toggle * @return {DU.Elemnent} this */ toggle : function(ani) { var me = this; me.isShow() ? me.hide() : me.show(); }, /** * @description 마우스가 Element에 안밖으로 움직일때 전달된 함수들을 호출하기 위한 event 핸들러를 설정한다. * @method hover * @param {Function} overFn 마우스가 Element에 들어갔을해 호출할 함수 * @param {Function} outFn 마우스가 Element에서 나왔을때 호출할 함수 * @param {Object} scope (optional) 함수들이 실행될 scope(this reference). * 기본적으로 Element의 DOM element. * @param {Object} options (optional) listener를 위한 옵션들. * {@link DU.util.LEventProvider#addListener the options parameter} 를 참조. * @return {DU.LElement} this */ hover : function(overFn, outFn, scope, options){ var me = this; me.on('mouseenter', overFn, scope || me.dom, options); me.on('mouseleave', outFn, scope || me.dom, options); return me; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.LElement ' + this.id; } }; var _initElement = function(el, attr) { this._queue = this._queue || []; this._events = this._events || {}; this._configs = this._configs || {}; this._configOrder = []; attr = attr || {}; attr.element = attr.element || el || null; this.DOM_EVENTS = { 'click': true, 'dblclick': true, 'keydown': true, 'keypress': true, 'keyup': true, 'mouseenter': true, 'mouseleave': true, 'mousedown': true, 'mousemove': true, 'mouseout': true, 'mouseover': true, 'mouseup': true, 'focus': true, 'blur': true, 'submit': true, // select 태그에 변경 이벤트가 처리되지 않아 추가함. 'change':true }; var isReady = false; // to determine when to init HTMLElement and content if (typeof attr.element === 'string') { // register ID for get() access _registerHTMLAttr.call(this, 'id', { value: attr.element }); } if (Dom.get(attr.element)) { isReady = true; _initHTMLElement.call(this, attr); _initContent.call(this, attr); } DU.util.LEvent.onAvailable(attr.element, function() { if (!isReady) { // otherwise already done _initHTMLElement.call(this, attr); } this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) }); }, this, true); DU.util.LEvent.onContentReady(attr.element, function() { if (!isReady) { // otherwise already done _initContent.call(this, attr); } this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) }); }, this, true); }; var _initHTMLElement = function(attr) { /** * @description Element instance가 참조할 HTMLElement. * @attribute element * @type HTMLElement */ this.setAttributeConfig('element', { value: Dom.get(attr.element), readOnly: true }); }; var _initContent = function(attr) { this.initAttributes(attr); this.setAttributes(attr, true); this.fireQueue(); }; /** * @description property에 값을 설정하고 beforeChange와 change event를 발생시틴다. * @private * @method _registerHTMLAttr * @param {DU.LElement} element config에 등록할 Element instance. * @param {String} key 등록할 config의 이름 * @param {Object} map config 인자의 key-value map */ var _registerHTMLAttr = function(key, map) { var el = this.get('element'); map = map || {}; map.name = key; map.method = map.method || function(value) { if (el) { el[key] = value; } }; map.value = map.value || el[key]; this._configs[key] = new DU.util.LAttribute(map, this); }; /** * Fires when the Element's HTMLElement can be retrieved by Id. *

    See: Element.addListener

    *

    Event fields:
    * <String> type available
    * <HTMLElement> * target the HTMLElement bound to this Element instance
    *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('available', handler);

    * @event available */ /** * Fires when the Element's HTMLElement subtree is rendered. *

    See: Element.addListener

    *

    Event fields:
    * <String> type contentReady
    * <HTMLElement> * target the HTMLElement bound to this Element instance
    *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('contentReady', handler);

    * @event contentReady */ /** * Fires before the Element is appended to another Element. *

    See: Element.addListener

    *

    Event fields:
    * <String> type beforeAppendTo
    * <HTMLElement/Element> * target the HTMLElement/Element being appended to *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('beforeAppendTo', handler);

    * @event beforeAppendTo */ /** * Fires after the Element is appended to another Element. *

    See: Element.addListener

    *

    Event fields:
    * <String> type appendTo
    * <HTMLElement/Element> * target the HTMLElement/Element being appended to *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('appendTo', handler);

    * @event appendTo */ DU.augment(DU.LElement, LAttributeProvider); // speedy lookup for elements never to box adjust var noBoxAdjust = DU.isStrict ? { select:1 } : { input:1, select:1, textarea:1 }; if(DU.browser.msie || DU.browser.gecko){ noBoxAdjust['button'] = 1; } })(); /** * @description DU.LElement onject들을 조회하기 위한 Static method. *

    이 method는 {@link DU.Component Component}들을 조회하지 않는다. * 이 method는 DOM element들을 캡슐화 하는 DU.Element object들을 조회한다. * 그것의 ID로 Component를 조회하기 위하여, {@link DU.ComponentMgr#get}를 사용한다.

    *

    같은 object를 일관되게 반환하기 위하여 simple 캐싱을 사용한다. * 만약 object가 AJAX나 DOM을 통해 똑같은 id로 재생성되었으면, 자동적으로 수정한다.

    * @param {Mixed} el DOM node나 존재하고 있는 Element의 id. * @return {Element} {@link DU.LElement Element} object나 맞는 element를 찾지 못했으면 null * @static */ var El = DU.LElement; var docEl; El.get = function(el){ var ex, elm, id; if(!el){ return null; } if(typeof el == "string"){ // element id if(!(elm = document.getElementById(el))){ return null; } if(ex = El.cache[el]){ ex.dom = elm; }else{ ex = El.cache[el] = new DU.LElement(elm); } return ex; }else if(el.tagName){ // dom element ex = new El(el); return ex; }else if(el instanceof El){ if(el != docEl){ el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid, // catch case where it hasn't been appended } return el; }else if(el.isComposite){ return el; }else if(DU.isArray(el)){ var obj = []; for(var i = 0 ; i < el.length ; i++) obj.push(new DU.LElement(el[i])); return obj; //El.select(el); }else if(el == document){ // create a bogus element object representing the document object if(!docEl){ var f = function(){}; f.prototype = El.prototype; docEl = new f(); docEl.dom = document; } return docEl; } return null; }; DU.get = El.get; El.cache = {}; El.PADDING = "padding"; El.MARGIN = "margin"; El.BORDER = "border"; El.LEFT = "-left"; El.RIGHT = "-right"; El.TOP = "-top"; El.BOTTOM = "-bottom"; El.WIDTH = "-width"; // special markup used throughout Ext when box wrapping elements El.borders = {l: El.BORDER + El.LEFT + El.WIDTH, r: El.BORDER + El.RIGHT + El.WIDTH, t: El.BORDER + El.TOP + El.WIDTH, b: El.BORDER + El.BOTTOM + El.WIDTH}; El.paddings = {l: El.PADDING + El.LEFT, r: El.PADDING + El.RIGHT, t: El.PADDING + El.TOP, b: El.PADDING + El.BOTTOM}; El.margins = {l: El.MARGIN + El.LEFT, r: El.MARGIN + El.RIGHT, t: El.MARGIN + El.TOP, b: El.MARGIN + El.BOTTOM}; El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i; ( function() { /** * Element 객체를 Chain 구조로 처리하는 객체 * @class LElementList * @constructor LElementList * @param o {HTMLElement} The html element that */ DU.LElementList = function(o) { /** * @description element의 배열 객체 * @property elements * @private * @type {Array} */ this.elements = []; /** * @description element의 배열 갯수 * @property length * @private * @type {Int} */ this.length = 0; this.add(o); }; DU.LElementList.prototype = { /** * @description Element객체 추가 * @method add * @param {DU.LElement} els 추가할 Element 객체 * @return {DU.LElementList} DU.LElementList 객체 리턴 */ add : function(els) { if (els) { if (!DU.isArray(els)) { this.elements = this.elements.concat(els); } else { var yels = this.elements; DU.each(els, function(e) { yels.push(e); }); } this.length = this.elements.length; } return this; }, /** * @description index에 대한 Element객체 리턴 * @method getAt * @param {Int} index 리턴할 위치 * @return {DU.LElement} DU.LElement 객체 리턴 */ getAt : function(index) { var me = this; if(!me.elements[index]){ return null; } return me.elements[index]; }, /** * @description each 메소드 * @method each * @private * @param {Function} fn 비교할 Function * @param {Object} scope this로 인식할 객체 정보 * @return {DU.LElementList} DU.LElementList 객체 리턴 */ each : function(fn, scope){ var me = this; DU.each(me.elements, function(e,i) { return fn.call(scope || e, e, me, i); }); return me; }, /** * @description 배열 초기화 메소드 * @method clear * @return {DU.LElementList} DU.LElementList 객체 리턴 */ clear : function(){ this.elements = []; this.length = 0; return this; }, /** * @description item들의 dom select 메소드 * @method select * @private * @param {String} selector selector 문장 * @param {Boolean} firstOnly 첫번째 Dom을 리턴할지 여부 * @return {DU.LElementList} DU.LElementList 객체 리턴 */ select : function(selector, firstOnly) { var newElement = []; this.each(function(item) { var me = item.select(selector, firstOnly); newElement = newElement.concat(me.elements || me); }, this.elements); return new DU.LElementList(newElement); }, /** * @description item들의 dom query 메소드 * @method query * @private * @param {String} selector selector 문장 * @param {Boolean} firstOnly 첫번째 Dom을 리턴할지 여부 * @return {DU.LElementList} DU.LElementList 객체 리턴 */ query : function(selector, firstOnly) { var newElement = []; this.each(function(item) { var me = item.query(selector, firstOnly); newElement = newElement.concat(me); }, this.elements); return new DU.LElementList(newElement); }, /** * @description item들의 dom filter 메소드 * @method filter * @private * @param {String} selector selector 문장 * @return {DU.LElementList} DU.LElementList 객체 리턴 */ filter : function(selector) { var newElement = []; this.each(function(item) { var me = item.filter(selector); newElement = newElement.concat(me.elements || me); }, this.elements); return new DU.LElementList(newElement); }, /** * @description LElement 배열을 리턴한다. * @method toArray * @return {Array} */ toArray: function() { return this.elements; }, /** * @description 현재 item들이 selector에 맞는 객체인지 확인하는 메소드 * @method selector * @private * @param {String} selector selector 문장 * @return {Boolean} */ test : function(selector) { var isTest = true; this.each(function(item) { var isV = item.test(selector); isTest = isV; return !isTest ? false:true; }, this.elements); return isTest; }, invoke : function(fn, args){ var els = this.elements, el = this.el; DU.each(els, function(e) { DU.LElement.prototype[fn].apply(e, args); }, this); return this; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.LElementList '; } }; })(); (function(){ var fnName, ElProto = DU.LElement.prototype, CelProto = DU.LElementList.prototype; for(var fnName in ElProto){ if(DU.isFunction(ElProto[fnName])){ (function(fnName){ CelProto[fnName] = CelProto[fnName] || function(){ return this.invoke(fnName, arguments); }; }).call(CelProto, fnName); } }; })(); /** * select 모듈은 CSS3 selector들이 DOM element들과 함께 사용될 수 있게 도움을 주는 method들을 제공한다. * @module util * @title Selector Utility * @namespace DU.util * @requires DU, dom */ (function() { /** * DOM element들의 collecting과 filtering에 도움을 주는 method들을 제공한다. * @namespace DU.util * @class LDomSelector * @static */ var LDomSelector = function() {}; var Y = DU.util; var reNth = /^(?:([-]?\d*)(n){1}|(odd|even)$)*([-+]?\d*)$/; var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g; LDomSelector.prototype = { /** * query들을 사용하기 위한 기본 document * @property document * @type object * @default window.document */ document: window.document, /** * 일반적으로 JS 예약어와 충돌하는 HTMLAttibute를 해결하기 위하여, * alias로 attribute를 매핑한다. * @property attrAliases * @type object */ attrAliases: { }, /** * attribute selector에 대응되는 shorthand 토큰의 매핑 * @property shorthand * @type object */ shorthand: { //'(?:(?:[^\\)\\]\\s*>+~,]+)(?:-?[_a-z]+[-\\w]))+#(-?[_a-z]+[-\\w]*)': '[id=$1]', '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]', '\\.(-?[_a-z]+[-\\w]*)': '[class~=$1]' }, /** * 연산자의 목록과 대응되는 boolean 함수들. * 이 함수들은 attribute와 attribute의 현재 node값이 전달된다. * @property operators * @type object */ operators: { '=': function(attr, val) { return attr === val; }, // Equality '!=': function(attr, val) { return attr !== val; }, // Inequality '~=': function(attr, val) { // Match one of space seperated words var s = ' '; return (s + attr + s).indexOf((s + val + s)) > -1; }, '|=': function(attr, val) { return getRegExp('^' + val + '[-]?').test(attr); }, // Match start with value followed by optional hyphen '^=': function(attr, val) { return attr.indexOf(val) === 0; }, // Match starts with value '$=': function(attr, val) { return attr.lastIndexOf(val) === attr.length - val.length; }, // Match ends with value '*=': function(attr, val) { return attr.indexOf(val) > -1; }, // Match contains value as substring '': function(attr, val) { return attr; } // Just test for existence of attribute }, /** * pseudo 클래스들의 목록과 대응되는 boolean 함수들. * 이 함수들은 현재 node와 pseudo 정규식으로 parsing되는 어떤값들로 호출이 된다. * @property pseudos * @type object */ pseudos: { 'root': function(node) { return node === node.ownerDocument.documentElement; }, 'nth-child': function(node, val) { return getNth(node, val); }, 'nth-last-child': function(node, val) { return getNth(node, val, null, true); }, 'nth-of-type': function(node, val) { return getNth(node, val, node.tagName); }, 'nth-last-of-type': function(node, val) { return getNth(node, val, node.tagName, true); }, 'first-child': function(node) { return getChildren(node.parentNode)[0] === node; }, 'last-child': function(node) { var children = getChildren(node.parentNode); return children[children.length - 1] === node; }, 'first-of-type': function(node, val) { return getChildren(node.parentNode, node.tagName.toLowerCase())[0]; }, 'last-of-type': function(node, val) { var children = getChildren(node.parentNode, node.tagName.toLowerCase()); return children[children.length - 1]; }, 'only-child': function(node) { var children = getChildren(node.parentNode); return children.length === 1 && children[0] === node; }, 'only-of-type': function(node) { return getChildren(node.parentNode, node.tagName.toLowerCase()).length === 1; }, 'empty': function(node) { return node.childNodes.length === 0; }, 'not': function(node, simple) { return !LDomSelector.test(node, simple); }, 'contains': function(node, str) { var text = node.innerText || node.textContent || ''; return text.indexOf(str) > -1; }, 'checked': function(node) { return node.checked === true; } }, /** * 제공된 node가 제공된 selector와 일치하는지 테스트한다. * @method test * * @param {HTMLElement | String} node 테스트될 HTMLElement로의 id나 node reference * @param {string} selector node를 상대로 테스트할 CSS LDomSelector * @return{boolean} node가 selector와 일치하는지에 대한 여부 * @static */ test: function(node, selector) { node = LDomSelector.document.getElementById(node) || node; //node = typeof node == "string" ? LDomSelector.document.getElementById(node) : node; if (!node) { return false; } var groups = selector ? selector.split(',') : []; if (groups.length) { for (var i = 0, len = groups.length; i < len; ++i) { if ( rTestNode(node, groups[i]) ) { // passes if ANY group matches return true; } } return false; } return rTestNode(node, selector); }, /** * 주어진 CSS selector에 기반한 node들의 집합을 filtering. * @method filter * * @param {array} nodes filter할 node/id들의 집합 * @param {string} selector 각 node를 테스트 하기 위해 사용되는 selector * @return{array} 주어진 selector와 일치하는 제공된 array로 부터의 node들의 array * @static */ filter: function(nodes, selector) { nodes = nodes || []; var node, result = [], tokens = tokenize(selector); if (!nodes.item) { // if not HTMLCollection, handle arrays of ids and/or nodes for (var i = 0, len = nodes.length; i < len; ++i) { if (!nodes[i].tagName) { // tagName limits to HTMLElements node = LDomSelector.document.getElementById(nodes[i]); if (node) { // skip IDs that return null nodes[i] = node; } else { } } } } result = rFilter(nodes, tokenize(selector)[0]); clearParentCache(); return result; }, /** * 주어진 CSS selector에 기반한 node들의 집합을 받는다. * @method query * * @param {string} selector 노드를 상대로 테스트할 CSS LDomSelector * @param {HTMLElement | String} root (optional) 쿼리가 시작할 id나 HTMLElement. 기본은 LDomSelector.document. * @param {Boolean} firstOnly (optional) 첫번째 일치값만 반환할지에 대한 여부 * @return {Array} 주어진 selector와 일치하는 node들의 array * @static */ query: function(selector, root, firstOnly) { var result = query(selector, root, firstOnly); return result; } }; var query = function(selector, root, firstOnly, deDupe) { var result = (firstOnly) ? null : []; if (!selector) { return result; } var groups = selector.split(','); // TODO: handle comma in attribute/pseudo if (groups.length > 1) { var found; for (var i = 0, len = groups.length; i < len; ++i) { found = arguments.callee(groups[i], root, firstOnly, true); result = firstOnly ? found : result.concat(found); } clearFoundCache(); return result; } if (root && !root.nodeName) { // assume ID root = LDomSelector.document.getElementById(root); if (!root) { return result; } } root = root || LDomSelector.document; var tokens = tokenize(selector); var idToken = tokens[getIdTokenIndex(tokens)], nodes = [], node, id, token = tokens.pop() || {}; if (idToken) { id = getId(idToken.attributes); } // use id shortcut when possible if (id) { node = LDomSelector.document.getElementById(id); /* * ie에서 getElementById로 얻어와도 name객체는 찾는 문제 해결 */ if(node && node.id != id) { var findNodeList = []; findNodeList = DU.util.LDom.getAllChildrenBy(root || document, findNodeList, function(child){ if(child.id == id) return true; return false; }); node = findNodeList.length > 0 ? findNodeList[0] : node; } if (node && (root.nodeName == '#document' || contains(node, root))) { if ( rTestNode(node, null, idToken) ) { if (idToken === token) { nodes = [node]; // simple selector } else { root = node; // start from here } } } else { return result; } } if (root && !nodes.length) { nodes = root.getElementsByTagName(token.tag); } if (nodes.length) { result = rFilter(nodes, token, firstOnly, deDupe); } clearParentCache(); return result; }; var contains = function() { if (document.documentElement.contains && !DU.browser.webkit < 422) { // IE & Opera, Safari < 3 contains is broken return function(needle, haystack) { return haystack.contains(needle); }; } else if ( document.documentElement.compareDocumentPosition ) { // gecko return function(needle, haystack) { /* //compareDocumentPosition: DOM Level 3에 정의된 패런트와 차일드 관계를 나타내는 메소드 //this.compareDocumentPosition(other)를 기준으로 8진수 값 //1: 같은 document에 존재하지 않음 //2: node가 other보다 앞에 있음 //4: node가 other보다 뒤에 있음 //8: node는 other의 자손 //10: node는 other의 선조 //20: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC */ return !!(haystack.compareDocumentPosition(needle) & 16); }; } else { // Safari < 3 return function(needle, haystack) { var parent = needle.parentNode; while (parent) { if (needle === parent) { return true; } parent = parent.parentNode; } return false; }; } }(); var rFilter = function(nodes, token, firstOnly, deDupe) { var result = firstOnly ? null : []; for (var i = 0, len = nodes.length; i < len; i++) { if (! rTestNode(nodes[i], '', token, deDupe)) { continue; } if (firstOnly) { return nodes[i]; } if (deDupe) { if (nodes[i]._found) { continue; } nodes[i]._found = true; foundCache[foundCache.length] = nodes[i]; } result[result.length] = nodes[i]; } return result; }; var rTestNode = function(node, selector, token, deDupe) { token = token || tokenize(selector).pop() || {}; if (!node.tagName || (token.tag !== '*' && node.tagName.toUpperCase() !== token.tag) || (deDupe && node._found) ) { return false; } if (token.attributes.length) { var attribute; for (var i = 0, len = token.attributes.length; i < len; ++i) { if (token.attributes[i][0] == "class" || token.attributes[i][0] == "className") { attribute = node.className; } else { attribute = node.getAttribute(token.attributes[i][0], 2); } if (attribute === null || attribute === undefined) { return false; } if ( LDomSelector.operators[token.attributes[i][1]] && !LDomSelector.operators[token.attributes[i][1]](attribute, token.attributes[i][2])) { return false; } } } if (token.pseudos.length) { for (var i = 0, len = token.pseudos.length; i < len; ++i) { if (LDomSelector.pseudos[token.pseudos[i][0]] && !LDomSelector.pseudos[token.pseudos[i][0]](node, token.pseudos[i][1])) { return false; } } } return (token.previous && token.previous.combinator !== ',') ? combinators[token.previous.combinator](node, token) : true; }; var foundCache = []; var parentCache = []; var regexCache = {}; var clearFoundCache = function() { for (var i = 0, len = foundCache.length; i < len; ++i) { try { // IE no like delete delete foundCache[i]._found; } catch(e) { foundCache[i].removeAttribute('_found'); } } foundCache = []; }; var clearParentCache = function() { if (!document.documentElement.children) { // caching children lookups for gecko return function() { for (var i = 0, len = parentCache.length; i < len; ++i) { delete parentCache[i]._children; } parentCache = []; }; } else return function() {}; // do nothing }(); var getRegExp = function(str, flags) { flags = flags || ''; if (!regexCache[str + flags]) { regexCache[str + flags] = new RegExp(str, flags); } return regexCache[str + flags]; }; var combinators = { ' ': function(node, token) { while (node = node.parentNode) { if (rTestNode(node, '', token.previous)) { return true; } } return false; }, '>': function(node, token) { return rTestNode(node.parentNode, null, token.previous); }, '+': function(node, token) { var sib = node.previousSibling; while (sib && sib.nodeType !== 1) { sib = sib.previousSibling; } if (sib && rTestNode(sib, null, token.previous)) { return true; } return false; }, '~': function(node, token) { var sib = node.previousSibling; while (sib) { if (sib.nodeType === 1 && rTestNode(sib, null, token.previous)) { return true; } sib = sib.previousSibling; } return false; } }; var getChildren = function() { if (document.documentElement.children) { // document for capability test return function(node, tag) { return (tag) ? node.children.tags(tag) : node.children || []; }; } else { return function(node, tag) { if (node._children) { return node._children; } var children = [], childNodes = node.childNodes; for (var i = 0, len = childNodes.length; i < len; ++i) { if (childNodes[i].tagName) { if (!tag || childNodes[i].tagName.toLowerCase() === tag) { children[children.length] = childNodes[i]; } } } node._children = children; parentCache[parentCache.length] = node; return children; }; } }(); /* an+b = get every _a_th node starting at the _b_th 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") an+0 = get every _a_th element, "0" may be omitted */ var getNth = function(node, expr, tag, reverse) { if (tag) tag = tag.toLowerCase(); reNth.test(expr); var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) n = RegExp.$2, // "n" oddeven = RegExp.$3, // "odd" or "even" b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ result = []; var siblings = getChildren(node.parentNode, tag); if (oddeven) { a = 2; // always every other op = '+'; n = 'n'; b = (oddeven === 'odd') ? 1 : 0; } else if ( isNaN(a) ) { a = (n) ? 1 : 0; // start from the first or no repeat } if (a === 0) { // just the first if (reverse) { b = siblings.length - b + 1; } if (siblings[b - 1] === node) { return true; } else { return false; } } else if (a < 0) { reverse = !!reverse; a = Math.abs(a); } if (!reverse) { for (var i = b - 1, len = siblings.length; i < len; i += a) { if ( i >= 0 && siblings[i] === node ) { return true; } } } else { for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { if ( i < len && siblings[i] === node ) { return true; } } } return false; }; var getId = function(attr) { for (var i = 0, len = attr.length; i < len; ++i) { if (attr[i][0] == 'id' && attr[i][1] === '=') { return attr[i][2]; } } }; var getIdTokenIndex = function(tokens) { for (var i = 0, len = tokens.length; i < len; ++i) { if (getId(tokens[i].attributes)) { return i; } } return -1; }; var patterns = { tag: /^((?:-?[_a-z]+[\w-]*)|\*)/i, attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, //attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^'"\]]*)['"]?\]*/i, pseudos: /^:([-\w]+)(?:\(['"]?(.+)['"]?\))*/i, combinator: /^\s*([>+~]|\s)\s*/ }; /** Break selector into token units per simple selector. Combinator is attached to left-hand selector. */ var tokenize = function(selector) { var token = {}, // one token per simple selector (left selector holds combinator) tokens = [], // array of tokens id, // unique id for the simple selector (if found) found = false, // whether or not any matches were found this pass match; // the regex match selector = replaceShorthand(selector); // convert ID and CLASS shortcuts to attributes /* Search for selector patterns, store, and strip them from the selector string until no patterns match (invalid selector) or we run out of chars. Multiple attributes and pseudos are allowed, in any order. for example: 'form:first-child[type=button]:not(button)[lang|=en]' */ do { found = false; // reset after full pass for (var re in patterns) { if (!DU.hasOwnProperty(patterns, re)) { continue; } if (re != 'tag' && re != 'combinator') { // only one allowed token[re] = token[re] || []; } if (match = patterns[re].exec(selector)) { // note assignment found = true; if (re != 'tag' && re != 'combinator') { // only one allowed //token[re] = token[re] || []; // capture ID for fast path to element if (re === 'attributes' && match[1] === 'id') { token.id = match[3]; } token[re].push(match.slice(1)); } else { // single selector (tag, combinator) token[re] = match[1]; } selector = selector.replace(match[0], ''); // strip current match from selector if (re === 'combinator' || !selector.length) { // next token or done token.attributes = fixAttributes(token.attributes); token.pseudos = token.pseudos || []; token.tag = token.tag ? token.tag.toUpperCase() : '*'; tokens.push(token); token = { // prep next token previous: token }; } } } } while (found); return tokens; }; var fixAttributes = function(attr) { var aliases = LDomSelector.attrAliases; attr = attr || []; for (var i = 0, len = attr.length; i < len; ++i) { if (aliases[attr[i][0]]) { // convert reserved words, etc attr[i][0] = aliases[attr[i][0]]; } if (!attr[i][1]) { // use exists operator attr[i][1] = ''; } } return attr; }; var replaceShorthand = function(selector) { var shorthand = LDomSelector.shorthand; var attrs = selector.match(patterns.attributes); // pull attributes to avoid false pos on "." and "#" if (attrs) { selector = selector.replace(patterns.attributes, 'REPLACED_ATTRIBUTE'); } for (var re in shorthand) { if (!DU.hasOwnProperty(shorthand, re)) { continue; } selector = selector.replace(getRegExp(re, 'gi'), shorthand[re]); } if (attrs) { for (var i = 0, len = attrs.length; i < len; ++i) { selector = selector.replace('REPLACED_ATTRIBUTE', attrs[i]); } } return selector; }; LDomSelector = new LDomSelector(); LDomSelector.patterns = patterns; Y.LDomSelector = LDomSelector; //msie 8일 경우 className이 아니고 class이다. if (DU.browser.msie) { // rewrite class for IE (others use getAttribute('class')// && eval(DU.browser.version) < 8 Y.LDomSelector.attrAliases['class'] = 'className'; Y.LDomSelector.attrAliases['for'] = 'htmlFor'; } })(); /** * LDateLocale은 현재 en과 ko만 default 탑재되어 있다. * 다른 나라의 locale이 필요할 경우는 Project의 공통 js파일에 DU.util.LDateLocale['fr']등의 형태로 정의를 해서 탑재하면 된다. * default 탑재된 en과 ko의 locale 정보의 수정은 du_config.js의 core.dateLocale 부분에서 override할 수 있다. * LDateLocale class는 DU.util.LDate에 의해 사용되는 모든 * localised date 문자열을 위한 컨테이너이며 base 클래스 이다. * 이것은 내부적으로 사용되지만, 새로운 date localisation을 제공하기 위해 확장될 수 있다. * * 자신의 LDateLocale 생성하기 위해서, 다음 과정을 따른다: *
      *
    1. 요구와 밀접하게 일치하는 기존의 locale을 찾는다.
    2. *
    3. 이것을 base class로 사용한다. 만약 맞지 않는다면, DU.util.LDateLocale을 사용한다.
    4. *
    5. DU.merge를 사용하는 base class의 확장으로서 자신의 class를 생성하고 * 필요한 곳에 자신의 localisation들을 추가한다.
    6. *
    * DU.util.LDateLocale['en'] 부터 확장된, DU.util.LDateLocale['en_US']와 * DU.util.LDateLocale['en_GB']을 참고한다. * * 예를 들어, French 프랑스어와 Canadian 프랑스어를 위한 locale들을 구현하려면, * 다음 작업을 수행한다: *
      *
    1. French 프랑스어를 위해서는 기존의 유사한 locale이 없으므로 * 기본으로 DU.util.LDateLocale 사용하여, 그것을 확장한다: *
      
       *      DU.util.LDateLocale['fr'] = DU.merge(DU.util.LDateLocale, {
      
       *          a: ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'],
      
       *          A: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
      
       *          b: ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'],
      
       *          B: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
      
       *          c: '%a %d %b %Y %T %Z',
      
       *          p: ['', ''],
      
       *          P: ['', ''],
      
       *          x: '%d.%m.%Y',
      
       *          X: '%T'
      
       *      });
      
       *   
      *
    2. *
    3. Canadian 프랑스어를 위해서는 French 프랑스어로 시작해서 \%x의 의미를 변경한다. *
      
       *      DU.util.LDateLocale['fr-CA'] = DU.merge(DU.util.LDateLocale['fr'], {
      
       *          x: '%Y-%m-%d'
      
       *      });
      
       *   
      *
    4. *
    * * 이렇게 하면, 새로운 locale들을 사용할 수 있다: *
    
     *    var d = new Date("2008/04/22");
    
     *    DU.util.LDate.format(d, {format: "%A, %d %B == %x"}, "fr");
    
     * 
    * will return: *
    
     *    mardi, 22 avril == 22.04.2008
    
     * 
    * And *
    
     *    DU.util.LDate.format(d, {format: "%A, %d %B == %x"}, "fr-CA");
    
     * 
    * Will return: *
    
     *   mardi, 22 avril == 2008-04-22
    
     * 
    * @module util * @namespace DU.util * @requires DU * @class LDateLocale */ DU.util.LDateLocale = { a: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], A: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], b: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], B: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], c: '%a %d %b %Y %T %Z', p: ['AM', 'PM'], P: ['am', 'pm'], q: '%m%d%Y', Q: '%m%d%Y%H%M%S', r: '%I:%M:%S %p', x: '%d/%m/%Y', X: '%T' }; DU.util.LDateLocale['en'] = DU.merge(DU.util.LDateLocale, {}); DU.util.LDateLocale['en_US'] = DU.merge(DU.util.LDateLocale['en'], { c: '%a %d %b %Y %I:%M:%S %p %Z', x: '%m/%d/%Y', X: '%I:%M:%S %p' }); DU.util.LDateLocale['en_GB'] = DU.merge(DU.util.LDateLocale['en'], { r: '%l:%M:%S %P %Z' }); DU.util.LDateLocale['en_AU'] = DU.merge(DU.util.LDateLocale['en']); DU.util.LDateLocale['ko'] = DU.merge(DU.util.LDateLocale, { a: ['일', '월', '화', '수', '목', '금', '토'], A: ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'], b: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], B: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'], c: '%Y년 %b월 %d일 %a %T %Z', p: ['오전', '오후'], P: ['오전', '오후'], q: '%Y%m%d', Q: '%Y%m%d%H%M%S', x: '%Y-%m-%d', X: '%T' }); DU.util.LDateLocale['ko_KR'] = DU.merge(DU.util.LDateLocale['ko']); DU.namespace("DU.util"); /** * @description 인스턴스를 얻어오는 메소드 * @method getInstance * @public * @static * @return {DU.util.LDateLocale} */ DU.util.LDateLocale.getInstance = function(sLocale) { var dateLocale = DU.util.LDateLocale[sLocale]; var dateLocaleList = (DU.getConfig) ? DU.getConfig().getFirst('$.core.dateLocale.' + sLocale) : []; for(var key in dateLocaleList) { var val = dateLocaleList[key]; dateLocale[key] = val; } return dateLocale; } DU.util.LDateLocale.prototype = { otype : 'DU.util.LDateLocale', instanceObj : null }; /** * Represents an HTML fragment template. Templates can be precompiled for greater performance. * For a list of available format functions, see {@link DU.util.LFormat}.
    * @module core * @namespace DU * @class DU.LTemplate * @constructor * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("") */ DU.LTemplate = function(html){ var me = this, a = arguments, buf = []; if (DU.isArray(html)) { html = html.join(""); } else if (a.length > 1) { DU.each(a, function(v) { if (DU.isObject(v)) { DU.apply(me, v); } else { buf.push(v); } }); html = buf.join(''); } /**@private*/ me.html = html; if (me.compiled) { me.compile(); } }; DU.LTemplate.prototype = { /** * Returns an HTML fragment of this template with the specified values applied. * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @return {String} The HTML fragment */ applyTemplate : function(values){ var me = this; return me.compiled ? me.compiled(values) : me.html.replace(me.re, function(m, name){ return values[name] !== undefined ? values[name] : ""; }); }, /** * Sets the HTML used as the template and optionally compiles it. * @param {String} html * @param {Boolean} compile (optional) True to compile the template (defaults to undefined) * @return {DU.LTemplate} this */ set : function(html, compile){ var me = this; me.html = html; me.compiled = null; return compile ? me.compile() : me; }, /** * The regular expression used to match template variables * @type RegExp * @property */ re : /\{([\w-]+)\}/g, /** * Compiles the template into an internal function, eliminating the RegEx overhead. * @return {DU.LTemplate} this */ compile : function(){ var me = this, sep = DU.isGecko ? "+" : ","; function fn(m, name){ name = "values['" + name + "']"; return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'"; } eval("this.compiled = function(values){ return " + (DU.isGecko ? "'" : "['") + me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) + (DU.isGecko ? "';};" : "'].join('');};")); return me; }, /** * Applies the supplied values to the template and inserts the new node(s) as the first child of el. * @param {Mixed} el The context element * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @param {Boolean} returnElement (optional) true to return a DU.LElement (defaults to undefined) * @return {HTMLElement/DU.LElement} The new node or Element */ insertFirst: function(el, values, returnElement){ return this.doInsert('afterBegin', el, values, returnElement); }, /** * Applies the supplied values to the template and inserts the new node(s) before el. * @param {Mixed} el The context element * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @param {Boolean} returnElement (optional) true to return a DU.LElement (defaults to undefined) * @return {HTMLElement/DU.LElement} The new node or Element */ insertBefore: function(el, values, returnElement){ return this.doInsert('beforeBegin', el, values, returnElement); }, /** * Applies the supplied values to the template and inserts the new node(s) after el. * @param {Mixed} el The context element * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @param {Boolean} returnElement (optional) true to return a DU.LElement (defaults to undefined) * @return {HTMLElement/DU.LElement} The new node or Element */ insertAfter : function(el, values, returnElement){ return this.doInsert('afterEnd', el, values, returnElement); }, /** * Applies the supplied values to the template and appends the new node(s) to el. * @param {Mixed} el The context element * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @param {Boolean} returnElement (optional) true to return a DU.LElement (defaults to undefined) * @return {HTMLElement/DU.LElement} The new node or Element */ append : function(el, values, returnElement){ return this.doInsert('beforeEnd', el, values, returnElement); }, doInsert : function(where, el, values, returnEl){ el = DU.getDom(el); var newNode = DU.LDomHelper.insertHtml(where, el, this.applyTemplate(values)); return returnEl ? DU.get(newNode, true) : newNode; }, /** * Applies the supplied values to the template and overwrites the content of el with the new node(s). * @param {Mixed} el The context element * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @param {Boolean} returnElement (optional) true to return a DU.LElement (defaults to undefined) * @return {HTMLElement/DU.LElement} The new node or Element */ overwrite : function(el, values, returnElement){ el = DU.getDom(el); el.innerHTML = this.applyTemplate(values); return returnElement ? DU.get(el.firstChild, true) : el.firstChild; } }; /** * Alias for {@link #applyTemplate} * Returns an HTML fragment of this template with the specified values applied. * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) * @return {String} The HTML fragment * @member DU.LTemplate * @method apply */ DU.LTemplate.prototype.apply = DU.LTemplate.prototype.applyTemplate; /** * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. * @param {String/HTMLElement} el A DOM element or its id * @param {Object} config A configuration object * @return {DU.LTemplate} The created template * @static */ DU.LTemplate.from = function(el, config){ el = DU.getDom(el); return new DU.LTemplate(el.value || el.innerHTML, config || ''); };