/* * */ /* * @(#) du_core.js * build version : 1.0.0 $Revision: 15341 $ * * Copyright ⓒ LG CNS, Inc. All rights reserved. * * devon@lgcns.com * http://www.dev-on.com * * Do Not Erase This Comment!!! (이 주석문을 지우지 말것) * * dujsf/license.txt를 반드시 읽어보고 사용하시기 바랍니다. * * 1. 사내 사용시 KAMS를 통해 요청하여 사용허가를 받아야만 소프트웨어 라이센스 계약서에 동의하는 것으로 간주됩니다. * 2. DevOn RUI가 포함된 제품을 판매할 경우에도 KAMS를 통해 요청하여 사용허가를 받아야만 합니다. * 3. KAMS를 통해 사용허가를 받지 않은 경우 소프트웨어 라이센스 계약을 위반한 것으로 간주됩니다. * 4. 별도로 판매될 경우 LGCNS의 소프트웨어 판매정책을 따릅니다. (KAMS에 문의 바랍니다.) * * (주의!) 원저자의 허락없이 재배포 할 수 없으며 * LG CNS 외부로의 유출을 하여서는 안 된다. */ /** * DU 객체는 네임스페이스, 상속, 로깅을 포함하는 utility이다. * @module core * @namespace * @title DU Global */ (function(){ /** * DU global namespace object. * DU가 이미 정의된 경우, 기존에 존재하는 DU object는 정의된 namespace들이 * 보존되도록 덮어쓰여지지 않는다. * @class DU * @static */ /** * millisecond 이후에 제공된 obecjt의 컨텍스트에서 제공된 함수를 실행한다. * periodic을 true로 설정하지 않는다면, 함수를 한번만 실행한다. * @method later * @static * @since 2.4.0 * @param when {int} fn이 실행될 때까지 기다릴 millisecond 숫자 * @param {object} o 컨텍스트 object * @param {Function|String} fn 실행할 'o' object에서 실행할 함수나 method의 이름 * @param {Array} data 함수에 제공될 데이터. * 이것은 하나의 항목이나 array를 받아들인다. * array가 제공되는 경우 함수는 각 array 항목에 대해 하나의 paprameter를 가지고 실행된다. * 하나의 array parameter를 전달할 필요가 있는 경우, 이것은 array [myarray]에서 * wrapping 되어야 할 필요가 있을 것이다. * @param {boolean} periodic true인 경우 취소될 때까지 제공된 interval에서 * 주기적으로 실행된다. * @return {object} timer object. timer를 멈추기 위하여 해당 object에서 cancel() method를 호출한다. */ DU.later = function(when, o, fn, data, periodic) { when = when || 0; o = o || {}; var m=fn, d=data, f, r; if (DU.isString(fn)) { m = o[fn]; } if (!m) { throw new TypeError("method undefined"); } if (!DU.isArray(d)) { d = [data]; } f = function() { m.apply(o, d); }; r = (periodic) ? setInterval(f, when) : setTimeout(f, when); return { interval: periodic, cancel: function() { if (this.interval) { clearInterval(r); } else { clearTimeout(r); } } }; } /** * 적합한 non-null값을 찾기 위한 편리한 method. * null/undefined/NaN에 대해서는 false, 0/false/''을 포함한 다른 값에 대해서는 * true를 반환한다. * @method isValue * @static * @since 2.3.0 * @param {any} o 테스트할 항목 * @return {boolean} null/undefined/NaN이 아니라면 true */ DU.isValue = function(o) { // return (o || o === false || o === 0 || o === ''); // Infinity fails return (DU.isObject(o) || DU.isString(o) || DU.isNumber(o) || DU.isBoolean(o)); } /** * LConfiguration 객체를 리턴한다. *

    * var config = DU.getConfig();

    * var name = "$.core.defaultLocale";

    * alert(typeof config.get(name));

    * alert(config.get(name));

    * 
* @method getConfig * @static * @param {Boolean} isNew 신규 객체 여부 * @return {DU.config.LConfiguration} */ DU.getConfig = function(isNew) { isNew = isNew && isNew == true ? true : false; if(this.isUndefined(this._configuration) || isNew) { this._configuration = DU.config.LConfiguration.getInstance(); } return this._configuration; }, /** * getMessageManager 객체를 리턴한다. * @method getMessageManager * @static * @param {Object} config 환경정보 객체 * @param {Boolean} isNew 신규 객체 여부 * @return {DU.message.LMessageManager} */ DU.getMessageManager = function(config, isNew) { this._messageManager = isNew ? undefined : this._messageManager; if(this.isUndefined(this._messageManager)) { this._messageManager = new DU.message.LMessageManager(config); } return this._messageManager; }, /** * js파일을 동적으로 로딩한다. *

    * DU.includeJs('/dujsf/js/locale/lang-en.js');

    * 
* @method includeJs * @static * @return void */ DU.includeJs = function(jsFile, sync) { var oHead = document.getElementsByTagName('head')[0]; var isInclude = false; var newJsFile = jsFile; newJsFile = DU.util.LString.startsWith(newJsFile, "./") ? newJsFile.substring(1) : newJsFile; var fullUrl = location.host + newJsFile; var childNodes = oHead.childNodes; for(var i = 0 ; i < childNodes.length; i++) { var headNode = childNodes[i]; if(headNode){ var currentSrc = headNode.syncSrc || headNode.src; if(currentSrc == fullUrl) { isInclude = true; break; } } } if (isInclude == false) { var oScript = document.createElement('script'); oScript.type = 'text/javascript'; if(sync || sync == true) { var o = DU.LConnect.getConnectionObject(); o.conn.open('GET', jsFile, false); o.conn.send(null); oScript.text = o.conn.responseText; oScript.syncSrc = fullUrl; } else { oScript.src = jsFile; } oHead.appendChild(oScript); } }, /** * css파일을 동적으로 로딩한다. *

    * DU.includeCSS('/dujsf/resources/css/du_logger.css');

    * 
* @method includeCSS * @static * @return void */ DU.includeCSS = function(cssFile, charset) { charset = DU.isUndefined(charset) ? DU.getConfig().get('$.core.css.charset')[0] : charset; var oHead = document.getElementsByTagName('head')[0]; var isInclude = false; for(var i = 0 ; i < oHead.childNodes.length; i++) { if(oHead.childNodes && oHead.childNodes[i].href && oHead.childNodes[i].href == location.host + cssFile) isInclude = true; } if(isInclude == false) { var oLink = document.createElement('link'); oLink.rel = 'stylesheet'; oLink.type = 'text/css'; oLink.charset = charset; oLink.href = cssFile; oHead.appendChild(oLink); } } /** * 주어진 CSS selector에 기반한 노드들의 집합을 필터링한다. * @method filter * * @param {array} nodes 필터링할 node/id들의 집합. * @param {string} selector 각 노드를 테스트할때 사용할 selector. * @return{array} 주어진 selector와 일치하는 제공된 array로부터의 노드들의 array * @static */ DU.filter = function (nodes, selector) { return DU.util.LDomSelector.filter(nodes, selector); } })(); /** * The static String class provides helper functions to deal with data of type * Object. * @module core * @title DU Global * @namespace prototype * @class Object * @static */ /** * static String 클래스는 String 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module core * @title DU Global * @namespace prototype * @class String * @static */ /** * @description 문자열에 모든 공백 제거 * @method trimAll * @return {String} 모든 공백 제거된 문자열 */ String.prototype.trimAll = function() { return DU.util.LString.trimAll(this); } /** * @description 문자열을 주어진 길이만큼 잘라낸다. * @method cut * @param {Int} start 시작위치 * @param {Int} length 잘라낼 길이 * @return {String} 잘라낸 후 문자열 */ String.prototype.cut = function(start, length) { return DU.util.LString.cut(this, start, length); } /** * @description 문자열을 처음부터 주어진 위치까지 잘라낸다. * @method lastCut * @param {Int} pos 잘라낼 위치 * @return {String} 잘라낸 후 문자열 */ String.prototype.lastCut = function(pos) { return DU.util.LString.lastCut(this, pos); } /** * @description 시작 문자열이 pattern에 맞는지 여부를 리턴한다. * @method startsWith * @param {String} pattern 문자패턴 * @return {Boolean} 결과 */ String.prototype.startsWith = function(pattern) { return DU.util.LString.startsWith(this, pattern); } /** * @description 종료 문자열이 pattern에 맞는지 여부를 리턴한다. * @method endsWith * @param {String} s 작업 대상 문자열 * @param {String} pattern 문자패턴 * @return {Boolean} 결과 */ String.prototype.endsWith = function(pattern) { return DU.util.LString.endsWith(this, pattern); } /** * 자바스크립트의 내장 객체인 String 객체에 simpleReplace 메소드를 추가한다. simpleReplace 메소드는 * 스트링 내에 있는 특정 스트링을 다른 스트링으로 모두 변환한다. String 객체의 replace 메소드와 동일한 * 기능을 하지만 간단한 스트링의 치환시에 보다 유용하게 사용할 수 있다. *

 *     var str = "abcde"

 *     str = str.simpleReplace("cd", "xx");

 * 
* 위의 예에서 str는 "abxxe"가 된다. * @param {String} oldStr required 바뀌어야 될 기존의 스트링 * @param {String} newStr required 바뀌어질 새로운 스트링 * @return {String} replaced String. */ String.prototype.simpleReplace = function(oldStr, newStr) { return DU.util.LString.simpleReplace(this, oldStr, newStr); } /** * 자바스크립트의 내장 객체인 String 객체에 insert 메소드를 추가한다. insert 메소드는 스트링의 특정 영역에 * 주어진 스트링을 삽입한다. *

 *     var str = "abcde"

 *     str = str.insert(3, "xyz");

 * 
* 위의 예에서 str는 "abcxyzde"가 된다. * @param {Int} index required 삽입할 위치. 해당 스트링의 index 바로 앞에 삽입된다. index는 0부터 시작. * @param {String} str required 삽입할 스트링. * @return {String} inserted String. */ String.prototype.insert = function(index, str) { return this.substring(0, index) + str + this.substr(index); } /** * @description 문자열을 구분자를 통해 잘라낸다. * @method advancedSplit * @param {String} delim 구분자 * @parma {String} 옵션 I : 옵션 T: * @return {Array} */ String.prototype.advancedSplit = function(delim, options){ return DU.util.LString.advancedSplit(this, delim, options); } /** * @description 첫 문자만 대문자로 변환한다. * @method firstUpperCase * @return {String} 변환 후 문자열 */ String.prototype.firstUpperCase = function() { return DU.util.LString.firstUpperCase(this); } /** * @description 스트링의 자릿수를 Byte 단위로 환산하여 알려준다. 영문, 숫자는 1Byte이고 한글은 2Byte이다.(자/모 중에 하나만 있는 글자도 2Byte이다.) * @method getByteLength * @static * @return {Int} 스트링의 길이 */ String.prototype.getByteLength = function() { return DU.util.LString.getByteLength(this); } /** * @description 입력된 문자열이 한글로 된 정보인지를 체크한다. 해당문자열이 한글과 스페이스의 조합일때만 true를 리턴한다. * @method isHangul * @return {Boolean} 한글 여부 */ String.prototype.isHangul = function() { return DU.util.LString.isHangul(this); } /** * static Date 클래스는 Date 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module core * @title DU Global * @namespace prototype * @class Date * @static */ /** * startDate와 endDate 사이에 포함되어 있는지 여부 * @method between * @param {Date} startDate 범위 시작일자 * @param {Date} endDate 범위 종료일자The end of the range * @return {Boolean} 날짜가 비교날짜 사이에 있으면 true, 아닐 경우 false. */ Date.prototype.between = function(startDate, endDate){ return DU.util.LDate.between(this, startDate, endDate) } /** * Date객체의 Pattern에 따른 날짜 비교 * @method compareString * @param {Date} date2 Date2 객체 * @param {Date} pattern [optional] format pattern 문자 * @return {Boolean} true */ Date.prototype.compareString = function(date2, pattern){ pattern = pattern || { format: '%q' }; return DU.util.LDate.compareString(this, date2, pattern); } /** * 1일에 해당되는 Date 객체를 리턴 * @method getFirstDayOfMonth * @return {Date} The JavaScript Date representing the first day of the month */ Date.prototype.getFirstDayOfMonth = function(){ return DU.util.LDate.getFirstDayOfMonth(this); } /** * 마지막 날짜에 해당되는 Date 객체를 리턴 * @method getLastDayOfMonth * @return {Date} The JavaScript Date representing the first day of the month */ Date.prototype.getLastDayOfMonth = function(){ return DU.util.LDate.getLastDayOfMonth(this); } /** * startOfWeek에 해당되는 요일에 맞는 Date 객체를 리턴 * @method getFirstDayOfWeek * @param {Date} dt The date in the week for which the first day is required. * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0) * @return {Date} The first day of the week */ Date.prototype.getFirstDayOfWeek = function(startOfWeek){ return DU.util.LDate.getFirstDayOfWeek(this, startOfWeek); } /** * static 문자열 클래스는 number 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module core * @title DU Global * @namespace prototype * @class Array * @static */ /** * static Number 클래스는 Number 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * @module core * @title DU Global * @namespace prototype * @class Number * @static */ /** * 사용자에게 표시하기 위한 native JavaScript Number와 문자열 포맷을 가져온다. * * @method format * @param nData {Number} Number. * @param {Object} oConfig (Optional) Optional 설정 값들: *
*
prefix {String} *
현재 지정자 "$"와 같은, 각 숫자 앞에 위치하는 문자열
*
decimalPlaces {Number} *
반올림 하기 위한 소수점 위치 숫자.
*
decimalSeparator {String} *
소수 구분 기호
*
thousandsSeparator {String} *
천 단위 구분 기호
*
suffix {String} *
"items"(공백을 참고하는)와 같은 각 숫자 이후에 추가된 문자열
*
* @return {String} 표시하기 위한 Formatted number */ Number.prototype.format = function(oConfig) { return DU.util.LNumber.format(this, oConfig); } /** * @description 요구되는 소수점 정밀도로 전달되는 숫자를 반올림 한다. * @method round * @param {Int} precision 첫번째 parameter의 값을 반올림하기 위한 소수점 위치 숫자 * @return {Int} 반올림 값 */ Number.prototype.round = function(precision){ return DU.util.LNumber.round(this, precision); } /** * The static String class provides helper functions to deal with data of type * Number. * @module core * @title DU Global * @namespace prototype * @class Function * @static */ /** * @description The static String class provides helper functions to deal with data of type String * @module util * @title LString * @requires DU */ DU.namespace("DU.util"); (function(){ /** * @description LString * @namespace DU.util * @class LString * @static */ DU.applyObject(DU.util.LString, { /** * @description 캐쉬되지 않는 고유한 Url을 만든다. * @method getTimeUrl * @param {String} url Array 배열 * @return {String} QueryString형 문자열 id=ddd&pwd=ccc */ getTimeUrl: function(url){ var append = '_dc=' + (new Date().getTime()); if (url.indexOf('?') !== -1) { url += '&' + append; } else { url += '?' + append; } return url; }, /** * @description 문자열에 모든 공백 제거 * @method trimAll * @return {String} 모든 공백 제거된 문자열 */ trimAll: function(){ return this.replace(/\s*/g, ""); }, /** * @description 문자열을 구분자를 통해 잘라낸다. * @method advancedSplit * @param {String} s 문자열 * @param {String} delim 구분자 * @parma {String} 옵션 I : 옵션 T: * @return {Array} */ advancedSplit: function(s, delim, options){ if (options == null || DU.util.LString.trim(options) == "") { return s.split(delim); } var optionI = false; var optionT = false; options = DU.util.LString.trim(options).toUpperCase(); for (var i = 0; i < options.length; i++) { if (options.charAt(i) == 'I') { optionI = true; } else if (options.charAt(i) == 'T') { optionT = true; } } var arr = new Array(); var cnt = 0; var startIdx = 0; var delimIdx = -1; var str = s; var temp = 0; while ((delimIdx = (str == null) ? -1 : str.indexOf(delim, startIdx)) != -1) { if (optionI && str.substr(delimIdx - 1, 2) == '\\' + delim) { str = DU.util.LString.cut(str, delimIdx - 1, 1); startIdx = delimIdx; continue; } arr[cnt++] = optionT ? DU.util.LString.trim(str.substring(0, delimIdx)) : str.substring(0, delimIdx); str = str.substr(delimIdx + 1); startIdx = 0; } arr[cnt] = (str == null) ? "" : str; return arr; }, /** * @description 문자열을 주어진 길이만큼 잘라낸다. * @method cut * @param {String} s 문자열 * @param {Int} start 시작위치 * @param {Int} length 잘라낼 길이 * @return {String} 잘라낸 후 문자열 */ cut: function(s, start, length){ return s.substring(0, start) + s.substr(start + length); }, /** * @description 문자열을 처음부터 주어진 위치까지 잘라낸다. * @method lastCut * @param {String} s 문자열 * @param {Int} pos 잘라낼 위치 * @return {String} 잘라낸 후 문자열 */ lastCut: function(s, pos){ if (s != null && s.length > pos) s = s.substring(0, s.length - pos); return s; }, /** * @description 첫 문자만 대문자로 변환한다. * @method firstUpperCase * @param {String} s 문자열 * @return {String} 변환 후 문자열 */ firstUpperCase: function(s){ if (s != null && s.length > 0) return s.charAt(0).toUpperCase() + s.substring(1); return s; }, /** * @description 시작 문자열이 pattern에 맞는지 여부를 리턴한다. * @method startsWith * @param {String} s 작업 대상 문자열 * @param {String} pattern 문자패턴 * @return {Boolean} 결과 */ startsWith: function(s, pattern){ return s.indexOf(pattern) === 0; }, /** * @description 종료 문자열이 pattern에 맞는지 여부를 리턴한다. * @method endsWith * @param {String} s 작업 대상 문자열 * @param {String} pattern 문자패턴 * @return {Boolean} 결과 */ endsWith: function(s, pattern){ var d = s.length - pattern.length; return d >= 0 && s.lastIndexOf(pattern) === d; }, /** * @description 문자열이 null이면 '' 공백 문자로 리턴 * @method nvl * @param {String} val * @return {String} val */ nvl: function(val){ return (val === null) ? '' : val; }, /** * @description 입력된 camel 표기법(firstName)의 문자열을 hungarian 표기법(first_name) 문자열로 변환한다. * @method camelToHungarian * @param {String} camel * @return {String} */ camelToHungarian: function(camel){ var caps = camel.match(/[A-Z]/g); if (caps) { var capsCount = caps.length; var c; if (capsCount > 0) { for (var i = 0; i < capsCount; i++) { c = caps[i]; camel = camel.replace(c, "_" + c.toLowerCase()); } } } return camel; }, /** * @description 스트링의 자릿수를 Byte 단위로 환산하여 알려준다. 영문, 숫자는 1Byte이고 한글은 2Byte이다.(자/모 중에 하나만 있는 글자도 2Byte이다.) * @method getByteLength * @static * @param {String} value * @return {Int} 스트링의 길이 */ getByteLength : function(value){ var byteLength = 0; if (DU.isNull(value)) { return 0; } var c; for(var i = 0; i < value.length; i++) { c = escape(value.charAt(i)); // alert(value.charAt(i) + "/" + c); if (c.length == 1) { // when English then 1byte byteLength ++; } else if (c.indexOf("%u") != -1) { // when Korean then 2byte // if charset = utf8 then length = 3 //byteLength += 2; byteLength += 3; // utf-8 : 3 } else if (c.indexOf("%") != -1) { // else 3byte byteLength += c.length/3; } } return byteLength; }, /** * @description 입력된 문자열이 한글로 된 정보인지를 체크한다. 해당문자열이 한글과 스페이스의 조합일때만 true를 리턴한다. * @method isHangul * @static * @param {String} oValue * @return {Boolean} 한글 여부 */ isHangul : function(oValue){ var pattern = new RegExp('[^가-?\x20]', 'i'); if (pattern.exec(oValue) != null) { return false; } else { return true; } }, /** * @description 입력된 문자열이 email로 된 정보인지를 체크한다. * @method isEmail * @static * @param {String} oValue * @return {Boolean} email 여부 */ isEmail: function(value) { return !(value.search(DU.util.LString.format) == -1); }, /** * @description 입력된 문자열이 csn로 된 정보인지를 체크한다. * @method isCsn * @static * @param {String} oValue * @return {Boolean} csn 여부 */ isCsn: function(value) { if (!value || (value+'').length != 10 || isNaN(value)) { return false; } value += ''; var sum = 0, nam = 0, checkDigit = -1; var checkArray = [1, 3, 7, 1, 3, 7, 1, 3, 5]; for (var i = 0; i < 9; i++) sum += value.charAt(i) * checkArray[i]; sum = sum + ((value.charAt(8) * 5) / 10); nam = Math.floor(sum) % 10; checkDigit = (nam == 0) ? 0 : 10 - nam; if (value.charAt(9) != checkDigit) { return false; } return true; }, /** * @description 입력된 문자열이 주민등록번호로 된 정보인지를 체크한다. * @method isSsn * @static * @param {String} oValue * @return {Boolean} ssn 여부 */ isSsn: function(value){ if ( !value || (value+'').length != 13 || isNaN(value) ) { return false; } value += ''; var jNum1 = value.substr(0, 6); var jNum2 = value.substr(6); /* ---------------------------------------------------------------- 잘못된 생년월일을 검사합니다. 2000년도부터 성구별 번호가 바뀌였슴으로 구별수가 2보다 작다면 1900년도 생이되고 2보다 크다면 2000년도 이상생이 됩니다. 단 1800년도 생은 계산에서 제외합니다. ---------------------------------------------------------------- */ bYear = (jNum2.charAt(0) <= "2") ? "19" : "20"; // 주민번호의 앞에서 2자리를 이어서 4자리의 생년을 저장합니다. bYear += jNum1.substr(0, 2); // 달을 구합니다. 1을 뺀것은 자바스크립트에서는 1월을 0으로 표기하기 때문입니다. bMonth = jNum1.substr(2, 2) - 1; bDate = jNum1.substr(4, 2); bSum = new Date(bYear, bMonth, bDate); // 생년월일의 타당성을 검사하여 거짓이 있을시 에러메세지를 나타냄 if ( bSum.getYear() % 100 != jNum1.substr(0, 2) || bSum.getMonth() != bMonth || bSum.getDate() != bDate) { return false; } total = 0; temp = new Array(13); for (var i = 1; i <= 6; i++) temp[i] = jNum1.charAt(i-1); for (var i = 7; i <= 13; i++) temp[i] = jNum2.charAt(i-7); for (var i = 1; i <= 12; i++) { k = i + 1; // 각 수와 곱할 수를 뽑아냅니다. 곱수가 만일 10보다 크거나 같다면 계산식에 의해 2로 다시 시작하게 됩니다. if(k >= 10) k = k % 10 + 2; // 각 자리수와 계산수를 곱한값을 변수 total에 누적합산시킵니다. total = total + (temp[i] * k); } // 마지막 계산식을 변수 last_num에 대입합니다. last_num = (11- (total % 11)) % 10; // laster_num이 주민번호의마지막수와 같은면 참을 틀리면 거짓을 반환합니다. if(last_num != temp[13]) { return false; } return true; }, /** * @description 입력된 문자열이 시간으로 된 정보인지를 체크한다. * @method isTime * @static * @param {String} oValue * @return {Boolean} ssn 여부 */ isTime: function(value) { var pos = value.indexOf(":"); if(pos > 0) value = DU.util.LString.cut(value, pos, 1); if(value.length != 4) return false; var hh = value.substring(0,2); var mm = value.substring(2,4); if(hh < 0 || hh > 23) return false; if(mm < 0 || mm > 59) return false; return true; }, /** * 자바스크립트의 내장 객체인 String 객체에 simpleReplace 메소드를 추가한다. simpleReplace 메소드는 * 스트링 내에 있는 특정 스트링을 다른 스트링으로 모두 변환한다. String 객체의 replace 메소드와 동일한 * 기능을 하지만 간단한 스트링의 치환시에 보다 유용하게 사용할 수 있다. *

         *     var str = "abcde"

         *     str = simpleReplace(str, "cd", "xx"); 

         * 
* 위의 예에서 str는 "abxxe"가 된다. * @static * @param {String} str required 기존의 스트링 * @param {String} oldStr required 바뀌어야 될 기존의 스트링 * @param {String} newStr required 바뀌어질 새로운 스트링 * @return {String} replaced String. */ simpleReplace : function(str, oldStr, newStr) { var rStr = oldStr; rStr = rStr.replace(/\\/g, "\\\\"); rStr = rStr.replace(/\^/g, "\\^"); rStr = rStr.replace(/\$/g, "\\$"); rStr = rStr.replace(/\*/g, "\\*"); rStr = rStr.replace(/\+/g, "\\+"); rStr = rStr.replace(/\?/g, "\\?"); rStr = rStr.replace(/\./g, "\\."); rStr = rStr.replace(/\(/g, "\\("); rStr = rStr.replace(/\)/g, "\\)"); rStr = rStr.replace(/\|/g, "\\|"); rStr = rStr.replace(/\,/g, "\\,"); rStr = rStr.replace(/\{/g, "\\{"); rStr = rStr.replace(/\}/g, "\\}"); rStr = rStr.replace(/\[/g, "\\["); rStr = rStr.replace(/\]/g, "\\]"); rStr = rStr.replace(/\-/g, "\\-"); var re = new RegExp(rStr, "g"); return str.replace(re, newStr); }, /** * value의 값을 클립보드에 저장한 후 결과를 리턴한다. * @method toClipboard * @static * @param {String} value 저장할 값 * @return {boolean} 결과 여부 */ toClipboard: function(value) { try { if(window.clipboardData){ clipboardData.setData("text",value); return true; }else if(window.netscape){ //http://www-archive.mozilla.org/xpfe/xptoolkit/clipboard.html netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard); if(!clip) return; var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable); if(!trans) return; trans.addDataFlavor('text/unicode'); var str = new Object(),len = new Object(); var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); str.data=value; trans.setTransferData("text/unicode",str,value.length*2); var clipid=Components.interfaces.nsIClipboard; if(!clipid) return false; clip.setData(trans,null,clipid.kGlobalClipboard); return true; } } catch(e) { return false; } return false; }, /** * value의 값을 클립보드값을 리턴한다. * @method getClipboard * @static * @return {String} 결과 */ getClipboard: function() { try { if(window.clipboardData){ return clipboardData.getData("text");; }else if(window.netscape){ //http://www-archive.mozilla.org/xpfe/xptoolkit/clipboard.html netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard); if (!clip) return; var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable); if (!trans) return; trans.addDataFlavor('text/unicode'); clip.getData(trans,clip.kGlobalClipboard); var str = new Object(); var len = new Object(); try { trans.getTransferData('text/unicode',str,len); } catch(error) { return; } if (str) { if (Components.interfaces.nsISupportsWString) str=str.value.QueryInterface(Components.interfaces.nsISupportsWString); else if (Components.interfaces.nsISupportsString) str=str.value.QueryInterface(Components.interfaces.nsISupportsString); else str = null; } if (str) return(str.data.substring(0,len.value / 2)); } } catch(e) { return false; } } }); DU.util.LString.format = /^((\w|[\-\.])+)@((\w|[\-\.])+)\.([A-Za-z]+)$/ })(); (function(){ 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(); }; /** * The static Date class provides helper functions to deal with data of type Date. * static Date 클래스는 Date 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. *
*
format {String}
*
strftime에 정의된 format을 지원합니다.
*
* strftime은 http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html에 * 오픈 그룹에 의해 정의된 여러가지 format 지정자들을 가지고 있다. * * PHP는 http://www.php.net/strftime에 정의된 자체의 몇가지 항목들을 추가한다. * * * 이러한 자바스크립트 구현은 모든 PHP 지정자와 몇가지를 더 지원한다. * *
arg \%a - 현재 locale의 요일의 단축표시 ex) ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] *
arg \%A - 현재 locale의 요일 표시 ex) ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] *
arg \%b - 현재 locale의 달의 단축표시 ex) ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] *
arg \%B - 현재 locale의 달 표시 ex) ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] *
arg \%c - 현재 locale의 선호되는 날짜와 시간 표시 ex) 미국 : %a %d %b %Y %T %Z, 한국 : %Y년 %b %d %a %T %Z *
arg \%C - century number 현재 년도를 100으로 나누고 정수로 만든값으로 00~99 *
arg \%d - 달의 일을 표시하는 값으로 01 ~ 31을 표시 *
arg \%D - %m/%d/%y와 같다. *
arg \%e - %d와 비슷하나 1자리수의 경우 0대신이 공백이 들어간다. ' 1' ~ '31' *
arg \%F - %Y-%m-%d와 같다. (ISO 8601 date format) *
arg \%g - Two digit representation of the year going by ISO-8601:1988 standards (see %V) *
arg \%G - The full four-digit version of %g *
arg \%h - %b와 같다. *
arg \%H - 24-hour 00 ~ 23 *
arg \%I - 12-hour 01 ~ 12 *
arg \%j - 년중 몇번째 일인지 표시 001 ~ 366 *
arg \%k - 24-hour 0 ~ 23 *
arg \%l - 12-hour 1 ~ 12 *
arg \%m - month 01 ~ 12 *
arg \%M - minute 00 ~ 59 *
arg \%n - 줄바꿈문자 *
arg \%p - 현재 locale의 `AM', `PM' *
arg \%P - %p와 같으나 소문자 'am', 'pm' *
arg \%q - 입력용 format으로 년월일을 표시하며 기본 %Y%m%d이다. *
arg \%Q - 입력용 format으로 년월일시분초를 표시하면 기본 %Y%m%d%H%M%S이다. *
arg \%r - %p를 붙인 12시간제 시간 표시 %I:%M:%S %p와 같다. *
arg \%R - 24시간 표시 %H:%M와 같다. *
arg \%s - Unix Epoch Time timestamp, 1970-01-01 00:00:00 UTC이후의 초 ex) 305815200는 September 10, 1979 08:40:00 AM이다. *
arg \%S - 초 00 ~ 59 *
arg \%t - tab문자 *
arg \%T - 현재시간 %H:%M:%S와 같다. *
arg \%u - 요일을 숫자로 표시 1이 월요일이다. 1 ~ 7 *
arg \%U - 지정한 년의 주번호 첫번째 일요일 부터 한주로 계산한다. *
arg \%V - 지정한 년의 주번호(ISO 8601:1988) 첫번째 월요일 부터 한주로 계산한다. 단 첫주는 적어도 4일이상이 되어야 한다. 01~53 *
arg \%w - 요일을 숫자로 표시 0이 일요일이다. 0 ~ 6 *
arg \%W - 지정한 년의 주번호 첫번째 월요일 부터 한주로 계산. *
arg \%x - 현재 locale의 기본 년월일 format ex) 2010-05-14, 14/05/10 *
arg \%X - 현재 locale의 시간 ex) 15:59:16 *
arg \%y - century를 뺀 년도 00 ~ 99 *
arg \%Y - century를 포함한 년도 ex) 2010 *
arg \%z - time zone(UTC) 약어 또는 전체 명 ex) -0500 또는 EST for Eastern Time *
arg \%Z - time zone name / abbreviation *
arg \%% - a literal `\%' character * @namespace DU.util * @requires DU * @class LDate * @static */ var Dt = { /***************************************LDateMath에서 가져온 function 시작***************************************/ /** * 해당 인스턴스에 지정된 분량의 시간을 추가한다. * @method add * @param {Date} date 추가시 수행할 자바스크립트 Date object * @param {String} field 추가 실행에 대해 사용될 필드 상수 * @param {Number} amount date에 추가할 유닛들의 숫자(필드 상수에서 측정된) * @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)이 -128 이하나 127 이상의 n으로 호출되었을 때, * 사파리2(webkit < 420) 에서의 버그에 대한 보고를 위한 private helper method이다. *

* 원인과 해결 방안은 다음에서 확인 가능하다: * http://brianary.blogspot.com/2006/03/safari-date-bug.html *

* @method _addDays * @param {Date} d 자바스크립트 date object * @param {Number} nDays date object에 추가할 날짜들의 숫자(음수 가능) * @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); }, /** * Subtracts the specified amount of time from the this instance. * 해당 인스턴스로 부터 지정된 분량의 시간을 차감한다. * @method subtract * @param {Date} date 차감시 수행할 자바스크립트 Date object * @param {Number} field 차감 실행에 대해 사용될 필드 상수 * @param {Number} amount date로부터 차감할 유닛들의 숫자(필드 상수에서 측정된) * @return {Date} Date object 결과 */ subtract: function(date, field, amount) { return this.add(date, field, (amount * -1)); }, /** * 주어진 날짜가 달력의 다른 날짜 이전인지 여부에 대하여 결정한다. * @method before * @param {Date} date 비교 argument와 비교할 Date object * @param {Date} compareTo 비교시 사용할 Date object * @return {Boolean} 비교 날짜 이전에 날짜가 있으면 true, 아니면 false */ before: function(date, compareTo) { var ms = compareTo.getTime(); if (date.getTime() < ms) { return true; } else { return false; } }, /** * 주어진 날짜가 달력의 다른날짜 이후인지 여부에 대하여 결정한다. * @method after * @param {Date} date 비교 argument와 비교할 Date object * @param {Date} compareTo 비교시 사용할 Date object * @return {Boolean} 비교 날짜 이후에 날짜가 있으면 true, 아니면 false */ after: function(date, compareTo) { var ms = compareTo.getTime(); if (date.getTime() > ms) { return true; } else { return false; } }, /** * 주어진 날짜가 달력의 두 날짜 사이에 있는지 여부에 대하여 결정한다. * @method between * @param {Date} date 체크할 날짜 * @param {Date} dateBegin 범위의 시작일 * @param {Date} dateEnd 범위의 종료일 * @return {Boolean} 비교할 날짜가 중간에 있으면 true, 아니면 false */ between: function(date, dateBegin, dateEnd) { if (this.after(date, dateBegin) && this.before(date, dateEnd)) { return true; } else { return false; } }, /** * Date객체의 Pattern에 따른 날짜 비교 * @method compareString * @param {Date} date1 Date1 객체 * @param {Date} date2 Date2 객체 * @param {Date} pattern format pattern 문자 * @return {Boolean} true */ compareString : function(date1, date2, pattern) { var dateString1 = this.format(date1, pattern); var dateString2 = this.format(date2, pattern); return (dateString1 == dateString2); }, /** * 주어진 연도의 1월 1일을 표시하는 자바스크립트 date object를 조회한다. * @method getJan1 * @param {Number} calendarYear 1월 1일을 조회하기 위한 달력의 연도 * @return {Date} 명시된 달력 연도의 1월 1일 */ getJan1: function(calendarYear) { return this.getDate(calendarYear, 0, 1); }, /** * 특정 연도의 1월 1일부터 특정 일자까지의 일수를 계산한다. * 0의 offset 값을 이 함수에서 반환하기 위해서는 1월 1일을 전달한다. * @method getDayOffset * @param {Date} date offset을 찾을 자바크스립트 date * @param {Number} calendarYear offset을 결정하기 위해 사용하는 연도 * @return {Number} 주어진 연도의 1월 1일 부터의 일수 */ getDayOffset: function(date, calendarYear) { var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1. // Find the number of days the passed in date is away from the calendar year start var dayOffset = Math.ceil((date.getTime() - beginYear.getTime()) / this.ONE_DAY_MS); return dayOffset; }, /** * 주어진 날짜에 대한 week number를 계산한다. * 올해의 첫번째 주를 1월 1일로 정의하는 것에 기반하는 standard U.S. week number와 * 올해의 첫번째 주를 1월 4일로 정의하는 것에 기반하는 ISO8601 week number를 일번적으로 지원할 수 있다. * * @method getWeekNumber * @param {Date} date week number를 찾을 자바스크립트 date * @param {Number} firstDayOfWeek 주의 첫번째 날짜의 인덱스(0 = Sun, 1 = Mon ... 6 = Sat). 기본값은 0 * @param {Number} janDate 올해의 한주를 정의 하는 1월의 첫째주의 date * 기본값은 1(Jan 1st)인, DU.widget.LDateMath.WEEK_ONE_JAN_DATE의 값. * U.S에 대해서는 일반적으로 1월 1일 이다. ISO8601은 올해의 첫째주를 정의하기 위하여 1월 4일을 사용한다. * @return {Number} 주어진 날짜를 포함하는 week numner */ getWeekNumber: function(date, firstDayOfWeek, janDate) { // Setup Defaults firstDayOfWeek = firstDayOfWeek || 0; janDate = janDate || this.WEEK_ONE_JAN_DATE; var targetDate = this.clearTime(date), startOfWeek, endOfWeek; if (targetDate.getDay() === firstDayOfWeek) { startOfWeek = targetDate; } else { startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek); } var startYear = startOfWeek.getFullYear(), startTime = startOfWeek.getTime(); // DST shouldn't be a problem here, math is quicker than setDate(); endOfWeek = new Date(startOfWeek.getTime() + 6 * this.ONE_DAY_MS); var weekNum; if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) { // If years don't match, endOfWeek is in Jan. and if the // week has WEEK_ONE_JAN_DATE in it, it's week one by definition. weekNum = 1; } else { // Get the 1st day of the 1st week, and // find how many days away we are from it. var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)), weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek); // Round days to smoothen out 1 hr DST diff var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime()) / this.ONE_DAY_MS); // Calc. Full Weeks var rem = daysDiff % 7; var weeksDiff = (daysDiff - rem) / 7; weekNum = weeksDiff + 1; } return weekNum; }, /** * 주어진 날짜에 대한 주의 첫번째 날짜를 가져 온다. * @param {Date} dt 첫번째 날짜가 필요한 주의 date * @param {Number} startOfWeek 주의 첫번째 날짜에 대한 인덱스, 0 = Sun, 1 = Mon ... 6 = Sat (기본값은 0) * @return {Date} 주의 첫번째 날짜 */ getFirstDayOfWeek: function(dt, startOfWeek) { startOfWeek = startOfWeek || 0; var dayOfWeekIndex = dt.getDay(), dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7; return this.subtract(dt, this.DAY, dayOfWeek); }, /** * 주어진 week가 두개의 다른 연도에 겹쳐지는지 대한 여부를 결정한다. * @method isYearOverlapWeek * @param {Date} weekBeginDate 주의 첫번째 날짜를 표시하는 자바스크립트 Date * @return {Boolean} 날짜가 두개의 다른 연도에 겹쳐지면 true */ isYearOverlapWeek: function(weekBeginDate) { var overlaps = false; var nextWeek = this.add(weekBeginDate, this.DAY, 6); if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) { overlaps = true; } return overlaps; }, /** * 주어진 week가 두개의 다른 달에 겹쳐지는지 대한 여부를 결정한다. * @method isMonthOverlapWeek * @param {Date} weekBeginDate 주의 첫번째 날짜를 표시하는 자바스크립트 Date * @return {Boolean} 날짜가 두개의 다른 달에 겹쳐지면 true */ isMonthOverlapWeek: function(weekBeginDate) { var overlaps = false; var nextWeek = this.add(weekBeginDate, this.DAY, 6); if (nextWeek.getMonth() != weekBeginDate.getMonth()) { overlaps = true; } return overlaps; }, /** * 주어진 날짜를 포함하는 달의 첫번째 날짜를 가져온다. * @method getFirstDayOfMonth * @param {Date} date 달의 시작을 계산하는데 사용할 자바스크립트 Date * @return {Date} 달의 첫째날을 표시하는 자바스크립트 Date */ getFirstDayOfMonth: function(date) { var start = this.getDate(date.getFullYear(), date.getMonth(), 1); return start; }, /** * 주어진 날짜를 포함하는 달의 마지막 날짜를 가져온다. * @method getLastDayOfMonth * @param {Date} date 달의 끝을 계산하는데 사용할 자바스크릅트 Date * @return {Date} 달의 마지막날을 표시하는 자바스크립트 Date */ getLastDayOfMonth: function(date) { var start = this.getFirstDayOfMonth(date); var nextMonth = this.add(start, this.MONTH, 1); var end = this.subtract(nextMonth, this.DAY, 1); return end; }, /** * 주어진 날짜로부터 시간 필드를 초기화 하고, 효과적으로 시간을 낮 12시로 설정한다. * @method clearTime * @param {Date} date 초기화할 시간 필드에 대한 자바스크립트 Date * @return {Date} 모든 시간 필드들이 초기화 된 자바스크립트 Date */ clearTime: function(date) { date.setHours(12, 0, 0, 0); return date; }, /** * 주어진 년, 월, 일을 표시하는 새로운 자바스크립트 Date object를 반환한다. * 새로운 Date object의 시간 필드(hr, min, sec, ms)들은 0으로 설정된다. * method는 100이하의 연도로 생성되기 위한 Date 인스턴스들을 허용한다. * "new Date(year, month, date)" 구현은 100 이하의 year (xx)가 제공되는 경우 * 19xx로 연도를 설정한다. *

* NOTE:argument의 Validation은 실행되지 않는다. * new Date(year, month[, date]) 생성자에 대해 ECMAScript-262 Date object 명시에 따라서 * argument의 적합성을 확보하는 것은 caller의 책임이다. *

* @method getDate * @param {Number} y Year. * @param {Number} m 0(Jan)부터 11(Dec)까지의 월 인덱스 * @param {Number} d (optional) 1부터 31까지의 날짜. 만약 제공되지 않으면 기본적으로 1. * @return {Date} 제공된 년월일로 설정된 자바스크립트 date object */ getDate: function(y, m, d) { var dt = null; if (DU.isUndefined(d)) { d = 1; } if (y >= 100) { dt = new Date(y, m, d); } else { dt = new Date(); dt.setFullYear(y); dt.setMonth(m); dt.setDate(d); dt.setHours(0, 0, 0, 0); } return dt; }, /***************************************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' }, /** * native JavaScript Date를 가져와서 사용자에게 표시할 문자열로 포맷화 한다. * * @method format * @param oDate {Date} Date. * @param {Object} oConfig (Optional) 부가적인 설정 값들 * @return {String} 표시를 위한 형식화된 날짜 */ format : function (oDate, oConfig) { oConfig = typeof oConfig === 'string' ? {format: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 get locale, default and so on... * @param {String} sLocale * @return DU.util.LDateLocale */ getLocale : function (sLocale){ sLocale = sLocale || DU.getConfig().getFirst("$.core.defaultLocale") || "en"; // Make sure we have a definition for the requested locale, or default to en. /* if(!(sLocale in DU.util.LDateLocale)) { if(sLocale.replace(/-[a-zA-Z]+$/, '') in DU.util.LDateLocale) { sLocale = sLocale.replace(/-[a-zA-Z]+$/, ''); } else { sLocale = "en"; } } */ 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 inx월에 해당되는 마지막 날짜 * @method getDayInMonth * @param {Int} inx * @return {Int} */ getDayInMonth : function(inx) { return DU.util.LDate.DAYS_IN_MONTH[inx]; }, /** * @description inx월에 다국어 표현 날짜 (예: 01월) * @method getMonthInYear * @param {Int} inx * @return {String} */ getMonthInYear : function(inx) { return DU.getMessageManager().get('$.core.monthInYear')[inx]; }, /** * @description inx월에 다국어 표현 짧은 날짜 (예: 1월) * @method getShortMonthInYear * @param {Int} inx * @return {String} */ getShortMonthInYear : function(inx) { return DU.getMessageManager().get('$.core.shortMonthInYear')[inx]; }, /** * @description inx에 해당되는 다국어 요일 (예: 월요일) * @method getDayInWeek * @param {Int} inx * @return {String} */ getDayInWeek : function(inx) { return DU.getMessageManager().get('$.core.dayInWeek')[inx]; }, /** * @description inx에 해당되는 다국어 짧은 요일 (예: 월) * @method getShortDayInWeek * @param {Int} inx * @return {String} */ getShortDayInWeek : function(inx) { return DU.getMessageManager().get('$.core.shortDayInWeek')[inx]; }, /** * @description o의 객체가 Date객체인지 여부 * @method isDate * @param {Object} o * @return {boolean} */ isDate : function(o) { return o && (typeof o.getFullYear == 'function'); } }; /** * 형식화된 문자열에 기반한 문자열로부터 세부 시간을 파싱하는 * strptime의 부분적인 구현. *

* 이러한 구현은 대부분 [00,59]보다 [00,61]을 제한하는 second format의 익셉션을 가진 * http://docs.python.org/lib/module-Dt.html에 문서화 된 * Python의 time 모듈에 대한 문서로 부터 cue를 가져온다. *

* 지원되는 서식은 다음과 같다: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DirectiveMeaning
%bLocale의 단축 월 이름
%BLocale의 전체 월 이름
%d[01,31]의 10진수로 된 월의 날짜.
%H[00,23]의 10진수로 된 시간(24시간제).
%I[00,12]의 10진수로 된 시간(12시간제).
%m[01.12]의 10진수로 된 월.
%M[00,59]의 10진수로 된 분.
%p * 지역의 AM이나 PM 여부 표시(시간을 파싱하기 위하여 %I 지시어가 * 사용되는 경우 시간 필드 출력에만 영향을 준다.) *
%S[00,59]의 10진수로 된 초.
%y[00,99]의 10진수로 된 세기값이 없는 년도.
%Y10진수로 된 세기값이 있는 년도.
%%"%" 리터럴 문자.
* * @param {String} format 명시된 서식 지시어 문자열 * @param {Object} locale 해당 parser를 생성하기 위해 사용되는 locale object * @constructor */ Dt.Parser = function(format, locale) { /** * 주어진 본래 서식 문자열 * * @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 format에 대하여 생성된 정규 표현식 * * @type RegExp */ this.regexp = new RegExp("^" + pattern.join("") + "$"); /** * 자리를 차지할 것으로 예상되는 일치 순서에 따르는 해당 parser의 regexp에 * 의해 매치될 예상되는 서식 지시어 코드 목록 * * @type Array */ this.expected = expected; }; /** * 지시어에 해당하는 데이터나 locale에 의존하는 지시어의 경우 locale을 가져가고 * 정규 표현식 패턴 조각을 생성하는 함수를 캡쳐하는 정규 표현식 패턴 조각에 지시 코드를 맵핑한다. * * @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 }; /** * 여러 데이터 항목들을 포함할수 있는 몇몇 지시어 목록으로 지정된 각 지시어에 대해 * 예상되는 캡쳐된 지시어 코드에 대하여 지시어 코드를 매핑한다. * * @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.applyObject(DU.util.LDate, Dt); })(); DU.namespace("DU.util"); /** * static Array 클래스는 Array 타입의 데이터를 처리하는데 도움을 주는 함수들을 제공한다. * * @module util * @namespace DU.util * @requires DU * @class LArray * @static */ DU.applyObject(DU.util.LArray, { /** * @description 객체를 복사하는 메소드 * @method clone * @public * @param {Object} obj 복사하고자 하는 원본 객체 * @return {Object} 복사된 객체 */ clone: function(obj){ return [].concat(obj); }, /** * items 배열에서 index에 해당하는 객체를 삭제하는 메소드 * @method removeAt * @param {Array} items Array 배열 * @param {Int} idx 삭제할 위치 * @return {Object} 삭제된 위치 */ removeAt : function(items,idx) { if (items.length > 0) { if (idx == 0) { return items.slice(1, items.length); } else if (idx == items.length - 1) { return items.slice(0, items.length - 1) } else { var a = items.slice(0, idx); var b = items.slice(idx + 1, items.length); return a.concat(b); } } else { return items; } }, /** * items 배열에서 oldIndex에 해당되는 데이터를 newIndex로 이동하는 메소드 * @method moveItem * @param {Array} items Array 배열 * @param {Int} oldIndex 이동할 위치 * @param {Int} newIndex 이동될 위치 * @return {Object} 삭제된 위치 */ moveItem : function(items, oldIndex, newIndex){ var temp = items[oldIndex]; items = DU.util.LArray.removeAt(items, oldIndex); var a = items.slice(0, newIndex); var b = items.slice(newIndex, items.length); a.push(temp); return a.concat(b); } }); /** * @description 숫자 유틸리티 * @module util * @title LNumber * @namespace DU.util * @class LNumber * @static */ DU.namespace("DU.util"); (function(){ /** * @description LNumber * @namespace DU.util * @class LNumber */ DU.util.LNumber = { /** * @description native 자바스크립트 Number를 가져오고 사용자에서 표시할 문자열로 형식화 한다. * @method format * @param nData {Int} Number. * @param {Object} oConfig (Optional) Optional config 객체: *

*
prefix {String} *
통화 지정자 "$"과 같은 각 숫자 앞에 문자열을 추가.
*
decimalPlaces {Number} *
반올림할 소수 자릿수의 숫자
*
decimalSeparator {String} *
소수점 구분기호
*
thousandsSeparator {String} *
천단위 구분기호
*
suffix {String} *
문자열은 "항목"(공백주의)처럼 각 번호 뒤에 추가
*
* @return {String} Formatted number for display. */ format: function(nData, oConfig){ if (DU.isNumber(nData)) { var bNegative = (nData < 0); var sOutput = nData + ""; var sDecimalSeparator = (oConfig.decimalSeparator) ? oConfig.decimalSeparator : "."; var nDotIndex; // Manage decimals if (DU.isNumber(oConfig.decimalPlaces)) { // Round to the correct decimal place var nDecimalPlaces = oConfig.decimalPlaces; var nDecimal = Math.pow(10, nDecimalPlaces); sOutput = Math.round(nData * nDecimal) / nDecimal + ""; nDotIndex = sOutput.lastIndexOf("."); if (nDecimalPlaces > 0) { // Add the decimal separator if (nDotIndex < 0) { sOutput += sDecimalSeparator; nDotIndex = sOutput.length - 1; } // Replace the "." else if (sDecimalSeparator !== ".") { sOutput = sOutput.replace(".", sDecimalSeparator); } // Add missing zeros while ((sOutput.length - 1 - nDotIndex) < nDecimalPlaces) { sOutput += "0"; } } } // Add the thousands separator if (oConfig.thousandsSeparator) { var sThousandsSeparator = oConfig.thousandsSeparator; nDotIndex = sOutput.lastIndexOf(sDecimalSeparator); nDotIndex = (nDotIndex > -1) ? nDotIndex : sOutput.length; var sNewOutput = sOutput.substring(nDotIndex); var nCount = -1; for (var i = nDotIndex; i > 0; i--) { nCount++; if ((nCount % 3 === 0) && (i !== nDotIndex) && (!bNegative || (i > 1))) { sNewOutput = sThousandsSeparator + sNewOutput; } sNewOutput = sOutput.charAt(i - 1) + sNewOutput; } sOutput = sNewOutput; } // Prepend prefix sOutput = (oConfig.prefix) ? oConfig.prefix + sOutput : sOutput; // Append suffix sOutput = (oConfig.suffix) ? sOutput + oConfig.suffix : sOutput; return sOutput; } // Still not a Number, just return unaltered else { return nData; } }, /** * @description 소수점 숫자 반올림 * @method round * @param {Int/String} value 반올림 할 값. * @param {Int} precision 반올림 자리수. * @return {Int} 반올림된 값. */ round : function(value, precision) { var result = Number(value); if (typeof precision == 'number') { result = Number(this.format(value, { decimalPlaces: precision })); } return result; }, /** * @description 통화량(돈)으로 숫자를 형식화 한다. * @method toMoney * @param {Number/String} value 형식화 할 숫자값 * @return {String} 형식화 된 통화량 문자열 */ toMoney : function(v,currency){ v = (Math.round((v-0)*100))/100; v = String(v); var count = Math.ceil(v.length / 3); var fstVal = ''; var newVal = ''; fstVal = v.substring(0, (v.length % 3) == 0 ? 3 : parseInt(v.length % 3, 10)); for (var idx = 1; idx < count; idx++) { var k = v.substr(v.length - 3 * idx, 3); newVal = ',' + k + newVal; } v = fstVal + newVal; return currency + v; }, /** * @description US 통화 단위로 숫자를 형식화 한다. * @method usMoney * @param {Number/String} value 형식화 할 숫자 값 * @return {String} 형식화된 통화량 문자열 */ usMoney : function(v){ v = (Math.round((v-0)*100))/100; v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v); v = String(v); var ps = v.split('.'); var whole = ps[0]; var sub = ps[1] ? '.'+ ps[1] : '.00'; var count = Math.ceil(whole.length / 3); var fstVal = ''; var newVal = ''; fstVal = whole.substring(0, (whole.length % 3) == 0 ? 3 : parseInt(whole.length % 3, 10)); for (var idx = 1; idx < count; idx++) { var k = v.substr(whole.length - 3 * idx, 3); newVal = ',' + k + newVal; } whole = fstVal + newVal; v = whole + sub; return "$"+ v; }, /** * @description 주어진 파라미터가 숫자인지 체크한다. * @method isNumber * @param {Object} v * @return {Boolean} */ isNumber : function(v) { v += ''; // 문자열로 변환 v = v.replace(/^\s*|\s*$/g, ''); // 좌우 공백 제거 if (v == '' || isNaN(s)) return false; return true; } }; })(); /** * The static String class provides helper functions to deal with data of type * Number. * @module util * @title DU Global * @static */ DU.namespace("DU.util"); /** * Json 문자열을 파싱하고 object를 Json 문자열로 변환하는 method를 제공한다. * @module util * @title Utility * @namespace DU.util * @requires DU */ /** * Json 문자열을 파싱하고 object를 Json 문자열로 변환하는 method를 제공한다. * @class LJson * @static */ DU.applyObject(DU.util.LJson, { }); /** * 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 getClassRegEx = function(className) { var re = reClassNameCache[className]; if (!re) { re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)'); reClassNameCache[className] = re; } return re; }; /** * DOM element들에 대해 도움이 되는 method들을 제공한다. * @namespace DU.util * @class LDom */ DU.applyObject(DU.util.LDom, { /** * element에 스타일 명세를 적용한다. * @param {String/HTMLElement} el 스타일을 적용할 element * @param {String/Object/Function} styles 예를 들어 "width:100px"와 같은 스타일 명세 문자열이나, * {width:"100px"}와 같은 form object나, 명세 같은 것을 반환하는 함수 */ 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()); } } }, /** * 페이지 좌표에 기반한 element의 현재 위치를 가져온다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method getXY * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @return {Array} element의 XY 위치 */ getXY: function(el) { var f = function(el) { // has to be part of document to have pageXY // el.ownerDocument 객체가 존재하지않을 수 도 있음. if(el.ownerDocument === null) return false; if ( ((el.parentNode === null || el.offsetParent === null || this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body)) { return false; } return getXY(el); }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * 페이지 좌표에 기반한 element의 현재 X 위치를 가져온다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method getX * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @return {Number | Array} element의 X 위치 */ getX: function(el) { var f = function(el) { return Y.LDom.getXY(el)[0]; }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * 페이지 좌표에 기반한 element의 현재 Y 위치를 가져온다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method getY * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @return {Number | Array} element의 Y 위치 */ getY: function(el) { var f = function(el) { return Y.LDom.getXY(el)[1]; }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * element가 어떻게 위치되었는지에 상관없이 페이지 좌표안에서 html element의 위치를 설정한다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method setXY * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @param {Array} pos 새로운 위치에 대한 X와 Y값을 포함하는 array(페이지에 기반한 좌표) * @param {Boolean} noRetry 기본적으로 처음 시도가 실패하는 경우 두번째로 위치를 설정하려고 시도한다. */ setXY: function(el, pos, noRetry) { var f = function(el) { var style_pos = this.getStyle(el, 'position'); if (style_pos == 'static') { // default to relative this.setStyle(el, 'position', 'relative'); style_pos = 'relative'; } var pageXY = this.getXY(el); if (pageXY === false) { // has to be part of doc to have pageXY return false; } // L-hidden일 경우 -10000 제거 if(DU.get(el).hasClass('L-hidden')) { pageXY[0] += 10000; pageXY[1] += 10000; } var delta = [ // assuming pixels; if not we will have to retry parseInt( this.getStyle(el, 'left'), 10 ), parseInt( this.getStyle(el, 'top'), 10 ) ]; if ( isNaN(delta[0]) ) {// in case of 'auto' delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft; } if ( isNaN(delta[1]) ) { // in case of 'auto' delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop; } if(DU.get(el).hasClass('L-hidden') && delta[0] == -10000) { delta[0] += 10000; } if(DU.get(el).hasClass('L-hidden') && delta[1] == -10000) { delta[1] += 10000; } if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; } if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; } if (!noRetry) { var newXY = this.getXY(el); if(DU.get(el).hasClass('L-hidden')) { newXY[0] += 10000; newXY[1] += 10000; } // if retry is true, try one more time if we miss if ( (pos[0] !== null && newXY[0] != pos[0]) || (pos[1] !== null && newXY[1] != pos[1]) ) { this.setXY(el, pos, true); } } }; Y.LDom.batch(el, f, Y.LDom, true); }, /** * element가 어떻게 위치되었는지에 상관없이 페이지 좌표안에서 html element의 X 위치를 설정한다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method setX * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @param {Int} x element에 대한 X 좌표로서 사용될 값 */ setX: function(el, x) { Y.LDom.setXY(el, [x, null]); }, /** * element가 어떻게 위치되었는지에 상관없이 페이지 좌표안에서 html element의 Y 위치를 설정한다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method setY * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @param {Int} x element에 대한 Y 좌표로서 사용될 값 */ setY: function(el, y) { Y.LDom.setXY(el, [null, y]); }, /** * 주어진 element의 region 위치를 반환한다. * element는 반드시 페이지 좌표를 가진 DOM 트리의 일부여야 한다. * (display:none이거나 element가 append되어 있지 않다면 false를 반환) * @static * @method getRegion * @param {String | HTMLElement | Array} el ID로서 사용할 문자열이나, 실제 DOM 참조, HTMLElement나 ID들의 array를 허용한다. * @return {Region | Array} Region이나 "top, left, bottom, right" 멤버 데이터를 포함하는 Region 인스턴스의 array */ getRegion: function(el) { var f = function(el) { if ( (el.parentNode === null || el.offsetParent === null || this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) { return false; } var region = Y.LRegion.getRegion(el); return region; }; return Y.LDom.batch(el, f, Y.LDom, true); }, /** * 클라이언트의 width를 반환한다.(viewport:화면 상의 화상 표시영역) * @method getClientWidth * @deprecated 지금은 getViewportWidth을 사용한다. 이 인터페이스는 예전 compat를 위해 그대로 유지됐다. * @return {Int} 페이지의 표시가능한 영역의 width */ getClientWidth: function() { return Y.LDom.getViewportWidth(); }, /** * 클라이언트의 height를 반환한다.(viewport:화면 상의 화상 표시영역) * @method getClientHeight * @deprecated 지금은 getViewportHeight을 사용한다. 이 인터페이스는 예전 compat를 위해 그대로 유지됐다. * @return {Int} 페이지의 표시가능한 영역의 height */ getClientHeight: function() { return Y.LDom.getViewportHeight(); }, /** * 주어진 element나 element의 collection에 대해 다른 클래스로 클래스를 교체한다. * 기존 클래스 이름이 존재하지 않는 경우 새로운 클래스 이름이 간단하게 추가된다. * @static * @method replaceClass * @param {String | HTMLElement | Array} el 클래스로부터 제거할 element나 collection * @param {String} oldClassName 교체될 클래스 이름 * @param {String} newClassName 예전 클래스 이름을 교체할 클래스 이름 * @return {Boolean | Array} 성공/실패 boolean이나 boolean값들의 array */ replaceClass: function(el, oldClassName, newClassName) { if (!newClassName || oldClassName === newClassName) { // avoid infinite loop return false; } var re = getClassRegEx(oldClassName); var f = function(el) { if ( !this.hasClass(el, oldClassName) ) { this.addClass(el, newClassName); // just add it if nothing to replace return true; // NOTE: return } el.className = el.className.replace(re, ' ' + newClassName + ' '); if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent this.removeClass(el, oldClassName); } el.className = DU.trim(el.className); // remove any trailing spaces return true; }; return Y.LDom.batch(el, f, Y.LDom, true); }, addClassOnMove : function(el, className, width){ // 테스트 전혀 안됨. el = DU.get(el); width = width || el.getWidth(); el.on("mousemove", function(e){ var headerRegion = el.getRegion(); var x = e.clientX; if (x - headerRegion.left <= width) { el.addClass(className); } else if (headerRegion.right - x <= width) { el.addClass(className); } else { el.removeClass(className); } }) el.on("mouseout", function(e){ el.removeClass(className); }); }, /** * @description dom에서 지정한 prefix를 className의 prefix로 가지는 className의 뒷값 가져오기 * @method findStringInClassName * @private * @param {HTMLElement} dom * @return {string} value */ findStringInClassName: function(dom, prefix){ var value = null; if (dom && prefix) { var cns = dom.className.split(" "); DU.util.LArray.each(cns, function(cn){ if (DU.util.LString.startsWith(cn, prefix)) { value = cn.substring(prefix.length); } }); } return value; }, /** * 제공된 boolean method에 의해 적용된 테스트로 전달되는 HTMLElement의 array를 반환한다. * 최적화된 성능을 위해 가능한 경우, tag나 root 노드를 포함한다. * 주의: 이 method는 live collection에 반하여 작동하며 * callback(노드의 삭제/추가 등)에서의 collection 수정은 역효과가 생길 것이다. * 대신 native "getElementsByTagName" method와 마찬가지로 반환된 노드 array를 반복해야 한다. * @static * @method getElementsBy * @param {Function} method element의 argument로서만 element를 받는 element들을 테스트 하기 위한 boolean method. * @param {String} tag (optional) collect 될 element들의 tag 이름 * @param {String | HTMLElement} root (optional) 시작지점으로서 사용할 HTMLElement나 ID * @param {Function} apply (optional) 검색되었을때 각 element에 적용할 함수 * @return {Array} HTMLElement의 array */ getElementsBy: function(method, tag, root, apply) { tag = tag || '*'; root = (root) ? Y.LDom.get(root) : null || document; if (!root) { return []; } var nodes = [], elements = root.getElementsByTagName(tag); for (var i = 0, len = elements.length; i < len; ++i) { if ( method(elements[i]) ) { nodes[nodes.length] = elements[i]; if (apply) { apply(elements[i]); } } } return nodes; }, /** * document의 height를 반환한다. * @static * @method getDocumentHeight * @return {Int} 실제 document의 height(body와 그것의 공백을 포함하는) */ getDocumentHeight: function() { var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight; var h = Math.max(scrollHeight, Y.LDom.getViewportHeight()); return h; }, /** * document의 width를 반환한다. * @static * @method getDocumentWidth * @return {Int} 실제 document의 width(body와 그것의 공백을 포함하는) */ getDocumentWidth: function() { var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth; var w = Math.max(scrollWidth, Y.LDom.getViewportWidth()); return w; }, /** * viewport의 현재 height를 반환한다. * @static * @method getViewportHeight * @return {Int} 페이지의 표시 가능한 부분의 height(스크롤바는 제외). */ getViewportHeight: function() { var height = self.innerHeight; // Safari, Opera var mode = document.compatMode; if ( (mode || isIE) && !isOpera ) { // IE, Gecko height = (mode == 'CSS1Compat') ? document.documentElement.clientHeight : // Standards document.body.clientHeight; // Quirks } return height; }, /** * viewport의 현재 width를 반환한다. * @static * @method getViewportWidth * @return {Int} 페이지의 표시 가능한 부분의 width(스크롤바는 제외). */ getViewportWidth: function() { var width = self.innerWidth; // Safari var mode = document.compatMode; if (mode || isIE) { // IE, Gecko, Opera width = (mode == 'CSS1Compat') ? document.documentElement.clientWidth : // Standards document.body.clientWidth; // Quirks } return width; }, /** * 제공된 boolean method에 의해 적용된 테스트로 전달되는 가장 가까운 ancestor를 반환한다. * 성능상의 이유로, ID들은 허용되지 않으며 argument의 validation은 생략한다. * @static * @method getAncestorBy * @param {HTMLElement} node 시작지점으로서 사용할 HTMLElement * @param {Function} method element의 argument로서만 element를 받는 element들을 테스트 하기 위한 boolean method. * @return {Object} HTMLElement나 만약 찾지 못하는 경우 null */ getAncestorBy: function(node, method) { while ( (node = node.parentNode) ) { // NOTE: assignment if ( testElement(node, method) ) { return node; } } return null; }, /** * 주어진 클래스 이름을 가진 가장 가까운 ancestor를 반환한다. * @static * @method getAncestorByClassName * @param {String | HTMLElement} node HTMLElement나 시작지점으로서 사용할 ID * @param {String} className 클래스 이름 * @return {Object} HTMLElement */ getAncestorByClassName: function(node, className) { node = Y.LDom.get(node); if (!node) { return null; } var method = function(el) { return Y.LDom.hasClass(el, className); }; return Y.LDom.getAncestorBy(node, method); }, /** * 주어진 tag 이름을 가진 가까운 ancestor를 반환한다. * @static * @method getAncestorByTagName * @param {String | HTMLElement} node HTMLElement나 시작지점으로서 사용할 ID * @param {String} tag 이름 * @return {Object} HTMLElement */ getAncestorByTagName: function(node, tagName) { node = Y.LDom.get(node); if (!node) { return null; } var method = function(el) { return el.tagName && el.tagName.toUpperCase() == tagName.toUpperCase(); }; return Y.LDom.getAncestorBy(node, method); }, /** * 이전에 sibling되어 있는 HTMLElement를 반환한다. * 성능상의 이유로, ID들은 허용되지 않으며 argument의 validation은 생략한다. * Returns the nearest HTMLElement sibling if no method provided. * method가 제공되지 않는 경우 가장 가까운 sibling HTMLElement를 반환한다. * @static * @method getPreviousSiblingBy * @param {HTMLElement} node 시작지점으로서 사용할 HTMLElement * @param {Function} method 노드의 argument로서만 테스트될 sibling 노드를 받는 * sibling을 테스트하기 위하여 사용되는 boolean 함수 * @return {Object} HTMLElement나 찾지 못하는 경우 null */ getPreviousSiblingBy: function(node, method) { while (node) { node = node.previousSibling; if ( testElement(node, method) ) { return node; } } return null; }, /** * 이전에 sibling되어 있는 HTMLElement를 반환한다. * @static * @method getPreviousSibling * @param {String | HTMLElement} node 시작지점으로서 사용할 HTMLElement나 ID * @return {Object} HTMLElement나 찾지 못하는 경우 null */ getPreviousSibling: function(node) { node = Y.LDom.get(node); if (!node) { return null; } return Y.LDom.getPreviousSiblingBy(node); }, /** * 다음에 sibling 되어 있는 HTMLElement를 반환한다. * @static * @method getNextSibling * @param {String | HTMLElement} node 시작지점으로서 사용할 HTMLElement난 ID * @return {Object} HTMLElement나 찾지 못하는 경우 null */ getNextSibling: function(node) { node = Y.LDom.get(node); if (!node) { return null; } return Y.LDom.getNextSiblingBy(node); }, /** * node의 자식중 해당 tagName을 가지는 첫번째 element를 return한다. * @static * @method getFirstChildByTagName * @param {HTMLElement} node 부모 node * @param {String} tagName 자식의 tagName * @return {Object} HTMLElement 또는 null */ getFirstChildByTagName : function(node,tagName){ //selecter 성능 문제때문에 childNodes를 search해서 찾음. var element = null; var child = null; var nodeCount = node.childNodes.length; for(var i=0;i -1) width = width.replace("px", ""); return width; }, /** * param 정보에 따라 du_config에 있는 기본 객체의 css skin이 적용되게 하는 기능 * @method applyIfCss * @param {Object} oConfig Config정보 * @param {String} jsonPath du_config.js에서 적용하고자 하는 css의 jsonpath * @return {void} * @static */ applyIfCss : function(oConfig, jsonPath) { DU.applyIf(oConfig, { isDefaultCSS : true }); if(oConfig && oConfig.isDefaultCSS == true) { DU.includeCSS(DU.getConfig().getFirst(jsonPath + '.cssPath')); } DU.applyIf(oConfig, { isDefaultSkinCSS : true }); if(oConfig && oConfig.isDefaultSkinCSS == true) { var skinKey = jsonPath + '.defaultSkin'; var skinList = DU.getConfig().getFirst(skinKey); for(skin in skinList) { DU.includeCSS(DU.getConfig().getFirst(skinKey + '.' + skin)); } } //option skin 추가 - 채민철K if(oConfig && oConfig.optionSkin) { var skinKey = jsonPath + '.optionSkin.' + oConfig.optionSkin; var skinList = DU.getConfig().getFirst(skinKey); for(skin in skinList){ DU.includeCSS(DU.getConfig().getFirst(skinKey + '.' + skin)); } } }, /** * param 정보에 따라 du_config에 있는 기본 객체의 properties를 적용되게 하는 기능 * @method applyIfProperties * @param {Object} oConfig Config정보 * @param {String} jsonPath du_config.js에서 적용하고자 하는 properties의 jsonpath * @return {void} * @static */ applyIfProperties : function(oConfig, jsonPath) { var propertiesKey = jsonPath + '.defaultProperties'; var propertiesList = DU.getConfig().getFirst(propertiesKey); for(property in propertiesList) { if(DU.isUndefined(oConfig[property])) oConfig[property] = propertiesList[property]; } return oConfig; }, /** * node의 특정 방향 t,r,b,t(top,right,bottom,left)중의 한 방향이 화면에서 안보이는지 여부 top,right,bottom,left * bottom 좌표값이 99인데 화면에 안가렸냐? isVisibleSide(99);, right 좌표값이 99인데 화면에 안가렸냐 ? isVisibleSide(99,"r"); * @method isVisibleSide * @param {Int} coord 비교하려는 방향의 좌표값 * @param {String} side t,r,b,t 비교하려는 방향 default는 b(bottom) * @return {Boolean} * @static */ isVisibleSide : function(coord,side){ var region = Y.LDom.getClientRegion(); side = side ? side : "b"; switch(side){ case "t": return region.top - coord < 0 ? false : true; break; case "r": return region.right - coord < 0 ? false : true; break; case "b": return region.bottom - coord < 0 ? false : true; break; case "l": return region.left - coord < 0 ? false : true; break; } } }); var getXY = function() { if (document.documentElement.getBoundingClientRect) { // IE return function(el) { var box = el.getBoundingClientRect(), round = Math.round; var rootNode = el.ownerDocument; return [round(box.left + Y.LDom.getDocumentScrollLeft(rootNode)), round(box.top + Y.LDom.getDocumentScrollTop(rootNode))]; }; } else { return function(el) { // manually calculate by crawling up offsetParents var pos = [el.offsetLeft, el.offsetTop]; var parentNode = el.offsetParent; // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent var accountForBody = (isSafari && Y.LDom.getStyle(el, 'position') == 'absolute' && el.offsetParent == el.ownerDocument.body); if (parentNode != el) { while (parentNode) { pos[0] += parentNode.offsetLeft; pos[1] += parentNode.offsetTop; if (!accountForBody && isSafari && Y.LDom.getStyle(parentNode,'position') == 'absolute' ) { accountForBody = true; } parentNode = parentNode.offsetParent; } } if (accountForBody) { //safari doubles in this case pos[0] -= el.ownerDocument.body.offsetLeft; pos[1] -= el.ownerDocument.body.offsetTop; } parentNode = el.parentNode; // account for any scrolled ancestors while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) { if (parentNode.scrollTop || parentNode.scrollLeft) { pos[0] -= parentNode.scrollLeft; pos[1] -= parentNode.scrollTop; } parentNode = parentNode.parentNode; } return pos; }; } }() // NOTE: Executing for loadtime branching })(); /** * Event 유틸리티는 event 시스템들을 만들기 위한 DOM Event들과 도구들을 관리하는 * 유틸리티들을 제공한다. * * @module util * @title Event Utility * @namespace DU.util * @requires DU */ //@TODO optimize //@TODO use event utility, lang abstractions //@TODO replace /** * LKeyListener는 DOM element에 대하여 발생한 keydown/keyup event를 listening하는데 * 편리한 인테페이스를 제공하는 유틸리티이다. * @namespace DU.util * @class LKeyListener * @constructor * @param {HTMLElement} attachTo key event가 첨부되어야할 element나 element ID * @param {String} attachTo key event가 첨부되어야할 element나 element ID * @param {Object} keyData 감지할 key를 표시하는 object literal. * 가능한 attribute들은 shift(boolean), alt(boolean), ctrl(boolean), * keys(키코드를 표시하는 정수나 정수들의 배열)가 있다. * @param {Function} handler keyevent가 감지되었을때 발생시킬 LCustomEvent handler * @param {Object} handler handler를 표시하는 object literal. * @param {String} event (Optional) listening할 keydown이나 keyup 이벤트. * 자동적으로 기본값은 keydown. * * @knownissue "keypress" event는 Safari 2.x나 그 이하 버전에서 완전히 깨진다. * 해결 방법은 key listening을 위해 "keydown"을 사용하는 것이다. * 그러나 기본적인 키 입력 행동을 막고자 하는 경우, 이것은 * 키 핸들링을 꽤 지저분하게 만들 것이다. * @knownissue keydown은 또한 Safari 2.x나 그 이하 버전에서 ESC 키에 대해 깨진다. * 이것은 listening에 대한 다른 키를 선택하더라도 현재 해결방법은 없다. */ DU.util.LKeyListener = function(attachTo, keyData, handler, event) { if (!attachTo) { } else if (!keyData) { } else if (!handler) { } if (!event) { event = DU.util.LKeyListener.KEYDOWN; } /** * 키가 눌렸을때 내부적으로 LCustomEvent가 발생한다. * @event keyEvent * @private * @param {Object} keyData 감지할 key를 표시하는 object literal * 가능한 attribute들은 shift(boolean), alt(boolean), ctrl(boolean), * keys(키코드를 표시하는 정수나 정수들의 배열)가 있다. */ var keyEvent = new DU.util.LCustomEvent("keyPressed"); /** * enable() 함수를 통해 LKeyListener가 활성화 될 때, LCustomEvent가 발생한다. * @event enabledEvent * @param {Object} keyData 감지할 key를 표시하는 object literal * 가능한 attribute들은 shift(boolean), alt(boolean), ctrl(boolean), * keys(키코드를 표시하는 정수나 정수들의 배열)가 있다. */ this.enabledEvent = new DU.util.LCustomEvent("enabled"); /** * disable() 함수를 통해 LKeyListener가 비활성화 될 때, LCustomEvent가 발생한다. * @event disabledEvent * @param {Object} keyData 감지할 key를 표시하는 object literal * 가능한 attribute들은 shift(boolean), alt(boolean), ctrl(boolean), * keys(키코드를 표시하는 정수나 정수들의 배열)가 있다. */ this.disabledEvent = new DU.util.LCustomEvent("disabled"); if (typeof attachTo == 'string') { attachTo = document.getElementById(attachTo); } if (typeof handler == 'function') { keyEvent.on(handler); } else { keyEvent.on(handler.fn, handler.scope, handler.correctScope); } /** * 키가 눌렸을 때 key event를 핸들링한다. * @method handleKeyPress * @param {DOMEvent} e The keypress DOM event * @param {Object} obj The DOM event scope object * @private */ function handleKeyPress(e, obj) { if (! keyData.shift) { keyData.shift = false; } if (! keyData.alt) { keyData.alt = false; } if (! keyData.ctrl) { keyData.ctrl = false; } // check held down modifying keys first if (e.shiftKey == keyData.shift && e.altKey == keyData.alt && e.ctrlKey == keyData.ctrl) { // if we pass this, all modifiers match var dataItem; if (keyData.keys instanceof Array) { for (var i=0;i 0) { for (var i = 0, len = hashParts.length; i < len; i++) { hashPart = hashParts[i].split("="); hash[decodeURIComponent(hashPart[0])] = decodeURIComponent(hashPart[1]); } } return hash; }, /** * 엑세스 가능한 모든 쿠키를 나타내는 object에 쿠키 문자열을 파싱한다. * @param {String} text 파싱할 쿠키 문자열. * @param {Boolean} decode (Optional) 쿠키값을 디코딩할지에 대한 여부를 표시. 기본값은 true. * @return {Object} 각각의 엑세스 가능한 쿠키에 대한 항목(entry)을 포함하는 object * @method _parseCookieString * @private * @static */ _parseCookieString: function(text /*:String*/, decode /*:Boolean*/) /*:Object*/{ var cookies /*:Object*/ = new Object(); if (DU.isString(text) && text.length > 0) { var decodeValue = (decode === false ? function(s){ return s; } : decodeURIComponent); if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)) { var cookieParts /*:Array*/ = text.split(/;\s/g); var cookieName /*:String*/ = null; var cookieValue /*:String*/ = null; var cookieNameValue /*:Array*/ = null; for (var i = 0, len = cookieParts.length; i < len; i++) { //check for normally-formatted cookie (name-value) cookieNameValue = cookieParts[i].match(/([^=]+)=/i); if (cookieNameValue instanceof Array) { cookieName = decodeURIComponent(cookieNameValue[1]); cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length + 1)); } else { //means the cookie does not have an "=", so treat it as a boolean flag cookieName = decodeURIComponent(cookieParts[i]); cookieValue = cookieName; } cookies[cookieName] = cookieValue; } } } return cookies; }, //------------------------------------------------------------------------- // Public Methods //------------------------------------------------------------------------- /** * 주어진 이름에 대한 쿠키값을 반환한다. * @param {String} name 조회할 쿠키의 이름 * @param {Function} converter (Optional) 함수를 반환하기 전에 값에 대해 실행하기 위한 함수. * 함수는 쿠키가 존재하지 않는 경우 사용되지 않는다. * @return {Variant} converter가 명시되지 않은 경우, 문자열이나 쿠키가 존재하지 않는다면 null을 반환한다. * converter가 명시된 경우 converter로부터의 반환값이나 쿠키가 존재하지 않는다면 null을 반환한다. * @method get * @static */ get: function(name /*:String*/, converter /*:Function*/) /*:Variant*/{ var cookies /*:Object*/ = this._parseCookieString(document.cookie); if (!DU.isString(name) || name === "") { throw new TypeError("Cookie.get(): Cookie name must be a non-empty string."); } if (DU.isUndefined(cookies[name])) { return null; } if (!DU.isFunction(converter)) { return cookies[name]; } else { return converter(cookies[name]); } }, /** * subcookie의 값을 반환한다. * @param {String} name 조회할 쿠키의 이름 * @param {String} subName 조회할 subcookie의 이름 * @param {Function} converter (Optional) 함수를 반환하기 전에 값에 대해 실행하기 위한 함수. * 함수는 쿠키가 존재하지 않는 경우 사용되지 않는다. * @return {Variant} 쿠키가 존재하지 않는 경우 null을 반환한다. * subcookie가 존재하지 않는 경유 역시 null을 반환한다. * converter가 명시되어 있지 않고 subcookie가 존재하는 경우 문자열을 반환한다. * conerter가 명시되어 있고 subcookie가 존재하는 경우 converter로 부터 반환된 값을 반환한다. * @method getSub * @static */ getSub: function(name /*:String*/, subName /*:String*/, converter /*:Function*/) /*:Variant*/{ var hash /*:Variant*/ = this.getSubs(name); if (hash !== null) { if (!DU.isString(subName) || subName === "") { throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string."); } if (DU.isUndefined(hash[subName])) { return null; } if (!DU.isFunction(converter)) { return hash[subName]; } else { return converter(hash[subName]); } } else { return null; } }, /** * 주어진 이름의 쿠키에 저장된 name-value 쌍을 포함하는 object를 반환한다. * @param {String} name 조회할 쿠키의 이름 * @return {Object} 주어진 이름의 쿠키가 존재하면 name-value쌍의 object, 존재하지 않으면 null을 반환한다. * @method getHash * @static */ getSubs: function(name /*:String*/) /*:Object*/{ //check cookie name if (!DU.isString(name) || name === "") { throw new TypeError("Cookie.getSubs(): Cookie name must be a non-empty string."); } var cookies = this._parseCookieString(document.cookie, false); if (DU.isString(cookies[name])) { return this._parseCookieHash(cookies[name]); } return null; }, /** * 이전에 유효 기간을 설정함으로 인하여 컴퓨터에서 쿠키를 삭제한다. * @param {String} name 삭제할 쿠키의 이름 * @param {Object} options (Optional) 하나 혹은 여러개의 쿠키 옵션들을 포함한 object: * path (a string), domain (a string), secure (true/false). * expire 옵션은 method에 의해 덮어 씌어질수 있다. * @return {String} 생성된 쿠키 문자열 * @method remove * @static */ remove: function(name /*:String*/, options /*:Object*/) /*:String*/{ //check cookie name if (!DU.isString(name) || name === "") { throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string."); } //set options options = options || {}; options.expires = new Date(0); //set cookie return this.set(name, "", options); }, /** * 주어진 이름을 가진 sub cookie를 삭제한다. * @param {String} name subcookie가 존재하는 쿠키의 이름 * @param {String} subName 삭제할 subcookie의 이름 * @param {Object} options (Optional) 하나 혹은 여러개의 쿠키 옵션들을 포함한 object: * path (a string), domain (a string), expires (a Date object), secure (true/false). * 이것은 원래 subcookie와 동일한 설정이어야 한다. * @return {String} 생성된 쿠키 문자열 * @method removeSub * @static */ removeSub: function(name /*:String*/, subName /*:String*/, options /*:Object*/) /*:String*/{ //check cookie name if (!DU.isString(name) || name === "") { throw new TypeError("Cookie.removeSub(): Cookie name must be a non-empty string."); } //check subcookie name if (!DU.isString(subName) || subName === "") { throw new TypeError("Cookie.removeSub(): Subcookie name must be a non-empty string."); } //get all subcookies for this cookie var subs = this.getSubs(name); //delete the indicated subcookie if (DU.isObject(subs) && DU.hasOwnProperty(subs, subName)) { delete subs[subName]; //reset the cookie return this.setSubs(name, subs, options); } else { return ""; } }, /** * Sets a cookie with a given name and value. * 주어진 이름과 값으로 쿠키를 설정한다. * @param {String} name 설정할 쿠키의 이름 * @param {Variant} value 쿠키에 대한 설정할 값 * @param {Object} options (Optional) 하나 혹은 여러개의 쿠키 옵션들을 포함한 object: * path (a string), domain (a string), expires (a Date object), secure (true/false). * @return {String} 생성된 쿠키 문자열 * @method set * @static */ set: function(name /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/{ if (!DU.isString(name)) { throw new TypeError("Cookie.set(): Cookie name must be a string."); } if (DU.isUndefined(value)) { throw new TypeError("Cookie.set(): Value cannot be undefined."); } var text /*:String*/ = this._createCookieString(name, value, true, options); document.cookie = text; return text; }, /** * 특정 값으로 주어진 이름의 sub cookie를 설정한다. * @param {String} name 설정할 쿠키의 이름 * @param {String} subName 설정할 subcookie의 이름 * @param {Variant} value 설정할 값 * @param {Object} options (Optional) 하나 혹은 여러개의 쿠키 옵션들을 포함한 object: * path (a string), domain (a string), expires (a Date object), secure (true/false). * @return {String} 생성된 쿠키 문자열 * @method setSub * @static */ setSub: function(name /*:String*/, subName /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/{ if (!DU.isString(name) || name === "") { throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string."); } if (!DU.isString(subName) || subName === "") { throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string."); } if (DU.isUndefined(value)) { throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined."); } var hash /*:Object*/ = this.getSubs(name); if (!DU.isObject(hash)) { hash = new Object(); } hash[subName] = value; return this.setSubs(name, hash, options); }, /** * name-value 쌍의 해시를 포함하는 주어진 이름의 쿠키를 설정한다. * @param {String} name 설정할 쿠키의 이름 * @param {Object} value name-value 쌍을 포함하는 object * @param {Object} options (Optional) 하나 혹은 여러개의 쿠키 옵션들을 포함한 object: * path (a string), domain (a string), expires (a Date object), secure (true/false). * @return {String} 생성된 쿠키 문자열 * @method setSubs * @static */ setSubs: function(name /*:String*/, value /*:Object*/, options /*:Object*/) /*:String*/{ if (!DU.isString(name)) { throw new TypeError("Cookie.setSubs(): Cookie name must be a string."); } if (!DU.isObject(value)) { throw new TypeError("Cookie.setSubs(): Cookie value must be an object."); } var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options); document.cookie = text; return text; } }; /** * LException 정의 * @class LException * @constructor LException * @param {Error|String} message 생성할 메시지나 Error 객체 * @param {Object} o 에러가 발생한 객체 정보 otype속성이 존재하는 객체만 지원 */ DU.LException = function(message, o) { if (message instanceof DU.LException) { this.message = message.message; this.name = message.name; this.fileName = message.fileName; this.number = message.number || message.lineNumber; this.description = message.description; this.stack = message.stack; } else if(typeof message == 'string') { this.message = message; } else { if(message) { this.message = message.message; this.name = message.name; this.fileName = message.fileName; this.number = message.number || message.lineNumber; this.stack = message.stack; } } /** * @description 에러 메시지 * @property message * @public * @type {String} */ this.callerOType = ''; if(DU.isUndefined(o) == false) { if(typeof o == 'string') this.callerOType = o; else this.callerOType = o.otype ? o.otype : ''; } this.caller = arguments.caller; this.error = null; try { foo.bar; } catch(testExcp) { this.error = testExcp; } if(!this.fileName && !this.isExceptionInfo && this.error.stack) { this.createExceptionInfo(this.error); } } DU.extend(DU.LException, Error, { /** * 객체의 Object Type을 리턴한다. * @method getOType * @return {String} 에러가 발생한 객체의 otype 값, otype값이 없으면 ''문자 */ getOType : function() { return this.callerOType; }, /** * 에러 메시지를 리턴한다. * @method getMessage * @return {String} 에러 메시지 */ getMessage : function() { return this.message; }, /** * stack 정보로 현재 에러 정보를 생성한다. * @method createExceptionInfo * @private * @param {Object} excp 에러 객체 * @return {void} */ createExceptionInfo: function(excp) { this.isExceptionInfo = true; var stackTrace = this.getErrorStack(excp); if(stackTrace.length > 1) { var idx = stackTrace[1].lastIndexOf(":"); if(idx > 0) { this.lineNumber = stackTrace[1].substring(idx + 1, stackTrace[1].length - 1); this.fileName = stackTrace[1].substring(0, idx); } } }, /** * 에러의 위치를 찾기 위한 Stack 정보를 리턴, 브라우져마다 안되는 경우 많음. * @method getErrorStack * @private * @return {String} Stack 정보를 리턴 */ getErrorStack : function(excp) { var stack = []; var name; if (!excp || !excp.stack) { return stack; } var stacklist = excp.stack.split('\n'); stack = stacklist; /* for (var i = 0; i < stacklist.length - 1; i++) { var framedata = stacklist[i]; name = framedata.match(/^(\w*)/)[1]; if (!name) { name = 'anonymous'; } stack[stack.length] = name; } // remove top level anonymous functions to match IE while (stack.length && stack[stack.length - 1] == 'anonymous') { stack.length = stack.length - 1; } */ return stack; }, /** * function의 이름을 리턴하는 메소드 * @method getFunctionName * @private * @return {String} 해당 Function의 이름, 단 test = function() 이런 구조여야 가능 */ getFunctionName : function (aFunction) { var regexpResult = aFunction.toString().match(/function(\s*)(\w*)/); if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) { return regexpResult[2]; } return 'anonymous'; }, /** * StackTrace 정보를 리턴 * @method getStackTrace * @return {String} StackTrace 정보를 리턴값 */ getStackTrace : function() { var result = ''; var caller = arguments.caller; if (typeof(this.caller) != 'undefined') { // IE, not ECMA for (var a = DU.isUndefined(this.caller) ? caller : this.caller; a != null; a = a.caller) { result += '> ' + this.getFunctionName(a.callee) + '\n'; if (a.caller == a) { result += '*'; break; } } } else { // Mozilla, not ECMA var stack = this.getErrorStack(this.error); for (var i = 1; i < stack.length; i++) { result += '> ' + stack[i] + '\n'; } } return result; } }); DU.LException.getException = function(message, o) { if (message instanceof DU.LException) { return message; } else { return new DU.LException(message, o); } } DU.getException = DU.LException.getException; /** * Utilities for state management * @module state * @title LProvider Utility * @namespace DU.state * @requires DU */ DU.namespace("state"); /** * Abstract LProvider utility. * @namespace DU.state * @class LProvider * @extends DU.util.LEventProvider * @constructor LProvider * @param hash {Object} The intial LProvider. */ DU.state.LProvider = function(){ /** * @description 키와 값을 가지는 속성. * @property state * @private * @static * @type Object */ this.state = {}; /** * @description 값의 상태가 바뀌면 호출되는 이벤트. * @event statechange * @param {Object} target this객체 * @param {String} type 변경 종류 * @param {String} key 키 * @param {Array|Object} value 값 */ this.createEvent('statechange'); DU.state.LProvider.superclass.constructor.call(this); }; // Copy static members to DataSource class DU.extend(DU.state.LProvider, DU.util.LEventProvider, { /** * @description 상태정보를 얻어오는 메소드 * @method get * @public * @param {String} key 상태정보를 얻어오는 키 이름 * @param {Object} defaultValue 값이 없을 경우 리턴되는 기본값 * @return {Object} 키 이름에 해당되는 결과 값 */ get: function(key, defaultValue){ return this.state[key] ? this.state[key] : defaultValue; }, /** * @description 상태정보를 저장하는 메소드 * @method set * @public * @param {String} key 저장하고자 하는 키 이름 * @param {Object} value 저장하고자 하는 키 이름 * @return void */ set: function(key, value){ this.state[key] = value; this.fireEvent('statechange', { target: this, type:'set', key: key, value: value}); }, /** * @description 상태정보를 삭제하는 메소드 * @method remove * @public * @param {String} key 지우고자 하는 키 이름 * @return void */ remove: function(key){ var value = this.state[key]; delete this.state[key]; this.fireEvent('statechange', { target: this, type:'remove', key: key, value: value}); } }); /** * Cookie Provider * @module state * @title Cookie Provider * @namespace DU.state */ DU.namespace("DU.state"); /** * Cookie Provider utility. * @namespace DU.state * @class LCookieProvider * @extends DU.state.LProvider * @constructor LCookieProvider * @param {Object} oConfig The intial LCookieProvider. */ DU.state.LCookieProvider = function(oConfig){ DU.state.LCookieProvider.superclass.constructor.call(this); /** * domain정보를 가지는 문자열 * @property domain * @type Object * @private */ this.domain = null; /** * Cookie 만료 Date 객체 * @property expires * @type {Date} * @private */ this.expires = new Date(new Date().getTime() + (1000 * 60 * 60 * 24)); //1 days; /** * Cookie Path * @property path * @type {String} * @private */ this.path = '/'; /** * Cookie secure * @property secure * @type {Boolean} * @private */ this.secure = false; var config = oConfig || {}; DU.applyObject(this, config); /** * @description 키와 값을 가지는 속성. * @property state * @private * @static * @type Object */ this.state = this._parseCookieString(document.cookie); } DU.extend(DU.state.LCookieProvider, DU.state.LProvider, { /** * @description 상태정보를 저장하는 메소드 * @method set * @public * @param {String} key 저장하고자 하는 키 이름 * @param {Object} value 저장하고자 하는 키 이름 * @return void */ set: function(name, value){ if (!DU.isString(name)) { throw new TypeError("LCookieProvider.set(): Argument must be an string."); } if (DU.isUndefined(value)) { throw new TypeError("LCookieProvider.set(): Value cannot be undefined."); } document.cookie = this._createCookieString(name, value, false, this); DU.state.LCookieProvider.superclass.set.call(this, name, value); }, /** * @description 상태정보를 삭제하는 메소드 * @method remove * @public * @param {String} key 지우고자 하는 키 이름 * @return void */ remove: function(name){ if (!DU.isString(name)) { throw new TypeError("LCookieProvider.set(): Argument must be an string."); } var options = { domain : this.domain, expires : this.expires, path : this.path, secure : this.secure }; options.expires = new Date(0); var text = 'yi-' + name + "=" + this.get(name) + "; expires=Thu, 01-Jan-70 00:00:01 GMT" + ((options.path == null) ? "" : ("; path=" + options.path)) + ((options.domain == null) ? "" : ("; domain=" + options.domain)) + ((options.secure == true) ? "; secure" : ""); document.cookie = text; DU.state.LCookieProvider.superclass.remove.call(this, name); }, /** * Creates a cookie string that can be assigned into document.cookie. * @param {String} name The name of the cookie. * @param {String} value The value of the cookie. * @param {encodeValue} encodeValue True to encode the value, false to leave as-is. * @param {Object} options (Optional) Options for the cookie. * @return {String} The formatted cookie string. * @method _createCookieString * @private * @static */ _createCookieString: function(name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/{ var text /*:String*/ = 'yi-' + encodeURIComponent(name) + "=" + (encodeValue ? encodeURIComponent(value) : value); if (DU.isObject(options)) { //expiration date if (options.expires instanceof Date) { text += "; expires=" + options.expires.toGMTString(); } //path if (DU.isString(options.path) && options.path != "") { text += "; path=" + options.path; } //domain if (DU.isString(options.domain) && options.domain != "") { text += "; domain=" + options.domain; } //secure if (options.secure === true) { text += "; secure"; } } return text; }, /** * Parses a cookie string into an object representing all accessible cookies. * @param {String} text The cookie string to parse. * @param {Boolean} decode (Optional) Indicates if the cookie values should be decoded or not. Default is true. * @return {Object} An object containing entries for each accessible cookie. * @method _parseCookieString * @private * @static */ _parseCookieString: function(text /*:String*/, decode /*:Boolean*/) /*:Object*/{ var cookies /*:Object*/ = new Object(); if (DU.isString(text) && text.length > 0) { var decodeValue = (decode === false ? function(s){ return s; } : decodeURIComponent); if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)) { var cookieParts /*:Array*/ = text.split(/;\s/g); var cookieName /*:String*/ = null; var cookieValue /*:String*/ = null; var cookieNameValue /*:Array*/ = null; for (var i = 0, len = cookieParts.length; i < len; i++) { //check for normally-formatted cookie (name-value) cookieNameValue = cookieParts[i].match(/([^=]+)=/i); if (cookieNameValue instanceof Array) { if (cookieNameValue[1] && cookieNameValue[1].substring(0, 3) == "yi-") { cookieName = decodeURIComponent(cookieNameValue[1].substring(3)); cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length + 1)); } } else { if (cookieParts[i] && cookieParts[i].substring(0, 3) == "yi-") { //means the cookie does not have an "=", so treat it as a boolean flag cookieName = decodeURIComponent(cookieParts[i].substring(3)); cookieValue = cookieName; } } cookies[cookieName] = cookieValue; } } } return cookies; } }); /** * Utilities for state management * @module state * @title Provider Utility * @namespace DU.state * @requires DU */ DU.namespace("state"); /** * LManager utility. * @namespace DU.state * @class LManager * @constructor LManager */ DU.state.LManager = function(provider){ /** * @description 현재 provider 객체 * @property provider * @private * @type Object */ this.provider = provider; if(this.provider == null) { this.provider = new DU.state.LProvider(); } }; // Copy static members to DataSource class DU.state.LManager.prototype = { /** * @description 상태정보를 얻어오는 메소드 * @method get * @public * @param {String} key 상태정보를 얻어오는 키 이름 * @param {Object} defaultValue 값이 없을 경우 리턴되는 기본값 * @return {Object} 키 이름에 해당되는 결과 값 */ get: function(key, defaultValue){ return this.provider.get(key, defaultValue); }, /** * @description 상태정보를 저장하는 메소드 * @method set * @public * @param {String} key 저장하고자 하는 키 이름 * @param {Object} value 저장하고자 하는 키 이름 * @return void */ set: function(key, value){ this.provider.set(key, value); }, /** * @description 상태정보를 삭제하는 메소드 * @method remove * @public * @param {String} key 지우고자 하는 키 이름 * @return void */ remove: function(key){ this.provider.remove(key); }, /** * @description Provider정보를 셋팅하는 메소드 * @method setProvider * @public * @param {String} provider 셋팅하고자 하는 provider * @return void */ setProvider : function(provider) { this.provider = provider; }, /** * @description Provider정보를 리턴하는 메소드 * @method getProvider * @public * @return {DU.state.LProvider} 선택된 provider 정보 */ getProvider : function() { return this.provider; } } /** * LConfiguration Provider * @module config * @title LConfiguration Provider * @namespace DU.config * @requires DU */ DU.namespace("DU.config"); /** * LConfiguration Provider utility. * @namespace DU.config * @class LConfigurationProvider * @extends DU.state.LProvider * @constructor LConfigurationProvider * @param {Object} oConfig The intial LConfigurationProvider. */ DU.config.LConfigurationProvider = function(oConfig){ DU.config.LConfigurationProvider.superclass.constructor.call(this); /** * @description Object data 객체 * @property data * @private * @type object */ this.data = null; var config = oConfig || {}; DU.applyObject(this, config, true); this.reload(); } DU.extend(DU.config.LConfigurationProvider, DU.state.LProvider, { /** * @description 상태정보를 얻어오는 메소드 * @method get * @public * @param {String} key 상태정보를 얻어오는 키 이름 * @param {Object} defaultValue 값이 없을 경우 리턴되는 기본값 * @return {Object} 키 이름에 해당되는 결과 값 */ get: function(name) { var value = DU.config.LConfigurationProvider.superclass.get.call(this, name); if(DU.isUndefined(value)== false) { return value; } else { value = DU.util.LJson.jsonPath(this.data, name, {resultType: "VALUE"}); if (value === false) { value = null; } else { this.state[name] = value; } return value; } }, /** * @description 상태정보를 저장하는 메소드 * @method set * @public * @param {String} key 저장하고자 하는 키 이름 * @param {Object} value 저장하고자 하는 키 이름 * @return void */ set: function(name, value){ if (!DU.isString(name)) { throw new TypeError("LConfigurationProvider.set(): Argument must be an string."); } if (DU.isUndefined(value)) { throw new TypeError("LConfigurationProvider.set(): Value cannot be undefined."); } var nameList = name.split('.'), i = 1, dataObj = this.data; for(; i < nameList.length - 1; i++) { if(!dataObj[nameList[i]]) dataObj[nameList[i]] = {}; dataObj = dataObj[nameList[i]]; } dataObj[nameList[i]] = value; DU.config.LConfigurationProvider.superclass.set.call(this, name, value); }, /** * @description 상태정보를 삭제하는 메소드 * @method remove * @public * @param {String} key 지우고자 하는 키 이름 * @return void */ remove: function(name){ if (!DU.isString(name)) { throw new TypeError("LConfigurationProvider.set(): Argument must be an string."); } DU.config.LConfigurationProvider.superclass.remove.call(this, name); }, /** * @description 상태정보를 다시 읽어드리는 메소드 * @method reload * @public * @return void */ reload : function() { if(DU.isNull(this.data)) { if(DU.isUndefined(DU.config.ConfigData)) DU.config.ConfigData = {}; this.data = DU.config.ConfigData; } this.state = {}; } }); /** * LConfiguration 유틸리티 * @module config * @title Provider Utility * @namespace DU.config * @requires DU */ DU.namespace("DU.config"); /** * LConfiguration utility. * @namespace DU.config * @class LConfiguration * @extends DU.state.LManager * @constructor LConfiguration */ DU.config.LConfiguration = function(){ if(DU.config.LConfiguration.caller != DU.config.LConfiguration.getInstance){ throw new DU.LException("Can't call the constructor method.", this); } DU.config.LConfiguration.superclass.constructor.call(this); /** * @description provider 객체 * @property provider * @private * @type object */ this.provider = new DU.config.LConfigurationProvider(); }; DU.config.LConfiguration.instanceObj = null; /** * @description 인스턴스를 얻어오는 메소드 * @method getInstance * @public * @static * @return {DU.config.LConfiguration} */ DU.config.LConfiguration.getInstance = function() { if(this.instanceObj == null){ this.instanceObj = new DU.config.LConfiguration(new DU.config.LConfigurationProvider()); } return this.instanceObj ; } DU.extend(DU.config.LConfiguration, DU.state.LManager, { /** * @description 객체의 문자열 * @property otype * @private * @type {String} */ otype : 'DU.config.LConfiguration', /** * @description 상태정보를 다시 읽어드리는 메소드 * @method reload * @public * @return void */ reload : function() { this.provider.reload(); }, /** * @description 가장 첫번째 데이터를 리턴한다. * @method getFirst * @public * @return {Object} */ getFirst : function(key, defaultValue) { var list = this.get(key); if(list != null && list.length > 0) { return list[0]; } return defaultValue ? defaultValue : null; } }); /** * @module message * @title DU Global * @static */ DU.namespace('DU.message'); /** * 메시지를 관리하는 기능 * @namespace DU.message * @class LMessageManager * @extends DU.util.LEventProvider * @constructor LMessageManager * @static */ DU.message.LMessageManager = function(oConfig) { DU.message.LMessageManager.superclass.constructor.call(this); /** * 메시지 데이터를 담아 놓은 객체 * @property localeData * @type Object * @private */ this.localeData = {}; if(this.isApplyDefaultMessage) { var currentLocaleData = this.localeData[DU.getConfig().get('$.core.defaultLocale')]; if(!currentLocaleData) { var defaultMessage = DU.getConfig().get('$.core.message.defaultMessage')[0]; this.localeData[DU.getConfig().get('$.core.defaultLocale')] = defaultMessage; } } /** * 현재 설정된 Locale 정보 * @property currentLocale * @type Object * @private */ this.currentLocale = DU.getConfig().getFirst('$.core.defaultLocale'); /** * @description Core Locale 정보를 읽어올때 발생하는 이벤트 * @event createRootLocale */ this.createEvent('createRootLocale'); if(DU.isObject(oConfig)) { if(oConfig.locale) { this.currentLocale = oConfig.locale; } if(oConfig.isAutoLoad) { this.isAutoLoad = oConfig.isAutoLoad; } } } DU.extend(DU.message.LMessageManager, DU.util.LEventProvider, { /** * @description 해당 Locale의 메시지가 없을 경우 서버에서 자동으로 Locale에 해당되는 메시지를 읽어 올지 결정하는 변수 * @config isAutoLoad * @type {Boolean} * @default true */ /** * @description 해당 Locale의 메시지가 없을 경우 서버에서 자동으로 Locale에 해당되는 메시지를 읽어 올지 결정하는 변수 * @property isAutoLoad * @type Boolean * @public */ isAutoLoad : true, /** * @description 해당 Locale의 메시지가 없을 경우 defaultLocale에 해당되는 메시지를 출력할지 결정하는 변수 * @config isApplyDefaultMessage * @type {Boolean} * @default true */ /** * @description 해당 Locale의 메시지가 없을 경우 defaultLocale에 해당되는 메시지를 출력할지 결정하는 변수 * @property isApplyDefaultMessage * @type Boolean * @public */ isApplyDefaultMessage : true, /** * 현재 셋팅되어 있는 Locale 정보를 변경한다. *

     * var mm = new DU.message.LMessageManager();

     * mm.setLocale('ko_KR');

     * 
* @method setLocale * @param {String} currentLocale 변경하고자 하는 locale 정보 * @return void */ setLocale : function(currentLocale) { this.currentLocale = currentLocale; }, /** * 메시지 데이터를 직접 추가한다. *

     * var localeData = { locale : 'ko_KR', message:{test:'테스트'} }

     * var mm = new DU.message.LMessageManager();

     * mm.addLocaleData(localeData);

     * 
* @method addLocaleData * @param {Object} localeData 실제 메시지 데이터를 가지는 객체 * @return void */ addLocaleData : function(localeData) { if(!localeData.locale) { throw new TypeError("LMessageManager.addLocaleData(): Not found locale attribute in localeData."); } var oldData = this.localeData[localeData.locale] || {}; var rootLevel = ["core", "base", "message"]; for(var i = 0 ; i < rootLevel.length ; i++) { if(!oldData[rootLevel[i]]) { oldData[rootLevel[i]] = {} } } for(var i = 0 ; i < rootLevel.length ; i++) { var levelCode = rootLevel[i]; for(m in localeData[levelCode]) { if(!Object.prototype[m]) { oldData[levelCode][m] = localeData[levelCode][m]; } } } this.localeData[localeData.locale] = oldData; }, /** * 메시지 데이터를 읽어 온다. *

     * var mm_Ko = new DU.message.LMessageManager();

     * mm_Ko.load({locale:'ko_KR'});

     * alert(mm_Ko.get('$.core.test'));

     * var mm_En = new DU.message.LMessageManager({locale:'en_US'});

     * 

     * var newData_ko_KR = {

     *     locale:'ko_KR',

     *     message : {

     *         test2:'?쒓?'		

     *     }

     * }

     * 

     * var newData_en_US = {

     *     locale:'en_US',

     *     message : {

     *         test2:'english'		

     *     }

     * }

     * 

     * mm_Ko.addLocaleData(newData_ko_KR);

     * mm_En.addLocaleData(newData_en_US);

     * 

     * var data = mm_Ko.get('$.message.test2');

     * alert(data);

     * 

     * var data = mm_En.get('$.message.test2');

     * alert(data);

     * 
* @method get * @param {Object} name 읽어오고자하는 메시지 키값 * @param {Array} paramArray 읽어올때 @로 대체될 값 * @return {String} 결과 메시지 */ get : function(name, paramArray, locale) { if(DU.isUndefined(name)) { throw new TypeError("LMessageManager.get(): not found name attribute"); } if(!DU.isUndefined(locale)) this.setLocale(locale); var currentLocaleData = this.localeData[this.currentLocale]; var message = currentLocaleData ? DU.util.LJson.jsonPath(currentLocaleData, name, {resultType: "VALUE"})[0] : null; if(DU.isNull(message) || DU.isUndefined(message)) { if(this.isAutoLoad) { this.load({locale:this.currentLocale}); currentLocaleData = this.localeData[this.currentLocale]; if (!currentLocaleData) { throw new Error('LMessageManager.get(): Not found message Data. : ' + this.currentLocale); } message = DU.util.LJson.jsonPath(currentLocaleData, name, {resultType: "VALUE"})[0]; } else { throw new Error('LMessageManager.get(): Not found message Data. : ' + this.currentLocale); } } if (DU.isNull(message) || DU.isUndefined(message)) { throw new Error('LMessageManager.get(): Not found message.'); } if(message == false) return null; var index = 0; var re = /@/g; var count = 0; if (paramArray == null) { return message; } while ( (index = message.indexOf("@", index)) != -1) { if (paramArray[count] == null) { paramArray[count] = ""; } message = message.substr(0, index) + String(paramArray[count]) + message.substring(index + 1); index = index + String(paramArray[count++]).length; } return message; }, /** * Locale에 해당되는 데이터를 읽어온다. * @method load * @param {Object} oConfig 읽어올 환경정보를 가지는 객체 * @return void */ load : function(oConfig) { oConfig = oConfig ? oConfig : {locale:this.currentLocale}; this.createRootLocale(oConfig); // 업무 메시지 로드 var contextPath = DU.getConfig().getFirst('$.core.contextPath'); var dujsfRootPath = DU.getConfig().getFirst('$.core.dujsfRootPath'); var localePath = DU.getConfig().getFirst('$.core.message.localePath'); if(!oConfig.jsFile) { oConfig.jsFile = 'lang-' + oConfig.locale + '.js'; } DU.includeJs(contextPath + dujsfRootPath + localePath + '/' + oConfig.jsFile, true); var currentLocaleData = eval('DU.message.locale.' + oConfig.locale); this.addLocaleData(currentLocaleData); }, /** * Core Locale에 해당되는 데이터를 읽어온다. * @method createRootLocale * @param {Object} oConfig 읽어올 환경정보를 가지는 객체 * @return void */ createRootLocale : function(oConfig) { // core 메시지 로드 this.fireEvent('createRootLocale'); var localeObj = eval('DU.message.locale.' + oConfig.locale); if(DU.isUndefined(localeObj)) { DU.namespace('DU.message.locale.' + oConfig.locale); } } }); (function() { // internal shorthand var Dom = DU.util.LDom, LAttributeProvider = DU.util.LAttributeProvider; DU.applyObject(DU.LElement.prototype, { /** * box-model 이슈를 위해 width와 height 설정을 자동으로 * 조절하기 위해서 true로 설정(기본적으로 true) */ autoBoxAdjust : true, /** * The default unit to append to CSS values where a unit isn't provided (defaults to px). * unit이 제공되지 않는 CSS 값들에 추가하기 위한 기본 unit(기본적으로 px). * @property defaultUnit * @type String */ defaultUnit : 'px', /** * @description element의 disabled CSS * @property CSS_ELEMENT_DISABLED * @private * @type {String} */ CSS_ELEMENT_DISABLED : 'L-disabled', /** * @description element의 invalid CSS * @property CSS_ELEMENT_INVALID * @private * @type {String} */ CSS_ELEMENT_INVALID : "L-invalid", /** * @description element의 repaint CSS * @property CSS_ELEMENT_REPAINT * @private * @type {String} */ CSS_ELEMENT_REPAINT : "L-repaint", /** * @description element의 masked CSS * @property CSS_ELEMENT_MASKED * @private * @type {String} */ CSS_ELEMENT_MASKED : "L-masked", /** * @description element의 masked의 출력 Panel CSS * @property CSS_ELEMENT_MASKED_PANEL * @private * @type {String} */ CSS_ELEMENT_MASKED_PANEL : "L-masked-panel", /** * @description 제공된 노드가 제공된 selector와 일치하는 경우를 테스트. * @method test * @param {string} selector node를 대상으로 테스트할 CSS LDomSelector. * @return{boolean} 노드가 selector와 일치하는지에 대한 여부. * @static */ test : function(selector) { return DU.util.LDomSelector.test(this.dom, selector); }, /** * @description HTMLElement method에 대한 Wrapper. * @method removeChild * @param {HTMLElement} child 삭제할 HTMLElement * @return {HTMLElement} 삭제된 DOM element. */ removeChild: function(child) { child = child.get ? child.get('element') : child; return this.get('element').removeChild(child); }, /** * @description HTMLElement method에 대한 Wrapper. * @method replaceChild * @param {HTMLElement} newNode 삽입할 HTMLElement * @param {HTMLElement} oldNode 교체할 HTMLElement * @return {HTMLElement} 교체된 DOM element. */ replaceChild: function(newNode, oldNode) { newNode = newNode.get ? newNode.get('element') : newNode; oldNode = oldNode.get ? oldNode.get('element') : oldNode; return this.get('element').replaceChild(newNode, oldNode); }, /** * HTMLElement chid 노드들의 array를 반환한다. * @static * @method getChildren * @return {Array} HTMLElement들의 static array */ getChildren : function(isDom) { var list = DU.util.LDom.getChildren(this.dom); if(!isDom || isDom === true) { for(var i = 0 ; i < list.length ; i++) list[i] = DU.get(list[i]); } return list; }, /** * @description mouse over시 class 적용 * @method addClassOnOver * @param {String} className 추가할 클래스 이름 * @return {DU.LElement} this */ addClassOnOver : function(className) { var me = this; me.hover(function(){ me.addClass(className); }, function(){ me.removeClass(className); }); return me; }, /** * @description mouse focus시 class 적용 * @method addClassOnFocus * @param {String} className 추가할 클래스 이름 * @return {DU.LElement} this */ addClassOnFocus : function(className) { var me = this; me.on('focus', function(e) { me.addClass(className); }, me.dom); me.on('blur', function(e) { me.removeClass(className); }, me.dom); return me; }, /** * @description mouse click시 class 적용 * @method addClassOnClick * @param {String} className 추가할 클래스 이름 * @return {DU.LElement} this */ addClassOnClick : function(className) { var me = this; me.on('mousedown', function(e) { me.addClass(className); }, me.dom); var mouseUp = function(e) { me.removeClass(className); me.unOn('blur', mouseUp); } me.on("mouseup", mouseUp, me.dom); return me; }, /** * @description Dom method를 위한 Wrapper. * @method replaceClass * @param {String} oldClassName 교체할 클래스 이름 * @param {String} newClassName 추가할 클래스 이름 */ replaceClass: function(oldClassName, newClassName) { return Dom.replaceClass(this.get('element'), oldClassName, newClassName); }, /** * @description toggle시에 class를 추가하거나 제거한다. * @method toggleClass * @param {String} className 추가할 클래스 이름 * @return {DU.LElement} this */ toggleClass: function(className) { return this.hasClass(className) ? this.removeClass(className) : this.addClass(className); }, /** * @description style 정보를 모두 적용한다. * @method applyStyles * @param {String|Object|Function} styles 적용할 style정보 * @return {DU.LElement} this */ applyStyles : function(styles){ DU.util.LDom.applyStyles(this, styles); return this; }, /** * @description 페이지 좌표에 기반한 element의 현재 위치를 가져온다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method getXY * @return {Array} element의 XY 위치 */ getXY: function() { return Dom.getXY(this.dom); }, /** * @description element가 위치되는 방법에 개의치 않고 페이지 좌표에서 html element의 위치를 설정한다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method setXY * @param {Array} pos 새 위치에 대한 X & Y를 포함한 값들(페이지에 기반한 좌표) * @param {Boolean} noRetry 첫번째가 실패할 경우 기본적으로 두번째로 위치를 설정한다. */ setXY: function(pos, noRetry) { return Dom.setXY(this.dom, pos, noRetry); }, /** * @description 페이지 좌표에 기반한 element의 현재 X 위치를 가져온다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method getX * @return {Number | Array} element의 X 위치 */ getX: function() { return Dom.getX(this.dom); }, /** * @description element가 위치되는 방법에 개의치 않고 페이지 좌표에서 html element의 X 위치를 설정한다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method setX * @param {Int} x element에 대한 X 좌표로 사용할 값. */ setX: function(x) { return Dom.setX(this.dom, x); }, /** * @description 페이지 좌표에 기반한 element의 현재 Y 위치를 가져온다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method getY * @return {Number | Array} element의 Y 위치 */ getY: function() { return Dom.getY(this.dom); }, /** * @description element가 위치되는 방법에 개의치 않고 페이지 좌표에서 html element의 Y 위치를 설정한다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method setY * @param {Int} y element에 대한 Y 좌표로 사용할 값. */ setY: function(y) { return Dom.setY(this.dom, y); }, /** * @description element가 위치되는 방법에 개의치 않고 페이지 좌표에서 element의 위치를 설정한다. * element는 반드시 페이지 좌표를 가지는 DOM 트리의 부분이어야 한다 * (display:none 혹은 element들이 추가되어 있지 않으면 false를 반환). * @method moveTo * @param {Number} x 새로운 위치에 대한 X값(페이지에 기반한 좌표) * @param {Number} y 새로운 위치에 대한 Y값(페이지에 기반한 좌표) * @param {Boolean/Object} anim (optional) 기본 animation에 대한 true, * 혹은 표준 standard Element animation config object * @return {DU.LElement} this */ moveTo : function(x, y, anim){ if(anim) { if(anim === true) anim = { points: { to: [x, y] } }; anim = DU.applyIf(anim, { type: 'LMotion' }); } var currAnim = this.getAnimation(anim, this.dom.id); (currAnim != null) ? currAnim.animate() : this.setXY([x, y]); return this; }, /** * @description animation 효과를 적용한다. * @method animate * @param {Object} anim animation 효과를 적용할 정보 * @return {DU.LElement} this */ animate: function(anim) { anim.type = anim.type || 'LMotion'; var currAnim = this.getAnimation(anim, this.dom.id); if (currAnim != null) { if (anim.delay) { DU.util.LFunction.defer(function(){ if (anim.onStart) currAnim.onStart.on(anim.onStart); currAnim.animate(); }, anim.delay, this); } else { if (anim.onStart) currAnim.onStart.on(anim.onStart); currAnim.animate(); } } return this; }, /* * @private */ addUnits : function(v, defaultUnit){ if(v === "" || v == "auto"){ return v; } if(v === undefined){ return ''; } if(typeof v == "number" || !El.unitPattern.test(v)){ return v + (defaultUnit || 'px'); } return v; }, /** * @description CSS 스타일을 사용하여 element의 left 위치를 직접 설정한다. ({@link #setX} 대신). * @method setLeft * @param {String} left left CSS property 값 * @return {DU.LElement} this */ setLeft : function(left){ this.setStyle("left", this.addUnits(left)); return this; }, /** * @description CSS 스타일을 사용하여 element의 top 위치를 직접 설정한다. ({@link #setY} 대신). * @method setTop * @param {String} top top CSS property 값 * @return {DU.LElement} this */ setTop : function(top){ this.setStyle("top", this.addUnits(top)); return this; }, /** * @description element의 CSS right 스타일을 설정한다. * @method setRight * @param {String} right right CSS property 값 * @return {DU.LElement} this */ setRight : function(right){ this.setStyle("right", this.addUnits(right)); return this; }, /** * @description element의 CSS bottom 스타일을 설정한다. * @method setBottom * @param {String} bottom bottom CSS property 값 * @return {DU.LElement} this */ setBottom : function(bottom){ this.setStyle("bottom", this.addUnits(bottom)); return this; }, /** * @description size를 설정한다. * @method setSize * @param {String} width element의 width offset * @param {String} height element의 height offset * @param {Boolean/Object} anim (optional) 기본 animation에 대한 true, * 혹은 표준 standard Element animation config object * @return {DU.LElement} this */ setSize : function(width, height, anim) { if(anim) { if(anim === true) anim = { width: { to: width }, height: { to: height } }; anim = DU.applyIf(anim, { type: 'LAnim' }); } var currAnim = this.getAnimation(anim, this.dom.id); if (currAnim != null) { currAnim.animate(); } else { this.setWidth(width); this.setHeight(height); } }, /** * @description left X 좌표를 가져온다. * @method getLeft * @param {Boolean} local 페이지 좌표 대신 local css 위치를 가져오기 위해서는 true * @return {Number} */ getLeft : function(local){ return !local ? this.getX() : parseInt(this.getStyle("left"), 10) || 0; }, /** * @description element의 right X 좌표를 가져온다.(element의 X 위치 + element width) * @method getRight * @param {Boolean} local 페이지 좌표 대신 local css 위치를 가져오기 위해서는 true * @return {Number} */ getRight : function(local){ return !local ? this.getX() + this.getWidth() : (this.getLeft(true) + this.getWidth()) || 0; }, /** * @description top Y 좌표를 가져온다. * @method getTop * @param {Boolean} local 페이지 좌표 대신 local css 위치를 가져오기 위해서는 true * @return {Number} */ getTop : function(local) { return !local ? this.getY() : parseInt(this.getStyle("top"), 10) || 0; }, /** * @description element의 bottom Y 좌표를 가져온다.(element의 Y 위치 + element height) * @method getBottom * @param {Boolean} local 페이지 좌표 대신 local css 위치를 가져오기 위해서는 true * @return {Number} */ getBottom : function(local){ return !local ? this.getY() + this.getHeight() : (this.getTop(true) + this.getHeight()) || 0; }, /** * @description 큐에 설정된 call들을 적용한다. * @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 제공된 parent 노드에 HTMLElement를 추가한다. * @method appendTo * @param {HTMLElement | Element} parentNode 추가될 노드 * @param {HTMLElement | Element} before 이전에 삽입할 부가적인 노드 * @return {HTMLElement} 추가된 DOM element */ appendTo: function(parent, before) { parent = (parent.get) ? parent.get('element') : Dom.get(parent); this.fireEvent('beforeAppendTo', { type: 'beforeAppendTo', target: parent }); before = (before && before.get) ? before.get('element') : Dom.get(before); var element = this.get('element'); if (!element) { return false; } if (!parent) { return false; } if (element.parent != parent) { if (before) { parent.insertBefore(element, before); } else { parent.appendChild(element); } } this.fireEvent('appendTo', { type: 'appendTo', target: parent }); return element; }, /** * @description attribute의 현재 값을 반환한다. * @method get * @param {String} key 반환될 값을 가진 attribute. * @return {Any} attribute의 현재 값. */ 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 여러 attribute의 값들을 설정한다. * @method setAttributes * @param {Object} map attribute들의 key-value map * @param {Boolean} silent change event들의 억제 여부 */ setAttributes: function(map, silent){ var el = this.get('element'); for (var key in map) { // need to configure if setting unconfigured HTMLElement attribute if ( !this._configs[key] && !DU.isUndefined(el[key]) ) { this.setAttributeConfig(key); } } // set based on configOrder for (var i = 0, len = this._configOrder.length; i < len; ++i) { if (map[this._configOrder[i]] !== undefined) { this.set(this._configOrder[i], map[this._configOrder[i]], silent); } } }, /** * 현재 overflow 설정을 저장하고 element의 overflow를 고정시킨다. - 삭제하기 위해서 {@link #unclip}를 사용한다. * @method clip * @return {DU.LElement} this */ clip : function(){ if(!this.isClipped){ this.isClipped = true; this.originalClip = { o: this.getStyle("overflow"), x: this.getStyle("overflow-x"), y: this.getStyle("overflow-y") }; this.setStyle("overflow", "hidden"); this.setStyle("overflow-x", "hidden"); this.setStyle("overflow-y", "hidden"); } return this; }, /** * @description {@link #clip} 호출되기 전에 원래 clip된 overflow를 반환한다. * @method unclip * @return {DU.LElement} this */ unclip : function(){ if(this.isClipped){ this.isClipped = false; var o = this.originalClip; if(o.o){this.setStyle("overflow", o.o);} if(o.x){this.setStyle("overflow-x", o.x);} if(o.y){this.setStyle("overflow-y", o.y);} } return this; }, /** * @description 객체를 다시 그린다. * @method repaint * @return {DU.Elemnent} this */ repaint : function() { var thisObj = this; if(!thisObj.hasClass(thisObj.CSS_ELEMENT_REPAINT)) thisObj.addClass(thisObj.CSS_ELEMENT_REPAINT); setTimeout(DU.util.LFunction.createDelegate(function() { thisObj.removeClass(thisObj.CSS_ELEMENT_REPAINT); }, thisObj), 1); return thisObj; }, /** * 특정 anchor 포인트들로 연결되는 다른 element를 가지고 해당 element를 정렬한다. * 다른 element가 socument인 경우 그것은 viewport(화면상의 화상표시 영역)로 정렬한다. * 다음은 지원되는 anchor 위치들의 모든 목록이다: tl, t, tr, l, c, r, bl, b, br * @param {Mixed} element 정렬할 element * @param {String} position 정렬할 위치 * @param {Array} offsets (optional) [x, y]에 의한 위치 offset * @return {DU.LElement} this */ alignTo : function(element, position, offsets){ return this.setXY(this.getAlignToXY(element, position, offsets)); }, /** * element의 anchor 위치에 의해 명시된 x,y 좌표를 가져온다. * @param {String} anchor (optional) 명시된 anchor 위치(기본적으로 "c"). * 제공되는 anchor 위치들에 대한 세부사항은 {@link #alignTo}을 참조한다. * @param {Boolean} local (optional) 페이지 좌표대신 local (element top/left-relative) anchor 위치를 * 가져오기 위해서는 true * of page coordinates * @param {Object} size (optional) anchor 위치를 계산하기 위해 사용되는 size를 포함한 object * {width: (target width), height: (target height)} (기본적으로 element의 현재 사이즈) * @return {Array} [x, y] element의 x와 y 좌표를 포함하는 array */ getAnchorXY : function(anchor, local, s){ anchor = (anchor || "tl").toLowerCase(); s = s || {}; var vp = this.dom == document.body || this.dom == document; var w = s.width || vp ? DU.util.LDom.getViewportWidth() : this.getWidth(); var h = s.height || vp ? DU.util.LDom.getViewportHeight() : this.getHeight(); var xy; var r = Math.round; var o = this.getXY(); var scroll = this.getScroll(); var extraX = vp ? scroll.left : !local ? o[0] : 0; var extraY = vp ? scroll.top : !local ? o[1] : 0; var hash = { c: [r(w * 0.5), r(h * 0.5)], t: [r(w * 0.5), 0], l: [0, r(h * 0.5)], r: [w, r(h * 0.5)], b: [r(w * 0.5), h], tl: [0, 0], bl: [0, h], br: [w, h], tr: [w, 0] }; xy = hash[anchor]; return [xy[0] + extraX, xy[1] + extraY]; }, /** * 다른 element와 해당 element를 정렬하기 위한 x,y 좌표를 가져온다. * 지원되는 위치 값들에 대한 더 많은 정보를 위해서는 {@link #alignTo}를 참조한다. * @param {Mixed} element 정렬하기 위한 element * @param {String} position 정렬하기 위한 위치 * @param {Array} offsets (optional) [x, y]에 의한 위치 offset * @return {Array} [x, y] */ getAlignToXY : function(el, p, o){ el = DU.get(el); if(!el || !el.dom) throw "LElement.alignToXY with an element that doesn't exist"; o = o || [0,0]; p = (p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase(); var d = this.dom, a1, a2, w, h, x, y, r, c = false, p1 = "", p2 = ""; var dw = DU.util.LDom.getViewportWidth() -10, // ie는 margin 10px dh = DU.util.LDom.getViewportHeight()-10; // ie는 margin 10px var p1y, p1x, p2y, p2x, swapY, swapX; var doc = document, docElement = doc.documentElement, docBody = doc.body; var scrollX = (docElement.scrollLeft || (docBody && docBody.scrollLeft) || 0)+5; var scrollY = (docElement.scrollTop || (docBody && docBody.scrollTop) || 0)+5; var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/); if(!m) throw "LElement.alignTo with an invalid alignment " + p; p2 = m[2]; p1 = m[1]; c = !!m[3]; a2 = el.getAnchorXY(p2, false); a1 = this.getAnchorXY(p1, true); x = a2[0] - a1[0] + o[0]; y = a2[1] - a1[1] + o[1]; if(c){ h = this.getHeight(); w = this.getWidth(); r = el.getRegion(); p2y = p2.charAt(0); p2x = p2.charAt(p2.length-1); p1y = p1.charAt(0); p1x = p1.charAt(p1.length-1); swapX = ( (p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r") ); swapY = ( (p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") ); if (x + w > dw + scrollX) x = swapX ? r.left-w : dw+scrollX-w; if (x < scrollX) x = swapX ? r.right : scrollX; if (y + h > dh + scrollY) y = swapY ? r.top-h : dh+scrollY-h; if (y < scrollY) y = swapY ? r.bottom : scrollY; } return [x,y]; }, /** * viewport의 element나 다른 element를 중앙에 위치시킨다. * @param {Mixed} centerIn (optional) element 중앙에 위치시킬 element */ center : function(centerIn){ return this.alignTo(centerIn || document, 'c-c'); }, /** * element의 현재 스크롤 위치를 반환한다. * @return {Object} {left: (scrollLeft), top: (scrollTop)} 포맷의 스크롤 위치를 포함한 object */ getScroll : function(){ var d = this.dom, doc = document, body = doc.body, docElement = doc.documentElement, l, t, ret; if(d == doc || d == body){ if(DU.browser.msie && DU.isStrict){ l = docElement.scrollLeft; t = docElement.scrollTop; }else{ l = window.pageXOffset; t = window.pageYOffset; } ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)}; }else{ ret = {left: d.scrollLeft, top: d.scrollTop}; } return ret; }, /** * @description scroll position 설정 * @param {object} objLeftTop {left:scrollLeft,top:scrollTop} * @return {void} */ setScroll : function(objLeftTop){ var d = this.dom, doc = document, body = doc.body, docElement = doc.documentElement, l, t, ret; if(d == doc || d == body){ if (objLeftTop.left) { var setOk = false; if (DU.browser.msie && DU.isStrict) { if (docElement.scrollLeft) { docElement.scrollLeft = objLeftTop.left; setOk = true; } } else { if (window.pageXOffset) { window.pageXOffset = objLeftTop.left; setOk = true; } } if(!setOk && body){ body.scrollLeft = objLeftTop.left; } } if (objLeftTop.top) { var setOk = false; if (DU.browser.msie && DU.isStrict) { if (docElement.scrollTop) { docElement.scrollTop = objLeftTop.top; setOk = true; } } else { if (window.pageYOffset) { window.pageYOffset = objLeftTop.top; setOk = true; } } if(!setOk && body){ body.scrollTop = objLeftTop.top; } } }else{ if (objLeftTop.left) { d.scrollLeft = objLeftTop.left; } if(objLeftTop.top){ d.scrollTop = objLeftTop.top; } } }, /** * @description targetEl이 안보이는 경우 scroll 이동하기 * @method getVisibleScrollXY * @param {string|HTMLElement} id * @param {boolean} movingX x축 자동 scroll 여부, default는 true * @param {boolean} movingY y축 자동 scroll 여부, default는 true * @return {array} [x,y] child의 변경된 좌표 return */ getVisibleScrollXY : function(targetEl,movingX,movingY){ movingX = movingX === false ? false : true; movingY = movingY === false ? false : true; var newScroll = null; if (movingX || movingY) { newScroll = {}; var scrollerEl = this; var scroll = scrollerEl.getScroll(); var startXY = scrollerEl.getXY(); var scrollerWH = [scrollerEl.getWidth(), scrollerEl.getHeight()]; var endXY = [startXY[0] + scrollerWH[0], startXY[1] + scrollerWH[1]]; var targetXY = targetEl.getXY(); var targetWH = [targetEl.getWidth(), targetEl.getHeight()]; var childs = scrollerEl.getChildren(); if (childs.length > 0) { //첫번째 놈만 비교한다. var childEl = DU.get(childs[0]); var childWH = [childEl.getWidth(), childEl.getHeight()]; var scrollBarWH = [childWH[0] > scrollerWH[0] ? 17 : 0, childWH[1] > scrollerWH[1] ? 17 : 0]; //scrollbar가 없을수도 있다. if (movingX) { if (targetXY[0] >= startXY[0] && targetXY[0] + targetWH[0] <= endXY[0] - scrollBarWH[0]) { newScroll.left = null; } else { if (targetXY[0] < startXY[0]) { newScroll.left = scroll.left - (startXY[0] - targetXY[0]); } else if (targetXY[0] + targetWH[0] > endXY[0] - scrollBarWH[0]) { newScroll.left = scroll.left + (targetXY[0] + targetWH[0] - (endXY[0] - scrollBarWH[0])); } } } if (movingY) { if (targetXY[1] >= startXY[1] && targetXY[1] + targetWH[1] <= endXY[1] - scrollBarWH[1]) { newScroll.top = null; } else { if (targetXY[1] < startXY[1]) { newScroll.top = scroll.top - (startXY[1] - targetXY[1]); } else if (targetXY[1] + targetWH[1] > endXY[1] - scrollBarWH[1]) { newScroll.top = scroll.top + (targetXY[1] + targetWH[1] - (endXY[1] - scrollBarWH[1])); } } } /*/ scroll event가 늦게 발생되는 문제로 custom event먼저 처리된 후 scroll event가 발생해 처리되지 결과적으로 되지 않는다. if(!this.delayTask) { this.delayTask = new DU.util.LDelayedTask(function(){ scrollerEl.setScroll(newScroll); this.delayTask = null; }, this); this.delayTask.delay(1000); } //*/ } } return newScroll; }, /** * @description targetEl이 안보이는 경우 scroll 이동하기 * @method moveScroll * @param {string|HTMLElement} id * @param {boolean} movingX x축 자동 scroll 여부, default는 true * @param {boolean} movingY y축 자동 scroll 여부, default는 true * @return {array} [x,y] child의 변경된 좌표 return */ moveScroll : function(id,movingX,movingY){ var targetEl = DU.get(id); var newScroll = this.getVisibleScrollXY(targetEl,movingX,movingY); if(newScroll != null) this.setScroll(newScroll); return targetEl.getXY(); }, /** * @description table의 colSpan, rowSpan 정보를 2차원 배열로 return한다. * @method setTableSpanWidth * @param {Int} cellCount cell수 */ setTableSpanWidth : function(widths){ this.getTableSpanInfo(widths); }, /** * @description table의 colSpan, rowSpan 정보를 2차원 배열로 return한다. * @method getTableSpanInfo * @param {Int} cellCount cell수 * @return {Array} 2차원 array */ getTableSpanInfo : function(widths){ var th = this; var cellCount = null; var isWidths = true; if (typeof widths === 'int') { cellCount = widths; isWidths = false; } else{ cellCount = widths.length; } //table 분석 var colSpans = new Array(); //초기화 var rowSpans = new Array(); for(var i=0;i 1){ rowSpans[c] = rs - 1; } var cs = cell.colSpan; if(cs > 1){ //colSpan만큼 같은 cell index를 할당한다. w = 0; for(var rc=0;rc used by Fx box Model에 대해 체크되어야 함. adjustWidth : function(width) { var isNum = (typeof width == "number"); if(isNum && this.autoBoxAdjust && !this.isBorderBox()){ width -= (this.getBorderWidth("lr") + this.getPadding("lr")); } return (isNum && width < 0) ? 0 : width; }, // private => used by Fx adjustHeight : function(height) { var isNum = (typeof height == "number"); if(isNum && this.autoBoxAdjust && !this.isBorderBox()){ height -= (this.getBorderWidth("tb") + this.getPadding("tb")); } return (isNum && height < 0) ? 0 : height; }, /** * @private size가 unit을 가지고 있는 경우 테스트, 그렇지 않으면 기본값을 추가한다. */ addUnits : function(size){ if(size === "" || size == "auto" || size === undefined){ size = size || ''; } else if(!isNaN(size) || !DU.LElement.unitPattern.test(size)){ size = size + (this.defaultUnit || 'px'); } return size; }, /** * @description 해당 element의 width를 설정한다. * @method setWidth * @public * @param {Mixed} width 새로운 width. 이것은 다음 중 하나일 것이다:
    *
  • 해당 element의 새로운 width를 명시하는 숫자 {@link #defaultUnit}s (기본적으로, pixels).
  • *
  • CSS width 스타일을 설정하기 위해 사용되는 문자열. Animation은 사용될 수 없다.
  • *
* @param {Boolean/Object} anim (optional) 기본 animation에 대한 true, * 혹은 표준 standard Element animation config object * @return {DU.LElement} this */ setWidth : function(width, anim){ if(anim) { if(anim === true) anim = { width: { to: width } }; anim = DU.applyIf(anim, { type: 'LAnim' }); } width = this.adjustWidth(width); if(!anim) { this.dom.style.width = this.addUnits(width); } else { var currAnim = this.getAnimation(anim, this.id); currAnim.animate(); } return this; }, /** * @description 해당 element의 height를 설정한다. * @method setHeight * @param {Mixed} height 새로운 height. 이것은 다음 중 하나일 것이다::
    *
  • 해당 element의 새로운 width를 명시하는 숫자 {@link #defaultUnit}s (기본적으로, pixels).
  • *
  • CSS width 스타일을 설정하기 위해 사용되는 문자열. Animation은 사용될 수 없다.
  • *
* @param {Boolean/Object} anim (optional) 기본 animation에 대한 true or animation 객체 * @return {DU.LElement} this */ setHeight : function(height, animate){ height = this.adjustHeight(height); if (!animate) { this.dom.style.height = this.addUnits(height); } else { var animConf = (typeof animate == 'object') ? animate : {height: { to: height }} ; new DU.animation.LAnim(this.id, animConf).animate(); } return this; }, /** * @description Heigth를 자동으로 셋팅한다. * @method autoHeight * @return {DU.LElement} this */ autoHeight : function() { this.clip(); var height = parseInt(this.dom.scrollHeight, 10); this.setHeight(height); this.unclip(); return this; }, /** * Mask 적용 * @method mask * @param {String} contentHtml [optional] 적용할 html 내용 * @return {void} */ mask : function(contentHtml) { if(!this.waitMaskEl) { var waitMask = document.createElement("div"); this.waitMaskEl = DU.get(waitMask); this.waitMaskEl.addClass(this.CSS_ELEMENT_MASKED); this.waitMaskEl.appendTo(DU.getBody(true)); var region = null; if(this.dom == document.body || this.dom == document) { region = { right:DU.util.LDom.getViewportWidth(), bottom:DU.util.LDom.getViewportHeight(), left:0, top:0 } } else region = this.getRegion(); this.waitMaskEl.setRegion(region); var waitPanel = document.createElement("div"); this.waitPanelEl = DU.get(waitPanel); this.waitPanelEl.addClass(this.CSS_ELEMENT_MASKED_PANEL); this.waitPanelEl.appendTo(DU.getBody(true)); if(!contentHtml) { contentHtml = DU.getConfig().getFirst("$.base.waitMask.contentHtml"); var sPos = contentHtml.indexOf('{dujsfRootPath}'); if(sPos >= 0) { contentHtml = contentHtml.substring(0, sPos) + DU.getConfig().getFirst('$.core.contextPath') + DU.getConfig().getFirst('$.core.dujsfRootPath') + contentHtml.substring(sPos + 15); } } this.waitPanelEl.html(contentHtml); var imgElList = this.waitPanelEl.select('img'); if(imgElList.length > 0) { var imgEl = imgElList.getAt(0); this.waitPanelEl.setHeight(imgEl.getHeight()); this.waitPanelEl.setWidth(imgEl.getWidth()); } //this.waitPanelEl.setTop(-13550); this.waitPanelEl.center(this.dom); } if((this.dom == document.body || this.dom == document) && this.waitMaskEl) this.waitMaskEl.setRegion(DU.util.LDom.getClientRegion()); }, /** * Mask 해제 * @method unmask * @return {void} */ unmask : function() { if (this.waitMaskEl) { this.waitPanelEl.remove(); delete this.waitPanelEl; this.waitMaskEl.remove(); delete this.waitMaskEl; } }, /** * node가 하위에 포함되어 있는지 확인하는 메소드 * @method isAncestor * @param {HTMLElement} node 포함여부를 확인하는 객체 * @return {Boolean} 포함여부 */ isAncestor : function(node) { return DU.util.LDom.isAncestor(this.dom, node); }, /** * @description 주어진 element의 region 위치를 반환한다. * element는 반드시 region을 가진 DOM tree의 일부분이어야 한다 * (diplay:none 혹은 element들이 추가되어 있지 않으면 false를 반환한다). * @method getRegion * @return {Region | Array} "top, left, bottom, right" 멤버데이터를 포함하는 region 인스턴스의 array나 region */ getRegion: function() { return Dom.getRegion(this.dom); }, /** * @description 현재 위치 정보를 {DU.util.LRegion} 객체 정보로 셋팅한다. * @method setRegion * @param {DU.util.LRegion} region Region객체 * @param {Boolean/Object} anim (optional) 기본 animation에 대한 true, * 혹은 표준 standard Element animation config object * @return {DU.LElement} this */ setRegion : function(region, anim) { if(anim) { if(anim === true) anim = { points: {to: [x, y]}, width: { to: width }, height: { to: height } }; anim = DU.applyIf(anim, { type: 'LMotion' }); } var width = region.right - region.left; var height = region.bottom-region.top; var x = region.left; var y = region.top; var currAnim = this.getAnimation(anim, this.dom.id); if(currAnim != null) currAnim.animate(); else { this.setSize(width, height); this.moveTo(x, y); } return this; }, /** * @description 객체를 사용 가능하게 하는 메소드 * @method enable * @public * @return {void} */ enable : function() { if(this.dom) { this.dom.disabled = false; this.removeClass(this.CSS_ELEMENT_DISABLED); } }, /** * @description 객체를 사용 불가능하게 하는 메소드 * @method disable * @public * @return {void} */ disable : function() { if(this.dom) { this.dom.disabled = true; this.addClass(this.CSS_ELEMENT_DISABLED); } }, /** * @description 객체를 사용 가능여부를 확인하는 메소드 * @method isDisable * @public * @return {void} */ isDisable : function() { return this.dom ? this.hasClass(this.CSS_ELEMENT_DISABLED) : false; }, /** * @description 객체를 유효한 상태로 설정하는 메소드 * @method valid * @public * @return {void} */ valid : function() { if(this.dom) { this.removeClass(this.CSS_ELEMENT_INVALID); if(this.dom.invalidTooltip) { this.dom.invalidTooltip.destroy(); this.dom.invalidTooltip = null; } } }, /** * @description 객체를 유효하지 않은 상태로 설정하는 메소드 * @method invalid * @public * @return {void} */ invalid : function(message) { if(this.dom) { message = message || DU.getMessageManager().get('$.base.msg052'); if(!this.hasClass(this.CSS_ELEMENT_INVALID)) this.addClass(this.CSS_ELEMENT_INVALID); if(DU.widget) { this.dom.invalidTooltip = this.dom.invalidTooltip || new DU.widget.LTooltip(this.dom.id+"tooltip", { context:this.dom.id, text:message, zIndex:30 }); this.dom.invalidTooltip.setBody(message); this.dom.invalidTooltip.render(); } } }, /** * @description 객체를 유효여부를 확인하는 메소드 * @method isValid * @public * @return {void} */ isValid : function() { return this.dom ? !this.hasClass(this.CSS_ELEMENT_INVALID) : true; }, // 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 border + right width border 를 가져올 것이다. * @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 padding + right width padding 를 가져올 것이다. * @return {Number} 함께 추가되어 전달된 side들의 padding */ getPadding : function(side){ return this.addStyles.call(this, side, DU.LElement.paddings); }, /** * @description 특정 side에 대한 margin의 width를 가져온다. * @method getMargins * @public * @param {String} side 여러값들을 추가하기 위하여 t, l, r, b나 이것들의 조합이 될 수 있음. * 예를 들어, 'lr'을 전달하면, left width padding + right width padding 를 가져올 것이다. * @return {Number} 함께 추가되어 전달된 side들의 padding */ getMargins : function(side){ return this.addStyles.call(this, side, DU.LElement.margins); } }); })(); (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); } }; })(); /** * document에 원격 리소스를 붙이거나 삽입하는 메커니즘을 제공한다. * @module util * @namespace DU.util * @requires DU */ /** * document에 하나 혹은 그 이상의 스크립트나 링크 노드를 삽입하거나 붙인다. * @namespace DU.util * @class LGet */ DU.util.LGet = function() { /** * 여러개의 요청을 관리하는 큐의 hash * @property queues * @private */ var queues={}, /** * 트랜잭션 id들을 생성하는데 사용되는 큐 인덱스 * @property qidx * @type int * @private */ qidx=0, /** * 유일한 노드 id를 생성하는데 사용되는 노드 인덱스 * @property nidx * @type int * @private */ nidx=0, // ridx=0, // sandboxFrame=null, /** * 프로세스를 여러개를 동시에 제거하는 것을 막기 위하여 사용되는 내부적 property * @property purging * @type boolean * @private */ purging=false, ua=DU.browser, lang=DU; /** * HTML element를 생성하고, 이는 document에 append 되지 않는다. * @method _node * @param {string} type element의 타입 * @param {string} attr the attributes * @param {Window} win (optional) element가 생성될 window * @return {HTMLElement} 생성된 노드 * @private */ var _node = function(type, attr, win) { var w = win || window, d=w.document, n=d.createElement(type); for (var i in attr) { if (attr[i] && DU.hasOwnProperty(attr, i)) { n.setAttribute(i, attr[i]); } } return n; }; /** * 링크 노드를 생성한다. * @method _linkNode * @param {string} url css 파일에 대한 url * @param {Window} win (optional) 노드가 생성될 window * @return {HTMLElement} 생성된 노드 * @private */ var _linkNode = function(url, win, charset) { var c = charset || "utf-8"; return _node("link", { "id": "yui__dyn_" + (nidx++), "type": "text/css", "charset": c, "rel": "stylesheet", "href": url }, win); }; /** * 스크립트 노드를 생성한다. * @method _scriptNode * @param {string} url 스크립트 파일에 대한 url * @param {Window} win (optional) 노드가 생성될 window * @return {HTMLElement} 생성된 노드 * @private */ var _scriptNode = function(url, win, charset) { var c = charset || "utf-8"; return _node("script", { "id": "yui__dyn_" + (nidx++), "type": "text/javascript", "charset": c, "src": url }, win); }; /** * callback 함수에 대한 데이터 payload를 반환한다. * @method _returnData * @private */ var _returnData = function(q, msg) { return { tId: q.tId, win: q.win, data: q.data, nodes: q.nodes, msg: msg, purge: function() { _purge(this.tId); } }; }; var _get = function(nId, tId) { var q = queues[tId], n = (lang.isString(nId)) ? q.win.document.getElementById(nId) : nId; if (!n) { _fail(tId, "target node not found: " + nId); } return n; }; /* * The request failed, execute fail handler with whatever * was accomplished. There isn't a failure case at the * moment unless you count aborted transactions * @method _fail * @param id {string} the id of the request * @private */ var _fail = function(id, msg) { var q = queues[id]; // execute failure callback if (q.onFailure) { var sc=q.scope || q.win; q.onFailure.call(sc, _returnData(q, msg)); } }; /** * 요청이 종료되고 요청자의 callback을 실행한다. * @method _finish * @param id {string} request의 id * @private */ var _finish = function(id) { var q = queues[id]; q.finished = true; if (q.aborted) { var msg = "transaction " + id + " was aborted"; _fail(id, msg); return; } // execute success callback if (q.onSuccess) { var sc=q.scope || q.win; q.onSuccess.call(sc, _returnData(q)); } }; /** * 타임아웃을 탐지한다. * @method _timeout * @param {string} id request의 id * @private */ var _timeout = function(id) { var q = queues[id]; if (q.onTimeout) { var sc=q.context || q; q.onTimeout.call(sc, _returnData(q)); } }; /** * 주어진 요청에 대한 다음 항목을 로딩한다. * @method _next * @param {string} id request의 id * @param {string} loaded 어떤 것이든 로딩된 것의 url * @private */ var _next = function(id, loaded) { var q = queues[id]; if (q.timer) { // Y.log('cancel timer'); q.timer.cancel(); } if (q.aborted) { var msg = "transaction " + id + " was aborted"; _fail(id, msg); return; } if (loaded) { q.url.shift(); if (q.varName) { q.varName.shift(); } } else { // This is the first pass: make sure the url is an array q.url = (lang.isString(q.url)) ? [q.url] : q.url; if (q.varName) { q.varName = (lang.isString(q.varName)) ? [q.varName] : q.varName; } } var w=q.win, d=w.document, h=d.getElementsByTagName("head")[0], n; if (q.url.length === 0) { // Safari 2.x workaround - There is no way to know when // a script is ready in versions of Safari prior to 3.x. // Adding an extra node reduces the problem, but doesn't // eliminate it completely because the browser executes // them asynchronously. if (q.type === "script" && ua.webkit && ua.webkit < 420 && !q.finalpass && !q.varName) { // Add another script node. This does not guarantee that the // scripts will execute in order, but it does appear to fix the // problem on fast connections more effectively than using an // arbitrary timeout. It is possible that the browser does // block subsequent script execution in this case for a limited // time. var extra = _scriptNode(null, q.win, q.charset); extra.innerHTML='DU.util.LGet._finalize("' + id + '");'; q.nodes.push(extra); h.appendChild(extra); } else { _finish(id); } return; } var url = q.url[0]; // if the url is undefined, this is probably a trailing comma problem in IE if (!url) { q.url.shift(); return _next(id); } if (q.timeout) { // Y.log('create timer'); q.timer = lang.later(q.timeout, q, _timeout, id); } if (q.type === "script") { n = _scriptNode(url, w, q.charset); } else { n = _linkNode(url, w, q.charset); } // track this node's load progress _track(q.type, n, id, url, w, q.url.length); // add the node to the queue so we can return it to the user supplied callback q.nodes.push(n); // add it to the head or insert it before 'insertBefore' if (q.insertBefore) { var s = _get(q.insertBefore, id); if (s) { s.parentNode.insertBefore(n, s); } } else { h.appendChild(n); } // FireFox does not support the onload event for link nodes, so there is // no way to make the css requests synchronous. This means that the css // rules in multiple files could be applied out of order in this browser // if a later request returns before an earlier one. Safari too. if ((ua.webkit || ua.gecko) && q.type === "css") { _next(id, url); } }; /** * 처리 대기열 및 해당 노드를 제거한다. * @method _autoPurge * @private */ var _autoPurge = function() { if (purging) { return; } purging = true; for (var i in queues) { var q = queues[i]; if (q.autopurge && q.finished) { _purge(q.tId); delete queues[i]; } } purging = false; }; /** * 특정 큐에 대한 노드를 제거한다. * @method _purge * @private */ var _purge = function(tId) { var q=queues[tId]; if (q) { var n=q.nodes, l=n.length, d=q.win.document, h=d.getElementsByTagName("head")[0]; if (q.insertBefore) { var s = _get(q.insertBefore, tId); if (s) { h = s.parentNode; } } for (var i=0; i= 420) { n.addEventListener("load", function() { f(id, url); }); // Nothing can be done with Safari < 3.x except to pause and hope // for the best, particularly after last script is inserted. The // scripts will always execute in the order they arrive, not // necessarily the order in which they were inserted. To support // script nodes with complete reliability in these browsers, script // nodes either need to invoke a function in the window once they // are loaded or the implementer needs to provide a well-known // property that the utility can poll for. } else { // Poll for the existence of the named variable, if it // was supplied. var q = queues[id]; if (q.varName) { var freq=DU.util.LGet.POLL_FREQ; q.maxattempts = DU.util.LGet.TIMEOUT/freq; q.attempts = 0; q._cache = q.varName[0].split("."); q.timer = lang.later(freq, q, function(o) { var a=this._cache, l=a.length, w=this.win, i; for (i=0; i this.maxattempts) { var msg = "Over retry limit, giving up"; q.timer.cancel(); _fail(id, msg); } else { } return; } } q.timer.cancel(); f(id, url); }, null, true); } else { lang.later(DU.util.LGet.POLL_FREQ, null, f, [id, url]); } } } // FireFox and Opera support onload (but not DOM2 in FF) handlers for // script nodes. Opera, but not FF, supports the onload event for link // nodes. } else { n.onload = function() { f(id, url); }; } }; return { /** * 필요시, ms 단위의 기본 poll 주기 * @property POLL_FREQ * @static * @type int * @default 10 */ POLL_FREQ: 10, /** * 자동 제거 전에 필요한 요청의 개수. * property PURGE_THRESH * @static * @type int * @default 20 */ PURGE_THRESH: 20, /** * 트랜잭션 실패 전에 Safari 2.x 브라우저에서 스크립트 로딩시 * varName에 대해 poll 하기 위한 시간 길이 * property TIMEOUT * @static * @type int * @default 2000 */ TIMEOUT: 2000, /** * Safari 브라우저에서 스크립트 로드 감지에 대해 도움을 준다. * @method _finalize * @param {string} id 트랜잭션 id * @private */ _finalize: function(id) { lang.later(0, null, _finish, id); }, /** * 트랜잭션을 취소한다. * @method abort * @param {string|object} o script()sk css()반환된 object나 tId */ abort: function(o) { var id = (lang.isString(o)) ? o : o.tId; var q = queues[id]; if (q) { q.aborted = true; } }, /** * 현재 document나 특정 window의 document의 head에 하나 혹은 * 그 이상의 스크립트 노드를 붙이거나 삽입한다. * * @method script * @static * @param {string|string[]} url 스크립트에 대한 url이나 url 배열 * @param {object} opts Options: *
*
onSuccess
*
* 스크립트 로딩이 완료됐을 때 실행할 callback. * callback은 다음과 같은 데이터를 가진 object back을 받는다: *
*
win
*
스크립트가 삽입될 window
*
data
*
요청이 만들어졌을때 전달되는 데이터 object
*
nodes
*
삽입된 노드들에 대한 참조를 포함하는 array
*
purge
*
실행되었을 때 삽입된 노드를 삭제할 함수
*
*
*
*
onFailure
*
* 스크립트 로딩이 실패 했을때 실행할 callback * callback은 다음과 같은 데이터를 가진 object back을 받는다: *
*
win
*
스크립트가 삽입될 window
*
data
*
요청이 만들어졌을때 전달되는 데이터 object
*
nodes
*
성공적으로 삽입된 노드들에 대한 참조를 포함하는 array
*
purge
*
실행되었을 때 삽입된 노드를 삭제할 함수
*
*
*
*
onTimeout
*
* 타임아웃이 발생했을 때 실행할 callback. * callback은 다음과 같은 데이터를 가진 object back을 받는다: *
*
win
*
스크립트가 삽입될 window
*
data
*
요청이 만들어졌을때 전달되는 데이터 object
*
nodes
*
삽입된 노드들에 대한 참조를 포함하는 array
*
purge
*
실행되었을 때 삽입된 노드를 삭제할 함수
*
*
*
*
scope
*
callback에 대한 실행 context
*
win
*
유틸리티가 점유하고 있는 것 이외의 다른 window
*
autopurge
*
* true로 설정하는 것은 유틸리티들이 한번 로드된 스크립트를 제거하는 루틴을 초기화 하게 한다. *
*
data
*
* 스크립트가 로드 되었을때 callback에 제공되는 데이터. *
*
varName
*
* 스크립트 로딩이 끝났을때 사용 사능한 변수. * Safari 2.x이나 그 이하 버전의 브라우저에서 스크립트 로딩을 감지하는데 도움을 주는데 사용한다. * 이 proerty의 타입은 url parameter에 전달되는 것과 일치해야 한다: * single url을 로딩 하는 경우, 문자열이 제공될 수 있다. * 여러 스크립트들을 로딩 하는 경우 반드시 각 스크립트에 대한 변수명을 포함하는 * array를 제공해야 한다. *
*
insertBefore
*
새로운 노드의 다음 sibling이 될 노드나 노드 id
*
*
charset
*
노드의 캐릭터셋(charset), 기본값은 utf-8
*
timeout
*
타임아웃 event의 취소나 발생 이전에 기다리는 것에 대한 밀리초 단위의 숫자
*

         * // assumes DU, dom, and event are already on the page

         *   DU.util.LGet.script(

         *   ["http://yui.yahooapis.com/2.3.1/build/dragdrop/dragdrop-min.js",

         *    "http://yui.yahooapis.com/2.3.1/build/animation/animation-min.js"], {

         *     onSuccess: function(o) {

         *       new DU.dd.LDDProxy("dd1"); // also new o.reference("dd1"); would work

         *       this.log("won't cause error because DU is the scope");

         *       this.log(o.nodes.length === 2) // true

         *       // o.purge(); // optionally remove the script nodes immediately

         *     },

         *     onFailure: function(o) {

         *     },

         *     data: "foo",

         *     timeout: 10000, // 10 second timeout

         *     scope: DU,

         *     // win: otherframe // target another window/frame

         *     autopurge: true // allow the utility to choose when to remove the nodes

         *   });

         * 
* @return {string} 트랜잭션에 대한 정보를 포함하고 있는 object tId */ script: function(url, opts) { return _queue("script", url, opts); }, /** * 현재 document나 특정 window의 document의 head에 하나 혹은 그 이상의 * css 링크 노드들을 붙이거나 삽입한다. * @method css * @static * @param url {string} css 파일에 대한 url * @param opts Options: *
*
onSuccess
*
* css 파일 로딩이 끝났을 때 실행할 callback. * callback은 다음과 같은 데이터를 가진 object back을 받는다: *
win
*
링크 노드가 삽입될 window
*
data
*
요청이 만들어졌을때 전달되는 데이터 object
*
nodes
*
삽입된 노드들에 대한 참조를 포함하는 array
*
purge
*
실행되었을 때 삽입된 노드를 삭제할 함수
*
*
* *
scope
*
callback에 대한 실행 context
*
win
*
유틸리티가 점유하고 있는 것 이외의 다른 window
*
data
*
* 노드가 로드 되었을때 callback에 제공되는 데이터. *
*
insertBefore
*
새로운 노드의 다음 sibling이 될 노드나 노드 id
*
charset
*
노드의 캐릭터셋(charset), 기본값은 utf-8
* *

         *      DU.util.LGet.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");

         * 
*

         *      DU.util.LGet.css(["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",

         * 
* @return {string} 트랜잭션에 대한 정보를 포함하고 있는 object tId */ css: function(url, opts) { return _queue("css", url, opts); } }; }(); /** * Collection 관리를 위한 유틸리티 * @module util * @title LCollection Utility * @namespace DU.util * @requires DU */ DU.namespace("util"); /** * LCollection utility. * @class LCollection * @static */ DU.util.LCollection = function() { this.items = []; this.keys = []; this.map = []; this.length = 0; } DU.util.LCollection.prototype = { /** * item을 idx위치에 삽입하는 메소드 * @method insert * @param {Int} idx 삽입할 위치 * @param {String} key 키 * @param {Object} item 입력할 객체 * @return {void} */ insert : function(idx, key, item) { if (arguments.length == 2) { item = key; key = key.id ? key.id : null; } if(idx >= this.length) return this.add(key, item); this.items.splice(idx, 0, item); this.keys.splice(idx, 0, key); if(key != null){ this.map[key] = item; } this.length++; }, /** * item을 추가하는 메소드 * @method add * @param {String} key 키 * @param {Object} item 입력할 객체 * @return {void} */ add : function(key, item) { if (arguments.length == 1) { item = key; key = key.id ? key.id : null; } this.keys.push(key); this.items.push(item); this.map[key] = item; this.length++; }, /** * item을 삭제하는 메소드 * @method remove * @param {String} key 키 * @return {Boolean} */ remove : function(key) { var o = this.map[key]; var idx = DU.util.LArray.indexOf(this.keys, key); if(idx < 0) return false; this.keys.splice(idx, 1); delete this.map[key]; this.items.splice(idx, 1); this.length--; if(this.length < 0) throw new DU.LException('Collection.remove() : IndexOutOfBoundsException ['+this.length+']'); return true; }, /** * key에 해당하는 item의 위치를 리턴하는 메소드 * @method indexOfKey * @param {String} key 키 * @return {Int} */ indexOfKey : function(key) { var o = this.map[key]; if(DU.isUndefined(o)) { return -1; } else { return DU.util.LArray.indexOf(this.items, o); } }, /** * key에 해당하는 item을 리턴하는 메소드 * @method get * @param {String} key 키 * @return {Object} */ get : function(key) { return this.map[key]; }, /** * key에 해당하는 item을 변경하는 메소드 * @method set * @param {String} key 키 * @param {Object} item 객체 * @return {Object} */ set : function(key, item) { var oldData = this.map[key]; this.map[key] = item; var idx = DU.util.LArray.indexOf(this.items, oldData); if(idx < 0) { throw new Error('Collection.set() : IndexOutOfBoundsException['+idx+']'); } this.items[idx] = item; }, /** * idx에 해당하는 key값을 리턴하는 메소드 * @method getKey * @param {Int} idx 위치 * @return {String} */ getKey : function(idx) { return this.keys[idx]; }, /** * idx 위치에 해당하는 item을 리턴하는 메소드 * @method getAt * @param {Int} idx 위치 * @return {Object} */ getAt : function(idx) { return this.items[idx]; }, /** * 모두 초기화 하는 메소드 * @method clear * @return {void} */ clear : function() { this.items = []; this.keys = []; this.map = []; this.length = 0; }, /** * items 정보에 해당되는 객체를 Function으로 호출하는 메소드 * @method each * @param {Function} func Array 배열 * @param {Object} scope Array 배열 * @return void */ each : function(func, scope) { var newItems = [].concat(this.items); var count = this.length; for(var i = 0 ; i < count; i++) { if(func.call(scope || window, this.keys[i], this.items[i], i, count) == false) break; } }, /** * func에 해당되는 값을 LCollection으로 리턴하는 메소드 * @method query * @param {Function} func Array 배열 * @param {Object} scope Array 배열 * @return {DU.util.LCollection} */ query : function(func, scope) { var newData = new DU.util.LCollection(); this.each(function(id, item, i, count){ if(func.call(scope || this, id, item, i) === true) { newData.add(id, item); } }, this); return newData; }, /** * func에 해당되는 값으로 정렬하는 메소드 * @method sort * @param {Function} func Array 배열 * @param {String} dir 정렬 방향 * @return {void} */ sort : function(fn, dir) { var desc = String(dir).toUpperCase() == "DESC" ? -1 : 1; this.items.sort(function(a, b) {return fn(a, b, dir) * desc;}); }, /** * 역순 정렬하는 메소드 * @method reverse * @return {void} */ reverse : function() { this.items.reverse(); }, /** * LCollection을 복제하여 리턴하는 메소드 * @method clone * @return {DU.util.LCollection} */ clone : function() { var o = new DU.util.LCollection(); for(var i = 0 ; i < this.length ; i++) { var key = this.getKey(i); o.insert(i, key, this.get(key)); } return o; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.util.LCollection '; } }; /** * @description

사이즈 조절 가능한 element를 만든다.

* @namespace DU.util * @requires DU, dom, dragdrop, element, event * @optional animation * @module util */ (function() { var D = DU.util.LDom, Event = DU.util.LEvent; /** * @constructor * @class LResize * @extends DU.LElement * @description

사이즈 조절 가능한 element를 만든다.

* @param {String/HTMLElement} el 리사이즈 가능하게 만들 element * @param {Object} attrs 설정 parameter를 포함하는 Object liternal */ var Resize = function(el, config) { var oConfig = { element: el, attributes: config || {} }; Resize.superclass.constructor.call(this, oConfig.element, oConfig.attributes); }; /** * @private * @static * @property _instances * @description 모든 리사이즈 인스턴스에 대한 내부적인 hash 테이블 * @type Object */ Resize._instances = {}; /** * @static * @method getResizeById * @description resize object와 연관된 element의 HTML id로 resize object를 가져온다. * @return {Object} The Resize Object */ Resize.getResizeById = function(id) { if (Resize._instances[id]) { return Resize._instances[id]; } return false; }; DU.extend(Resize, DU.LElement, { /** * @private * @property CSS_RESIZE * @description 기본 CSS 클래스 이름 * @type String */ CSS_RESIZE: 'L-resize', /** * @private * @property CSS_DRAG * @description 드래그가 가능할때 추가되는 클래스 이름 * @type String */ CSS_DRAG: 'L-draggable', /** * @private * @property CSS_HOVER * @description 핸들링만 하는 hover에 대해 사용된 클래스 이름 * @type String */ CSS_HOVER: 'L-resize-hover', /** * @private * @property CSS_PROXY * @description proxy element에 주어진 클래스 이름 * @type String */ CSS_PROXY: 'L-resize-proxy', /** * @private * @property CSS_WRAP * @description wrap element에 주어진 클래스 이름 * @type String */ CSS_WRAP: 'L-resize-wrap', /** * @private * @property CSS_KNOB * @description knob style 핸들링을 만들기 위해 사용된 클래스 이름 * @type String */ CSS_KNOB: 'L-resize-knob', /** * @private * @property CSS_HIDDEN * @description 히든값의 모든 것을 핸들링 하기 위한 wrap element에 주어진 클래스 이름 * @type String */ CSS_HIDDEN: 'L-resize-hidden', /** * @private * @property CSS_HANDLE * @description 단일 핸들링의 이름에 대한 기반으로 사용하는 모든 핸들링에 주어진 클래스 이름. * Handle "t"는 this.CSS_HANDLE 뿐만 아니라 this.CSS_HANDLE + '-t'을 가져 올것이다. * @type String */ CSS_HANDLE: 'L-resize-handle', /** * @private * @property CSS_STATUS * @description status element에 주어진 클래스 이름 * @type String */ CSS_STATUS: 'L-resize-status', /** * @private * @property CSS_GHOST * @description ghost property가 활성화 될때 wrap element에 주어진 클래스 이름 * @type String */ CSS_GHOST: 'L-resize-ghost', /** * @private * @property CSS_RESIZING * @description 리사이즈 액션이 자리를 차지할때 wrap element에 주어진 클래스 이름. * @type String */ CSS_RESIZING: 'L-resize-resizing', /** * @private * @property _resizeEvent * @description 리사이즈하는데 사용되는 마우스 event * @type Event */ _resizeEvent: null, /** * @private * @property dd * @description 드래그 설정가능이 true인 경우 사용된 * DU.dd.LDragDrop 인스턴스 * @type Object */ dd: null, /** * @private * @property browser * @description DU.env.ua property의 복사본 * @type Object */ browser: DU.browser, /** * @private * @property _locked * @description 리사이즈가 잠긴 경우를 표시하기 위한 flag * @type Boolean */ _locked: null, /** * @private * @property _positioned * @description element가 절대 좌표로 위치한 경우를 표시하기 위한 flag * @type Boolean */ _positioned: null, /** * @private * @property _dds * @description 리사이즈를 핸들링 하기 위해 사용되는 * DU.dd.LDragDrop 인스턴스들의 모든 것에 대한 참조를 포함하는 object * @type Object */ _dds: null, /** * @private * @property _wrap * @description element wrapper의 HTML 참조 * @type HTMLElement */ _wrap: null, /** * @private * @property _proxy * @description element proxy의 HTML 참조 * @type HTMLElement */ _proxy: null, /** * @private * @property _handles * @description 리사이즈 핸들링의 모든 것에 대한 참조들을 포함하고 있는 object * @type Object */ _handles: null, /** * @private * @property _currentHandle * @description 현재 활성화된 핸들링의 문자열 식별자. 예. 'r', 'br', 'tl' * @type String */ _currentHandle: null, /** * @private * @property _currentDD * @description 현재 활성화된 LDD object에의 링크 * @type Object */ _currentDD: null, /** * @private * @property _cache * @description 리사이즈된 element에 대한 key 정보를 포함하는 조회 테이블. * 예. height, width, x 위치, y 위치 등.. * @type Object */ _cache: null, /** * @private * @property _active * @description 리사이즈가 활성화 되어 있는 경우를 표시하기 위한 flag. event들을 위해 사용된다. * @type Boolean */ _active: null, /** * @private * @method _createProxy * @description proxy 설정이 true인 경우 proxy element를 생성한다. */ _createProxy: function() { if (this.get('proxy')) { this._proxy = document.createElement('div'); this._proxy.className = this.CSS_PROXY; this._proxy.style.height = this.get('element').clientHeight + 'px'; this._proxy.style.width = this.get('element').clientWidth + 'px'; this._wrap.parentNode.appendChild(this._proxy); } else { this.set('animate', false); } }, /** * @private * @method _createWrap * @description wrap 설정이 true인 경우 wrap element를 생성한다. * 다음 element 타입들은 자동으로 wrap 될것이다: img, textarea, input, iframe, select */ _createWrap: function() { this._positioned = false; //Force wrap for elements that can't have children switch (this.get('element').tagName.toLowerCase()) { case 'img': case 'textarea': case 'input': case 'iframe': case 'select': this.set('wrap', true); break; } if (this.get('wrap') === true) { this._wrap = document.createElement('div'); this._wrap.id = this.get('element').id + '_wrap'; this._wrap.className = this.CSS_WRAP; D.setStyle(this._wrap, 'width', this.get('width') + 'px'); D.setStyle(this._wrap, 'height', this.get('height') + 'px'); D.setStyle(this._wrap, 'z-index', this.getStyle('z-index')); this.setStyle('z-index', 0); var pos = D.getStyle(this.get('element'), 'position'); D.setStyle(this._wrap, 'position', ((pos == 'static') ? 'relative' : pos)); D.setStyle(this._wrap, 'top', D.getStyle(this.get('element'), 'top')); D.setStyle(this._wrap, 'left', D.getStyle(this.get('element'), 'left')); if (D.getStyle(this.get('element'), 'position') == 'absolute') { this._positioned = true; D.setStyle(this.get('element'), 'position', 'relative'); D.setStyle(this.get('element'), 'top', '0'); D.setStyle(this.get('element'), 'left', '0'); } var par = this.get('element').parentNode; par.replaceChild(this._wrap, this.get('element')); this._wrap.appendChild(this.get('element')); } else { this._wrap = this.get('element'); if (D.getStyle(this._wrap, 'position') == 'absolute') { this._positioned = true; } } if (this.get('draggable')) { this._setupDragDrop(); } if (this.get('hover')) { D.addClass(this._wrap, this.CSS_HOVER); } if (this.get('knobHandles')) { D.addClass(this._wrap, this.CSS_KNOB); } if (this.get('hiddenHandles')) { D.addClass(this._wrap, this.CSS_HIDDEN); } D.addClass(this._wrap, this.CSS_RESIZE); }, /** * @private * @method _setupDragDrop * @description element에 DU.dd.LDragDrop 인스턴스를 셋업한다. */ _setupDragDrop: function() { D.addClass(this._wrap, this.CSS_DRAG); this.dd = new DU.dd.LDD(this._wrap, this.get('id') + '-resize', { dragOnly: true, useShim: this.get('useShim') }); this.dd.on('dragEvent', function() { this.fireEvent('dragEvent', arguments); }, this, true); }, /** * @private * @method _createHandles * @description 설정에 명시된대로 핸들링을 생성한다. */ _createHandles: function() { this._handles = {}; this._dds = {}; var h = this.get('handles'); for (var i = 0; i < h.length; i++) { this._handles[h[i]] = document.createElement('div'); this._handles[h[i]].id = D.generateId(this._handles[h[i]]); this._handles[h[i]].className = this.CSS_HANDLE + ' ' + this.CSS_HANDLE + '-' + h[i]; var k = document.createElement('div'); k.className = this.CSS_HANDLE + '-inner-' + h[i]; this._handles[h[i]].appendChild(k); this._wrap.appendChild(this._handles[h[i]]); Event.on(this._handles[h[i]], 'mouseover', this._handleMouseOver, this, true); Event.on(this._handles[h[i]], 'mouseout', this._handleMouseOut, this, true); this._dds[h[i]] = new DU.dd.LDragDrop(this._handles[h[i]], this.get('id') + '-handle-' + h, { useShim: this.get('useShim') }); this._dds[h[i]].setPadding(15, 15, 15, 15); this._dds[h[i]].on('startDragEvent', this._handleStartDrag, this._dds[h[i]], this); this._dds[h[i]].on('mouseDownEvent', this._handleMouseDown, this._dds[h[i]], this); } this._status = document.createElement('span'); this._status.className = this.CSS_STATUS; document.body.insertBefore(this._status, document.body.firstChild); }, /** * @private * @method _ieSelectFix * @description Internet Explorer에서 드래그를 시작할때 onselectstart handler로써 사용할 함수 */ _ieSelectFix: function() { return false; }, /** * @private * @property _ieSelectBack * @description 해당 property에 현재 "onselectstart" method의 복사본을 유지할 것이며, * 그것을 사용한 이후에 그것을 재설정한다. */ _ieSelectBack: null, /** * @private * @method _setAutoRatio * @param {Event} ev 마우스 event. * @description 이 method는 "autoRatio" 설정이 되어 있는 경우를 확인한다. * 만약 설정된 경우 "Shift Key"가 눌린 경우를 확인한다. 또한 그런 경우 config ratio를 true로 설정한다. */ _setAutoRatio: function(ev) { if (this.get('autoRatio')) { if (ev && ev.shiftKey) { //Shift Pressed this.set('ratio', true); } else { this.set('ratio', this._configs.ratio._initialConfig.value); } } }, /** * @private * @method _handleMouseDown * @param {Event} ev 마우스 event. * @description 이 method는 MouseDown에 autoRatio를 준비한다. */ _handleMouseDown: function(ev) { if (this._locked) { return false; } if (D.getStyle(this._wrap, 'position') == 'absolute') { this._positioned = true; } if (ev) { this._setAutoRatio(ev); } if (this.browser.msie) { this._ieSelectBack = document.body.onselectstart; document.body.onselectstart = this._ieSelectFix; } }, /** * @private * @method _handleMouseOver * @param {Event} ev 마우스 event. * @description 핸들링에 대한 CSS 클래스 이름들을 추가한다. */ _handleMouseOver: function(ev) { if (this._locked) { return false; } //Internet Explorer needs this D.removeClass(this._wrap, this.CSS_RESIZE); if (this.get('hover')) { D.removeClass(this._wrap, this.CSS_HOVER); } var tar = Event.getTarget(ev); if (!D.hasClass(tar, this.CSS_HANDLE)) { tar = tar.parentNode; } if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) { D.addClass(tar, this.CSS_HANDLE + '-active'); for (var i in this._handles) { if (DU.hasOwnProperty(this._handles, i)) { if (this._handles[i] == tar) { D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active'); break; } } } } //Internet Explorer needs this D.addClass(this._wrap, this.CSS_RESIZE); }, /** * @private * @method _handleMouseOut * @param {Event} ev 마우스 event. * @description 핸들링에 대한 CSS 클래스 이름을 제거한다. */ _handleMouseOut: function(ev) { //Internet Explorer needs this D.removeClass(this._wrap, this.CSS_RESIZE); if (this.get('hover') && !this._active) { D.addClass(this._wrap, this.CSS_HOVER); } var tar = Event.getTarget(ev); if (!D.hasClass(tar, this.CSS_HANDLE)) { tar = tar.parentNode; } if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) { D.removeClass(tar, this.CSS_HANDLE + '-active'); for (var i in this._handles) { if (DU.hasOwnProperty(this._handles, i)) { if (this._handles[i] == tar) { D.removeClass(tar, this.CSS_HANDLE + '-' + i + '-active'); break; } } } } //Internet Explorer needs this D.addClass(this._wrap, this.CSS_RESIZE); }, /** * @private * @method _handleStartDrag * @param {Object} args LCustomEvent로부터 전달된 argument들 * @param {Object} dd 작업할 DU.dd.LDragDrop object * @description proxy를 리사이즈 하고, DU.dd.LDragDrop handler를 * 셋업하고 div의 상태를 업데이트 하며, 캐시를 준비한다. */ _handleStartDrag: function(args, dd) { var tar = dd.getDragEl(); if (D.hasClass(tar, this.CSS_HANDLE)) { if (D.getStyle(this._wrap, 'position') == 'absolute') { this._positioned = true; } this._active = true; this._currentDD = dd; if (this._proxy) { this._proxy.style.visibility = 'visible'; this._proxy.style.zIndex = '1000'; this._proxy.style.height = this.get('element').clientHeight + 'px'; this._proxy.style.width = this.get('element').clientWidth + 'px'; } for (var i in this._handles) { if (DU.hasOwnProperty(this._handles, i)) { if (this._handles[i] == tar) { this._currentHandle = i; var handle = '_handle_for_' + i; D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active'); dd.on('dragEvent', this[handle], this, true); dd.on('mouseUpEvent', this._handleMouseUp, this, true); break; } } } D.addClass(tar, this.CSS_HANDLE + '-active'); if (this.get('proxy')) { var xy = D.getXY(this.get('element')); D.setXY(this._proxy, xy); if (this.get('ghost')) { this.addClass(this.CSS_GHOST); } } D.addClass(this._wrap, this.CSS_RESIZING); this._setCache(); this._updateStatus(this._cache.height, this._cache.width, this._cache.top, this._cache.left); this.fireEvent('startResize', { type: 'startresize', target: this}); } }, /** * @private * @method _setCache * @description this._cache hash 테이블을 셋업한다. */ _setCache: function() { this._cache.xy = D.getXY(this._wrap); D.setXY(this._wrap, this._cache.xy); this._cache.height = this.get('clientHeight'); this._cache.width = this.get('clientWidth'); this._cache.start.height = this._cache.height; this._cache.start.width = this._cache.width; this._cache.start.top = this._cache.xy[1]; this._cache.start.left = this._cache.xy[0]; this._cache.top = this._cache.xy[1]; this._cache.left = this._cache.xy[0]; this.set('height', this._cache.height, true); this.set('width', this._cache.width, true); }, /** * @private * @method _handleMouseUp * @param {Event} ev 마우스 event. * @description listener들을 초기화 하고, proxy element를 숨기며 클래스 이름을 삭제한다. */ _handleMouseUp: function(ev) { this._active = false; var handle = '_handle_for_' + this._currentHandle; this._currentDD.unOn('dragEvent', this[handle], this, true); this._currentDD.unOn('mouseUpEvent', this._handleMouseUp, this, true); if (this._proxy) { this._proxy.style.visibility = 'hidden'; this._proxy.style.zIndex = '-1'; if (this.get('setSize')) { this.resize(ev, this._cache.height, this._cache.width, this._cache.top, this._cache.left, true); } else { this.fireEvent('resize', { ev: 'resize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left }); } if (this.get('ghost')) { this.removeClass(this.CSS_GHOST); } } if (this.get('hover')) { D.addClass(this._wrap, this.CSS_HOVER); } if (this._status) { D.setStyle(this._status, 'display', 'none'); } if (this.browser.msie) { document.body.onselectstart = this._ieSelectBack; } if (this.browser.msie) { D.removeClass(this._wrap, this.CSS_RESIZE); } for (var i in this._handles) { if (DU.hasOwnProperty(this._handles, i)) { D.removeClass(this._handles[i], this.CSS_HANDLE + '-active'); } } if (this.get('hover') && !this._active) { D.addClass(this._wrap, this.CSS_HOVER); } D.removeClass(this._wrap, this.CSS_RESIZING); D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-' + this._currentHandle + '-active'); D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-active'); if (this.browser.msie) { D.addClass(this._wrap, this.CSS_RESIZE); } this._resizeEvent = null; this._currentHandle = null; if (!this.get('animate')) { this.set('height', this._cache.height, true); this.set('width', this._cache.width, true); } this.fireEvent('endResize', { ev: 'endResize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left }); }, /** * @private * @method _setRatio * @param {Number} h The height offset. * @param {Number} w The with offset. * @param {Number} t The top offset. * @param {Number} l The left offset. * @description Height, Width, Top, Left을 사용하여 원래 element 사이즈에 기반해서 재계산을 한다. * @return {Array} 새로운 Height, Width, Top, Left 설정 */ _setRatio: function(h, w, t, l) { var oh = h, ow = w; if (this.get('ratio')) { var orgH = this._cache.height, orgW = this._cache.width, nh = parseInt(this.get('height'), 10), nw = parseInt(this.get('width'), 10), maxH = this.get('maxHeight'), minH = this.get('minHeight'), maxW = this.get('maxWidth'), minW = this.get('minWidth'); switch (this._currentHandle) { case 'l': h = nh * (w / nw); h = Math.min(Math.max(minH, h), maxH); w = nw * (h / nh); t = (this._cache.start.top - (-((nh - h) / 2))); l = (this._cache.start.left - (-((nw - w)))); break; case 'r': h = nh * (w / nw); h = Math.min(Math.max(minH, h), maxH); w = nw * (h / nh); t = (this._cache.start.top - (-((nh - h) / 2))); break; case 't': w = nw * (h / nh); h = nh * (w / nw); l = (this._cache.start.left - (-((nw - w) / 2))); t = (this._cache.start.top - (-((nh - h)))); break; case 'b': w = nw * (h / nh); h = nh * (w / nw); l = (this._cache.start.left - (-((nw - w) / 2))); break; case 'bl': h = nh * (w / nw); w = nw * (h / nh); l = (this._cache.start.left - (-((nw - w)))); break; case 'br': h = nh * (w / nw); w = nw * (h / nh); break; case 'tl': h = nh * (w / nw); w = nw * (h / nh); l = (this._cache.start.left - (-((nw - w)))); t = (this._cache.start.top - (-((nh - h)))); break; case 'tr': h = nh * (w / nw); w = nw * (h / nh); l = (this._cache.start.left); t = (this._cache.start.top - (-((nh - h)))); break; } oh = this._checkHeight(h); ow = this._checkWidth(w); if ((oh != h) || (ow != w)) { t = 0; l = 0; if (oh != h) { ow = this._cache.width; } if (ow != w) { oh = this._cache.height; } } } return [oh, ow, t, l]; }, /** * @private * @method _updateStatus * @param {Number} h 새로운 height 설정 * @param {Number} w 새로운 width 설정 * @param {Number} t 새로운 top 설정 * @param {Number} l 새로운 left 설정 * @description Height, Width, Top, Left을 사용하여 element 사이즈를 가지고 status element를 업데이트 한다. */ _updateStatus: function(h, w, t, l) { if (this._resizeEvent && (!DU.isString(this._resizeEvent))) { if (this.get('status')) { D.setStyle(this._status, 'display', 'inline'); } h = ((h === 0) ? this._cache.start.height : h); w = ((w === 0) ? this._cache.start.width : w); var h1 = parseInt(this.get('height'), 10), w1 = parseInt(this.get('width'), 10); if (isNaN(h1)) { h1 = parseInt(h, 10); } if (isNaN(w1)) { w1 = parseInt(w, 10); } var diffH = (parseInt(h, 10) - h1); var diffW = (parseInt(w, 10) - w1); this._cache.offsetHeight = diffH; this._cache.offsetWidth = diffW; this._status.innerHTML = '' + parseInt(h, 10) + ' x ' + parseInt(w, 10) + '' + ((diffH > 0) ? '+' : '') + diffH + ' x ' + ((diffW > 0) ? '+' : '') + diffW + ''; D.setXY(this._status, [Event.getPageX(this._resizeEvent) + 12, Event.getPageY(this._resizeEvent) + 12]); } }, /** * @method lock * @description 리사이즈 될 수 없도록 리사이즈를 locking 한다. * @param {Boolean} dd 드래그 가능하도록 설정되어 있는 경우에도 역시 lock 됨 * @return {DU.util.LResize} 리사이즈 인스턴스 */ lock: function(dd) { this._locked = true; if (dd && this.dd) { D.removeClass(this._wrap, 'L-draggable'); this.dd.lock(); } return this; }, /** * @method unlock * @description 리사이즈 될 수 있도록 unlocking 한다. * @param {Boolean} dd 드래그 가능하도록 설정되어 있는 경우에도 역시 unlock 됨 * @return {DU.util.LResize} 리사이즈 인스턴스 */ unlock: function(dd) { this._locked = false; if (dd && this.dd) { D.addClass(this._wrap, 'L-draggable'); this.dd.unlock(); } return this; }, /** * @method isLocked * @description 리사이즈 인스턴스의 lock 상태를 체크한다. * @return {Boolean} */ isLocked: function() { return this._locked; }, /** * @method reset * @description start 상태가 되도록 element를 리셋한다. * @return {DU.util.LResize} 리사이즈 인스턴스 */ reset: function() { this.resize(null, this._cache.start.height, this._cache.start.width, this._cache.start.top, this._cache.start.left, true); return this; }, /** * @private * @method resize * @param {Event} ev 마우스 event. * @param {Number} h 새로운 height 설정 * @param {Number} w 새로운 width 설정 * @param {Number} t 새로운 top 설정 * @param {Number} l 새로운 left 설정 * @param {Boolean} force element를 리사이즈한다.(proxy 리사이즈에 대해 사용됨) * @param {Boolean} silent beforeResize Event 전에는 발생하지 않도록 한다. * @description handler로 부터의 데이터에 기반한 wrapper나 proxy element를 리사이즈 한다. * @return {DU.util.LResize} 리사이즈 인스턴스 */ resize: function(ev, h, w, t, l, force, silent) { if (this._locked) { return false; } this._resizeEvent = ev; var el = this._wrap, anim = this.get('animate'), set = true; if (this._proxy && !force) { el = this._proxy; anim = false; } this._setAutoRatio(ev); if (this._positioned) { if (this._proxy) { t = this._cache.top - t; l = this._cache.left - l; } } var ratio = this._setRatio(h, w, t, l); h = parseInt(ratio[0], 10); w = parseInt(ratio[1], 10); t = parseInt(ratio[2], 10); l = parseInt(ratio[3], 10); if (t == 0) { //No Offset, get from cache t = D.getY(el); } if (l == 0) { //No Offset, get from cache l = D.getX(el); } if (this._positioned) { if (this._proxy && force) { if (!anim) { el.style.top = this._proxy.style.top; el.style.left = this._proxy.style.left; } else { t = this._proxy.style.top; l = this._proxy.style.left; } } else { if (!this.get('ratio') && !this._proxy) { t = this._cache.top + -(t); l = this._cache.left + -(l); } if (t) { if (this.get('minY')) { if (t < this.get('minY')) { t = this.get('minY'); } } if (this.get('maxY')) { if (t > this.get('maxY')) { t = this.get('maxY'); } } } if (l) { if (this.get('minX')) { if (l < this.get('minX')) { l = this.get('minX'); } } if (this.get('maxX')) { if ((l + w) > this.get('maxX')) { l = (this.get('maxX') - w); } } } } } if (!silent) { var beforeReturn = this.fireEvent('beforeResize', { ev: 'beforeResize', target: this, height: h, width: w, top: t, left: l }); if (beforeReturn === false) { return false; } } this._updateStatus(h, w, t, l); if (this._positioned) { if (this._proxy && force) { //Do nothing } else { if (t) { D.setY(el, t); this._cache.top = t; } if (l) { D.setX(el, l); this._cache.left = l; } } } if (h) { if (!anim) { set = true; if (this._proxy && force) { if (!this.get('setSize')) { set = false; } } if (set) { if (this.browser.msie > 6) { if (h === this._cache.height) { h = h + 1; } } el.style.height = h + 'px'; } if ((this._proxy && force) || !this._proxy) { if (this._wrap != this.get('element')) { this.get('element').style.height = h + 'px'; } } } this._cache.height = h; } if (w) { this._cache.width = w; if (!anim) { set = true; if (this._proxy && force) { if (!this.get('setSize')) { set = false; } } if (set) { el.style.width = w + 'px'; } if ((this._proxy && force) || !this._proxy) { if (this._wrap != this.get('element')) { this.get('element').style.width = w + 'px'; } } } } if (anim) { if (DU.util.LAnim) { var _anim = new DU.util.LAnim(el, { height: { to: this._cache.height }, width: { to: this._cache.width } }, this.get('animateDuration'), this.get('animateEasing')); if (this._positioned) { if (t) { _anim.attributes.top = { to: parseInt(t, 10) }; } if (l) { _anim.attributes.left = { to: parseInt(l, 10) }; } } if (this._wrap != this.get('element')) { _anim.onTween.on(function() { this.get('element').style.height = el.style.height; this.get('element').style.width = el.style.width; }, this, true); } _anim.onComplete.on(function() { this.set('height', h); this.set('width', w); this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l }); }, this, true); _anim.animate(); } } else { if (this._proxy && !force) { this.fireEvent('proxyResize', { ev: 'proxyresize', target: this, height: h, width: w, top: t, left: l }); } else { this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l }); } } return this; }, /** * @private * @method _handle_for_br * @param {Object} args LCustomEvent로부터의 argument * @description Bottom Right handle에 대한 사이즈를 핸들링 한다. */ _handle_for_br: function(args) { var newW = this._setWidth(args.e); var newH = this._setHeight(args.e); this.resize(args.e, (newH + 1), newW, 0, 0); }, /** * @private * @method _handle_for_bl * @param {Object} args LCustomEvent로부터의 argument * @description Bottom Left handle에 대한 사이즈를 핸들링 한다. */ _handle_for_bl: function(args) { var newW = this._setWidth(args.e, true); var newH = this._setHeight(args.e); var l = (newW - this._cache.width); this.resize(args.e, newH, newW, 0, l); }, /** * @private * @method _handle_for_tl * @param {Object} args LCustomEvent로부터의 argument * @description Top Left handle에 대한 사이즈를 핸들링 한다. */ _handle_for_tl: function(args) { var newW = this._setWidth(args.e, true); var newH = this._setHeight(args.e, true); var t = (newH - this._cache.height); var l = (newW - this._cache.width); this.resize(args.e, newH, newW, t, l); }, /** * @private * @method _handle_for_tr * @param {Object} args LCustomEvent로부터의 argument * @description Top Right handle에 대한 사이즈를 핸들링 한다. */ _handle_for_tr: function(args) { var newW = this._setWidth(args.e); var newH = this._setHeight(args.e, true); var t = (newH - this._cache.height); this.resize(args.e, newH, newW, t, 0); }, /** * @private * @method _handle_for_r * @param {Object} args LCustomEvent로부터의 argument * @description Right handle에 대한 사이즈를 핸들링 한다. */ _handle_for_r: function(args) { this._dds.r.setYConstraint(0,0); var newW = this._setWidth(args.e); this.resize(args.e, 0, newW, 0, 0); }, /** * @private * @method _handle_for_l * @param {Object} args LCustomEvent로부터의 argument * @description Left handle에 대한 사이즈를 핸들링 한다. */ _handle_for_l: function(args) { this._dds.l.setYConstraint(0,0); var newW = this._setWidth(args.e, true); var l = (newW - this._cache.width); this.resize(args.e, 0, newW, 0, l); }, /** * @private * @method _handle_for_b * @param {Object} args LCustomEvent로부터의 argument * @description Bottom handle에 대한 사이즈를 핸들링 한다. */ _handle_for_b: function(args) { this._dds.b.setXConstraint(0,0); var newH = this._setHeight(args.e); this.resize(args.e, newH, 0, 0, 0); }, /** * @private * @method _handle_for_t * @param {Object} args LCustomEvent로부터의 argument * @description Top handle에 대한 사이즈를 핸들링 한다. */ _handle_for_t: function(args) { this._dds.t.setXConstraint(0,0); var newH = this._setHeight(args.e, true); var t = (newH - this._cache.height); this.resize(args.e, newH, 0, t, 0); }, /** * @private * @method _setWidth * @param {Event} ev 마우스 event. * @param {Boolean} flip 이동의 방향성 여부에 대한 argument * @description 마우스 event에 기반한 width를 계산한다. * @return {Number} 새로운 값 */ _setWidth: function(ev, flip) { var xy = this._cache.xy[0], w = this._cache.width, x = Event.getPageX(ev), nw = (x - xy); if (flip) { nw = (xy - x) + parseInt(this.get('width'), 10); } nw = this._snapTick(nw, this.get('yTicks')); nw = this._checkWidth(nw); return nw; }, /** * @private * @method _checkWidth * @param {Number} w 체크할 width * @description maxWidth와 minWidth에 대하여 전달된 값을 체크한다. * @return {Number} 새로운 값 */ _checkWidth: function(w) { if (this.get('minWidth')) { if (w <= this.get('minWidth')) { w = this.get('minWidth'); } } if (this.get('maxWidth')) { if (w >= this.get('maxWidth')) { w = this.get('maxWidth'); } } return w; }, /** * @private * @method _checkHeight * @param {Number} h 체크할 height * @description maxHeight와 minHeight에 대하여 전달된 값을 체크한다. * @return {Number} The new value */ _checkHeight: function(h) { if (this.get('minHeight')) { if (h <= this.get('minHeight')) { h = this.get('minHeight'); } } if (this.get('maxHeight')) { if (h >= this.get('maxHeight')) { h = this.get('maxHeight'); } } return h; }, /** * @private * @method _setHeight * @param {Event} ev 마우스 event. * @param {Boolean} flip 이동의 방향성 여부에 대한 argument * @description 마우스 event에 기반한 height를 계산한다. * @return {Number} 새로운 값 */ _setHeight: function(ev, flip) { var xy = this._cache.xy[1], h = this._cache.height, y = Event.getPageY(ev), nh = (y - xy); if (flip) { nh = (xy - y) + parseInt(this.get('height'), 10); } nh = this._snapTick(nh, this.get('xTicks')); nh = this._checkHeight(nh); return nh; }, /** * @private * @method _snapTick * @param {Number} size tick에 대한 사이즈 * @param {Number} pix The tick pixels. * @description 사용된 tick에 기반한 숫자를 조절한다. * @return {Number} 새로 snap된 위치 */ _snapTick: function(size, pix) { if (!size || !pix) { return size; } var _s = size; var _x = size % pix; if (_x > 0) { if (_x > (pix / 2)) { _s = size + (pix - _x); } else { _s = size - _x; } } return _s; }, /** * @private * @method init * @description Resize 클래스의 초기화 method */ init: function(p_oElement, p_oAttributes) { this._locked = false; this._cache = { xy: [], height: 0, width: 0, top: 0, left: 0, offsetHeight: 0, offsetWidth: 0, start: { height: 0, width: 0, top: 0, left: 0 } }; Resize.superclass.init.call(this, p_oElement, p_oAttributes); this.set('setSize', this.get('setSize')); if (p_oAttributes.height) { this.set('height', parseInt(p_oAttributes.height, 10)); } if (p_oAttributes.width) { this.set('width', parseInt(p_oAttributes.width, 10)); } var id = p_oElement; if (!DU.isString(id)) { id = D.generateId(id); } Resize._instances[id] = this; this._active = false; this._createWrap(); this._createProxy(); this._createHandles(); }, /** * @method getProxyEl * @description proxy에 대한 HTML 참조를 가져오며, proxy가 아닌 경우 null을 반환한다. * @return {HTMLElement} The proxy element */ getProxyEl: function() { return this._proxy; }, /** * @method getWrapEl * @description wrap element에 대한 HTML 참조를 가져오며, warp되어 있지 않다면, 현재 element를 반환한다. * @return {HTMLElement} The wrap element */ getWrapEl: function() { return this._wrap; }, /** * @method getStatusEl * @description status element에 대한 HTML 참조를 가져온다. * @return {HTMLElement} The status element */ getStatusEl: function() { return this._status; }, /** * @method getActiveHandleEl * @description 현재 활성화된 resize handle에 대한 HTML 참조를 가져온다. * @return {HTMLElement} 활성화된 handle element */ getActiveHandleEl: function() { return this._handles[this._currentHandle]; }, /** * @method isActive * @description 리사이즈 작업이 현재 element에 활성화 되어 있는지에 따라 true나 false를 반환한다. * @return {Boolean} */ isActive: function() { return ((this._active) ? true : false); }, /** * @private * @method initAttributes * @description 리사이즈 가능한 element를 생성하기 위해 사용된 설정 attribute의 모든 것을 초기화 한다. * @param {Object} attr 유틸리티를 생성하기 위해 사용된 설정 attribute의 집합을 명시하는 Object literal */ initAttributes: function(attr) { Resize.superclass.initAttributes.call(this, attr); /** * @attribute useShime * @description 이 설정은 resize handle과 드래그 가능한 property에 대하여 * LDragDrop 인스턴스에 전달될 것이다. * 이 property는 iframe과 다른 element들에 대하여 작업하기 위해서 resize를 * 핸들링하고자 하는 경우 사용되어야 한다. * @type Boolean */ this.setAttributeConfig('useShim', { value: ((attr.useShim === true) ? true : false), validator: DU.isBoolean, method: function(u) { for (var i in this._dds) { if (DU.hasOwnProperty(this._dds, i)) { this._dds[i].useShim = u; } } if (this.dd) { this.dd.useShim = u; } } }); /** * @attribute setSize * @description 리사이즈된 element의 사이즈를 설정하며 false로 설정하면 element는 자동으로 * 라사이즈 되지 않는다. resize event는 최종 사용자가 리사이즈 할수 있는 배열을 포함할 것이다. * 이 설정은 true 설정인 proxy와 false 설정인 animattion에서만 작동할 것이다. * @type Boolean */ this.setAttributeConfig('setSize', { value: ((attr.setSize === false) ? false : true), validator: DU.isBoolean }); /** * @attribute wrap * @description element를 wrapping 한다. * @type Boolean */ this.setAttributeConfig('wrap', { writeOnce: true, validator: DU.isBoolean, value: attr.wrap || false }); /** * @attribute handles * @description 사용하기 위한 handle들(다음 조합의): 't', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'. 기본값: ['r', 'b', 'br']. * 모든 바로 가기를 사용할 수 있다. 참고: 8방향의 리사이징은 absolute 위치인 element에 대하여 수행되어야 한다. * @type Array */ this.setAttributeConfig('handles', { writeOnce: true, value: attr.handles || ['r', 'b', 'br'], validator: function(handles) { if (DU.isString(handles) && handles.toLowerCase() == 'all') { handles = ['t', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr']; } if (!DU.isArray(handles)) { handles = handles.replace(/, /g, ','); handles = handles.split(','); } this._configs.handles.value = handles; } }); /** * @attribute width * @description element의 width * @type Number */ this.setAttributeConfig('width', { value: attr.width || parseInt(this.getStyle('width'), 10), validator: DU.isNumber, method: function(width) { width = parseInt(width, 10); if (width > 0) { if (this.get('setSize')) { this.setStyle('width', width + 'px'); } this._cache.width = width; this._configs.width.value = width; } } }); /** * @attribute height * @description element의 height * @type Number */ this.setAttributeConfig('height', { value: attr.height || parseInt(this.getStyle('height'), 10), validator: DU.isNumber, method: function(height) { height = parseInt(height, 10); if (height > 0) { if (this.get('setSize')) { this.setStyle('height', height + 'px'); } this._cache.height = height; this._configs.height.value = height; } } }); /** * @attribute minWidth * @description element의 최소 width * @type Number */ this.setAttributeConfig('minWidth', { value: attr.minWidth || 15, validator: DU.isNumber }); /** * @attribute minHeight * @description element의 최소 height * @type Number */ this.setAttributeConfig('minHeight', { value: attr.minHeight || 15, validator: DU.isNumber }); /** * @attribute maxWidth * @description element의 최대 width * @type Number */ this.setAttributeConfig('maxWidth', { value: attr.maxWidth || 10000, validator: DU.isNumber }); /** * @attribute maxHeight * @description element의 최대 height * @type Number */ this.setAttributeConfig('maxHeight', { value: attr.maxHeight || 10000, validator: DU.isNumber }); /** * @attribute minY * @description element의 최소 y 좌표 * @type Number */ this.setAttributeConfig('minY', { value: attr.minY || false }); /** * @attribute minX * @description element의 최소 x 좌표 * @type Number */ this.setAttributeConfig('minX', { value: attr.minX || false }); /** * @attribute maxY * @description element의 최대 y 좌표 * @type Number */ this.setAttributeConfig('maxY', { value: attr.maxY || false }); /** * @attribute maxX * @description element의 최대 x 좌표 * @type Number */ this.setAttributeConfig('maxX', { value: attr.maxX || false }); /** * @attribute animate * @description element를 리사이즈 하기 위해서 animation을 사용하여야 한다.(proxy를 사용하는 경우만 사용할 수 있음) * @type Boolean */ this.setAttributeConfig('animate', { value: attr.animate || false, validator: function(value) { var ret = true; if (!DU.util.LAnim) { ret = false; } return ret; } }); /** * @attribute animateEasing * @description animation에 대해 적용하기 위한 LEasing. * @type Object */ this.setAttributeConfig('animateEasing', { value: attr.animateEasing || function() { var easing = false; if (DU.util.LEasing && DU.util.LEasing.easeOut) { easing = DU.util.LEasing.easeOut; } return easing; }() }); /** * @attribute animateDuration * @description The Duration to apply to the animation. * @description animation에 대해 적용하기 위한 Duration * @type Number */ this.setAttributeConfig('animateDuration', { value: attr.animateDuration || 0.5 }); /** * @attribute proxy * @description 실제 element 대신 proxy element를 리사이즈 한다. * @type Boolean */ this.setAttributeConfig('proxy', { value: attr.proxy || false, validator: DU.isBoolean }); /** * @attribute ratio * @description 리사이징할 때 element의 ratio를 유지한다. * @type Boolean */ this.setAttributeConfig('ratio', { value: attr.ratio || false, validator: DU.isBoolean }); /** * @attribute ghost * @description 리사이즈 되는 element에 대한 불투명 필터를 적용한다.(proxy에서만 작동함) * @type Boolean */ this.setAttributeConfig('ghost', { value: attr.ghost || false, validator: DU.isBoolean }); /** * @attribute draggable * @description element를 드래그 가능하게 만들기 위한 편의성 method * @type Boolean */ this.setAttributeConfig('draggable', { value: attr.draggable || false, validator: DU.isBoolean, method: function(dd) { if (dd && this._wrap) { this._setupDragDrop(); } else { if (this.dd) { D.removeClass(this._wrap, this.CSS_DRAG); this.dd.unreg(); } } } }); /** * @attribute hover * @description 마우스가 over 됐을때 handle만 보여준다. * @type Boolean */ this.setAttributeConfig('hover', { value: attr.hover || false, validator: DU.isBoolean }); /** * @attribute hiddenHandles * @description handle을 보여주지 않고, 사용자가 커서만 사용하게 한다. * @type Boolean */ this.setAttributeConfig('hiddenHandles', { value: attr.hiddenHandles || false, validator: DU.isBoolean }); /** * @attribute knobHandles * @description 최대 사이즈 handle의 경우 대신 더 작은 handle을 사용한다. * @type Boolean */ this.setAttributeConfig('knobHandles', { value: attr.knobHandles || false, validator: DU.isBoolean }); /** * @attribute xTicks * @description 리사이즈할 span에 대한 x tick의 숫자 * @type Number or False */ this.setAttributeConfig('xTicks', { value: attr.xTicks || false }); /** * @attribute yTicks * @description 리사이즈할 span에 대한 y tick의 숫자 * @type Number or False */ this.setAttributeConfig('yTicks', { value: attr.yTicks || false }); /** * @attribute status * @description resize의 상태(새로운 사이즈)를 보여준다. * @type Boolean */ this.setAttributeConfig('status', { value: attr.status || false, validator: DU.isBoolean }); /** * @attribute autoRatio * @description 리사이즈하는 동안 shift 키를 사용하는 것은 ratio 설정을 토글할 것이다. * @type Boolean */ this.setAttributeConfig('autoRatio', { value: attr.autoRatio || false, validator: DU.isBoolean }); }, /** * @method destroy * @description resize object와 그것의 모든 element와 listner들을 없앤다. */ destroy: function() { for (var h in this._handles) { if (DU.hasOwnProperty(this._handles, h)) { Event.purgeElement(this._handles[h]); this._handles[h].parentNode.removeChild(this._handles[h]); } } if (this._proxy) { this._proxy.parentNode.removeChild(this._proxy); } if (this._status) { this._status.parentNode.removeChild(this._status); } if (this.dd) { this.dd.unreg(); D.removeClass(this._wrap, this.CSS_DRAG); } if (this._wrap != this.get('element')) { this.setStyle('position', ''); this.setStyle('top', ''); this.setStyle('left', ''); this._wrap.parentNode.replaceChild(this.get('element'), this._wrap); } this.removeClass(this.CSS_RESIZE); delete DU.util.LResize._instances[this.get('id')]; //Brutal Object Destroy for (var i in this) { if (DU.hasOwnProperty(this, i)) { this[i] = null; delete this[i]; } } }, /** * @method toString * @description Resize object를 표한하는 문자열을 반환한다. * @return {String} */ toString: function() { if (this.get) { return 'Resize (#' + this.get('id') + ')'; } return 'Resize Utility'; } }); DU.util.LResize = Resize; /** * @event dragEvent * @description DU.dd.LDragDrop dragEvent가 드래그 가능한 설정 옵션에 대해 발생되었을때 발생하는 event * @type DU.util.LCustomEvent */ /** * @event startResize * @description resize 액션이 시작되었을때 발생하는 event * @type DU.util.LCustomEvent */ /** * @event endResize * @description 드래그 인스턴스로부터의 mouseUp event가 발생했을때 발생하는 event * @type DU.util.LCustomEvent */ /** * @event resize * @description 모든 element 리사이즈에 발생하는 event(proxy 설정 세팅에 사용될때 오직 한번만 발생함) * @type DU.util.LCustomEvent */ /** * @event beforeResize * @description 모든 element 리사이즈 이전 및 사이즈 계산 후에 발생하며, 리사이즈가 중단될때 false를 반환하는 event * @type DU.util.LCustomEvent */ /** * @event proxyResize * @description 모든 proxy 리사이즈에 발생하는 event(proxy 설정 세팅에 사용될때만 발생함) * @type DU.util.LCustomEvent */ })(); (function(){ /** * @description LResizeMonitor, autoWidth일 경우 부모가 display none에서 block으로 될때 resize event를 fire한다. 이때 자식들의 sizing을 할 수 있다. * @module util * @title LResizeMonitor * @requires DU.util.LEventProvider */ DU.util.LResizeMonitor = function(oConfig){ var config = oConfig || {}; DU.util.LResizeMonitor.superclass.constructor.call(this); DU.applyObject(this, config, true); this.resizedEvent = this.createEvent('resized'); this.init(oConfig); } DU.extend(DU.util.LResizeMonitor, DU.util.LEventProvider, { /** * @description 객체를 초기화하는 메소드 * @method init * @private * @param {Object} oConfig 환경정보 객체 * @return void */ init: function(oConfig){ this.el = document.createElement('iframe'); this.el.scrolling = "no"; this.el.frameBorder = "0"; this.el.style.width = "90%"; this.el.style.height = "0px"; }, /** * @description 객체를 초기화하는 메소드 * @method init * @private * @param {Object} oConfig 환경정보 객체 * @return void */ onResized: function(e){ this.resizedEvent.fire(); }, /** * @description width auto시 resize가 일어나는지 모니터링하는 target object 설정 * @method monitor * @public * @param {String|Object} target 객체를 붙이고자 하는 Node정보 * @return void */ monitor: function(target){ var el = DU.get(target); el.appendChild(this.el); var ifrEl = DU.get(this.el); this.el.name = this.el.id; this.onResizedDelegate = DU.util.LFunction.createDelegate(this.onResized, this); try{ if (!DU.util.LEvent.on(this.el.contentWindow, "resize", this.onResizedDelegate)) { //IE DU.util.LEvent.on(this.el, "resize", this.onResizeDelegate); } } catch(e) { DU.log(e.message); } } }); })(); /** * @module util * @title LDelayedTask 유틸리티 * @namespace DU.util * @requires DU */ /** *

LDelayedTask 클래스는 새로운 타임아웃이 예전 타임아웃을 취소하는 serTimeout를 수행하는 * method의 실행을 "buffer"하기 위해 편리한 방법을 제공한다. * 취소될때, 타스크는 실행 전에 특정 time period를 기다릴 것이다. * 해당 time period 동안 타스크가 다시 취소된다면, 원래 호출도 취소될 것이다. * 이러한 연속성때문에 함수는 각 반복에 대해 오직 한번만 호출된다.

*

이 method는 사용자가 텍스트 필드에 타이핑을 마쳤는지의 여부를 확인하는 것 같은 * 일들에 특별히 유용하다. * 예제로써, 키가 눌렸을때 validation을 수행하는 것이다. * 밀리초의 특정 번호에 대한 키입력 이벤트를 버퍼링하기 위해 이 클래스를 사용할수 있으며, * 그것들이 그정도의 시간동안 중지하는 경우에만 수행을 한다. * 사용법:



var task = new DU.util.LDelayedTask(function(){

    alert(DU.getDom('myInputField').value.length);

});

// Wait 500ms before calling our function. If the user presses another key 

// during that 500ms, it will be cancelled and we'll wait another 500ms.

DU.get('myInputField').on('keypress', function(){

    task.{@link #delay}(500); 

});

 * 
*

요점을 설명하기 위하여 여기에 LDelayedTask를 사용하는 것을 유념해야 한다. * {@link DU.util.LEventProvider#addListener addListener/on}에 대한 설정 옵션 buffer는 * buffer 이벤트에 지연된 타스크를 셋업할 것이다. *

  • method - {Function} REQUIRED callback 함수.
  • *
  • scope - {Object} callback을 실행하기 위한 scope. 기본적으로 global window scope 이다.
  • *
  • argument - {Array} 개별의 argument들로서 method에 전달될 parameter들.
  • *
  • timeout - {number} 이전 callback의 완료 이후와 해당 callback을 실행하기 전에 기다리기 위한 밀리초 지연시간. 음수값은 즉시 실행 차단을 일으킨다. 기본값은 0.
  • *
  • until - {Function} 각각의 반복 이전에 실행될 boolean 함수. 완료를 표시하고 다음 callback으로 진행하기 위해서 true를 반환한다.
  • *
  • iterations - {Number} chain에서 다음 callback으로 진행하기 이전에 callback을 실행하기 위한 번호. until과 호환되지 않는다.
  • * * * @namespace DU.util * @class LChain * @constructor * @param {Function|Object} callback 큐를 초기화 하기 위한 callback의 개수 */ DU.util.LChain = function () { /** * The callback queue * @property q * @type {Array} * @private */ this.q = [].slice.call(arguments); /** * callback 큐가 실행을 통해 비게되면 event가 발생한다.(chain.stop()에 대한 호출을 통해서가 아님) * @event end */ this.createEvent('end'); }; DU.util.LChain.prototype = { /** * timeout은 실행을 일시정지하거나 종료하기 위해 사용되며, Chain의 실행 상태를 표시한다. * 0은 일시정지나 종료를 표시, -1은 실행의 blocking을 표시, 다른 양의 숫자들은 non-blocking 실행을 표시한다. * @property id * @type {number} * @private */ id : 0, /** * chain 실행을 시작하거나 마지막 일시정지 위치로부터 실행을 재개한다. * @method run * @return {Chain} the Chain instance */ run : function () { // Grab the first callback in the queue var c = this.q[0], fn; // If there is no callback in the queue or the Chain is currently // in an execution mode, return if (!c) { this.fireEvent('end'); return this; } else if (this.id) { return this; } fn = c.method || c; if (typeof fn === 'function') { var o = c.scope || {}, args = c.argument || [], ms = c.timeout || 0, me = this; if (!(args instanceof Array)) { args = [args]; } // Execute immediately if the callback timeout is negative. if (ms < 0) { this.id = ms; if (c.until) { for (;!c.until();) { // Execute the callback from scope, with argument fn.apply(o,args); } } else if (c.iterations) { for (;c.iterations-- > 0;) { fn.apply(o,args); } } else { fn.apply(o,args); } this.q.shift(); this.id = 0; return this.run(); } else { // If the until condition is set, check if we're done if (c.until) { if (c.until()) { // Shift this callback from the queue and execute the next // callback this.q.shift(); return this.run(); } // Otherwise if either iterations is not set or we're // executing the last iteration, shift callback from the queue } else if (!c.iterations || !--c.iterations) { this.q.shift(); } // Otherwise set to execute after the configured timeout this.id = setTimeout(function () { // Execute the callback from scope, with argument fn.apply(o,args); // Check if the Chain was not paused from inside the callback if (me.id) { // Indicate ready to run state me.id = 0; // Start the fun all over again me.run(); } },ms); } } return this; }, /** * 큐의 끝에 callback을 추가한다. * @method add * @param c {Function|Object} callback 함수 참조나 object literal * @return {Chain} the Chain instance */ add : function (c) { this.q.push(c); return this; }, /** * 현재 callback의 현재 실행 이후에 Chain의 실행 일시정지를 완료한다. * 간헐적으로 호출되는 경우 보류중인 callback에 대한 timeout을 지운다. * 일시정지된 Chain들은 chain.run()으로 재시작될 수 있다. * @method pause * @return {Chain} the Chain instance */ pause: function () { clearTimeout(this.id); this.id = 0; return this; }, /** * 현재 callback의 현재 실행 이후에 Chain의 큐를 종료하고 지우는 것을 완료한다. * @method stop * @return {Chain} the Chain instance */ stop : function () { this.pause(); this.q = []; return this; } }; DU.applyPrototype(DU.util.LChain,DU.util.LEventProvider); /** * @description LFormat * @module util * @title LFormat * @requires DU */ DU.namespace("DU.util"); (function(){ /** * @description LFormat * @namespace DU.util * @class LFormat */ DU.util.LFormat = { /** * @description String을 Data Object로 변환한다. * java.sql.date의 toString은 yyyy-MM-dd로 return이 되므로 이에 대응 *
    *
    oConfig.format의 약어 내용은 아래와 같다.
    *
    strftime에 정의된 format을 지원한다.
    *
    * strftime은 http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html에 * 오픈 그룹에 의해 정의된 여러가지 format 지정자들을 가지고 있다. * * PHP는 http://www.php.net/strftime에 정의된 자체의 몇가지 항목들을 추가한다. * * 이러한 자바스크립트 구현은 모든 PHP 지정자와 몇가지를 더 지원한다. * *
    arg \%a - 현재 locale의 요일의 단축표시 ex) ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] *
    arg \%A - 현재 locale의 요일 표시 ex) ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] *
    arg \%b - 현재 locale의 달의 단축표시 ex) ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] *
    arg \%B - 현재 locale의 달 표시 ex) ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] *
    arg \%c - 현재 locale의 선호되는 날짜와 시간 표시 ex) 미국 : %a %d %b %Y %T %Z, 한국 : %Y년 %b %d %a %T %Z *
    arg \%C - century number 현재 년도를 100으로 나누고 정수로 만든값으로 00~99 *
    arg \%d - 달의 일을 표시하는 값으로 01 ~ 31을 표시 *
    arg \%D - %m/%d/%y와 같다. *
    arg \%e - %d와 비슷하나 1자리수의 경우 0대신이 공백이 들어간다. ' 1' ~ '31' *
    arg \%F - %Y-%m-%d와 같다. (ISO 8601 date format) *
    arg \%g - Two digit representation of the year going by ISO-8601:1988 standards (see %V) *
    arg \%G - The full four-digit version of %g *
    arg \%h - %b와 같다. *
    arg \%H - 24-hour 00 ~ 23 *
    arg \%I - 12-hour 01 ~ 12 *
    arg \%j - 년중 몇번째 일인지 표시 001 ~ 366 *
    arg \%k - 24-hour 0 ~ 23 *
    arg \%l - 12-hour 1 ~ 12 *
    arg \%m - month 01 ~ 12 *
    arg \%M - minute 00 ~ 59 *
    arg \%n - 줄바꿈문자 *
    arg \%p - 현재 locale의 `AM', `PM' *
    arg \%P - %p와 같으나 소문자 'am', 'pm' *
    arg \%q - 입력용 format으로 년월일을 표시하며 기본 %Y%m%d이다. *
    arg \%Q - 입력용 format으로 년월일시분초를 표시하면 기본 %Y%m%d%H%M%S이다. *
    arg \%r - %p를 붙인 12시간제 시간 표시 %I:%M:%S %p와 같다. *
    arg \%R - 24시간 표시 %H:%M와 같다. *
    arg \%s - Unix Epoch Time timestamp, 1970-01-01 00:00:00 UTC이후의 초 ex) 305815200는 September 10, 1979 08:40:00 AM이다. *
    arg \%S - 초 00 ~ 59 *
    arg \%t - tab문자 *
    arg \%T - 현재시간 %H:%M:%S와 같다. *
    arg \%u - 요일을 숫자로 표시 1이 월요일이다. 1 ~ 7 *
    arg \%U - 지정한 년의 주번호 첫번째 일요일 부터 한주로 계산한다. *
    arg \%V - 지정한 년의 주번호(ISO 8601:1988) 첫번째 월요일 부터 한주로 계산한다. 단 첫주는 적어도 4일이상이 되어야 한다. 01~53 *
    arg \%w - 요일을 숫자로 표시 0이 일요일이다. 0 ~ 6 *
    arg \%W - 지정한 년의 주번호 첫번째 월요일 부터 한주로 계산. *
    arg \%x - 현재 locale의 기본 년월일 format ex) 2010-05-14, 14/05/10 *
    arg \%X - 현재 locale의 시간 ex) 15:59:16 *
    arg \%y - century를 뺀 년도 00 ~ 99 *
    arg \%Y - century를 포함한 년도 ex) 2010 *
    arg \%z - time zone(UTC) 약어 또는 전체 명 ex) -0500 또는 EST for Eastern Time *
    arg \%Z - time zone name / abbreviation *
    arg \%% - a literal `\%' character * @method stringToDate * @param {String} sDate * @param {Object} oConfig : oConfig.format(strptime), oConfig.locale * @return {Date} */ stringToDate: function(sDate, oConfig){ //debugger; if(sDate){ if(!oConfig){ //format과 locale이 없으면 default로 변환 //java.sql.date의 toString은 yyyy-MM-dd로 return이 된다. var dates = sDate.split('-'); var yyyy = parseInt(dates[0], 10); var MM = parseInt(dates[1], 10)-1; var dd = parseInt(dates[2], 10); return new Date(yyyy,MM,dd); }else{ return DU.util.LDate.parse(sDate, oConfig) } } else { return sDate; } }, /* * @description stringToDate의 기능중 type q에 해당되는 변환이 많이 발생하여 미리 구현해 놓음. * type q : '%y%m%d * @method stringToDate * @param {String} sDate * @param {Object} oConfig : oConfig.format(strptime), oConfig.locale * @return {Date} */ stringToDateByTypeQ: function(sDate) { return DU.util.LFormat.stringToDate(sDate, { format: '%q' }); }, /** * @description String을 Data Object로 변환한다. * java.sql.timestamp의 toString은 yyyy-MM-dd HH:mm:ss.ms로 return되므로 이에 대응. * @method stringToTimestamp * @param {String} sDate * @param {Object} oConfig : oConfig.format(strptime), oConfig.locale * @return {Date} */ stringToTimestamp: function(sDate, oConfig){ if(sDate){ if(!oConfig){ //format과 locale이 없으면 default로 변환 //java.sql.timestamp의 toString은 yyyy-MM-dd HH:mm:ss.ms로 return이 된다. var dt = v.split(' '); var dates = dt[0].split('-'); var times = dt[1].split(':'); var mss = times[2].split('.'); var yyyy = parseInt(dates[0], 10); var MM = parseInt(dates[1], 10)-1; var dd = parseInt(dates[2], 10); var HH = parseInt(times[0], 10); var mm = parseInt(times[1], 10); var ss = parseInt(mss[0], 10); var ms = parseInt(mss[1], 10); return new Date(yyyy,MM,dd,HH,mm,ss,ms); }else{ return DU.util.LDate.parse(sDate, oConfig) } } else { return sDate; } }, /** * @description Date값을 지정된 포맷 형식으로 변환한다. * @method dateToString * @param {Date} oDate * @param {Object} oConfig : oConfig.format(strptime), oConfig.locale * @return {String} 지정된 포맷의 날짜 */ dateToString: function(oDate, oConfig){ if(!oDate){ return ""; } return DU.util.LDate.format(oDate, oConfig); }, /** * @description 값을 천단위 구분 쉼표(',')로 표시한다. * @method numberFormat * @param {Int} v * @param {Object} oConfig 환경 정보 *
    * thousandsSeparator {String} 천단위 구분 쉼표 *
    * @return {String} */ numberFormat: function(v, oConfig) { oConfig = oConfig || { thousandsSeparator : ',' }; return DU.util.LNumber.format(v, oConfig); }, /** * @description config에서 설정된 default rocale을 참조하여 해당 국가의 통화를 리턴한다. * @method moneyFormat * @param {Int} v * @param {String} currency * @return {String} */ moneyFormat : function(v, currency) { if (currency == null || currency == undefined) { if (DU.getConfig().getFirst("$.core.defaultLocale") == 'en_US') { return DU.util.LNumber.usMoney(v); } else { var configCurrency = DU.getConfig().getFirst("$.core.format.moneyFormat.currency"); currency = (!(DU.isNull(configCurrency) || DU.isUndefined(configCurrency))) ? configCurrency : '₩'; return DU.util.LNumber.toMoney(v, currency); } } else { var oConfig = new Object(); oConfig.thousandsSeparator = ','; oConfig.prefix = currency; if(currency == '$') { oConfig.decimalPlaces = 2; } return DU.util.LNumber.format(v, oConfig); } }, /** * @description 비율(%)을 표시하고, 소수점자리 수를 지정하면 그 밑으로 반올림하여 해당 소수점을 표시한다. * @method rateFormat * @param {Int} v * @param {String} rate 비율단위 * @param {Int} point 소수점자리수 * @return {String} */ rateFormat : function(v, rate, point) { var format = new Object(); format.decimalPlaces = point || {}; format.suffix = rate; return DU.util.LNumber.format(v, format); }, /** * @description 어떤 값을 지정된 포맷 패턴을 사용하여 Time 형식으로 바꾸어준다. * @method timeFormat * @param {String} sTime 595959 * @param {Object} oConfig : oConfig.format(strptime), oConfig.locale * @return {Object} */ timeFormat: function (sTime, oConfig) { var oConfig = oConfig || {}; var format = "%Y%m%d%H%M%S"; if(sTime == null || sTime == undefined || sTime.length == 0) { return ""; } var length = sTime.length; // sTime의 자리수 6자리로 만들기 if(length > 6) { sTime = sTime.substr(0, 6); } else { sTime = sTime + ""; for(var idx = 0; idx < (6-length); idx++) { sTime = sTime + "0"; } } var sDate = "20090909" + sTime; var oDate = DU.util.LDate.parse(sDate, {format:format,locale:oConfig.locale}); if (oConfig.format == undefined || oConfig.format == null) { oConfig.format = "%T"; } return DU.util.LDate.format(oDate, oConfig); }, /** * @description 중량 단위를 표시한다. * @method weightFormat * @param {Int} v * @param {String} unit 중량의 단위 ex.kg, g, mg * @param {Boolean} thousandsSeparator 천 단위 쉼표 * @param {Int} point 소수점 자리수 * @return {String} */ weightFormat: function(v, unit, thousandsSeparator, point) { var format = new Object(); format.decimalPlaces = point || {}; format.suffix = unit || {}; if(thousandsSeparator) { format.thousandsSeparator = ','; } return DU.util.LNumber.format(v, format); }, /** * @description 길이 단위를 표시한다. * @method lengthFormat * @param {Int} v * @param {String} unit 길이 단위 ex.m, km * @param {Int} point 소수점 자리수 * @param {Boolean} thousandsSeparator 천 단위 쉼표 * @return {String} */ lengthFormat: function(v, unit, thousandsSeparator, point) { var format = new Object(); format.decimalPlaces = point || {}; format.suffix = unit || {}; if(thousandsSeparator) { format.thousandsSeparator = ','; } return DU.util.LNumber.format(v, format); }, /** * @description LFormat에 등록된 모든 function을 renderer로 처리할 수 있게 해주는 메소드 * LFormat에 있는 Function의 arguments는 fnName뒤에 순차적으로 넣으면 됨. *
    
    
             * DU.util.LFormat.rendererWrapper('dateToString', {format:'%x'});
    
             * DU.util.LRenderer.dateRenderer('%x')
    
             * 위 두개는 동일한 기능으로 수행 가능
    
             * 
    * @method rendererWrapper * @param {String} fnName LFormat에 등록된 모든 function명 * @return {Function} */ rendererWrapper: function(fnName) { var fn = eval('DU.util.LFormat.' + fnName); var a=arguments; var param = []; for(var i = 1 ; i < a.length; i++) param.push(a[i]); return function(v){ return fn.call(v, param); }; } }; })(); DU.namespace("DU.util"); /** * Renderer * * @namespace DU.util * @requires DU * @class LRenderer * @static */ DU.util.LRenderer = { /** * 여러 번 date 형식을 재사용할 수 있는 date rendering 함수를 리턴한다. * @method dateRenderer * @param {String} format * @param {String} locale * @return {function} */ dateRenderer: function(format,locale){ return function(v){ return DU.util.LFormat.dateToString(v, {format:format,locale:locale}); }; }, /** * 여러번 number 형식을 재사용할 수 있는 number rendering 함수를 리턴한다. * @method numberRenderer * @param * @return {function} */ numberRenderer : function() { return function(v){ return DU.util.LFormat.numberFormat(v); }; }, /** * 여러번 money 형식을 재사용할 수 있는 money rendering 함수를 리턴한다. * @method moneyRenderer * @param * @return {function} */ moneyRenderer : function(currency) { return function(v){ return DU.util.LFormat.moneyFormat(v, currency); }; }, /** * 여러번 rate 형식을 재사용할 수 있는 rate rendering 함수를 리턴한다. * @method rateRenderer * @param {Object} point * @return {function} */ rateRenderer : function(point) { return function(v){ return DU.util.LFormat.stringToRate(v, point); }; }, /** * 여러번 time 형식을 재사용할 수 있는 time rendering 함수를 리턴한다. * @method timeRenderer * @param {Object} format * @return {function} */ timeRenderer: function (format) { return function(v){ return DU.util.LFormat.timeFormat(v, {format:format}); }; }, /** * 여러번 weight 형식을 재사용할 수 있는 weight rendering 함수를 리턴한다. * @method weightRenderer * @param {Object} unit * @param {Object} point * @param {Object} thousandsSeparator * @return {function} */ weightRenderer: function(unit, thousandsSeparator, point) { return function(v){ return DU.util.LFormat.weightFormat(v, unit, thousandsSeparator, point); }; }, /** * 여러번 length 형식을 재사용할 수 있는 length rendering 함수를 리턴한다. * @method lengthRenderer * @param {Object} unit * @param {Object} point * @param {Object} thousandsSeparator * @return {function} */ lengthRenderer: function(unit, thousandsSeparator, point) { return function(v){ return DU.util.LFormat.lengthFormat(v, unit, thousandsSeparator, point); }; } }; (function () { /** * LPlugin * @namespace DU.util * @class LPlugin * @extends DU.util.LEventProvider * @constructor * @param {Object} userConfig 해당 overlay에 대한 집합이어 하는 설정을 포함하는 설정 object literal. * 더 자세한 사항은 설정 문서를 참고 한다. */ DU.util.LPlugin = function (oConfig) { var config = oConfig || {}; DU.applyObject(this, config, true); }; DU.extend(DU.util.LPlugin, DU.util.LEventProvider, { initPlugin : function(parent) { for(m in this) { if(m != 'constructor' && m != 'initPlugin' && !DU.util.LEventProvider.prototype[m]) { parent[m] = this[m]; } } } }); }()); (function() { DU.namespace('DU.animation'); var Y = DU.animation; /** * HTMLElement에 animation 효과주는 class * @module animation * @requires event, dom */ /** * Base animation class는 animation효과들을 주는 interface를 제공한다. *

    Usage: var myAnim = new DU.animation.LAnim(el, { width: { from: 10, to: 100 } }, 1, DU.animation.LEasing.easeOut);

    * @class LAnim * @namespace DU.animation * @requires DU.animation.LAnimMgr * @requires DU.animation.LEasing * @requires DU.animation.LDom * @requires DU.animation.LEvent * @requires DU.animation.LCustomEvent * @constructor * @param {String | HTMLElement} el animation효과가 적용될 element * @param {Object} attributes animation효과 관련 attribute, 각 attribute는 object로 to나 by가 정의되어 있다. 이외에 from, units(px)가 있으며 camelCase로 표기한다. * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based * @param {Function} method (optional, defaults to DU.animation.LEasing.easeNone) Computes the values that are applied to the attributes per frame (generally a DU.animation.LEasing method) */ var LAnim = function(el, attributes, duration, method) { if (!el) { } this.init(el, attributes, duration, method); }; LAnim.NAME = 'LAnim'; LAnim.prototype = { /** * LAnim instance에 대한 이름 * @method toString * @return {String} */ toString: function() { var el = this.getEl() || {}; var id = el.id || el.tagName; return (this.constructor.NAME + ': ' + id); }, patterns: { // cached for performance noNegatives: /width|height|opacity|padding/i, // keep at zero or above offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset }, /** * animation의 "method"에 의해 계산된 값을 return * @method doMethod * @param {String} attr The name of the attribute. * @param {Number} start The value this attribute should start from for this animation. * @param {Number} end The value this attribute should end at for this animation. * @return {Number} The Value to be applied to the attribute. */ doMethod: function(attr, start, end) { return this.method(this.currentFrame, start, end - start, this.totalFrames); }, /** * attribute에 값 설정 * @method setAttribute * @param {String} attr attribute명. * @param {Number} val attribute에 할당될 값. * @param {String} unit 값의 unit ('px', '%', etc.) */ setAttribute: function(attr, val, unit) { if ( this.patterns.noNegatives.test(attr) ) { val = (val > 0) ? val : 0; } DU.util.LDom.setStyle(this.getEl(), attr, val + unit); }, /** * attribute의 값 return. * @method getAttribute * @param {String} attr attribute명 * @return {Number} val 값 */ getAttribute: function(attr) { var el = this.getEl(); var val = DU.util.LDom.getStyle(el, attr); if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { return parseFloat(val); } var a = this.patterns.offsetAttribute.exec(attr) || []; var pos = !!( a[3] ); // top or left var box = !!( a[2] ); // width or height // use offsets for width/height and abs pos top/left if ( box || (DU.util.LDom.getStyle(el, 'position') == 'absolute' && pos) ) { val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; } else { // default to zero for other 'auto' val = 0; } return val; }, /** * 기본 unit * @method getDefaultUnit * @param {attr} attr attribute명 * @return {String} 기본 unit */ getDefaultUnit: function(attr) { if ( this.patterns.defaultUnit.test(attr) ) { return 'px'; } return ''; }, /** * animation동안 사용될 실제 값들. subclass를 사용할때만 사용됨. * @method setRuntimeAttribute * @param {Object} attr attribute object * @private */ setRuntimeAttribute: function(attr) { var start; var end; var attributes = this.attributes; this.runtimeAttributes[attr] = {}; var isset = function(prop) { return (typeof prop !== 'undefined'); }; if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { return false; // note return; nothing to animate to } start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); // To beats by, per SMIL 2.1 spec if ( isset(attributes[attr]['to']) ) { end = attributes[attr]['to']; } else if ( isset(attributes[attr]['by']) ) { if (start.constructor == Array) { end = []; for (var i = 0, len = start.length; i < len; ++i) { end[i] = start[i] + attributes[attr]['by'][i] * 1; // times 1 to cast "by" } } else { end = start + attributes[attr]['by'] * 1; } } this.runtimeAttributes[attr].start = start; this.runtimeAttributes[attr].end = end; // set units if needed this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr); return true; }, /** * Constructor for LAnim instance. * @method init * @param {String | HTMLElement} el animate될 element * @param {Object} attributes animation효과 관련 attribute, 각 attribute는 object로 to나 by가 정의되어 있다. 이외에 from, units(px)가 있으며 camelCase로 표기한다. * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based * @param {Function} method (optional, defaults to DU.animation.LEasing.easeNone) Computes the values that are applied to the attributes per frame (generally a DU.animation.LEasing method) */ init: function(el, attributes, duration, method) { /** * animation이 작동될 지 여부 * @property isAnimated * @private * @type Boolean */ var isAnimated = false; /** * animation 시작시 생성되는 date object * @property startTime * @private * @type Date */ var startTime = null; /** * animation 실행시 보여질 frame 수 * @property actualFrames * @private * @type Int */ var actualFrames = 0; /** * animate될 element * @property el * @private * @type HTMLElement */ el = DU.util.LDom.get(el); /** * animate될 attribute들의 collection * 각 attribute들은 적어도 "to" 또는 "by"가 정의되어 있어야 한다. * "to"를 설정하면 해당 값으로 animation이 끝난다. * "by"를 설정하면 시작값에 해당 값을 더해서 animation이 끝난다. * 둘다 설정하면 "to"만 사용되고 "by"는 무시된다. * option으로 "from"은 animation시작값으로 기본값은 현재 값이다. 그리고 "unit"은 값들의 단위를 지정한다. * @property attributes * @type Object */ this.attributes = attributes || {}; /** * animation 시간으로 기본값은 "1"(초) * @property duration * @type Number */ this.duration = !DU.isUndefined(duration) ? duration : 1; /** * animation동안 attribute에 제공될 값을 만드는 method * Default는 "DU.animation.LEasing.easeNone". * @property method * @type Function */ this.method = method || Y.LEasing.easeNone; /** * duration에 초를 사용할 지 여부 * Defaults to true. * @property useSeconds * @type Boolean */ this.useSeconds = true; // default to seconds /** * timeline에서 현재 animation의 위치 * time-based animation에서 LAnimMgr에 의해 animation이 제시간에 끝났는지 확인용으로 사용된다. * @property currentFrame * @type Int */ this.currentFrame = 0; /** * 실행될 총 frame수 * time-based animation에서 LAnimMgr에 의해 animation이 제시간에 끝났는지 확인용으로 사용된다. * @property totalFrames * @type Int */ this.totalFrames = Y.LAnimMgr.fps; /** * animate될 element를 변경한다. * @method setEl */ this.setEl = function(element) { el = DU.util.LDom.get(element); }; /** * animate될 element를 return * @method getEl * @return {HTMLElement} */ this.getEl = function() { return el; }; /** * animate되고 있는지 여부 return * @method isAnimated * @return {Boolean} isAnimated의 현재 값 */ this.isAnimated = function() { return isAnimated; }; /** * animation 시작 시간 return * @method getStartTime * @return {Date} 시작시간 */ this.getStartTime = function() { return startTime; }; this.runtimeAttributes = {}; /** * animation manager에 등록하면서 animation을 시작한다. * @method animate */ this.animate = function() { if ( this.isAnimated() ) { return false; } this.currentFrame = 0; this.totalFrames = ( this.useSeconds ) ? Math.ceil(Y.LAnimMgr.fps * this.duration) : this.duration; if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration this.totalFrames = 1; } Y.LAnimMgr.registerElement(this); return true; }; /** * animation을 정지한다. 일반적으로 animation이 끝났을 경우 LAnimMgr에 의해 call된다. * @method stop * @param {Boolean} finish (optional) true면 마지막 frame로 이동한다. */ this.stop = function(finish) { if (!this.isAnimated()) { // nothing to stop return false; } if (finish) { this.currentFrame = this.totalFrames; this._onTween.fire(); } Y.LAnimMgr.stop(this); }; var onStart = function() { this.onStart.fire(); this.runtimeAttributes = {}; for (var attr in this.attributes) { this.setRuntimeAttribute(attr); } isAnimated = true; actualFrames = 0; startTime = new Date(); }; /** * 각각의 animated attribute에 대한 시작값, 종료값을 각 프레임 별로 넣고, 결과값을 각 attribute에 적용한다. * @private */ var onTween = function() { var data = { duration: new Date() - this.getStartTime(), currentFrame: this.currentFrame }; data.toString = function() { return ( 'duration: ' + data.duration + ', currentFrame: ' + data.currentFrame ); }; this.onTween.fire(data); var runtimeAttributes = this.runtimeAttributes; for (var attr in runtimeAttributes) { this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); } actualFrames += 1; }; var onComplete = function() { var actual_duration = (new Date() - startTime) / 1000 ; var data = { duration: actual_duration, frames: actualFrames, fps: actualFrames / actual_duration }; data.toString = function() { return ( 'duration: ' + data.duration + ', frames: ' + data.frames + ', fps: ' + data.fps ); }; isAnimated = false; actualFrames = 0; this.onComplete.fire(data); }; /** * subclassing에 유용한, onStart 이후에 발생하는 Custom event * @private */ this._onStart = new DU.util.LCustomEvent('_start', this, true); /** * animation의 시작 event * @event onStart */ this.onStart = new DU.util.LCustomEvent('start', this); /** * 각 frame별로 fire되는 event * @event onTween */ this.onTween = new DU.util.LCustomEvent('tween', this); /** * onTween 이후에 발생하는 Custom event * @private */ this._onTween = new DU.util.LCustomEvent('_tween', this, true); /** * animation 종료 event * @event onComplete */ this.onComplete = new DU.util.LCustomEvent('complete', this); /** * onComplete 이후에 발생하는 Custom event * @private */ this._onComplete = new DU.util.LCustomEvent('_complete', this, true); this._onStart.on(onStart); this._onTween.on(onTween); this._onComplete.on(onComplete); } }; Y.LAnim = LAnim; })(); /** * animation과 theading 처리 * LAnim과 subclass 들에서 사용됨 * @class LAnimMgr * @namespace DU.animation */ DU.animation.LAnimMgr = new function() { /** * animation Interval을 위한 참조값. * @property thread * @private * @type Int */ var thread = null; /** * 등록된 animation object들 중 현재 queue object. * @property queue * @private * @type Array */ var queue = []; /** * 활성화(active)된 animation 갯수. * @property tweenCount * @private * @type Int */ var tweenCount = 0; /** * 기본 frame 비율 (frames per second). * 더 나은 x-browser calibration을 위해 임의로 증가(느린 브라우저들은 frame이 더 떨어짐) * @property fps * @type Int * */ this.fps = 1000; /** * milliseconds 단위의 지연 간격, 가능한 가장 빠른 속도로 deafult 설정된다. * @property delay * @type Int * */ this.delay = 1; /** * animation queue에 animation instance를 추가한다. * 모든 animation instance는 움직이기 위해 반드시 등록되어야 한다. * @method registerElement * @param {object} tween 등록될 LAnim instance 객체 */ this.registerElement = function(tween) { queue[queue.length] = tween; tweenCount += 1; tween._onStart.fire(); this.start(); }; /** * animation queue로 부터 animation instace를 제거한다. * 모든 animation instance는 움직이기 위해 반드시 등록되어야 한다. * @method unRegister * @param {object} tween 등록된 LAnim instance 객체 * @param {Int} index LAnim instance의 index * @private */ this.unRegister = function(tween, index) { index = index || getIndex(tween); if (!tween.isAnimated() || index == -1) { return false; } tween._onComplete.fire(); queue.splice(index, 1); tweenCount -= 1; if (tweenCount <= 0) { this.stop(); } return true; }; /** * animation thread를 시작한다. * 한번에 오직 하나의 thread만 실행할 수 있다. * @method start */ this.start = function() { if (thread === null) { thread = setInterval(this.run, this.delay); } }; /** * animation thread나 특정 animation instance를 중지한다. * @method stop * @param {object} tween 중지할 특정 LAnim instance(optional) * 만약 instance가 주어지지 않는다면, Manager는 thread와 모든 animation을 중지한다. */ this.stop = function(tween) { if (!tween) { clearInterval(thread); for (var i = 0, len = queue.length; i < len; ++i) { this.unRegister(queue[0], 0); } queue = []; thread = null; tweenCount = 0; } else { this.unRegister(tween); } }; /** * 각각의 animation frame를 처리하는 간격마다 호출된다. * @method run */ this.run = function() { for (var i = 0, len = queue.length; i < len; ++i) { var tween = queue[i]; if ( !tween || !tween.isAnimated() ) { continue; } if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) { tween.currentFrame += 1; if (tween.useSeconds) { correctFrame(tween); } tween._onTween.fire(); } else { DU.animation.LAnimMgr.stop(tween, i); } } }; var getIndex = function(anim) { for (var i = 0, len = queue.length; i < len; ++i) { if (queue[i] == anim) { return i; // note return; } } return -1; }; /** * animation을 제시간으로 유지하기 위한 fly frame correction. * @method correctFrame * @private * @param {Object} tween The LAnim instance being corrected. */ var correctFrame = function(tween) { var frames = tween.totalFrames; var frame = tween.currentFrame; var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); var elapsed = (new Date() - tween.getStartTime()); var tweak = 0; if (elapsed < tween.duration * 1000) { // check if falling behind tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); } else { // went over duration, so jump to end tweak = frames - (frame + 1); } if (tweak > 0 && isFinite(tweak)) { // adjust if needed if (tween.currentFrame + tweak >= frames) {// dont go past last frame tweak = frames - (frame + 1); } tween.currentFrame += tweak; } }; }; (function() { /** * color 전환을 위한 LAnim subclass. *

    Usage: var myAnim = new Y.LColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.LEasing.easeOut); 컬러값은 다음과 같이 지정될 수 있다. 112233, #112233, * [255,255,255], or rgb(255,255,255)

    * @class LColorAnim * @namespace DU.animation * @requires DU.animation.LAnim * @requires DU.animation.LAnimMgr * @requires DU.animation.LEasing * @requires DU.animation.LBezier * @requires DU.util.LDom * @requires DU.util.LEvent * @constructor * @extends DU.animation.LAnim * @param {HTMLElement | String} el animated 되어질 element에 대한 참조 * @param {Object} attributes animated될 attribute * 각각의 attribute는 최소한 "to"나 "by" member가 정의된 object이다. * 추가적인 옵션 member들은 "from"(defaults to current value)과 "unit"(defaults to "px") 이다. * 모든 attribute 이름은 camelCase 방식을 사용한다. * @param {Number} duration (optional, 기본값 1초) animation의 길이 (frames or seconds), defaults to time-based * @param {Function} method (optional, DU.animation.LEasing.easeNone 기본값) 각 frame별 attribute에 적용되는 값을 계산 (일반적으로 DU.animation.LEasing method) */ var LColorAnim = function(el, attributes, duration, method) { LColorAnim.superclass.constructor.call(this, el, attributes, duration, method); }; LColorAnim.NAME = 'LColorAnim'; LColorAnim.DEFAULT_BGCOLOR = '#fff'; // shorthand var Y = DU.animation; DU.extend(LColorAnim, Y.LAnim); var superclass = LColorAnim.superclass; var proto = LColorAnim.prototype; proto.patterns.color = /color$/i; proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i; proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i; proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i; proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari /** * Attempts to parse the given string and return a 3-tuple. * 주어진 문자열에 대한 parsing을 시도하고 3-tuple을 반환한다. * @method parseColor * @param {String} s parsing 할 문자열 * @return {Array} rgb값의 3-tuple */ proto.parseColor = function(s) { if (s.length == 3) { return s; } var c = this.patterns.hex.exec(s); if (c && c.length == 4) { return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ]; } c = this.patterns.rgb.exec(s); if (c && c.length == 4) { return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ]; } c = this.patterns.hex3.exec(s); if (c && c.length == 4) { return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ]; } return null; }; proto.getAttribute = function(attr) { var el = this.getEl(); if (this.patterns.color.test(attr) ) { var val = DU.util.LDom.getStyle(el, attr); var that = this; if (this.patterns.transparent.test(val)) { // bgcolor default var parent = DU.util.LDom.getAncestorBy(el, function(node) { return !that.patterns.transparent.test(val); }); if (parent) { val = Y.LDom.getStyle(parent, attr); } else { val = LColorAnim.DEFAULT_BGCOLOR; } } } else { val = superclass.getAttribute.call(this, attr); } return val; }; proto.doMethod = function(attr, start, end) { var val; if ( this.patterns.color.test(attr) ) { val = []; for (var i = 0, len = start.length; i < len; ++i) { val[i] = superclass.doMethod.call(this, attr, start[i], end[i]); } val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')'; } else { val = superclass.doMethod.call(this, attr, start, end); } return val; }; proto.setRuntimeAttribute = function(attr) { superclass.setRuntimeAttribute.call(this, attr); if ( this.patterns.color.test(attr) ) { var attributes = this.attributes; var start = this.parseColor(this.runtimeAttributes[attr].start); var end = this.parseColor(this.runtimeAttributes[attr].end); // fix colors if going "by" if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) { end = this.parseColor(attributes[attr].by); for (var i = 0, len = start.length; i < len; ++i) { end[i] = start[i] + end[i]; } } this.runtimeAttributes[attr].start = start; this.runtimeAttributes[attr].end = end; } }; Y.LColorAnim = LColorAnim; })(); /** * 처음부터 끝까지 어떻게 animation을 진행할지 결정하는 Singleton 패턴 * @class LEasing * @namespace DU.animation */ DU.animation.LEasing = { /** * point들 간의 speed를 통일한다. * @method easeNone * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeNone: function (t, b, c, d) { return c*t/d + b; }, /** * 끝쪽을 향하여 천천히 가속하기 시작한다. * @method easeIn * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeIn: function (t, b, c, d) { return c*(t/=d)*t + b; }, /** * 끝쪽을 향하여 빠르게 감속하기 시작한다. * @method easeOut * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeOut: function (t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, /** * 끝쪽을 향하여 천천히 감속하기 시작한다. * @method easeBoth * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeBoth: function (t, b, c, d) { if ((t/=d/2) < 1) { return c/2*t*t + b; } return -c/2 * ((--t)*(t-2) - 1) + b; }, /** * 끝쪽을 향하여 천천히 가속하기 시작한다. * @method easeInStrong * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeInStrong: function (t, b, c, d) { return c*(t/=d)*t*t*t + b; }, /** * 끝쪽을 향하여 빠르게 감속하기 시작한다. * @method easeOutStrong * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeOutStrong: function (t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }, /** * 끝쪽을 향하여 천천히 감속하기 시작한다. * @method easeBothStrong * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ easeBothStrong: function (t, b, c, d) { if ((t/=d/2) < 1) { return c/2*t*t*t*t + b; } return -c/2 * ((t-=2)*t*t*t - 2) + b; }, /** * 탄성 효과 snap in * @method elasticIn * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} a 진폭 (optional) * @param {Number} p 주기 (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ elasticIn: function (t, b, c, d, a, p) { if (t == 0) { return b; } if ( (t /= d) == 1 ) { return b+c; } if (!p) { p=d*.3; } if (!a || a < Math.abs(c)) { a = c; var s = p/4; } else { var s = p/(2*Math.PI) * Math.asin (c/a); } return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; }, /** * 탄성 효과 snap out * @method elasticOut * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} a 진폭 (optional) * @param {Number} p 주기 (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ elasticOut: function (t, b, c, d, a, p) { if (t == 0) { return b; } if ( (t /= d) == 1 ) { return b+c; } if (!p) { p=d*.3; } if (!a || a < Math.abs(c)) { a = c; var s = p / 4; } else { var s = p/(2*Math.PI) * Math.asin (c/a); } return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; }, /** * 탄성 효과 snap both * @method elasticBoth * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} a 진폭 (optional) * @param {Number} p 주기 (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ elasticBoth: function (t, b, c, d, a, p) { if (t == 0) { return b; } if ( (t /= d/2) == 2 ) { return b+c; } if (!p) { p = d*(.3*1.5); } if ( !a || a < Math.abs(c) ) { a = c; var s = p/4; } else { var s = p/(2*Math.PI) * Math.asin (c/a); } if (t < 1) { return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; } return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; }, /** * Backtracks slightly, then reverses direction and moves to end. * @method backIn * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} s Overshoot (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ backIn: function (t, b, c, d, s) { if (typeof s == 'undefined') { s = 1.70158; } return c*(t/=d)*t*((s+1)*t - s) + b; }, /** * Overshoots end, then reverses and comes back to end. * @method backOut * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} s Overshoot (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ backOut: function (t, b, c, d, s) { if (typeof s == 'undefined') { s = 1.70158; } return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, /** * Backtracks slightly, then reverses direction, overshoots end, * then reverses and comes back to end. * @method backBoth * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @param {Number} s Overshoot (optional) * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ backBoth: function (t, b, c, d, s) { if (typeof s == 'undefined') { s = 1.70158; } if ((t /= d/2 ) < 1) { return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; } return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; }, /** * Bounce off of start. * @method bounceIn * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ bounceIn: function (t, b, c, d) { return c - DU.animation.LEasing.bounceOut(d-t, 0, c, d) + b; }, /** * Bounces off end. * @method bounceOut * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ bounceOut: function (t, b, c, d) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; }, /** * Bounces off start and end. * @method bounceBoth * @param {Number} t 현재값을 계산하기 위해 사용되는 시간값 * @param {Number} b 시작값 * @param {Number} c 시작값과 종료값 사이의 Delta * @param {Number} d animation 총 길이 * @return {Number} 현재 animation 프레임에 대한 계산된 값 */ bounceBoth: function (t, b, c, d) { if (t < d/2) { return DU.animation.LEasing.bounceIn(t*2, 0, c, d) * .5 + b; } return DU.animation.LEasing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b; } }; (function() { /** * "attribute"의 "points" member에 의해 정의된 경로를 따라 element들을 이동하기 위한 LAnim subclass. * 모든 "points"는 x, y 좌표 배열이다. *

    Usage: var myAnim = new DU.animation.LMotion(el, { points: { to: [800, 800] } }, 1, DU.animation.LEasing.easeOut);

    * @class LMotion * @namespace DU.animation * @requires DU.animation.LAnim * @requires DU.animation.LAnimMgr * @requires DU.animation.LEasing * @requires DU.animation.LBezier * @requires DU.util.LDom * @requires DU.util.LEvent * @requires DU.util.LCustomEvent * @constructor * @extends DU.animation.LColorAnim * @param {String | HTMLElement} el animated 되어질 element에 대한 참조 * @param {Object} attributes animated될 attribute * 각각의 attribute는 최소한 "to"나 "by" member가 정의된 object이다. * 추가적인 옵션 member들은 "from"(defaults to current value)과 "unit"(defaults to "px") 이다. * 모든 attribute 이름은 camelCase 방식을 사용한다. * @param {Number} duration (optional, 기본값 1초) animation의 길이 (frames or seconds), defaults to time-based * @param {Function} method (optional, DU.animation.LEasing.easeNone 기본값) 각 frame별 attribute에 적용되는 값을 계산 (일반적으로 DU.animation.LEasing method) */ var LMotion = function(el, attributes, duration, method) { if (el) { // dont break existing subclasses not using DU.extend LMotion.superclass.constructor.call(this, el, attributes, duration, method); } }; LMotion.NAME = 'LMotion'; // shorthand var Y = DU.animation; DU.extend(LMotion, Y.LColorAnim); var superclass = LMotion.superclass; var proto = LMotion.prototype; proto.patterns.points = /^points$/i; proto.setAttribute = function(attr, val, unit) { if ( this.patterns.points.test(attr) ) { unit = unit || 'px'; superclass.setAttribute.call(this, 'left', val[0], unit); val[1] ? superclass.setAttribute.call(this, 'top', val[1], unit) : ''; } else { superclass.setAttribute.call(this, attr, val, unit); } }; proto.getAttribute = function(attr) { if ( this.patterns.points.test(attr) ) { var val = [ superclass.getAttribute.call(this, 'left'), superclass.getAttribute.call(this, 'top') ]; } else { val = superclass.getAttribute.call(this, attr); } return val; }; proto.doMethod = function(attr, start, end) { var val = null; if ( this.patterns.points.test(attr) ) { var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100; val = Y.LBezier.getPosition(this.runtimeAttributes[attr], t); } else { val = superclass.doMethod.call(this, attr, start, end); } return val; }; proto.setRuntimeAttribute = function(attr) { if ( this.patterns.points.test(attr) ) { var el = this.getEl(); var attributes = this.attributes; var start; var control = attributes['points']['control'] || []; var end; var i, len; if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points control = [control]; } else { // break reference to attributes.points.control var tmp = []; for (i = 0, len = control.length; i< len; ++i) { tmp[i] = control[i]; } control = tmp; } if (DU.util.LDom.getStyle(el, 'position') == 'static') { // default to relative DU.util.LDom.setStyle(el, 'position', 'relative'); } if ( isset(attributes['points']['from']) ) { DU.util.LDom.setXY(el, attributes['points']['from']); // set position to from point } else { DU.util.LDom.setXY( el, DU.util.LDom.getXY(el) ); } // set it to current position start = this.getAttribute('points'); // get actual top & left // TO beats BY, per SMIL 2.1 spec if ( isset(attributes['points']['to']) ) { end = translateValues.call(this, attributes['points']['to'], start); var pageXY = DU.util.LDom.getXY(this.getEl()); for (i = 0, len = control.length; i < len; ++i) { control[i] = translateValues.call(this, control[i], start); } } else if ( isset(attributes['points']['by']) ) { end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ]; for (i = 0, len = control.length; i < len; ++i) { control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ]; } } this.runtimeAttributes[attr] = [start]; if (control.length > 0) { this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control); } this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end; } else { superclass.setRuntimeAttribute.call(this, attr); } }; var translateValues = function(val, start) { var pageXY = DU.util.LDom.getXY(this.getEl()); val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ]; return val; }; var isset = function(prop) { return (typeof prop !== 'undefined'); }; Y.LMotion = LMotion; })(); (function() { /** * LAnim subclass for scrolling elements to a position defined by the "scroll" member of "attributes". * "attribute"의 "scroll" member에 의해 정의된 위치로 element들을 스크롤하기 위한 LAnim subclass. * All "scroll" members are arrays with x, y scroll positions. * 모든 "scroll" member는 x, y 스크롤 위치 배열이다. *

    Usage: var myAnim = new DU.animation.LScroll(el, { scroll: { to: [0, 800] } }, 1, DU.animation.LEasing.easeOut);

    * @class LScroll * @namespace DU.animation * @requires DU.animation.LAnim * @requires DU.animation.LAnimMgr * @requires DU.animation.LEasing * @requires DU.animation.LBezier * @requires DU.util.LDom * @requires DU.util.LEvent * @requires DU.util.LCustomEvent * @extends DU.animation.LColorAnim * @constructor * @param {String or HTMLElement} el animated 되어질 element에 대한 참조 * @param {Object} attributes animated될 attribute * 각각의 attribute는 최소한 "to"나 "by" member가 정의된 object이다. * 추가적인 옵션 member들은 "from"(defaults to current value)과 "unit"(defaults to "px") 이다. * 모든 attribute 이름은 camelCase 방식을 사용한다. * @param {Number} duration (optional, 기본값 1초) animation의 길이 (frames or seconds), defaults to time-based * @param {Function} method (optional, DU.animation.LEasing.easeNone 기본값) 각 frame별 attribute에 적용되는 값을 계산 (일반적으로 DU.animation.LEasing method) */ var LScroll = function(el, attributes, duration, method) { if (el) { // dont break existing subclasses not using DU.extend LScroll.superclass.constructor.call(this, el, attributes, duration, method); } }; LScroll.NAME = 'LScroll'; // shorthand var Y = DU.animation; DU.extend(LScroll, Y.LColorAnim); var superclass = LScroll.superclass; var proto = LScroll.prototype; proto.doMethod = function(attr, start, end) { var val = null; if (attr == 'scroll') { val = [ this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames), this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames) ]; } else { val = superclass.doMethod.call(this, attr, start, end); } return val; }; proto.getAttribute = function(attr) { var val = null; var el = this.getEl(); if (attr == 'scroll') { val = [ el.scrollLeft, el.scrollTop ]; } else { val = superclass.getAttribute.call(this, attr); } return val; }; proto.setAttribute = function(attr, val, unit) { var el = this.getEl(); if (attr == 'scroll') { el.scrollLeft = val[0]; el.scrollTop = val[1]; } else { superclass.setAttribute.call(this, attr, val, unit); } }; Y.LScroll = LScroll; })(); /** * control point 들의 숫자에 대한 LBezier spline 들을 계산하기 위하여 사용된다. * @class LBezier * @namespace DU.animation * */ DU.animation.LBezier = new function() { /** * t 에 기반한 animated element의 현재 위치를 가져온다. * 각각의 point는 "x"와 "y" 값들의 array 이다. (0 = x, 1 = y) * 적어도 start와 end의 2개 point가 필요하다. * 처음 point는 start이며, 마지막 point는 end이다. * 추가적인 control point들은 옵션이다. * @method getPosition * @param {Array} points LBezier point들을 포함하고 있는 array * @param {Number} t 현재 위치를 지정하기 위한 기본이 되는 0에서 1사이의 숫자 * @return {Array} 정수 x와 y member data를 포함하는 array */ this.getPosition = function(points, t) { var n = points.length; var tmp = []; for (var i = 0; i < n; ++i){ tmp[i] = [points[i][0], points[i][1]]; // save input } for (var j = 1; j < n; ++j) { for (i = 0; i < n - j; ++i) { tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; } } return [ tmp[0][0], tmp[0][1] ]; }; };