/* * */ /* * @(#) du_ext_base.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.namespace('DU.dd'); /** * 드래그드랍 유틸리티는 드래그드랍 어플케이션을 만드는데 대한 프레임웍을 제공한다. * 특정 element에 대하여 드래그드랍을 활성화 하는 것에 더하여, * 드래그드랍 element들이 매니저 클래스에 의하여 추적되고, 다양한 element들 * 사이에 상호작용이 드래그 동안 추적되고 구현 코드들은 이러한 중요한 순간들에 * 대하여 통지를 받는다. * @module dragdrop * @title Drag and Drop * @requires dom,event * @namespace DU.dd */ // Only load the library once. Rewriting the manager class would orphan // existing drag and drop instances. if (!DU.dd.LDragDropMgr) { /** * LDragDropMgr는 window에서 모든 LDragDrop 항목에 대한 element 상호작용을 * 추적하는 싱글턴 패턴이다. * 일반적으로 이 클래스를 직접 호출하지는 않겠지만, 이것은 LDragDrop에 * 유용할 수 있는 도움을 주는 method를 가진다. * @class LDragDropMgr * @static */ DU.dd.LDragDropMgr = function() { var Event = DU.util.LEvent, Dom = DU.util.LDom; return { /** * 이 property는 모든 LDragDrop 인스턴스에서의 shim element의 광범위한 사용 전환에 사용된다, * 기본적으로 backcompat에 대해 false이다.(사용법: DU.dd.LDDM.useShim = true) * @property useShim * @type Boolean * @static */ useShim: false, /** * 이 property는 shim이 화면 상에 활성화 되어 있는지 여부를 결정하는데 사용되며, 기본적으로 false이다. * @private * @property _shimActive * @type Boolean * @static */ _shimActive: false, /** * 이 property는 LDDM.useShim의 현재 상태를 저장하기 위한 LDragDrop object에 * useShim이 설정될 때 사용되며 이것은 드래그 작업이 완료되었을때 재설정 될 수 있다. * @private * @property _shimState * @type Boolean * @static */ _shimState: false, /** * 이 property는 useShim이 true로 설정될 때 사용되며, 이것은 디버깅에 대한 * shim의 opacity를 .5로 설정할 것이다. (사용법: DU.dd.LDDM._debugShim = true;) * @private * @property _debugShim * @type Boolean * @static */ _debugShim: false, /** * shim element를 생성할 method(L-ddm-shim의 id가 주어진), 이것은 또한 mousemove와 * mouseup listener에 붙으며, window에 스크롤 listener에 붙는다. * @private * @method _createShim * @static */ _createShim: function() { var s = document.createElement('div'); s.id = 'L-ddm-shim'; if (document.body.firstChild) { document.body.insertBefore(s, document.body.firstChild); } else { document.body.appendChild(s); } s.style.display = 'none'; s.style.backgroundColor = 'red'; s.style.position = 'absolute'; s.style.zIndex = '99999'; Dom.setStyle(s, 'opacity', '0'); this._shim = s; Event.on(s, "mouseup", this.handleMouseUp, this, true); Event.on(s, "mousemove", this.handleMouseMove, this, true); Event.on(window, 'scroll', this._sizeShim, this, true); }, /** * window 스크롤 event와 활성화로부터 호출된 shim의 사이즈를 변경한다. * @private * @method _sizeShim * @static */ _sizeShim: function() { if (this._shimActive) { var s = this._shim; s.style.height = Dom.getDocumentHeight() + 'px'; s.style.width = Dom.getDocumentWidth() + 'px'; s.style.top = '0'; s.style.left = '0'; } }, /** * 이 method는 필요한 경우 shim element를 생성하고 shim element를 보여주며, * element의 사이즈를 변경하고 true로 _shimActive property를 설정한다. * @private * @method _activateShim * @static */ _activateShim: function() { if (this.useShim) { if (!this._shim) { this._createShim(); } this._shimActive = true; var s = this._shim, o = '0'; if (this._debugShim) { o = '.5'; } Dom.setStyle(s, 'opacity', o); this._sizeShim(); s.style.display = 'block'; } }, /** * shim element를 숨기고 _shimActive property를 false로 설정한다. * @private * @method _deactivateShim * @static */ _deactivateShim: function() { this._shim.style.display = 'none'; this._shimActive = false; }, /** * 마우스 이동을 추적하기 위해 document위에 shim으로써 사용하기 위해 만들어진 HTML element * @private * @property _shim * @type HTMLElement * @static */ _shim: null, /** * 등록된 LDragDrop object들의 2차원 배열. 첫번재 차원은 * LDragDrop 항목 그룹이며, 두번째는 LDragDrop object이다. * @property ids * @type {string: string} * @private * @static */ ids: {}, /** * 드래그 handle들에 의해 정의된 element id들의 array. * html element 자신이 아닌 실제 handle인 mousedown event를 생성한 element인지에 * 대한 여부를 결정하는데 사용된다. * @property handleIds * @type {string: string} * @private * @static */ handleIds: {}, /** * 현재 드래그 되고 있는 LDragDrop object * @property dragCurrent * @type LDragDrop * @private * @static **/ dragCurrent: null, /** * 위에 떠 있는 LDragDrop object(s) * @property dragOvers * @type Array * @private * @static */ dragOvers: {}, /** * 커서와 드래그 되고 있는 object 사이의 X 거리 * @property deltaX * @type int * @private * @static */ deltaX: 0, /** * 커서와 드래그 되고 있는 object 사이의 Y 거리 * @property deltaY * @type int * @private * @static */ deltaY: 0, /** * 정의한 event들의 기본 작업을 막아야 할지에 대한 여부를 결정하기 위한 flag. * 기본적으로 true 이지만, 기본 작업을 원하는 경우 false로 설정할 수 있다.(추천하지는 않음) * @property preventDefault * @type boolean * @static */ preventDefault: true, /** * 생성한 event들의 전파를 그만두어야 할지에 대한 여부를 결정하기 위한 flag. * 기본적으로 true 이지만 마우스 클릭이 필요한 다른 특징을 포함하는 * html element인 경우 false로 설정하고자 할 수도 있다. * @property stopPropagation * @type boolean * @static */ stopPropagation: true, /** * 드래그와 드랍이 초기화 되었을때 true로 설정된 내부적인 flag. * @property initialized * @private * @static */ initialized: false, /** * 모든 드래그와 드랍은 비활성화 될 수 있다. * @property locked * @private * @static */ locked: false, /** * 상호작용의 현재 집합에 대한 추가적인 정보를 제공한다. * event hanlder들로부터 액세스될 수 있다. * 이것은 다음과 같은 property들을 포함한다: *

         *

         *       out:       onDragOut interactions

         *       enter:     onDragEnter interactions

         *       over:      onDragOver interactions

         *       drop:      onDragDrop interactions

         *       point:     커서의 위치

         *       draggedRegion: interaction시 드래그된 element의 위치

         *       sourceRegion: interaction시 source element의 위치

         *       validDrop: boolean

         * 
* @property interactionInfo * @type object * @static */ interactionInfo: null, /** * element가 등록된 처음에 호출된다. * @method init * @private * @static */ init: function() { this.initialized = true; }, /** * point 모드에서 드래그드랍 상호작용은 드래그드랍 동안 커서의 위치에 의해 정의된다. * @property POINT * @type int * @static * @final */ POINT: 0, /** * intersect에서 드래그드랍 상호작용은 커서 위치나 두개 혹은 그 이상의 * 드래그드랍 object들의 overlap 양에 의해 정의된다. * @property INTERSECT * @type int * @static * @final */ INTERSECT: 1, /** * intersect에서 드래그드랍 상호작용은 두개 혹은 그 이상의 * 드래그드랍 object들의 overlap에 의해서만 정의된다. * @property STRICT_INTERSECT * @type int * @static * @final */ STRICT_INTERSECT: 2, /** * The current drag and drop mode. Default: POINT * @property mode * @type int * @static */ mode: 0, /** * 모드 드래그드랍 object들의 method를 실행한다. * @method _execOnAll * @private * @static */ _execOnAll: function(sMethod, args) { for (var i in this.ids) { for (var j in this.ids[i]) { var oDD = this.ids[i][j]; if (! this.isTypeOfDD(oDD)) { continue; } oDD[sMethod].apply(oDD, args); } } }, /** * 드래그다랍의 초기화. 전역 event handler를 설정한다. * @method _onLoad * @private * @static */ _onLoad: function() { this.init(); Event.on(document, "mouseup", this.handleMouseUp, this, true); Event.on(document, "mousemove", this.handleMouseMove, this, true); Event.on(window, "unload", this._onUnload, this, true); Event.on(window, "resize", this._onResize, this, true); // Event.on(window, "mouseout", this._test); }, /** * 모든 드래드드랍 object들에 제한을 재설정한다. * @method _onResize * @private * @static */ _onResize: function(e) { this._execOnAll("resetConstraints", []); }, /** * 모든 드래그드랍을 기능적으로 lock 한다. * @method lock * @static */ lock: function() { this.locked = true; }, /** * 모든 드래그드랍을 기능적으로 unlock 한다. * @method unlock * @static */ unlock: function() { this.locked = false; }, /** * 드래그드랍이 lock 되어 있는지 여부 확인 * @method isLocked * @return {boolean} 드래그드랍이 lock되어 있으면 true 아니면 false * @static */ isLocked: function() { return this.locked; }, /** * 드래그가 초기화 되었을때 모든 드래그드랍 object들에 대해 설정된 * 위치 캐시는 드래그가 종료되었을 때 삭제된다. * @property locationCache * @private * @static */ locationCache: {}, /** * 드래그 하는 동안 끊임없는 각 드래그드랍 linked element의 lookup을 * 강제하고자 하는 경우 useCache를 false로 설정한다. * @property useCache * @type boolean * @static */ useCache: true, /** * 드래그가 초기화 되기 이전 mousedown이후에 마우스를 움직일 * 필요가 있을 경우 픽셀의 숫자. 기본값은 3 * @property clickPixelThresh * @type int * @static */ clickPixelThresh: 3, /** * mouseup event를 가져오지 않는 경우 드래그를 초기화 하기 위한 * mousedown event이후의 밀리초 숫자. 기본값은 1000. * @property clickTimeThresh * @type int * @static */ clickTimeThresh: 1000, /** * 드래그 픽셀 threshold나 mousedown 시간 threshold가 만났는지를 * 표시하는 flag * @property dragThreshMet * @type boolean * @private * @static */ dragThreshMet: false, /** * 클릭시간 threshold에 대해 사용된 타임아웃 * @property clickTimeout * @type Object * @private * @static */ clickTimeout: null, /** * 드래그 threshold이 만났을때 나중에 사용하기 위해 저장된 * mousedown event의 X 위치 * @property startX * @type int * @private * @static */ startX: 0, /** * 드래그 threshold이 만났을때 나중에 사용하기 위해 저장된 * mousedown event의 Y 위치 * @property startY * @type int * @private * @static */ startY: 0, /** * 드래그 event가 마우스 이동 threshold가 아닌 클릭 타임아웃으로 부터 * 발생한 경우인지에 대해 결정하기 위한 flag * @property fromTimeout * @type boolean * @private * @static */ fromTimeout: false, /** * 각각의 LDragDrop 인스턴스는 반드시 LDragDropMgr와 같이 등록되어야 한다. * 이것은 LDragDrop.init()에서 실행된다. * @method regDragDrop * @param {LDragDrop} oDD 등록할 LDragDrop object * @param {String} sGroup 해당 element가 속할 그룹의 이름 * @static */ regDragDrop: function(oDD, sGroup) { if (!this.initialized) { this.init(); } if (!this.ids[sGroup]) { this.ids[sGroup] = {}; } this.ids[sGroup][oDD.id] = oDD; }, /** * 제공된 그룹으로 부터 제공된 dd 인스턴스를 제거한다. * LDragDrop.removeFromGroup에 의해 실행되며 해당 함수를 직접 호출하지는 않는다. * @method removeDDFromGroup * @private * @static */ removeDDFromGroup: function(oDD, sGroup) { if (!this.ids[sGroup]) { this.ids[sGroup] = {}; } var obj = this.ids[sGroup]; if (obj && obj[oDD.id]) { delete obj[oDD.id]; } }, /** * 드래그드랍 항목을 등록 취소한다. * 이것은 LDragDrop.unreg에서 실행되며, 이것을 직접 호출하는대신 method를 사용한다. * @method _remove * @private * @static */ _remove: function(oDD) { for (var g in oDD.groups) { if (g) { var item = this.ids[g]; if (item && item[oDD.id]) { delete item[oDD.id]; } } } delete this.handleIds[oDD.id]; }, /** * 각각의 LDragDrop handle element는 반드시 등록되어야 한다. * 이것은 LDragDrop.setHandleElId()이 실행될때 자동적으로 수행된다. * @method regHandle * @param {String} sDDId element가 핸들링 할 LDragDrop id * @param {String} sHandleId 드래그 될 element의 id * handle * @static */ regHandle: function(sDDId, sHandleId) { if (!this.handleIds[sDDId]) { this.handleIds[sDDId] = {}; } this.handleIds[sDDId][sHandleId] = sHandleId; }, /** * 주어진 element가 드래그드랍 항목으로써 등록되어 있는지에 대한 * 여부를 결정하는 유틸리티 함수 * @method isDragDrop * @param {String} id 체크할 element id * @return {boolean} 해당 element가 LDragDrop인 경우 true, 아니면 false * @static */ isDragDrop: function(id) { return ( this.getDDById(id) ) ? true : false; }, /** * 인스턴스가 속할 모든 그룹에 전달된 드래그드랍 인스턴스들을 반환한다. * @method getRelated * @param {LDragDrop} p_oDD 연관된 데이터를 가져올 object * @param {boolean} bTargetsOnly true인 경우 타겟팅 가능한 object만 반환한다. * @return {LDragDrop[]} 연관된 인스턴스 * @static */ getRelated: function(p_oDD, bTargetsOnly) { var oDDs = []; for (var i in p_oDD.groups) { for (var j in this.ids[i]) { var dd = this.ids[i][j]; if (! this.isTypeOfDD(dd)) { continue; } if (!bTargetsOnly || dd.isTarget) { oDDs[oDDs.length] = dd; } } } return oDDs; }, /** * 특정 드래그 obj에 대해 특정 dd 대상이 유효한 대상인 경우 true를 반환한다. * @method isLegalTarget * @param {LDragDrop} 드래그 obj * @param {LDragDrop} 대상 * @return {boolean} dd obj애 대해 대상이 유요한 대상인 경우 true * @static */ isLegalTarget: function (oDD, oTargetDD) { var targets = this.getRelated(oDD, true); for (var i=0, len=targets.length;i this.clickPixelThresh || diffY > this.clickPixelThresh) { this.startDrag(this.startX, this.startY); } } if (this.dragThreshMet) { if (dc && dc.events.b4Drag) { dc.b4Drag(e); dc.fireEvent('b4DragEvent', { e: e}); } if (dc && dc.events.drag) { dc.onDrag(e); dc.fireEvent('dragEvent', { e: e}); } if (dc) { this.fireEvents(e, false); } } this.stopEvent(e); } }, /** * 위(hover)에 있거나 드랍한 LDragDrop element를 찾기 위해 모든 element들을 반복 * @method fireEvents * @param {Event} e the event * @param {boolean} isDrop 드랍 작업인지 mouseover 작업인지 여부 * @private * @static */ fireEvents: function(e, isDrop) { var dc = this.dragCurrent; // If the user did the mouse up outside of the window, we could // get here even though we have ended the drag. // If the config option dragOnly is true, bail out and don't fire the events if (!dc || dc.isLocked() || dc.dragOnly) { return; } var x = DU.util.LEvent.getPageX(e), y = DU.util.LEvent.getPageY(e), pt = new DU.util.LPoint(x,y), pos = dc.getTargetCoord(pt.x, pt.y), el = dc.getDragEl(), events = ['out', 'over', 'drop', 'enter'], curRegion = new DU.util.LRegion( pos.y, pos.x + el.offsetWidth, pos.y + el.offsetHeight, pos.x ), oldOvers = [], // cache the previous dragOver array inGroupsObj = {}, inGroups = [], data = { outEvts: [], overEvts: [], dropEvts: [], enterEvts: [] }; // Check to see if the object(s) we were hovering over is no longer // being hovered over so we can fire the onDragOut event for (var i in this.dragOvers) { var ddo = this.dragOvers[i]; if (! this.isTypeOfDD(ddo)) { continue; } if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) { data.outEvts.push( ddo ); } oldOvers[i] = true; delete this.dragOvers[i]; } for (var sGroup in dc.groups) { if ("string" != typeof sGroup) { continue; } for (i in this.ids[sGroup]) { var oDD = this.ids[sGroup][i]; if (! this.isTypeOfDD(oDD)) { continue; } if (oDD.isTarget && !oDD.isLocked() && oDD != dc) { if (this.isOverTarget(pt, oDD, this.mode, curRegion)) { inGroupsObj[sGroup] = true; // look for drop interactions if (isDrop) { data.dropEvts.push( oDD ); // look for drag enter and drag over interactions } else { // initial drag over: dragEnter fires if (!oldOvers[oDD.id]) { data.enterEvts.push( oDD ); // subsequent drag overs: dragOver fires } else { data.overEvts.push( oDD ); } this.dragOvers[oDD.id] = oDD; } } } } } this.interactionInfo = { out: data.outEvts, enter: data.enterEvts, over: data.overEvts, drop: data.dropEvts, point: pt, draggedRegion: curRegion, sourceRegion: this.locationCache[dc.id], validDrop: isDrop }; for (var inG in inGroupsObj) { inGroups.push(inG); } // notify about a drop that did not find a target if (isDrop && !data.dropEvts.length) { this.interactionInfo.validDrop = false; if (dc.events.invalidDrop) { dc.onInvalidDrop(e); dc.fireEvent('invalidDropEvent', { e: e }); } } for (i = 0; i < events.length; i++) { var tmp = null; if (data[events[i] + 'Evts']) { tmp = data[events[i] + 'Evts']; } if (tmp && tmp.length) { var type = events[i].charAt(0).toUpperCase() + events[i].substr(1), ev = 'onDrag' + type, b4 = 'b4Drag' + type, cev = 'drag' + type + 'Event', check = 'drag' + type; if (this.mode) { if (dc.events[b4]) { dc[b4](e, tmp, inGroups); dc.fireEvent(b4 + 'Event', { event: e, info: tmp, group: inGroups }); } if (dc.events[check]) { dc[ev](e, tmp, inGroups); dc.fireEvent(cev, { event: e, info: tmp, group: inGroups }); } } else { for (var b = 0, len = tmp.length; b < len; ++b) { if (dc.events[b4]) { dc[b4](e, tmp[b].id, inGroups[0]); dc.fireEvent(b4 + 'Event', { event: e, info: tmp[b].id, group: inGroups[0] }); } if (dc.events[check]) { dc[ev](e, tmp[b].id, inGroups[0]); dc.fireEvent(cev, { event: e, info: tmp[b].id, group: inGroups[0] }); } } } } } }, /** * INTERSECT 모드에 있을때 드래그드랍 event들에 의해 반환된 드래그드랍 * object들의 목록으로부터 가장 일치하는 것을 가져오는 helper 함수 * 이는 커서가 가리키는 첫번째 object나 드래그된 element와 가장 크게 * 오버랩된 object를 반환한다. * @method getBestMatch * @param {LDragDrop[]} dds 드래그드랍 object들의 array * targeted * @return {LDragDrop} 가장 일치하는 object * @static */ getBestMatch: function(dds) { var winner = null; var len = dds.length; if (len == 1) { winner = dds[0]; } else { // Loop through the targeted items for (var i=0; i * DU.dd.LDragDropMgr.refreshCache(ddinstance.groups); * * 다른 방법으로: * * DU.dd.LDragDropMgr.refreshCache({group1:true, group2:true}); * * @TODO 이것은 실제 인덱싱 배열이어야 한다. 이 method는 둘중에 * 양자택일로 선택되어야 한다. * @method refreshCache * @param {Object} groups 새로고침할 그룹들의 연관 배열 * @static */ refreshCache: function(groups) { // refresh everything if group array is not provided var g = groups || this.ids; for (var sGroup in g) { if ("string" != typeof sGroup) { continue; } for (var i in this.ids[sGroup]) { var oDD = this.ids[sGroup][i]; if (this.isTypeOfDD(oDD)) { var loc = this.getLocation(oDD); if (loc) { this.locationCache[oDD.id] = loc; } else { delete this.locationCache[oDD.id]; } } } } }, /** * element가 존재하고 DOM안에 있는지 확인하기 위해 체크한다. * 주요 목적은 innerHTML 에서 DOM으로부터 드래그드랍 object를 제거하기 위해 * 사용되는 경우를 핸들링하는 것이다. * IE 브라우저는 그런 element의 offsetParent를 액세스 하려고 할때 * 'unspecified error'를 제공한다. * @method verifyEl * @param {HTMLElement} el 체크할 element * @return {boolean} element가 사용가능한 경우 true * @static */ verifyEl: function(el) { try { if (el) { var parent = el.offsetParent; if (parent) { return true; } } } catch(e) { } return false; }, /** * 설정할 padding 값을 포함한 드래그드랍 element의 위치와 사이즈를 포함하는 * Region object를 반환한다. * @method getLocation * @param {LDragDrop} oDD 위치를 가져올 드래그드랍 object * @return {DU.util.LRegion} a 설정될 padding 인스턴스를 포함한 element가 나타내는 * 전체 영역을 표시하는 Region object * @static */ getLocation: function(oDD) { if (! this.isTypeOfDD(oDD)) { return null; } var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l; try { pos= DU.util.LDom.getXY(el); } catch (e) { } if (!pos) { return null; } x1 = pos[0]; x2 = x1 + el.offsetWidth; y1 = pos[1]; y2 = y1 + el.offsetHeight; t = y1 - oDD.padding[0]; r = x2 + oDD.padding[1]; b = y2 + oDD.padding[2]; l = x1 - oDD.padding[3]; return new DU.util.LRegion( t, r, b, l ); }, /** * 커서가 대상 위에 있는 경우 확인하기 위한 커서 위치를 체크한다. * @method isOverTarget * @param {DU.util.LPoint} pt 평가할 지점(point) * @param {LDragDrop} oTarget 검사할 LDragDrop object * @param {boolean} intersect intersect mode인 경우 true * @param {DU.util.LRegion} curRegion 드래그된 element의 사전에 캐싱된 위치 * @return {boolean} 마우스가 대상 위에 있는 경우 true * @private * @static */ isOverTarget: function(pt, oTarget, intersect, curRegion) { // use cache if available var loc = this.locationCache[oTarget.id]; if (!loc || !this.useCache) { loc = this.getLocation(oTarget); this.locationCache[oTarget.id] = loc; } if (!loc) { return false; } oTarget.cursorIsOver = loc.contains( pt ); // LDragDrop is using this as a sanity check for the initial mousedown // in this case we are done. In POINT mode, if the drag obj has no // contraints, we are done. Otherwise we need to evaluate the // region the target as occupies to determine if the dragged element // overlaps with it. var dc = this.dragCurrent; if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) { //if (oTarget.cursorIsOver) { //} return oTarget.cursorIsOver; } oTarget.overlap = null; // Get the current location of the drag element, this is the // location of the mouse event less the delta that represents // where the original mousedown happened on the element. We // need to consider constraints and ticks as well. if (!curRegion) { var pos = dc.getTargetCoord(pt.x, pt.y); var el = dc.getDragEl(); curRegion = new DU.util.LRegion( pos.y, pos.x + el.offsetWidth, pos.y + el.offsetHeight, pos.x ); } var overlap = curRegion.intersect(loc); if (overlap) { oTarget.overlap = overlap; return (intersect) ? true : oTarget.cursorIsOver; } else { return false; } }, /** * unload event handler * @method _onUnload * @private * @static */ _onUnload: function(e, me) { this.unregAll(); }, /** * 드래그드랍 event와 object들을 깨끗이 삭제한다. * @method unregAll * @private * @static */ unregAll: function() { if (this.dragCurrent) { this.stopDrag(); this.dragCurrent = null; } this._execOnAll("unreg", []); //for (var i in this.elementCache) { //delete this.elementCache[i]; //} //this.elementCache = {}; this.ids = {}; }, /** * DOM element들의 캐시 * @property elementCache * @private * @static * @deprecated elements are not cached now */ elementCache: {}, /** * 명시된 DOM element에 대한 wrapper를 가져온다. * @method getElWrapper * @param {String} id 가져올 element의 id * @return {ElementWrapper} wrapping된 element * @private * @deprecated 이 wrapper는 유용하지 않다. * @static */ getElWrapper: function(id) { var oWrapper = this.elementCache[id]; if (!oWrapper || !oWrapper.el) { oWrapper = this.elementCache[id] = new this.ElementWrapper(DU.util.LDom.get(id)); } return oWrapper; }, /** * 실제 DOM element를 반환한다. * @method getElement * @param {String} id 가져올 element의 id * @return {Object} The element * @deprecated 대신 DU.util.LDom.get을 사용한다. * @static */ getElement: function(id) { return DU.util.LDom.get(id); }, /** * DOM element에 대한 스타일 property를 반환한다.(다시 말하면, document.getElById(id).style) * @method getCss * @param {String} id 가져올 element의 id * @return {Object} element의 스타일 property * @deprecated 대신 DU.util.LDom 을 사용한다. * @static */ getCss: function(id) { var el = DU.util.LDom.get(id); return (el) ? el.style : null; }, /** * 캐시된 element에 대한 inner 클래스 * @private * @deprecated */ ElementWrapper: function(el) { /** * The element * @property el */ this.el = el || null; /** * The element id * @property id */ this.id = this.el && el.id; /** * A reference to the style property * @property css */ this.css = this.el && el.style; }, /** * html element에 대한 X 위치를 반환한다. * @method getPosX * @param el 위치를 가져올 element * @return {int} X 좌표 * @deprecated 대신 DU.util.LDom.getX을 사용한다. * @static */ getPosX: function(el) { return DU.util.LDom.getX(el); }, /** * html element에 대한 Y 위치를 반환한다. * @method getPosY * @param el 위치를 가져올 element * @return {int} Y 좌표 * @deprecated 대신 DU.util.LDom.getY을 사용한다. * @static */ getPosY: function(el) { return DU.util.LDom.getY(el); }, /** * 두 노드를 바꾼다. * IE 브라우저에서는 다른 IE 작동을 에뮬레이팅 하기 위해서 native method를 사용한다. * @method swapNode * @param n1 바꿀 첫번째 노드 * @param n2 바꿀 두번째 노드 * @static */ swapNode: function(n1, n2) { if (n1.swapNode) { n1.swapNode(n2); } else { var p = n2.parentNode; var s = n2.nextSibling; if (s == n1) { p.insertBefore(n1, n2); } else if (n2 == n1.nextSibling) { p.insertBefore(n2, n1); } else { n1.parentNode.replaceChild(n2, n1); p.insertBefore(n1, s); } } }, /** * 현재 스크롤 위치를 반환한다. * @method getScroll * @private * @static */ getScroll: function () { var t, l, dde=document.documentElement, db=document.body; if (dde && (dde.scrollTop || dde.scrollLeft)) { t = dde.scrollTop; l = dde.scrollLeft; } else if (db) { t = db.scrollTop; l = db.scrollLeft; } else { } return { top: t, left: l }; }, /** * 특정 element의 스타일 property를 반환한다. * @method getStyle * @param {HTMLElement} el element * @param {string} styleProp 스타일 property * @return {string} 스타일 property의 값 * @deprecated 대신 DU.util.LDom.getStyle을 사용한다. * @static */ getStyle: function(el, styleProp) { return DU.util.LDom.getStyle(el, styleProp); }, /** * scrollTop을 가져온다. * @method getScrollTop * @return {int} document의 scrollTop * @static */ getScrollTop: function () { return this.getScroll().top; }, /** * scrollLeft을 가져온다. * @method getScrollLeft * @return {int} document의 scrollTop * @static */ getScrollLeft: function () { return this.getScroll().left; }, /** * 대상 element의 위치에 대한 element의 x/y 위치를 설정한다. * @method moveToEl * @param {HTMLElement} moveEl 이동할 element * @param {HTMLElement} targetEl element의 위치 참조 * @static */ moveToEl: function (moveEl, targetEl) { var aCoord = DU.util.LDom.getXY(targetEl); DU.util.LDom.setXY(moveEl, aCoord); }, /** * 클라이언트 height를 가져온다. * @method getClientHeight * @return {int} 픽셀 단위의 클라이언트 height * @deprecated 대신 DU.util.LDom.getViewportHeight을 사용한다. * @static */ getClientHeight: function() { return DU.util.LDom.getViewportHeight(); }, /** * 클라이언트 width를 가져온다. * @method getClientWidth * @return {int} 픽셀 단위의 클라이언트 width * @deprecated 대신 DU.util.LDom.getViewportWidth을 사용한다. * @static */ getClientWidth: function() { return DU.util.LDom.getViewportWidth(); }, /** * 숫자 배열 정렬 함수 * @method numericSort * @static */ numericSort: function(a, b) { return (a - b); }, /** * 내부적인 카운터 * @property _timeoutCount * @private * @static */ _timeoutCount: 0, /** * 덜 중요한 로딩 순서를 만들려고 시도한다. * 해당 파일이 event 유틸리티 이전에 로딩된 경우 이것이 없으면 에러를 가져온다. * @method _addListeners * @private * @static */ _addListeners: function() { var LDDM = DU.dd.LDDM; if ( DU.util.LEvent && document ) { LDDM._onLoad(); } else { if (LDDM._timeoutCount > 2000) { } else { setTimeout(LDDM._addListeners, 10); if (document && document.body) { LDDM._timeoutCount += 1; } } } }, /** * 클릭되었는지 여부를 결정하기 위해서 handle element에 대해 * 인접한 parent와 모든 child 노드를 재귀적으로 탐색한다. * @method handleWasClicked * @param node 검사할 html element * @static */ handleWasClicked: function(node, id) { if (this.isHandle(id, node.id)) { return true; } else { // check to see if this is a text node child of the one we want var p = node.parentNode; while (p) { if (this.isHandle(id, p.id)) { return true; } else { p = p.parentNode; } } } return false; } }; }(); // shorter alias, save a few bytes DU.dd.LDDM = DU.dd.LDragDropMgr; DU.dd.LDDM._addListeners(); } (function() { var Event=DU.util.LEvent; var Dom=DU.util.LDom; /** * 대상을 드래그하거나 드랍할 수 있는 항목의 인터페이스나 기본 작업을 정의한다. * 이것은 startDrag, onDrag, onDragOver, onDragOut에 대한 * event handler 오버라이딩 되거나 상속되도록 디자인 되어졌다. * html 참조가 LDragDrop 인스턴스와 연결될 수 있는 건 3개 까지이다. * * 이 클래스는 연결된 element가 사용 가능한지를 확인하는 onload event까지 * 인스턴스화 될 수 없다. * 다음은 "group1" 그룹의 어떤 다른 LDragDrop obj와 상호작용할 * LDragDrop obj을 정의한다: *

 *  dd = new DU.dd.LDragDrop("div1", "group1");

 * 
* 구현된 event handler가 없기 때문에, 위의 코드를 실행하고자 하는 경우 * 실제 아무 것도 일어나지 않는다. * 일반적으로 이 클래스 또는 기본 구현중의 하나를 오버라이드 하거나, 또한 클래스의 인스턴스에서 * 원하는 method를 오버라이드 할 수 있다. *

 *  dd.onDragDrop = function(e, id) {

 *    alert("dd was dropped on " + id);

 *  }

 * 
* @namespace DU.dd * @class LDragDrop * @constructor * @param {String} id 해당 인스턴스에 연결된 element의 id * @param {String} sGroup LDragDrop object들과 연관된 그룹 * @param {object} config 설정가능한 attribue를 포함한 object * LDragDrop에 대해 유요한 property들: * padding, isTarget, maintainOffset, primaryButtonOnly, */ DU.dd.LDragDrop = function(id, sGroup, config) { if (id) { this.init(id, sGroup, config); } }; DU.dd.LDragDrop.prototype = { /** * 사용될 event들을 포함하는 object literal * 이것들중 어떤 것이든 false로 설정하면, event는 발생하지 않을 것이다. * @property events * @type object */ events: null, /** * @method on * @description LEventProvider.on에 대한 바로가기, DU.util.LEventProvider.on을 참조한다. */ /* * * 상속이 되어 있으므로 제거, 이 주석을 제외시키면 반복 참조로 인한 스텍 에러 발생 on: function() { this.on.apply(this, arguments); }, */ /** * 해당 object와 연관된 element의 id * 사이즈와 해당 element의 위치가 드래그와 드랍 object가 상호작용할때 * 결정하기 위해서 사용되기 때문에 이것은 "linked element"로서 참조되는 것이다. * @property id * @type String */ id: null, /** * 생성자에 전달된 LConfiguration attribute * @property config * @type object */ config: null, /** * 드래그 될 element의 id * 기본적으로 이것은 linked element로써 동일하나 다른 element로 바뀔 수 있다. 예:DU.dd.LDDProxy * @property dragElId * @type String * @private */ dragElId: null, /** * 드래그 작업을 초기화 하는 element의 id * 기본적으로 이것은 linked element이나 해당 element의 child로 바뀔 수 있다. * linked html element 안의 header element가 클릭되었을 때 * 이것은 오직 드래그를 시작하는 것과 같은 것을 할 수 있게 한다. * @property handleElId * @type String * @private */ handleElId: null, /** * 클릭된 경우 무시될 HTML 태그들의 연관 배열 * @property invalidHandleTypes * @type {string: string} */ invalidHandleTypes: null, /** * 클릭된 경우 무시될 element들에 대한 id들의 연관 배열 * @property invalidHandleIds * @type {string: string} */ invalidHandleIds: null, /** * 클릭된 경우 무시될 element에 대한 css 클래스 이름들의 인덱싱 배열 * @property invalidHandleClasses * @type string[] */ invalidHandleClasses: null, /** * 드래그가 시작될때 linked element의 absolute X 위치 * @property startPageX * @type int * @private */ startPageX: 0, /** * 드래그가 시작될때 linked element의 absolute Y 위치 * @property startPageY * @type int * @private */ startPageY: 0, /** * 그룹은 관련된 LDragDrop object들의 논리적인 집합을 정의한다. * 인스턴스는 같은 그룹의 다른 LDragDrop object와 상호작용할 때만 event를 가져온다. * 이것은 원하는 경우 여러 그룹에 하나의 LDragDrop subclass 사용을 하게 해준다. * @property groups * @type {string: string} */ groups: null, /** * 개별적인 그래드/드랍 인스턴스들은 lock 될 수 있다. * 이것은 onmousedown 드래그 시작을 방지할 것이다. * @property locked * @type boolean * @private */ locked: false, /** * 해당 인스턴스를 lock 한다. * @method lock */ lock: function() { this.locked = true; }, /** * 해당 인스턴스를 unlock 한다. * @method unlock */ unlock: function() { this.locked = false; }, /** * 기본적으로 모든 인스턴스들은 드랍 대상이 될 수 있다. * 이것은 isTarget을 false로 설정함으로써 비활성화 될 수 있다. * @property isTarget * @type boolean */ isTarget: true, /** * 해당 object와의 드랍 교차점 계산을 위한 이 드래그드랍 object에 대한 * 설정된 padding 값 * @property padding * @type int[] */ padding: null, /** * 해당 flag가 true인 경우 드랍 event들을 발생시키지 않는다. * element는 유일한 drag element이다.(드랍이 아닌 이동에 대해) * @property dragOnly * @type Boolean */ dragOnly: false, /** * 해당 flag가 true인 경우 shim은 마우스 event 추적을 위한 screen/viewable 영역에 배치될 것이다. * iframe과 다른 컨트롤에서의 element 드래그에 도움이 된다. * @property useShim * @type Boolean */ useShim: false, /** * linked element에 대한 참조를 캐싱한다. * @property _domRef * @private */ _domRef: null, /** * 내부적인 typeof flag * @property __ygDragDrop * @private */ __ygDragDrop: true, /** * 수평 제한이 적용됐을 때 true로 설정한다. * @property constrainX * @type boolean * @private */ constrainX: false, /** * 수직 제한이 적용됐을 때 true로 설정한다. * @property constrainY * @type boolean * @private */ constrainY: false, /** * left 제한 * @property minX * @type int * @private */ minX: 0, /** * right 제한 * @property maxX * @type int * @private */ maxX: 0, /** * up 제한 * @property minY * @type int * @type int * @private */ minY: 0, /** * down 제한 * @property maxY * @type int * @private */ maxY: 0, /** * 클릭 위치와 소스 element의 위치 사이의 차이 * @property deltaX * @type int * @private */ deltaX: 0, /** * 클릭 위치와 소스 element의 위치 사이의 차이 * @property deltaY * @type int * @private */ deltaY: 0, /** * 제한을 재설정할 때 offset를 포함한다. * 페이지가 바뀔때 element의 위치를 그것의 parent와 똑같게 유지하고자 * 한다면 true로 설정한다. * @property maintainOffset * @type boolean */ maintainOffset: false, /** * 수평 눈금/간격을 명시하고자 하는 경우 element가 snap할 픽셀 위치의 array. * 해당 array는 tick 간격을 정의할때 자동적으로 생성된다. * @property xTicks * @type int[] */ xTicks: null, /** * 수직 눈금/간격을 명시하고자 하는 경우 element가 snap할 픽셀 위치의 array. * 해당 array는 tick 간격을 정의할때 자동적으로 생성된다. * @property yTicks * @type int[] */ yTicks: null, /** * 기본적으로 드래그와 드랍 인스턴스는 첫번째 버튼 클릭에만 * 반응을 한다.(오른손잡이용 마우스에 대한 왼쪽 클릭) * 브라우저에 의해 전달되는 마우스 클릭으로 시작하는 드래그와 드랍을 * 가능하게 하기 위해서 true로 설정한다. * @property primaryButtonOnly * @type boolean */ primaryButtonOnly: true, /** * 연결된 dom element가 약세스 가능할때까지 사용가능한 property는 false이다. * @property available * @type boolean */ available: false, /** * 기본적으로 드래그는 mousedown이 linked element 영역 안에서 * 일어나는 경우에만 초기화 될 수 있다. * 이것은 이전의 mouseup이 window 밖에서 발생한 경우 mousedown을 * 잘못 보고하는 몇몇 브라우저 들에서 버그를 부분적으로 해결하기 위해 일부 이루어진다. * outer handle이 정의되어 있는 경우 해당 property를 true로 설정한다. * * @property hasOuterHandles * @type boolean * @default false */ hasOuterHandles: false, /** * 다른 dd object에 의해 대상이 된 경우 확인하기 위해 테스팅 할때 * 드래그와 드랍 object에 할당되는 property. * 이 property는 마우스 교차의 포커스를 결정하는데 도움을 주기 위하여 * 교차 모드가 사용될 수 있다. * LDDM.getBestMatch은 여러 대상들이 같은 상호작용의 일부분일때 * 교차 모드에서 가장 가까운 일치점을 결정하는데 이 property를 처음으로 사용한다. * @property cursorIsOver * @type boolean */ cursorIsOver: false, /** * 다른 dd object에 의해 대상이 된 경우 확인하기 위해 테스팅 할때 * 드래그와 드랍 object에 할당되는 property. * 이것은 해당 대상에 오버랩되는 드래그 가능한 element 영역을 표시하는 region 이다. * LDDM.getBestMatch는 여러 대상들이 같은 상호작용의 일부분일때 * 교차 모드에서 가장 가까운 일치점을 결정하기 위해서 다른 대상의 사이즈에 * 오버랩의 사이즈를 비교하기 위하여 이 property를 사용한다. * @property overlap * @type DU.util.LRegion */ overlap: null, /** * startDrag event 전에 즉시 실행하는 코드 * @method b4StartDrag * @private */ b4StartDrag: function(x, y) { }, /** * 드래그/드랍 object가 클릭되고 드래그나 mousedown 시간 초입이 * 만난 이후에 호출되는 추상 method. * @method startDrag * @param {int} X 클릭 위치 * @param {int} Y 클릭 위치 */ startDrag: function(x, y) { /* override this */ }, /** * onDrag event 이전에 즉시 실행되는 코드 * @method b4Drag * @private */ b4Drag: function(e) { }, /** * object를 드래그 할때 onMouseMove event 동안 호출되는 추상 method. * @method onDrag * @private * @param {Event} e mousemove event */ onDrag: function(e) { /* override this */ }, /** * 해당 element가 처음으로 다른 LDragDrop obj위에 올라갔을때 호출되는 추상 method * @method onDragEnter * @private * @param {Event} e mousemove event * @param {String|LDragDrop[]} id POINT 모드에서 위에 올라가는 element의 id. * INTERSECT 모드에서, 위에 올라가는 하나 혹은 그 이상의 dragdrop 항목들의 array. */ onDragEnter: function(e, id) { /* override this */ }, /** * onDragOver event이전에 즉시 실행되는 코드 * @method b4DragOver * @private */ b4DragOver: function(e) { }, /** * 해당 element가 처음으로 다른 LDragDrop obj위에 올라갔을때 호출되는 추상 method * @method onDragOver * @private * @param {Event} e mousemove event * @param {String|LDragDrop[]} id POINT 모드에서, 위에 올라가는 element의 id. * INTERSECT 모드에서, 위에 올라가는 하나 혹은 그 이상의 dragdrop 항목들의 array. */ onDragOver: function(e, id) { /* override this */ }, /** * onDragOut event 이전에 즉시 실행되는 코드 * @method b4DragOut * @private */ b4DragOut: function(e) { }, /** * element위에 더 이상 아무것도 없을때 호출되는 추상 method. * @method onDragOut * @private * @param {Event} e mousemove event * @param {String|LDragDrop[]} id POINT 모드에서, 위에 올라가는 element의 id. * INTERSECT 모드에서, 위에 올라가는 하나 혹은 그 이상의 dragdrop 항목들의 array. */ onDragOut: function(e, id) { /* override this */ }, /** * onDragDrop event 이전에 즉시 실행되는 코드 * @method b4DragDrop * @private */ b4DragDrop: function(e) { }, /** * 다른 LDragDrop obj 위에 해당 항목이 드랍 되었을때 호출되는 추상 method. * @method onDragDrop * @private * @param {Event} e mouseup event * @param {String|LDragDrop[]} id POINT 모드에서, 위에 올라가는 element의 id. * INTERSECT 모드에서, 위에 올라가는 하나 혹은 그 이상의 dragdrop 항목들의 array. */ onDragDrop: function(e, id) { /* override this */ }, /** * 드랍 대상을 가지고 있지 않은 영역에 항목을 드랍했을때 호출되는 추상 method * @method onInvalidDrop * @private * @param {Event} e mouseup event */ onInvalidDrop: function(e) { /* override this */ }, /** * endDrag event 이전에 즉시 실행되는 코드 * @method b4EndDrag * @private */ b4EndDrag: function(e) { }, /** * object 드래그가 완료되었을때 발생하는 event * @method endDrag * @private * @param {Event} e mouseup event */ endDrag: function(e) { /* override this */ }, /** * onMouseDown event 이전에 즉시 실행되는 코드 * @method b4MouseDown * @private * @param {Event} e mousedown event */ b4MouseDown: function(e) { }, /** * 드래그/드랍 obj가 mousedown을 가질때 발생하는 event handler * @method onMouseDown * @private * @param {Event} e mousedown event */ onMouseDown: function(e) { /* override this */ }, /** * 드래그/드랍 obj가 mouseup을 가질때 발생하는 event handler * @method onMouseUp * @private * @param {Event} e mouseup event */ onMouseUp: function(e) { /* override this */ }, /** * 초기 위치가 결정된 이후에 어떻게 해야할지에 대한 onAvailable method를 오버라이드 한다. * @method onAvailable * @private */ onAvailable: function () { }, /** * linked element에 대한 참조를 반환한다. * @method getEl * @return {HTMLElement} html element */ getEl: function() { if (!this._domRef) { this._domRef = Dom.get(this.id); } return this._domRef; }, /** * 드래그한 실제 element에 대한 참조를 반환한다. * 이것은 html element로써 같지만, 다른 element에 할당될 수도 있다. * 이에 대한 예제는 DU.dd.LDDProxy에서 찾을 수 있다. * @method getDragEl * @return {HTMLElement} html element */ getDragEl: function() { return Dom.get(this.dragElId); }, /** * LDragDrop object을 설정한다. * DU.dd.LDragDrop subclass의 생성자에서 반드시 호출되어야 한다. * @method init * @param id linked element의 id * @param {String} sGroup 연관된 항목들의 그룹 * @param {object} config 설정 attribute들 */ init: function(id, sGroup, config) { this.initTarget(id, sGroup, config); Event.on(this._domRef || this.id, "mousedown", this.handleMouseDown, this, true); // Event.on(this.id, "selectstart", Event.preventDefault); for (var i in this.events) { this.createEvent(i + 'Event'); } }, /** * 타겟팅 기능만 초기화 한다. object는 mousedown handler를 가져오지 않는다. * @method initTarget * @private * @param id linked element의 id * @param {String} sGroup 연관된 항목들의 그룹 * @param {object} config 설정 attribute들 */ initTarget: function(id, sGroup, config) { // configuration attributes this.config = config || {}; this.events = {}; // create a local reference to the drag and drop manager this.LDDM = DU.dd.LDDM; // initialize the groups object this.groups = {}; // assume that we have an element reference instead of an id if the // parameter is not a string if (typeof id !== "string") { this._domRef = id; id = Dom.generateId(id); } // set the id this.id = id; // add to an interaction group this.addToGroup((sGroup) ? sGroup : "default"); // We don't want to register this as the handle with the manager // so we just set the id rather than calling the setter. this.handleElId = id; Event.onAvailable(id, this.handleOnAvailable, this, true); // the linked element is the element that gets dragged by default this.setDragElId(id); // by default, clicked anchors will not start drag operations. // @TODO what else should be here? Probably form fields. this.invalidHandleTypes = { A: "A" }; this.invalidHandleIds = {}; this.invalidHandleClasses = []; this.applyConfig(); }, /** * 생성자에 전달된 설정 paramete들을 적용한다. * 이것은 상속된 체인을 통해 각 레벨에서 발생하는 것으로 되어 있다. * 그래서 LDDProxy 구현은 각 object에서 사용가능한 모든 parameter들을 가져오기 위해서 * LDDProxy, LDD, LDragDrop에 대한 설정 적용을 실행할 것이다. * @method applyConfig * @private */ applyConfig: function() { this.events = { mouseDown: true, b4MouseDown: true, mouseUp: true, b4StartDrag: true, startDrag: true, b4EndDrag: true, endDrag: true, drag: true, b4Drag: true, invalidDrop: true, b4DragOut: true, dragOut: true, dragEnter: true, b4DragOver: true, dragOver: true, b4DragDrop: true, dragDrop: true }; if (this.config.events) { for (var i in this.config.events) { if (this.config.events[i] === false) { this.events[i] = false; } } } // configurable properties: // padding, isTarget, maintainOffset, primaryButtonOnly this.padding = this.config.padding || [0, 0, 0, 0]; this.isTarget = (this.config.isTarget !== false); this.maintainOffset = (this.config.maintainOffset); this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); this.dragOnly = ((this.config.dragOnly === true) ? true : false); this.useShim = ((this.config.useShim === true) ? true : false); }, /** * linked element가 사용 가능할때 실행한다. * @method handleOnAvailable * @private */ handleOnAvailable: function() { this.available = true; this.resetConstraints(); this.onAvailable(); }, /** * 대상 지역에 픽셀 단위의 padding을 설정한다. * 대상을 계산하는데에 대한 가상 object 사이즈를 효과적으로 증가(감소)시킨다. * 약칭의 css-style을 제공한다; 하나의 parameter만 전달된 경우 모든 side가 * padding을 가질 것이며, 두개만 전달된 경우 top과 bottom은 첫번째 parameter, * left와 right는 두번째를 가질 것이다. * @method setPadding * @param {int} iTop Top pad * @param {int} iRight Right pad * @param {int} iBot Bot pad * @param {int} iLeft Left pad */ setPadding: function(iTop, iRight, iBot, iLeft) { // this.padding = [iLeft, iRight, iTop, iBot]; if (!iRight && 0 !== iRight) { this.padding = [iTop, iTop, iTop, iTop]; } else if (!iBot && 0 !== iBot) { this.padding = [iTop, iRight, iTop, iRight]; } else { this.padding = [iTop, iRight, iBot, iLeft]; } }, /** * linked element의 초기 배치를 저장한다. * @method setInitialPosition * @private * @param {int} diffX X offset, 기본값 0 * @param {int} diffY Y offset, 기본값 0 */ setInitPosition: function(diffX, diffY) { var el = this.getEl(); if (!this.LDDM.verifyEl(el)) { if (el && el.style && (el.style.display == 'none')) { } else { } return; } var dx = diffX || 0; var dy = diffY || 0; var p = Dom.getXY( el ); this.initPageX = p[0] - dx; this.initPageY = p[1] - dy; this.lastPageX = p[0]; this.lastPageY = p[1]; this.setStartPosition(p); }, /** * element의 시작 위치를 설정한다. * 이것은 obj가 초기화될 때 설정되며, 드래그가 시작될 때 재설정 된다. * @method setStartPosition * @private * @param {int} pos 현재 위치 (이전 확인지점으로부터) */ setStartPosition: function(pos) { var p = pos || Dom.getXY(this.getEl()); this.deltaSetXY = null; this.startPageX = p[0]; this.startPageY = p[1]; }, /** * 해당 인스턴스를 연관된 드래그/드랍 object들의 그룹에 추가한다. * 모든 인스턴스는 적어도 하나의 그룹에 속하며, 필요한 만큼 많은 그룹에 속할 수 있다. * @method addToGroup * @param {string} sGroup 그룹의 이름 */ addToGroup: function(sGroup) { this.groups[sGroup] = true; this.LDDM.regDragDrop(this, sGroup); }, /** * 제공된 상호작용 그룹으로부터 해당 인스턴스를 제거한다. * @method removeFromGroup * @param {string} sGroup 드랍할 그룹 */ removeFromGroup: function(sGroup) { if (this.groups[sGroup]) { delete this.groups[sGroup]; } this.LDDM.removeDDFromGroup(this, sGroup); }, /** * 드래그 동안 커서와 함께 움직일 linked element이외의 element를 * 명시하게 한다. * @method setDragElId * @param {string} id 드래그를 초기화 하기 위해 사용될 element의 id */ setDragElId: function(id) { this.dragElId = id; }, /** * 드래그 작업을 초기화하기 위해 사용되어야 할 linked element의 child를 * 명시하게 한다. * content div가 텍스트와 링크들을 가지는 경우가 이에 대한 예제가 될 것이다. * content 영역의 어디든지 클릭하는 것은 일반적으로 드래그 작업을 시작할 것이다. * 드래그 작업을 시작하는 content div 안의 element를 명시하기 위해 * 이 method를 사용한다. * @method setHandleElId * @param {string} id 드래그를 초기화 하기 위해 사용되는 element의 id */ setHandleElId: function(id) { if (typeof id !== "string") { id = Dom.generateId(id); } this.handleElId = id; this.LDDM.regHandle(this.id, id); }, /** * 드래그 handle로써 linked element의 바깥 element를 설정하게 한다. * @method setOuterHandleElId * @param {string} id 드래그를 초기화 하기 위해 사용되는 element의 id */ setOuterHandleElId: function(id) { if (typeof id !== "string") { id = Dom.generateId(id); } Event.on(id, "mousedown", this.handleMouseDown, this, true); this.setHandleElId(id); this.hasOuterHandles = true; }, /** * 해당 element에 대한 모든 드래그와 드랍에 대한 연결고리를 삭제한다. * @method unreg */ unreg: function() { Event.removeListener(this.id, "mousedown", this.handleMouseDown); this._domRef = null; this.LDDM._remove(this); }, /** * 해당 인스턴스가 lock 되어 있거나 드래그드랍 매니저가 lock 되어 있는 경우 true를 반환한다. * (이는 페이지에 모든 드래그/드랍이 비활성되어 있는 것을 의미함.) * @method isLocked * @return {boolean} 해당 obj나 모든 드래그/드랍이 lock되어 있으면 true, 아니면 false */ isLocked: function() { return (this.LDDM.isLocked() || this.locked); }, /** * 해당 object가 클릭 되었을때 발생한다. * @method handleMouseDown * @private * @param {Event} e event 객체 * @param {DU.dd.LDragDrop} oDD 클릭된 dd object(해당 dd obj) */ handleMouseDown: function(e, oDD) { var button = e.which || e.button; if (this.primaryButtonOnly && button > 1) { return; } if (this.isLocked()) { return; } // firing the mousedown events prior to calculating positions var b4Return = this.b4MouseDown(e), b4Return2 = true; if (this.events.b4MouseDown) { b4Return2 = this.fireEvent('b4MouseDownEvent', e); } var mDownReturn = this.onMouseDown(e), mDownReturn2 = true; if (this.events.mouseDown) { mDownReturn2 = this.fireEvent('mouseDownEvent', e); } if ((b4Return === false) || (mDownReturn === false) || (b4Return2 === false) || (mDownReturn2 === false)) { return; } this.LDDM.refreshCache(this.groups); // var self = this; // setTimeout( function() { self.LDDM.refreshCache(self.groups); }, 0); // Only process the event if we really clicked within the linked // element. The reason we make this check is that in the case that // another element was moved between the clicked element and the // cursor in the time between the mousedown and mouseup events. When // this happens, the element gets the next mousedown event // regardless of where on the screen it happened. var pt = new DU.util.LPoint(Event.getPageX(e), Event.getPageY(e)); if (!this.hasOuterHandles && !this.LDDM.isOverTarget(pt, this) ) { } else { if (this.clickValidator(e)) { // set the initial element position this.setStartPosition(); // start tracking mousemove distance and mousedown time to // determine when to start the actual drag this.LDDM.handleMouseDown(e, this); // this mousedown is mine this.LDDM.stopEvent(e); } else { } } }, /** * @method clickValidator * @private * @description 클릭된 element가 실제 핸들이거나 handle의 유효한 child인지를 * 유효성 검사하는 method. * @param {Event} e event 객체 */ clickValidator: function(e) { var target = DU.util.LEvent.getTarget(e); return ( this.isValidHandleChild(target) && (this.id == this.handleElId || this.LDDM.handleWasClicked(target, this.id)) ); }, /** * 마우스 위치의 click offset보다 적은 위치로 옮기고자 하는 경우에 * 위치하기 위한 element의 위치를 찾는다. * @method getTargetCoord * @private * @param {int} iPageX 클릭의 X 좌표 * @param {int} iPageY 클릭의 Y 좌표 * @return 좌표를 포함하는 object (Object.x and Object.y) */ getTargetCoord: function(iPageX, iPageY) { var x = iPageX - this.deltaX; var y = iPageY - this.deltaY; if (this.constrainX) { if (x < this.minX) { x = this.minX; } if (x > this.maxX) { x = this.maxX; } } if (this.constrainY) { if (y < this.minY) { y = this.minY; } if (y > this.maxY) { y = this.maxY; } } x = this.getTick(x, this.xTicks); y = this.getTick(y, this.yTicks); return {x:x, y:y}; }, /** * 클릭됐을 때 드래그 작업을 시작하지 말아야할 tag 이름을 명시하게 한다. * 이것은 드래그를 시작하는 것 이외의 다른 일을 하는 드래그 handle 안에 * 링크들을 끼워 넣기 용이하도록 디자인 된다. * @method addInvalidHandleType * @param {string} tagName 제외되는 element의 타입 */ addInvalidHandleType: function(tagName) { var type = tagName.toUpperCase(); this.invalidHandleTypes[type] = type; }, /** * 드래그를 초기화 하지 말아야 할 드래그 handle의 child에 대한 element id를 명시하게 해준다. * @method addInvalidHandleId * @param {string} id 무시하고자 하는 element의 id */ addInvalidHandleId: function(id) { if (typeof id !== "string") { id = Dom.generateId(id); } this.invalidHandleIds[id] = id; }, /** * 드래그를 초기화 하지 않아야 할 element들의 css 클래스를 명시하게 해준다. * @method addInvalidHandleClass * @param {string} cssClass 무시하고자 하는 element의 클래스 */ addInvalidHandleClass: function(cssClass) { this.invalidHandleClasses.push(cssClass); }, /** * Unsets an excluded tag name set by addInvalidHandleType * addInvalidHandleType에 의해 설정한 제외된 tag 이름을 unset한다. * @method removeInvalidHandleType * @param {string} tagName 제외하지 않을 element의 타입 */ removeInvalidHandleType: function(tagName) { var type = tagName.toUpperCase(); // this.invalidHandleTypes[type] = null; delete this.invalidHandleTypes[type]; }, /** * 유효하지 않은 handle id를 제거한다. * @method removeInvalidHandleId * @param {string} id 다시 활성화할 element의 id */ removeInvalidHandleId: function(id) { if (typeof id !== "string") { id = Dom.generateId(id); } delete this.invalidHandleIds[id]; }, /** * 유효하지 않은 css 클래스를 제거한다. * @method removeInvalidHandleClass * @param {string} cssClass 다시 활성화하고자 하는 element의 클래스 */ removeInvalidHandleClass: function(cssClass) { for (var i=0, len=this.invalidHandleClasses.length; i= this.minX; i = i - iTickSize) { if (!tickMap[i]) { this.xTicks[this.xTicks.length] = i; tickMap[i] = true; } } for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) { if (!tickMap[i]) { this.xTicks[this.xTicks.length] = i; tickMap[i] = true; } } this.xTicks.sort(this.LDDM.numericSort) ; }, /** * 간격이 setYConstraint()에서 명시된 경우 수직 tick 마크의 array를 생성한다. * @method setYTicks * @private */ setYTicks: function(iStartY, iTickSize) { this.yTicks = []; this.yTickSize = iTickSize; var tickMap = {}; for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) { if (!tickMap[i]) { this.yTicks[this.yTicks.length] = i; tickMap[i] = true; } } for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) { if (!tickMap[i]) { this.yTicks[this.yTicks.length] = i; tickMap[i] = true; } } this.yTicks.sort(this.LDDM.numericSort) ; }, /** * 기본적으로 element는 화면에서 어떤 곳에든 드래그될 수 있다. * element의 수평 움직임을 제한하기 위해서 이 method를 사용한다. * y 축에의 드래그를 lock 하고자 하는 경우 parameter에 0,0을 전달한다. * @method setXConstraint * @param {int} iLeft element가 left로 움직일 수 있는 픽셀 숫자 * @param {int} iRight element가 right로 움직일 수 있는 픽셀 숫자 * @param {int} iTickSize element가 한번에 움직여야 할 iTickSize 픽셀을 명시하기 위한 부가적인 parameter */ setXConstraint: function(iLeft, iRight, iTickSize) { this.leftConstraint = parseInt(iLeft, 10); this.rightConstraint = parseInt(iRight, 10); this.minX = this.initPageX - this.leftConstraint; this.maxX = this.initPageX + this.rightConstraint; if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); } this.constrainX = true; }, /** * 해당 인스턴스에 적용된 모든 제한을 삭제한다. * 그것들은 시간에 대한 제한의 종속성을 유지할 수가 없기 때문에 tick들 또한 삭제한다. * @method clearConstraints */ clearConstraints: function() { this.constrainX = false; this.constrainY = false; this.clearTicks(); }, /** * 해당 인스턴스에 대해 정의된 tick 간격을 삭제한다. * @method clearTicks */ clearTicks: function() { this.xTicks = null; this.yTicks = null; this.xTickSize = 0; this.yTickSize = 0; }, /** * 기본적으로 element는 화면에서 어떤 곳에든 드래그될 수 있다. * element의 수직 움직임을 제한하기 위해서 이 method를 사용한다. * x 축에의 드래그를 lock 하고자 하는 경우 parameter에 0,0을 전달한다. * @method setYConstraint * @param {int} iUp element가 up으로 움직일 수 있는 픽셀 숫자 * @param {int} iDown element가 down으로 움직일 수 있는 픽셀 숫자 * @param {int} iTickSize element가 한번에 움직여야 할 iTickSize 픽셀을 명시하기 위한 부가적인 parameter */ setYConstraint: function(iUp, iDown, iTickSize) { this.topConstraint = parseInt(iUp, 10); this.bottomConstraint = parseInt(iDown, 10); this.minY = this.initPageY - this.topConstraint; this.maxY = this.initPageY + this.bottomConstraint; if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); } this.constrainY = true; }, /** * 수동으로 dd element의 재위치 하고자 하는 경우 resetConstraints가 반드시 호출되어야 한다. * @method resetConstraints */ resetConstraints: function() { // Maintain offsets if necessary if (this.initPageX || this.initPageX === 0) { // figure out how much this thing has moved var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0; var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0; this.setInitPosition(dx, dy); // This is the first time we have detected the element's position } else { this.setInitPosition(); } if (this.constrainX) { this.setXConstraint( this.leftConstraint, this.rightConstraint, this.xTickSize ); } if (this.constrainY) { this.setYConstraint( this.topConstraint, this.bottomConstraint, this.yTickSize ); } }, /** * 일반적으로 드래그 element는 픽셀 단위로 움직이지만 한번에 픽셀의 숫자 단위로 * 움직이게 명시할 수가 있다. * 이와 같이 설정했을때 이 method는 위치를 결정한다. * @method getTick * @private * @param {int} val object를 위치시키고자 하는 곳 * @param {int[]} tickArray 유요한 지점의 정렬된 array * @return {int} 가장 가까운 tick */ getTick: function(val, tickArray) { if (!tickArray) { // If tick interval is not defined, it is effectively 1 pixel, // so we return the value passed to us. return val; } else if (tickArray[0] >= val) { // The value is lower than the first tick, so we return the first // tick. return tickArray[0]; } else { for (var i=0, len=tickArray.length; i= val) { var diff1 = val - tickArray[i]; var diff2 = tickArray[next] - val; return (diff2 > diff1) ? tickArray[i] : tickArray[next]; } } // The value is larger than the last tick, so we return the last // tick. return tickArray[tickArray.length - 1]; } }, /** * toString method * @method toString * @return {string} dd obj의 문자열 표현 */ toString: function() { return ("LDragDrop " + this.id); } }; DU.augment(DU.dd.LDragDrop, DU.util.LEventProvider); /** * @event mouseDownEvent * @description mousedown event에의 접근을 제공한다. mousedown는 드래그 작업에서 항상 같은 결과를 내지 않는다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4MouseDownEvent * @description mouseDownEvent가 발생하기 전에 mousedown event에의 접근을 제공한다. false 반환은 드래그를 취소한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event mouseUpEvent * @description 드래그 작업이 끝났을 때 LDragDropMgr 안에서부터 발생하는 이벤트 * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4StartDragEvent * @description startDragEvent 전에 발생하며 false 반환은 startDrag Event를 취소한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event startDragEvent * @description mousedown과 드래그 threshold가 만난 이후에 발생한다. 드래그 threshold 기본값은 마우스 움직임의 3픽셀이거나 mousedown의 홀딩의 1초이다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4EndDragEvent * @description endDragEvent 이전에 발생한다. false 반환은 event를 취소한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event endDragEvent * @description 드래그가 초기화된 이후에 mouseup event에서 발생한다.(startDrag 발생) * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEvent * @description 드래그 하는 동안 모든 mousemove event가 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragEvent * @description dragEvent 이전에 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event invalidDropEvent * @description 드래그된 object가 드랍 대상을 포함하고 있지 않은 위치에 드랍되었을때 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOutEvent * @description dragOutEvent 이전에 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOutEvent * @description 드래그된 object가 onDragEnter를 발생시킨 object 위에 더 이상 있지 않으면 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEnterEvent * @description 드래그된 object가 다른 타겟가능한 드래그나 드랍 object와 처음으로 상호작용할 때 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOverEvent * @description dragOverEvent 이전에 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOverEvent * @description 드래그와 드랍 object위에 있는 동안 모든 mousemove event가 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragDropEvent * @description Fires before the dragDropEvent * @description dragDropEvent 이전에 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragDropEvent * @description Fires when the dragged objects is dropped on another. * @description 드래그된 object가 다른데 드랍됐을 때 발생한다. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ })(); /** * 드래그 하는 동안 연결된 element가 마우스 커서를 따라가는 * 것에 대한 LDragDrop 구현 * @namespace DU.dd * @class LDD * @extends DU.dd.LDragDrop * @constructor * @param {String} id 연결된 element의 id * @param {String} sGroup 연관된 LDragDrop 항목들의 그룹 * @param {object} config LDD에 대한 설정 가능한 attribute Vaild attribute를 포함한 object: scroll */ DU.dd.LDD = function(id, sGroup, config) { if (id) { this.init(id, sGroup, config); } }; DU.extend(DU.dd.LDD, DU.dd.LDragDrop, { /** * true로 설정하는 경우 유틸리티는 drag and drop element가 viewport 경계 근처까지 드래그될 때 * 브라우저 window를 스크롤하려고 자동적으로 시도할 것이다. * 기본값은 true. * @property scroll * @type boolean */ scroll: true, /** * 연결된 element의 top left corner와 element가 클릭된 위치 사이의 거리에 대한 * 포인터 offset을 설정한다. * @method autoOffset * @param {int} iPageX 클릭의 X 좌표 * @param {int} iPageY 클릭의 Y 좌표 */ autoOffset: function(iPageX, iPageY) { var x = iPageX - this.startPageX; var y = iPageY - this.startPageY; this.setDelta(x, y); }, /** * 포인터 offset을 설정한다. * 특정 위치에 offset이 가도록 강제하기 위해서 이것을 직접 호출할 수 있다. * (예를 들어, DU.widget.Slider로 다 object의 중앙에 설정하기 위해 0,0을 전달한다) * @method setDelta * @param {int} iDeltaX left로부터의 거리 * @param {int} iDeltaY top부터의 거리 */ setDelta: function(iDeltaX, iDeltaY) { this.deltaX = iDeltaX; this.deltaY = iDeltaY; }, /** * 드래그 element를 클릭된 element상의 위치에 관련된 * 커서 위치를 포함하는 mousedown이나 클릭 event의 위치에 설정한다. * 커서가 있는 곳에 element를 놓고자 하는 경우 이것을 override 한다. * @method setDragElPos * @param {int} iPageX mousedown이나 드래그 event의 X 좌표 * @param {int} iPageY mousedown이나 드래그 event의 Y 좌표 */ setDragElPos: function(iPageX, iPageY) { // the first time we do this, we are going to check to make sure // the element has css positioning var el = this.getDragEl(); this.alignElWithMouse(el, iPageX, iPageY); }, /** * element를 클릭된 element상의 위치에 관련된 * 커서 위치를 포함하는 mousedown이나 클릭 event의 위치에 설정한다. * 커서가 있는 곳에 element를 놓고자 하는 경우 이것을 override 한다. * @method alignElWithMouse * @param {HTMLElement} el the element to move * @param {int} iPageX mousedown이나 드래그 event의 X 좌표 * @param {int} iPageY mousedown이나 드래그 event의 Y 좌표 */ alignElWithMouse: function(el, iPageX, iPageY) { var oCoord = this.getTargetCoord(iPageX, iPageY); if (!this.deltaSetXY) { var aCoord = [oCoord.x, oCoord.y]; DU.util.LDom.setXY(el, aCoord); var newLeft = parseInt( DU.util.LDom.getStyle(el, "left"), 10 ); var newTop = parseInt( DU.util.LDom.getStyle(el, "top" ), 10 ); this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ]; } else { DU.util.LDom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px"); DU.util.LDom.setStyle(el, "top", (oCoord.y + this.deltaSetXY[1]) + "px"); } this.cachePosition(oCoord.x, oCoord.y); var self = this; setTimeout(function() { self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth); }, 0); }, /** * 필요시 container들을 리셋하고 체크 마크를 하기 위하여 가장 최근 위치를 저장한다. * 원래 위치로부터의 offset인 element의 픽셀 숫자를 계산하기 위하여 이것을 * 알아야 할 필요가 있다. * @method cachePosition * @param iPageX 현재 x 위치(부가적으로, 이것은 다시 찾아볼 필요가 없도록 그냥 만들어짐) * @param iPageY 현재 y 위치(부가적으로, 이것은 다시 찾아볼 필요가 없도록 그냥 만들어짐) */ cachePosition: function(iPageX, iPageY) { if (iPageX) { this.lastPageX = iPageX; this.lastPageY = iPageY; } else { var aCoord = DU.util.LDom.getXY(this.getEl()); this.lastPageX = aCoord[0]; this.lastPageY = aCoord[1]; } }, /** * 드래그된 object가 window의 보이는 경계 너머로 이동한 경우 * window를 자동 스크롤한다. * @method autoScroll * @param {int} x 드래그 element의 x 위치 * @param {int} y 드래그 element의 x 위치 * @param {int} h 드래그 element의 height * @param {int} w 드래그 element의 width * @private */ autoScroll: function(x, y, h, w) { if (this.scroll) { // The client height var clientH = this.LDDM.getClientHeight(); // The client width var clientW = this.LDDM.getClientWidth(); // The amt scrolled down var st = this.LDDM.getScrollTop(); // The amt scrolled right var sl = this.LDDM.getScrollLeft(); // Location of the bottom of the element var bot = h + y; // Location of the right of the element var right = w + x; // The distance from the cursor to the bottom of the visible area, // adjusted so that we don't scroll if the cursor is beyond the // element drag constraints var toBot = (clientH + st - y - this.deltaY); // The distance from the cursor to the right of the visible area var toRight = (clientW + sl - x - this.deltaX); // How close to the edge the cursor must be before we scroll // var thresh = (document.all) ? 100 : 40; var thresh = 40; // How many pixels to scroll per autoscroll op. This helps to reduce // clunky scrolling. IE is more sensitive about this ... it needs this // value to be higher. var scrAmt = (document.all) ? 80 : 30; // LScroll down if we are near the bottom of the visible page and the // obj extends below the crease if ( bot > clientH && toBot < thresh ) { window.scrollTo(sl, st + scrAmt); } // LScroll up if the window is scrolled down and the top of the object // goes above the top border if ( y < st && st > 0 && y - st < thresh ) { window.scrollTo(sl, st - scrAmt); } // LScroll right if the obj is beyond the right border and the cursor is // near the border. if ( right > clientW && toRight < thresh ) { window.scrollTo(sl + scrAmt, st); } // LScroll left if the window has been scrolled to the right and the obj // extends past the left border if ( x < sl && sl > 0 && x - sl < thresh ) { window.scrollTo(sl - scrAmt, st); } } }, /* * Sets up config options specific to this class. Overrides * DU.dd.LDragDrop, but all versions of this method through the * inheritance chain are called */ applyConfig: function() { DU.dd.LDD.superclass.applyConfig.call(this); this.scroll = (this.config.scroll !== false); }, /* * Event that fires prior to the onMouseDown event. Overrides * DU.dd.LDragDrop. */ b4MouseDown: function(e) { this.setStartPosition(); // this.resetConstraints(); this.autoOffset(DU.util.LEvent.getPageX(e), DU.util.LEvent.getPageY(e)); }, /* * Event that fires prior to the onDrag event. Overrides * DU.dd.LDragDrop. */ b4Drag: function(e) { this.setDragElPos(DU.util.LEvent.getPageX(e), DU.util.LEvent.getPageY(e)); }, toString: function() { return ("LDD " + this.id); } ////////////////////////////////////////////////////////////////////////// // Debugging ygDragDrop events that can be overridden ////////////////////////////////////////////////////////////////////////// /* startDrag: function(x, y) { }, onDrag: function(e) { }, onDragEnter: function(e, id) { }, onDragOver: function(e, id) { }, onDragOut: function(e, id) { }, onDragDrop: function(e, id) { }, endDrag: function(e) { } */ /** * @event mouseDownEvent * @description Provides access to the mousedown event. The mousedown does not always result in a drag operation. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4MouseDownEvent * @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event mouseUpEvent * @description Fired from inside LDragDropMgr when the drag operation is finished. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4StartDragEvent * @description Fires before the startDragEvent, returning false will cancel the startDrag Event. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event startDragEvent * @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4EndDragEvent * @description Fires before the endDragEvent. Returning false will cancel. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event endDragEvent * @description Fires on the mouseup event after a drag has been initiated (startDrag fired). * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEvent * @description Occurs every mousemove event while dragging. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragEvent * @description Fires before the dragEvent. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event invalidDropEvent * @description Fires when the dragged objects is dropped in a location that contains no drop targets. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOutEvent * @description Fires before the dragOutEvent * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOutEvent * @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEnterEvent * @description Occurs when the dragged object first interacts with another targettable drag and drop object. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOverEvent * @description Fires before the dragOverEvent. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOverEvent * @description Fires every mousemove event while over a drag and drop object. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragDropEvent * @description Fires before the dragDropEvent * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragDropEvent * @description Fires when the dragged objects is dropped on another. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ }); /** * 드래그 작업동안 커서를 따라가는 document안의 빈 bordered div를 삽입하는 LDragDrop 구현 * 클릭할 때, frame div은 연결된 html element의 치수로 사이즈가 변경되며, * 연결된 element의 정확한 위치로 이동된다. * * "frame" element에 대한 참조는 페이지의 모든 LDDProxy element들의 위치에 * 드래그되면서 만들어진 싱글 proxy element를 참조하십시오. * @namespace DU.dd * @class LDDProxy * @extends DU.dd.LDD * @constructor * @param {String} id 연결된 html element의 id * @param {String} sGroup 연관된 LDragDrop object들의 그룹 * @param {object} config 설정 가능한 attribute를 포함한 object * LDragDrop에 추가적으로 LDDProxy에 대해 유효한 속성들: * resizeFrame, centerFrame, dragElId */ DU.dd.LDDProxy = function(id, sGroup, config) { if (id) { this.init(id, sGroup, config); this.initFrame(); } }; /** * 기본 드래그 frame div id * @property DU.dd.LDDProxy.dragElId * @type String * @static */ DU.dd.LDDProxy.dragElId = "duddfdiv"; DU.extend(DU.dd.LDDProxy, DU.dd.LDD, { /** * 기본적으로 드래그하고 싶은 element와 사이즈를 같게 하기 위하여 * 드래그 frame을 리사이즈 한다.(이것은 프레임 효과를 얻을 것이다.) * 다른 행동을 원하는 경우 이것을 꺼버릴 수 있다. * @property resizeFrame * @type boolean */ resizeFrame: true, /** * 기본적으로 프레임은 드래그 element가 있는 곳에 정확하게 위치하며, * DU.dd.LDD에 의해 제공되는 커서 offset을 사용한다. * 커서를 중심으로 드래그 frame을 가진 obj를 포함하지 않는 경우에만 * 다른 옵션이 작동한다. 이 효과를 위해서 centerFrame을 true로 설정한다. * @property centerFrame * @type boolean */ centerFrame: false, /** * proxy element가 아직 존재하지 않는 경우 생성한다. * @method createFrame */ createFrame: function() { var self=this, body=document.body; if (!body || !body.firstChild) { setTimeout( function() { self.createFrame(); }, 50 ); return; } var div=this.getDragEl(), Dom=DU.util.LDom; if (!div) { div = document.createElement("div"); div.id = this.dragElId; var s = div.style; s.position = "absolute"; s.visibility = "hidden"; s.cursor = "move"; s.border = "2px solid #aaa"; s.zIndex = 999; s.height = "25px"; s.width = "25px"; var _data = document.createElement('div'); Dom.setStyle(_data, 'height', '100%'); Dom.setStyle(_data, 'width', '100%'); /** * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer. * Since it is "transparent" then the events pass through it to the iframe below. * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through. */ Dom.setStyle(_data, 'background-color', '#ccc'); Dom.setStyle(_data, 'opacity', '0'); div.appendChild(_data); /** * It seems that IE will fire the mouseup event if you pass a proxy element over a select box * Placing the IFRAME element inside seems to stop this issue */ if (DU.browser.msie) { //Only needed for Internet Explorer var ifr = document.createElement('iframe'); ifr.setAttribute('src', 'javascript: false;'); ifr.setAttribute('scrolling', 'no'); ifr.setAttribute('frameborder', '0'); div.insertBefore(ifr, div.firstChild); Dom.setStyle(ifr, 'height', '100%'); Dom.setStyle(ifr, 'width', '100%'); Dom.setStyle(ifr, 'position', 'absolute'); Dom.setStyle(ifr, 'top', '0'); Dom.setStyle(ifr, 'left', '0'); Dom.setStyle(ifr, 'opacity', '0'); Dom.setStyle(ifr, 'zIndex', '-1'); Dom.setStyle(ifr.nextSibling, 'zIndex', '2'); } // appendChild can blow up IE if invoked prior to the window load event // while rendering a table. It is possible there are other scenarios // that would cause this to happen as well. body.insertBefore(div, body.firstChild); } }, /** * 드래그 frame element에 대한 초기화. 모든 subclass들의 생성자에서 호출되어야 한다. * @method initFrame */ initFrame: function() { this.createFrame(); }, applyConfig: function() { DU.dd.LDDProxy.superclass.applyConfig.call(this); this.resizeFrame = (this.config.resizeFrame !== false); this.centerFrame = (this.config.centerFrame); this.setDragElId(this.config.dragElId || DU.dd.LDDProxy.dragElId); }, /** * 드래그 frame을 클릭된 object의 수치로 리사이즈하고 object의 위로 위치시키고, * 최종적으로 그것을 표시한다. * @method showFrame * @param {int} iPageX X click position * @param {int} iPageY Y click position * @private */ showFrame: function(iPageX, iPageY) { var el = this.getEl(); var dragEl = this.getDragEl(); var s = dragEl.style; this._resizeProxy(); if (this.centerFrame) { this.setDelta( Math.round(parseInt(s.width, 10)/2), Math.round(parseInt(s.height, 10)/2) ); } this.setDragElPos(iPageX, iPageY); DU.util.LDom.setStyle(dragEl, "visibility", "visible"); }, /** * proxy는 resizeFrame가 false로 설정되어 있지 않다면, 드래그가 초기화 될때 * 연결된 element의 수치로 자동적으로 리사이즈 된다. * @method _resizeProxy * @private */ _resizeProxy: function() { if (this.resizeFrame) { var DOM = DU.util.LDom; var el = this.getEl(); var dragEl = this.getDragEl(); var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth" ), 10); var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth" ), 10); var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10); var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth" ), 10); if (isNaN(bt)) { bt = 0; } if (isNaN(br)) { br = 0; } if (isNaN(bb)) { bb = 0; } if (isNaN(bl)) { bl = 0; } var newWidth = Math.max(0, el.offsetWidth - br - bl); var newHeight = Math.max(0, el.offsetHeight - bt - bb); DOM.setStyle( dragEl, "width", newWidth + "px" ); DOM.setStyle( dragEl, "height", newHeight + "px" ); } }, // overrides DU.dd.LDragDrop b4MouseDown: function(e) { this.setStartPosition(); var x = DU.util.LEvent.getPageX(e); var y = DU.util.LEvent.getPageY(e); this.autoOffset(x, y); // This causes the autoscroll code to kick off, which means autoscroll can // happen prior to the check for a valid drag handle. // this.setDragElPos(x, y); }, // overrides DU.dd.LDragDrop b4StartDrag: function(x, y) { // show the drag frame this.showFrame(x, y); }, // overrides DU.dd.LDragDrop b4EndDrag: function(e) { DU.util.LDom.setStyle(this.getDragEl(), "visibility", "hidden"); }, // overrides DU.dd.LDragDrop // By default we try to move the element to the last location of the frame. // This is so that the default behavior mirrors that of DU.dd.LDD. endDrag: function(e) { var DOM = DU.util.LDom; var lel = this.getEl(); var del = this.getDragEl(); // Show the drag frame briefly so we can get its position // del.style.visibility = ""; DOM.setStyle(del, "visibility", ""); // Hide the linked element before the move to get around a Safari // rendering bug. //lel.style.visibility = "hidden"; DOM.setStyle(lel, "visibility", "hidden"); DU.dd.LDDM.moveToEl(lel, del); //del.style.visibility = "hidden"; DOM.setStyle(del, "visibility", "hidden"); //lel.style.visibility = ""; DOM.setStyle(lel, "visibility", ""); }, toString: function() { return ("LDDProxy " + this.id); } /** * @event mouseDownEvent * @description Provides access to the mousedown event. The mousedown does not always result in a drag operation. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4MouseDownEvent * @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event mouseUpEvent * @description Fired from inside LDragDropMgr when the drag operation is finished. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4StartDragEvent * @description Fires before the startDragEvent, returning false will cancel the startDrag Event. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event startDragEvent * @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4EndDragEvent * @description Fires before the endDragEvent. Returning false will cancel. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event endDragEvent * @description Fires on the mouseup event after a drag has been initiated (startDrag fired). * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEvent * @description Occurs every mousemove event while dragging. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragEvent * @description Fires before the dragEvent. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event invalidDropEvent * @description Fires when the dragged objects is dropped in a location that contains no drop targets. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOutEvent * @description Fires before the dragOutEvent * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOutEvent * @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragEnterEvent * @description Occurs when the dragged object first interacts with another targettable drag and drop object. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragOverEvent * @description Fires before the dragOverEvent. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragOverEvent * @description Fires every mousemove event while over a drag and drop object. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event b4DragDropEvent * @description Fires before the dragDropEvent * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ /** * @event dragDropEvent * @description Fires when the dragged objects is dropped on another. * @type DU.util.LCustomEvent See Element.addListener for more information on listening for this event. */ }); /** * 이동이 아닌, 대상이 드랍될 수 있는 LDragDrop 구현. * You would get the same result by simply omitting implementation * for the event callbacks, but this way we reduce the processing cost of the * event listener and the callbacks. * event callbakc에 대한 간단하게 생략된 구현에 의해 똑같은 결과를 얻을 수 있다. * 그러나 이러한 방법은 event listener나 callback의 처리 비용을 감소시킨다. * @class LDDTarget * @extends DU.dd.LDragDrop * @constructor * @param {String} id 드랍 대상인 element의 id * @param {String} sGroup 연관된 LDragDrop object들의 그룹 * @param {object} config 설정 가능한 attribute를 포함한 object * LDragDrop에 추가적으로 LDDTarget에 대해 유효한 속성들: * none */ DU.dd.LDDTarget = function(id, sGroup, config) { if (id) { this.initTarget(id, sGroup, config); } }; // DU.dd.LDDTarget.prototype = new DU.dd.LDragDrop(); DU.extend(DU.dd.LDDTarget, DU.dd.LDragDrop, { toString: function() { return ("LDDTarget " + this.id); } }); /** * region은 그리드 상에서의 object의 표시이다. * 이것은 기본적으로 직사각형인 top, right, bottom, left의 범위에 의해 정의된다. * 만약 다른 형태의 모양이 필요한 경우 해당 클래스는 그것을 지원하기 위하여 상속될 수 있다. * @module util * @namespace DU.util * @class LRegion * @param {Int} t top 범위 * @param {Int} r right 범위 * @param {Int} b bottom 범위 * @param {Int} l left 범위 * @constructor */ DU.util.LRegion = function(t, r, b, l) { /** * region의 top 범위 * @property top * @type Int */ this.top = t; /** * set/getXY를 가진 대칭에 대한 인덱스로서의 region의 top 범위 * @property 1 * @type Int */ this[1] = t; /** * region의 right 범위 * @property right * @type int */ this.right = r; /** * region의 bottom 범위 * @property bottom * @type Int */ this.bottom = b; /** * region의 left 범위 * @property left * @type Int */ this.left = l; /** * set/getXY를 가진 대칭에 대한 인덱스로서의 region의 left 범위 * @property 0 * @type Int */ this[0] = l; }; /** * 해당 region이 전달된 region을 포함하는 경우 true를 반환한다. * @method contains * @param {Region} region 평가할 region * @return {Boolean} region에 해당 region이 포함되어 있으면 true, 아니면 false */ DU.util.LRegion.prototype.contains = function(region) { return ( region.left >= this.left && region.right <= this.right && region.top >= this.top && region.bottom <= this.bottom ); }; /** * region의 영역을 반환한다. * @method getArea * @return {Int} region의 영역 */ DU.util.LRegion.prototype.getArea = function() { return ( (this.bottom - this.top) * (this.right - this.left) ); }; /** * Returns the region where the passed in region overlaps with this one * 해당 region와 중복되는 region에 전달되는 region을 반환한다. * @method intersect * @param {Region} region 교차되는 region * @return {Region} 중복된 region이나 중복되지 않는 경우 null */ DU.util.LRegion.prototype.intersect = function(region) { var t = Math.max( this.top, region.top ); var r = Math.min( this.right, region.right ); var b = Math.min( this.bottom, region.bottom ); var l = Math.max( this.left, region.left ); if (b >= t && r >= l) { return new DU.util.LRegion(t, r, b, l); } else { return null; } }; /** * 해당 region과 전달된 region을 둘다 포함하는 가장 작은 영역을 표시하는 * region을 반환한다. * @method union * @param {Region} region 조합되어 만들기 위한 region * @return {Region} 조합된 region */ DU.util.LRegion.prototype.union = function(region) { var t = Math.min( this.top, region.top ); var r = Math.max( this.right, region.right ); var b = Math.max( this.bottom, region.bottom ); var l = Math.min( this.left, region.left ); return new DU.util.LRegion(t, r, b, l); }; /** * toString * @method toString * @return region property 문자열 */ DU.util.LRegion.prototype.toString = function() { return ( "Region {" + "top: " + this.top + ", right: " + this.right + ", bottom: " + this.bottom + ", left: " + this.left + "}" ); }; /** * DOM element에 의해 차지되는 region을 반환한다. * @method getRegion * @param {HTMLElement} el The element * @return {Region} element가 차지하는 region * @static */ DU.util.LRegion.getRegion = function(el) { var p = DU.util.LDom.getXY(el); var t = p[1]; var r = p[0] + el.offsetWidth; var b = p[1] + el.offsetHeight; var l = p[0]; return new DU.util.LRegion(t, r, b, l); }; /** * point는 그리드 상에서의 싱글 포인트를 표현하는 특정한 영역이다. * @module util * @namespace DU.util * @class LPoint * @param {Int} x point의 X 위치 * @param {Int} y point의 Y 위치 * @constructor * @extends DU.util.LRegion */ DU.util.LPoint = function(x, y) { if (DU.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc. y = x[1]; // dont blow away x yet x = x[0]; } /** * right, left나 인덱스가 0인 point의 X 위치(Dom.getXY symmetry에 대한) * @property x * @type Int */ this.x = this.right = this.left = this[0] = x; /** * The Y position of the point, which is also the top, bottom and index one (for Dom.getXY symmetry) * top, bottom이나 인덱스가 1인 point의 Y 위치(Dom.getXY symmetry에 대한) * @property y * @type Int */ this.y = this.top = this.bottom = this[1] = y; }; DU.util.LPoint.prototype = new DU.util.LRegion(); DU.namespace('DU.data'); /** * LDataSet * * @module data * @requires DU, event * @title LDataSet Utility */ (function() { /** * LDataSet * @namespace DU.data * @class LDataSet * @extends DU.util.LEventProvider * @constructor LDataSet * @param config {Object} The intial LDataSet. */ DU.data.LDataSet = function(config) { var config = config || {}; config = DU.util.LDom.applyIfProperties(config, '$.base.dataset'); DU.applyObject(this, config); /** * @description 데이터 전체의 구조가 변경될 경우 수행하는 이벤트 sort, filter ...등 * @event dataChanged * @param {Object} target this객체 */ this.createEvent('dataChanged'); /** * @description 데이터가 추가될 경우 수행하는 이벤트 * @event add * @param {Object} target this객체 * @param {DU.data.LRecord} record record객체 * @param {Int} row row의 값 */ this.createEvent('add'); /** * @description 데이터가 수정될 경우 수행하는 이벤트 * @event update * @param {Object} target this객체 * @param {DU.data.LRecord} record record객체 * @param {Int} row row의 값 * @param {Int} col col의 값 * @param {String} rowId row의 record id값 * @param {String} colId col의 column field id값 * @param {Object} value 값 * @param {Object} originValue 원본값 */ this.createEvent('update'); /** * @description 데이터가 삭제될 경우 수행하는 이벤트 * @event remove * @param {Object} target this객체 * @param {DU.data.LRecord} record record객체 * @param {Int} row row의 값 */ this.createEvent('remove'); /** * @description load이 발생하기전 수행하는 이벤트 * @event beforeLoad */ this.createEvent('beforeLoad'); /** * @description load이 발생한 후 * @event load * @param {Object} target this객체 */ this.createEvent('load'); /** * @description load딩시 에러가 발생했을 경우 * @event loadException * @param {Object} target this객체 * @param {Object} throwObject Exception 객체 */ this.createEvent('loadException'); /** * @description Row가 변경되지전에 변경을 해도 되는지 체크하는 이벤트 * 일반적으로 유효성 체크로 사용 * @event canRowPosChange * @param {Object} target this객체 * @param {Int} row target 위치 * @param {Int} oldRow 현재 row 위치 */ this.createEvent('canRowPosChange'); /** * @description Row의 변경이 된 후 수행하는 이벤트 * @event rowPosChanged * @param {Object} target this객체 * @param {Int} row 현재 위치 * @param {Int} row 이전 위치 */ this.createEvent('rowPosChanged'); /** * @description commit시 발생하는 이벤트 * @event commit * @param {Object} target this객체 */ this.createEvent('commit'); /** * @description undo시 발생하는 이벤트 * @event undo * @param {Object} target this객체 * @param {Int} row 현재 위치 * @param {DU.data.LRecord} record record 객체 * @param {Int} beforeState 이전 Record 상태 (DU.data.LRecord.STATE_NORMAL | DU.data.LRecord.STATE_INSERT | DU.data.LRecord.STATE_UPDATE | DU.data.LRecord.STATE_DELETE) */ this.createEvent('undo'); /** * @description setRowSelectMark시 발생하는 이벤트 * @event rowSelectMark * @param {Object} target this객체 * @param {Int} row 현재 위치 * @param {Boolean} isSelect 선택 여부 * @param {DU.data.LRecord} record record 객체 */ this.createEvent('rowSelectMark'); /** * @description clearAllSelectMark시 발생하는 이벤트 * @event clearAllSelectMark * @param {Object} target this객체 */ this.createEvent('clearAllSelectMark'); /** * @description 데이터의 row가 invalid될 경우 호출되는 이벤트 * @event invalid * @param {Object} target this객체 * @param {Int} row row의 위치값 * @param {String} colId field의 아이디 * @param {String} message 출력될 메시지 * @param {Object} value 값 */ this.createEvent('invalid'); /** * @description 데이터의 row가 valid될 경우 호출되는 이벤트 * @event valid * @param {Object} target this객체 * @param {Int} row row의 위치값 * @param {String} colId field의 아이디 */ this.createEvent('valid'); DU.data.LDataSet.superclass.constructor.call(this); /** * @description Record정보를 가지는 Collection객체 * @property data * @private * @type {DU.util.LCollection} */ this.data = new DU.util.LCollection(); /** * @description Select된 LRecord id정보를 객체 * @property selectedData * @private * @type {DU.util.LCollection} */ this.selectedData = new DU.util.LCollection(); /** * @description 변경 Record정보를 가지는 Collection객체 * @property modified * @private * @type {DU.util.LCollection} */ this.modified = new DU.util.LCollection(); /** * @description 데이터셋의 초기값을 가지는 Collection객체 * @property snapshot * @private * @type {DU.util.LCollection} */ this.snapshot = new DU.util.LCollection(); for(var i = 0 ; i < this.fields.length; i++) { if(!(this.fields[i] instanceof DU.data.LField)) this.fields[i] = new DU.data.LField(this.fields[i]); } if (this.defaultFailureHandler === true) { var loadExceptionHandler = DU.getConfig().getFirst("$.base.dataset.loadExceptionHandler"); this.on('loadException', loadExceptionHandler); } } DU.extend(DU.data.LDataSet, DU.util.LEventProvider, { /** * @description 데이터 처리 방식 (기본 0:Array) * @property dataSetType * @private * @type {Int} */ dataSetType : DU.data.LDataSet.DATA_TYPE, /** * @description 현재 위치 * @property rowPosition * @private * @type {Int} */ rowPosition : -1, /** * @description dataSet의 id * @config id * @type {String} * @default null */ /** * @description dataSet의 id * @property id * @private * @type {String} */ id : null, /** * @description loadData 메소드 호출후 첫번째 row로 이동할지 여부를 설정한다. * @config focusFirstRow * @type {Boolean} * @default true */ /** * @description loadData 메소드 호출후 첫번째 row로 이동할지 여부를 설정한다. * @property focusFirstRow * @private * @type {Boolean} */ focusFirstRow : true, /** * @description Field 정보를 가지는 배열 * @config fields * @type {Array} * @default null */ /** * @description Field 정보를 가지는 배열 * @property fields * @protected * @type {Array} */ fields : null, /** * @description 최종 Option정보를 가지는 객체 * @property lastOptions * @protected * @type {Object} */ lastOptions : null, /** * @description Record의 index * @property recordIndex * @protected * @type {Int} */ recordIndex : 0, /** * @description server에서 return된 metaData의 total count, 전체 record수이며, paging시에는 전체 count 수이다. * @property totalCount * @protected * @type {Int} */ totalCount : null, /** * @description response timeout 시간 (async일때문 작동함.) * @config timeout * @type {Int} * @default null */ /** * @description response timeout 시간 (async일때문 작동함.) * @property timeout * @protected * @type {Int} */ timeout : null, /** * @description idx위치에 Record객체를 추가한다. * @method insert * @public * @param {int} idx 입력 위치값 * @param {DU.data.LRecord} record 입력하고자 하는 record 객체 * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return {Int} row 값 */ insert: function(idx, record, option) { if(idx < 0) return; option = option || {}; record.dataSet = this; record.fields = this.fields; record.loadIndex = this.recordIndex++; this.data.insert(idx, record); if (record.state != DU.data.LRecord.STATE_NORMAL) this.modified.add(record.id, record); //record.unOnAll(); record.onFieldChanged = DU.util.LFunction.createDelegate(this.onFieldChanged, this); //record.on('fieldChanged', this.onFieldChangedDelegate); if(option.ignoreEvent !== true) { this.fireEvent('add', { target: this, record: record, row: idx, currentRow: this.rowPosition }); } return idx; }, /** * @description Record객체를 추가한다. * @method add * @public * @param {DU.data.LRecord} record 입력하고자 하는 record 객체 * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return {Int} row 값 */ add: function(record, option) { if (record === undefined) return this.newRecord(); else return this.insert(this.data.length, record, option); }, /** * @description Record객체 배열을 추가한다. * @method addAll * @public * @param {DU.data.LRecord} records 입력하고자 하는 record 객체 배열 * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return void */ addAll: function(records, option) { for (var i = 0; i < records.length; i++) { this.add(records[i].clone(), option); } }, /** * @description Record객체를 삭제한다. * @method removeAt * @public * @param {int} index 지우고자 하는 위치값 * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return {DU.data.LRecord} 삭세된 Record객체 */ removeAt: function(index, option) { option = option || {}; var record = this.data.getAt(index); this.data.remove(record.id); this.selectedData.remove(record.id); if (record.state == DU.data.LRecord.STATE_INSERT) { this.modified.remove(record.id); } else { this.modified.add(record.id, record); record.setState(DU.data.LRecord.STATE_DELETE); } record.removeRow = index; if(option.ignoreEvent !== true) { this.fireEvent('remove', { target: this, record: record, row: index, currentRow: this.rowPosition }); this.validRow(index); } this.focusRow(true); return record; }, /** * @description 모든 Record객체를 지우고 removeAll 이벤트를 발생시킨다. * @method removeAll * @public * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return void */ removeAll: function(option) { option = option || {}; if (this.getCount() > 0) { while(0 < this.getCount()) this.removeAt(0, option); } if (option.ignoreEvent !== true) { this.fireEvent('dataChanged', { target: this }); } }, /** * @description fields 정보를 제외한 모든 정보를 초기화 한다. * @method clearData * @public * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return void */ clearData: function(option) { option = option || {}; var count = this.getCount(); this.data = new DU.util.LCollection(); this.modified = new DU.util.LCollection(); this.selectedData = new DU.util.LCollection(); this.snapshot = new DU.util.LCollection(); this.recordIndex = 0; this.totalCount = 0; if (option.ignoreEvent != true) { this.fireEvent('dataChanged', { target: this, oldCount: count }); } this.focusRow(); }, /** * @description Id에 해당되는 Record객체를 리턴한다. * @method get * @public * @param {String} id 얻고자 하는 Record객체의 아이디 * @return {DU.data.LRecord} 아이디에 해당되는 LRecord 객체 */ get: function(id) { return this.data.get(id); }, /** * @description 위치값에 해당되는 Record객체를 리턴한다. * @method getAt * @public * @param {int} idx 얻고자 하는 Record객체의 위치값 * @return {DU.data.LRecord} 아이디에 해당되는 LRecord 객체 */ getAt: function(idx) { var record = this.data.getAt(idx); return record; }, /** * @description Id에 해당되는 Record의 index를 리턴한다. * @method indexOfKey * @public * @param {String} id 얻고자 하는 Record객체의 아이디 * @return {Int} */ indexOfKey: function(id) { return this.data.indexOfKey(id); }, /** * @description Id에 해당되는 Record의 index를 리턴한다. * @method findRow * @public * @param {String} fieldName 검색할 field명 * @param {String} value 검색할 field의 값 * @param {Int} startIndex 검색 시작할 index * @return {Int} */ findRow:function(fieldName, value, startIndex) { var idx = -1; startIndex = startIndex || 0; this.data.each(function(id, record, i) { if(i < startIndex) return; if(record.get(fieldName) == value) { idx = i; return false; } }, this.data); return idx; }, /** * @description id정보에 해당되는 field 배열 index를 리턴 * @method fieldIndex * @private * @param {String} id field의 Id * @return {Int} */ fieldIndex: function(id) { this.fieldMap = this.fieldMap || {}; var idx = this.fieldMap[id]; if(!idx) { for (var i = 0; i < this.fields.length; i++) { if (this.fields[i].id == id) { this.fieldMap[id] = i; return i; } } idx = -1; } return idx; }, /** * @description id정보에 해당되는 {DU.data.LField} 객체 리턴 * @method getFieldById * @private * @param {String} id field의 Id * @return {DU.data.LField} */ getFieldById: function(id) { var idx = this.fieldIndex(id); if (idx > -1) { return this.fields[idx]; } else { return null; } }, /** * @description DataSet객체 정보를 모두 지운다. (이벤트 포함) * @method destroy * @public * @return void */ destroy: function() { this.data = null; this.modified.clear(); this.modified = null; this.unOnAll(); }, /** * @description URL을 통해 데이터정보를 읽어온다. * @method load * @public * @param {Object} options 환경정보 객체 *
* url {String} 서버 호출 url
* params {Object} 서버에 전달할 파라미터 객체
* method {String} get or post
* sync {boolean} sync 여부 (default : false)
* state {DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} load시 record의 기본 상태 *
* @return void */ load : function(options) { var config = options || {}; var currentDataSet = this; config = DU.applyIf(config, { method : 'POST', callback : { success : function(conn) { currentDataSet.doSuccess(currentDataSet, conn); }, failure : function(conn) { var e = new Error(conn.responseText); currentDataSet.fireEvent('loadException', {target:currentDataSet, throwObject:e, conn:conn}); } }, url : null, params: {}, sync: this.sync }); config.callback = DU.applyIf(config.callback, { timeout: ((this.timeout || 60) * 1000) }); this.lastOptions = config; this.lastOptions.state = this.lastOptions.state || DU.data.LRecord.STATE_NORMAL; var params = ''; if (typeof config.params == 'object') params = DU.util.LObject.serialize(config.params); else params = config.params; params.duDataSetId = this.id; this.fireEvent('beforeLoad', {target:this}); if(config.sync && config.sync == true) { DU.LConnect.syncRequest(config.method, config.url, config.callback, params); } else { DU.LConnect.asyncRequest(config.method, config.url, config.callback, params); } }, /** * @description URL을 통해 데이터정보를 읽어온다. * @method doSuccess * @private * @param {Object} dataSet LDataSet 객체 * @param {Object} conn 응답 객체 * @return void */ doSuccess : function(dataSet, conn) { try { var dataSetData = this.getReadData(conn); dataSet.loadData(dataSetData, dataSet.lastOptions); } catch(e) { dataSet.fireEvent('loadException', {target:dataSet, throwObject:e}); throw e; } }, /** * @description 현재 DataSet 기준으로 결과 데이터를 Object로 변환하여 리턴 * @method getReadData * @param {Object} conn 응답 객체 * @return {Object} */ getReadData : function(conn) { return { records: this.getReadDataMulti(this, conn) }; }, /** * @description 결과 데이터를 Array로 변환하여 리턴 * @method getReadDataMulti * @private * @param {Object} dataSet LDataSet 객체 * @param {Object} conn 응답 객체 * @return {Array] */ getReadDataMulti : function(dataSet, conn){ throw new DU.LException("구현 안됨."); }, /** * @description {DU.data.LRecord}객체의 자동 순번 id * @method getNewRecordId * @public * @return {String} */ getNewRecordId: function() { return 'r' + (++DU.data.LRecord.AUTO_ID); }, /** * @description 신규 Record객체를 생성한다. * @method newRecord * @public * @param {Int} idx [optional] 위치 * @param {Object} option [optional] option객체 *
* isInitData {boolean} 데이터를 초기화할지 여부를 설정
* moveRow {boolean} record 추가시 해당 위치로 이동할지 여부 *
* @return void */ newRecord: function(idx, option) { if(idx < 0) return; //신규 생성후 해당 행으로 이동 부분 option추가 - 채민철K option = DU.applyIf(option || {}, { isInitData:true, moveRow:true }); var id = this.getNewRecordId(); var state = DU.data.LRecord.STATE_INSERT; var arrayData = { id: id }; for (var i = 0; i < this.fields.length; i++) arrayData[this.fields[i].id] = this.fields[i].defaultValue || undefined; var newRecord = this.createRecord(arrayData, {id:id, state:option.state, isInitData:option.isInitData}); var row = DU.isUndefined(idx) ? this.getCount() : idx; if (option.moveRow) { var recordId = (this.getRow() > -1) ? this.getAt(this.getRow()).getId() : null; if (this.doCanRowPosChange(row) == false) return false; if(recordId != null) row = this.indexOfKey(recordId) < 0 ? this.getRow() + 1 : row; } this.insert(row, newRecord, option); if (option.moveRow) { this.doRowPosChange(row, true); } return row; }, /** * @description Record객체를 생성한다. * @method createRecord * @protected * @param {Object} data 데이터 객체 * @param {String} id [optional] Record의 아이디 * @param {Int} state [optional] Record 객체의 cud 상태 * @return void */ createRecord: function(arrayData, option) { option = DU.applyIf(option || {}, { id:null }); option.fields = this.fields; option.dataSet = this; return new DU.data.LRecord(arrayData, option); }, /** * @description record의 값이 변경되면 호출되는 이벤트 * @method onFieldChanged * @protected * @param {Object} e event객체 * @return void */ onFieldChanged : function(e) { var record = e.target; var row = this.indexOfKey(record.id); this.setState(row, record.state); this.fireEvent('update', { target: this, record: record, row: row, col: this.fieldIndex(e.colId), rowId: record.id, colId: e.colId, value: e.value, originValue: e.originValue }); }, /** * @description data에서 Record형 객체를 읽어온다. * @method readRecord * @private * @param {Object} data 데이터 객체 * @param {Object} options 환경정보 객체 *
* add {boolean} 신규 여부 *
* @return void */ readRecord: function(data, options) { var records = data.records; if (!options.add || options.add == false) { this.data = null; this.selectedData = new DU.util.LCollection(); this.data = new DU.util.LCollection(); this.modified = new DU.util.LCollection(); this.recordIndex = 0; this.rowPosition = -1; } for (var i = 0; i < records.length; i++) { this.add(records[i], {ignoreEvent: true}); } }, /** * @description data를 읽어온다. * @method loadData * @public * @param {Object} data 데이터 객체 * @param {Object} options 환경정보 객체 *
* state {DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} load시 record의 기본 상태 *
* @return void */ loadData : function(data, options) { this.isLoading = true; options = options || {} var dataRecords = data.records; var records = []; var currentId = this.data.length; var state = options.state || DU.data.LRecord.STATE_NORMAL; for(var i = 0 ; i < dataRecords.length ; i++) { var record = this.createRecord(dataRecords[i], {id:this.getNewRecordId(), state:state}); records.push(record); } var newData = [].concat(data); newData.records = records; this.readRecord(newData, options); this.snapshot = this.data.clone(); if(this.lastSortInfo) { if(this.lastSortInfo.fieldName) this.sortField(this.lastSortInfo.fieldName, this.lastSortInfo.direction); else this.sort(this.lastSortInfo.fn, this.lastSortInfo.direction); } this.isLoading = false; this.fireEvent('load', { target: this }); if(this.focusFirstRow !== false) this.focusRow(); }, /** * @description 데이터의 갯수를 리턴한다. * @method getCount * @public * @return {int} 결과값 */ getCount: function() { return this.data.length; }, /** * @description 변경된 데이터 정보를 리턴한다. * @method getModifiedRecords * @public * @return {Object} 변경된 Record객체 배열 */ getModifiedRecords: function() { return this.modified == null ? new DU.util.LCollection() : this.modified; }, /** * @description 데이터 정보를 문자열로 리턴한다. * @method serializeDataSet * @public * @return {String} 문자열 */ serializeDataSet: function() { var result = []; for (var i = 0; i < this.getCount(); i++) { var record = this.getAt(i); result.push(record.serialize()); } return result.join('&'); }, /** * @description 변경된 데이터 정보를 문자열로 리턴한다. * @method serializeModifiedDataSet * @public * @return {String} 변경된 문자열 */ serializeModifiedDataSet: function() { var modifiedRecords = this.getModifiedRecords(); var result = []; for (var i = 0; i < modifiedRecords.length; i++) { var record = modifiedRecords.getAt(i); result.push(record.serialize()); } return result.join('&'); }, /** * @description LDataSet 배열의 데이터 정보를 리턴한다. * @method serializeDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeDataSetList: function(dataSets) { var params = []; for (var i = 0; i < dataSets.length; i++) { var dataSetEl = DU.util.LDom.get(dataSets[i]); params.push(dataSetEl.serializeDataSet()); } return params.join('&'); }, /** * @description LDataSet 배열의 변경된 데이터 정보를 리턴한다. * @method serializeModifiedDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeModifiedDataSetList: function(dataSets) { var params = []; for (var i = 0; i < dataSets.length; i++) { var dataSetEl = DU.util.LDom.get(dataSets[i]); params.push(dataSetEl.serializeModifiedDataSet()); } return params.join('&'); }, /** * @description 변경된 데이터 정보를 확정한다. * @method commit * @public * @return void */ commit: function() { this.data.each(function(id, record) { record.commit(); }, this.data); this.modified.clear(); this.recordIndex = this.data.length; this.snapshot = this.data.clone(); this.fireEvent('commit', { target: this }); }, /** * @description idx에 해당되는 Record를 복원한다. * @method undo * @public * @param {int} idx 복원하고자 하는 위치값 * @return void */ undo: function(idx) { var record = this.data.getAt(idx); var state = record.state; if(state == DU.data.LRecord.STATE_NORMAL) return; record.undo(); this.modified.remove(record.id); if (state == DU.data.LRecord.STATE_INSERT) { // 신규건일때는 record에서 수정건에 대해서 처리 못함. this.data.remove(record.id); } else this.setRowSelectMark(idx, false); this.fireEvent('undo', { target: this, row: idx, record:record, beforeState:state }); this.validRow(idx); this.focusRow(true); }, /** * @description 전체 데이터를 초기 로딩데이터로 복원한다. * @method undoAll * @public * @return void */ undoAll: function() { this.data = this.snapshot.clone(); this.modified = new DU.util.LCollection(); this.recordIndex = this.data.length; for(var i = 0 ; i < this.data.length ; i++) { var record = this.data.getAt(i); if(record.isModified() == true) { record.undo(); record.state = this.lastOptions ? this.lastOptions.state : DU.data.LRecord.STATE_NORMAL; } } this.fireEvent('dataChanged', {target:this}); this.focusRow(); }, /** * @description 현재 위치가 선택이 안되어 있으면 현재 위치를 셋팅하는 메소드 * @method focusRow * @param {boolean} ignoreCRPCE ignoreCanRowPosChangeEvent canRowPosChange 이벤트를 무시할지 여부 * @private * @return void */ focusRow : function(ignoreCRPCE) { var row = this.getRow(); var count = this.getCount(); var config = {isForce:true, ignoreCanRowPosChangeEvent:ignoreCRPCE}; if(count > 0) { if(row < 0) this.setRow(0, config); else if(row > count - 1) this.setRow(count - 1, {ignoreCanRowPosChangeEvent:ignoreCRPCE}); // isForce가 없어야 함. else this.setRow(row, config); } else { this.setRow(-1, config); } }, /** * @description 현재 위치를 리턴 * @method getRow * @public * @return {int} 현재 위치를 리턴 */ getRow: function() { return this.rowPosition; }, /** * @description 현재 위치를 변경 * @method setRow * @public * @param {int} row 변경하고자 하는 위치값 * @param {Object} config config 객체 * (isForce : [optional] 같은 위치가 선택되어도 다시 이벤트를 호출하게 하는 속성, * ignoreCanRowPosChangeEvent : [optional] canRowPosChange 이벤트를 무시할지 여부) * @return void */ setRow: function(row, oConfig) { var config = oConfig || {}; config = DU.applyIf(config, { isForce: false, ignoreCanRowPosChangeEvent: false }); if (this.getCount() < row || -1 > row || (this.getRow() == row && config.isForce !== true)) return; if (config.ignoreCanRowPosChangeEvent == false && this.doCanRowPosChange(row) == false) return; this.doRowPosChange(row, config.isForce); }, /** * @description row 위치를 변경 할 수 있는지 여부 * @method doCanRowPosChange * @protected * @param {Int} row 변경할 row 값 * @return {boolean} */ doCanRowPosChange: function(row) { return this.fireEvent('canRowPosChange', { target: this, row: row, oldRow: this.rowPosition }); }, /** * @description row 위치를 변경 * @method doRowPosChange * @protected * @param {Int} row 변경할 row 값 * @param {Boolean} isForce [optional] 같은 위치가 선택되어도 다시 이벤트를 호출하게 하는 속성 * @return {boolean} */ doRowPosChange: function(row, isForce) { isForce = isForce === true ? true : false; if (this.getCount() < row || -1 > row || (this.getRow() == row && isForce !== true)) return; var oldRowPosition = this.rowPosition; this.rowPosition = row; this.fireEvent('rowPosChanged', { target: this, row: this.rowPosition, oldRow: oldRowPosition }); }, /** * @description DataSet의 가장 첫번째로 이동 * @method first * @public * @return void */ first: function(config) { if (this.rowPosition == 0) { return; } this.setRow(0, config); }, /** * @description DataSet의 현재위치 이전으로 이동, 최초면 return false * @method previous * @public * @return {boolean} */ previous: function(config) { if (this.rowPosition == 0) { return false; } this.setRow(this.rowPosition - 1, config); return true; }, /** * @description DataSet의 현재위치 다음으로 이동, 마지막이면 return false * @method next * @public * @return {boolean} */ next: function(config) { if (this.rowPosition + 1 > this.getCount() - 1) { return false; } this.setRow(this.rowPosition + 1, config); return true; }, /** * @description DataSet의 가장 마지막으로 이동 * @method last * @public * @return void */ last: function(config) { if (this.rowPosition == this.getCount() - 1) { return; } this.setRow(this.getCount() - 1, config); }, /** * @description 현재 Row가 Insert상태 여부 확인 * @public * @method isInsertRow * @param {int} row 상태 확인 위치 * @return {Boolean} */ isInsertRow: function(row) { return this.getAt(row).state == DU.data.LRecord.STATE_INSERT; }, /** * @description 현재 Row가 Update상태 여부 확인 * @public * @method isUpdateRow * @param {int} row 상태 확인 위치 * @return {Boolean} */ isUpdateRow: function(row) { return this.getAt(row).state == DU.data.LRecord.STATE_UPDATE; }, /** * @description 현재 Row가 변경상태 여부 확인 * @public * @method isModifiedRow * @param {int} row 상태 확인 위치 * @return {Boolean} */ isModifiedRow: function(row) { return this.isInsertRow(row) || this.isUpdateRow(row); }, /** * @description DataSet에 변경정보가 존재하는지 확인 * @method isUpdate * @public * @return {Boolean} */ isUpdate: function() { return this.modified.length > 0 ? true : false; }, /** * @description DataSet의 row 위치를 마크한다. * @method setRowSelectMark * @public * @param {int} row 위치 * @param {Boolean} isSelect 마크 여부 * @return {void} */ setRowSelectMark : function(row, isSelect) { if(row < 0 || row > this.getCount()) return false; var record = this.getAt(row); if(isSelect) { if(this.selectedData.get(record.id) == null) this.selectedData.add(record.id, record); } else { this.selectedData.remove(record.id); } this.fireEvent('rowSelectMark', {target:this, row:row, isSelect:isSelect, record:record}); }, /** * @description 지정된 row외의 DataSet에 선택된 row를 선택 false로 설정한다. * @method setRowDeselectMarkExceptOne * @public * @param row 지정된 row index외의 select된 행을 false로 설정한다. * @return {void} */ setRowDeselectMarkExceptOne : function(row) { //선택한 것 외에는 선택 해제하기 if (this.getMarkedCount() > 1) { for (var i = 0; i < this.getCount(); i++) { if (this.isRowSelectMark(i) && i != row) { this.setRowSelectMark(i, false); } } } }, /** * @description DataSet의 row 위치가 마크되어 있는지 리턴한다. * @method isRowSelectMark * @public * @param {int} row 위치 * @return {Boolean} 마크 여부 */ isRowSelectMark : function(row) { var record = this.getAt(row); if(record && this.selectedData.get(record.id)) return true; else return false; }, /** * @description DataSet의 마크된 건수를 리턴한다. * @method getMarkedCount * @public * @return {Int} 마크된 건수 리턴 */ getMarkedCount : function() { return this.selectedData.length; }, /** * @description DataSet을 sInx부터 eInx까지 마크한다. * @method setRangeSelect * @public * @param {int} sInx 시작 위치 * @param {int} eInx 종료 위치 * @param {Boolean} isSelect 마크 여부 * @return {void} */ setRangeSelect : function(sInx, eInx, isSelect) { for(var i = sInx ; i < this.getCount() && i <= eInx ; i++) { this.setRowSelectMark(i, isSelect); } }, /** * @description DataSet을 전체의 마크를 설정한다. * @method setAllSelectMark * @public * @param {Boolean} isSelect 마크 여부 * @return {void} */ setAllSelectMark : function(isSelect) { this.setRangeSelect(0, this.getCount(), isSelect); }, /** * @description DataSet에 선택된 모든 마크 정보를 지운다. * @method clearAllSelectMark * @public * @return {void} */ clearAllSelectMark : function() { this.selectedData.clear(); this.fireEvent('clearAllSelectMark', {target:this}); }, /** * @description DataSet에 마크된 row를 모두 삭제한다. * @method removeSelectMark * @public * @return {void} */ removeSelectMark : function() { for(var i = 0 ; i < this.selectedData.length ; ) { var key = this.selectedData.getKey(i); var row = this.indexOfKey(key); this.removeAt(row); } }, /** * @description DataSet에 filter를 적용한다. * function에서 true인 데이터만 남는다. * * dataSet.filter( * function(id, record) { * if(record.get('col4') == 'R2') * return true; * } * ); * * @method filter * @public * @param {Function} fn 정보를 비교할 Function * @param {Object} scope scope정보 옵션 * @return {void} */ filter : function(fn, scope, focusRow) { this.rowPosition = -1; focusRow = focusRow === true ? true : false; this.data = this.query(fn, scope || this); this.fireEvent('dataChanged', {target:this}); if(focusRow && this.getCount() > 0) this.setRow(0); }, /** * @description DataSet에 적용된 filter를 지운다. * @method clearFilter * @public * @return {void} */ clearFilter : function(focusRow) { this.data = this.snapshot.clone(); focusRow = DU.isUndefined(focusRow) ? true : focusRow; this.fireEvent('dataChanged', {target:this}); if(focusRow && this.getCount() > 0) this.setRow(0); }, /** * @description DataSet에 filter가 적용되었는지 여부 * @method isFilter * @public * @return {void} */ isFilter : function() { return this.snapshot.length != this.data.length; }, /** * @description DataSet에 query에 해당되는 데이터를 리턴한다. * @method query * @public * @param {Function} fn 정보를 비교할 Function * @param {Object} scope scope정보 옵션 * @return {void} */ query : function(fn, scope) { this.data = this.snapshot.clone(); return this.data.query(fn, scope || this); }, /** * @description 서버에서 리턴한 DataSet의 총 갯수를 리턴한다. * @method getTotalCount * @public * @return {Int} 총갯수 */ getTotalCount : function() { return this.totalCount || this.snapshot.length; }, /** * @description DataSet의 시작위치 부터 끝위치에 해당하는 Record배열을 리턴한다. * @method getRecords * @public * @param {Int} startIndex 시작 위치 * @param {Int} endIndex 끝 위치 * @return {Array} */ getRecords : function(startIndex, endIndex) { startIndex = startIndex || 0; endIndex = (DU.isUndefined(endIndex) || (endIndex > this.getCount() - 1)) ? this.getCount() - 1 : endIndex; var recordList = new Array(); for(var i = 0 ; i < this.getCount() ; i++) recordList.push(this.getAt(i)); return recordList; }, /** * @description DataSet을 정렬한다. * @method sort * @public * @param {Function} fn 정보를 비교할 Function * @param {String} desc 정렬 방식 [asc|desc] * @return {Array} */ sort : function(fn, desc) { this.data.sort(fn, desc); this.lastSortInfo = { fn:fn, direction:desc } this.fireEvent('dataChanged', {target:this}); this.focusRow(); }, /** * @description field에 해당하는 DataSet을 정렬한다. * @method sortField * @public * @param {String} fieldName field 명 * @param {String} desc 정렬 방식 [asc|desc] * @return {Array} */ sortField : function(fieldName, desc) { var field = this.getFieldById(fieldName); if(field == null) return false; this.sortDirection = this.sortDirection || {}; if(!desc) { var fieldDesc = this.sortDirection[fieldName]; desc = !fieldDesc ? "asc" : (fieldDesc == "asc") ? "desc" : "asc";; } var sortType = field.sortType; this.sort(function(r1, r2, c){ var v1 = sortType(r1.get(fieldName)); var v2 = sortType(r2.get(fieldName)); return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); }, desc); this.lastSortInfo.fieldName = fieldName; this.sortDirection[fieldName] = desc; return desc; }, /** * data의 reverse 메소드 수행 * @method reverseRecords * @return {void} */ reverseRecords : function() { this.data.reverse(); this.fireEvent('dataChanged', {target:this}); }, /** * record의 row 위치를 리턴한다. * @method indexOfRecord * @param oRecord {DU.data.LRecord} LRecord 객체 * @return {Int} Record의 row 위치 */ indexOfRecord : function(oRecord) { if(oRecord) { for(var i=(this.getCount()-1); i > -1; i--) { if(oRecord.getId() === this.getAt(i).getId()) { return i; } } } return null; }, /** * DataSet의 row 위치 데이터의 상태를 변경한다. * @method setState * @param row {Int} row 위치 * @param state {DU.data.LRecord.STATE_NORMAL|DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} state Record에 해당되는 상태값 * @return {void} */ setState : function(row, state) { var record = this.getAt(row); if(record.state != state) record.state = state; var row = this.indexOfKey(record.id); if (record.state == DU.data.LRecord.STATE_NORMAL && record.getOriginData().length == 0) this.modified.remove(record.id); else { if (this.modified.indexOfKey(record.id) < 0) { this.modified.add(record.id, record); } else { this.modified.set(record.id, record); } } }, /** * @description DataSet를 newId객체로 복사하여 리턴한다. * @method clone * @public * @param {String} newId 신규 DataSet객체 Id * @return {DU.data.LDataSet} */ clone : function(id) { var newDataSet = new this.constructor({ id:id, fields:this.fields }); for(var i = 0 ; i < this.data.length ; i++) { var record = this.data.getAt(i); var newRecord = record.clone(); newDataSet.add(newRecord); newDataSet.snapshot.add(newRecord); } return newDataSet; }, /** * @description DataSet의 row와 col의 값을 리턴한다. * @method getValue * @public * @param {Int} row row의 위치 * @param {Int} col col의 위치 * @return {Object} */ getValue : function(row, col) { if(col < 0 || col > this.fields.length - 1) return null; var colId = this.fields[col].id; return this.getNameValue(row, colId); }, /** * @description DataSet의 row와 col의 값을 리턴한다. * @method getNameValue * @public * @param {Int} row row의 위치 * @param {String} colId col의 Id * @return {Object} */ getNameValue : function(row, colId) { if(row < 0 || row > this.getCount() - 1) return null; var record = this.getAt(row); return record.get(colId); }, /** * @description DataSet의 row와 col의 값을 셋팅한다. * @method setValue * @public * @param {Int} row row의 위치 * @param {Int} col col의 위치 * @param {Object} value value 값 * @return {Object} */ setValue : function(row, col, value) { if(col < 0 || col > this.fields.length - 1) throw new DU.LException('Invalid col'); var colId = this.fields[col].id; this.setNameValue(row, colId, value); }, /** * @description DataSet의 row와 col의 값을 셋팅한다. * @method setNameValue * @public * @param {Int} row row의 위치 * @param {String} colId col의 Id * @param {Object} value value 값 * @return {Object} */ setNameValue : function(row, colId, value) { if(row < 0 || row > this.getCount() - 1) throw new DU.LException('Invalid row'); var record = this.getAt(row); record.set(colId, value); }, /** * @description DataSet의 colId의 해당되는 컬럼값의 합계를 리턴한다. * @method sum * @public * @param {String} colId col의 Id * @param {Int} startRow [optional] row의 시작위치 * @param {Int} endRow [optional] row의 종료위치 * @return {Int} */ sum: function(colId, startRow, endRow) { startRow = startRow || 0; endRow = endRow || this.getCount(); var value = 0; for (var row = startRow; row <= endRow; row++) value += dataSet.getNameValue(row, colId); return value; }, /** * @description DataSet의 row가 invalid될경우 호출되는 메소드 * @method invalid * @public * @param {Int} row row의 위치 * @param {String} colId field의 아이디 * @param {String} message 출력할 메시지 * @param {Object} value 원본 값 * @param {boolean} isMulti 멀티건 여부 * @return {void} */ invalid: function(row, colId, message, value, isMulti) { this.fireEvent('invalid', {target:this, row:row, colId:colId, message:message, value:value, isMulti:isMulti}); }, /** * @description DataSet의 row가 valid될경우 호출되는 메소드 * @method valid * @protected * @param {Int} row row의 위치 * @param {String} colId field의 아이디 * @param {boolean} isMulti 멀티건 여부 * @return {void} */ valid: function(row, colId, isMulti) { this.fireEvent('valid', {target:this, row:row, colId:colId, isMulti:isMulti}); }, /** * @description DataSet의 row가 valid될경우 호출되는 메소드 * @method valid * @public * @param {Int} row row의 위치 * @param {String} colId field의 아이디 * @param {boolean} isMulti 멀티건 여부 * @return {void} */ validRow: function(row) { for(var i = 0 ; i < this.fields.length; i++) this.valid(row, this.fields[i].id, false); }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.data.LDataSet ' + this.id; } }); })(); // array type DU.data.LDataSet.DATA_TYPE = 0; /** * 데이터의 컬럼정보를 가지는 객체 * * @namespace DU.data * @module data * @requires DU * @requires event */ /** * LField utility. * @namespace DU.data * @class LField * @constructor LField */ DU.namespace('DU.data'); DU.data.LField = function(config) { if(typeof config == "string"){ config = {id: config, name:config}; } this.id = config.id; DU.applyObject(this, config); }; DU.data.LField.prototype = { /** * @description field의 id * @config id * @type {String} * @default null */ /** * @description field의 id * @property id * @private * @type {String} */ id: null, /** * @description 객체의 문자열 * @property otype * @private * @type {String} */ otype:'DU.data.LField', /** * @description field객체의 종류(number, string, date) * @config type * @type {String} * @default 'string' */ /** * @description field객체의 종류(number, string, date) * @property type * @private * @type {String} */ type:'string', /** * @description field의 기본 출력값 * @config defaultValue * @type {String|Int|Date} * @default undefined */ /** * @description field의 기본 출력값 * @property defaultValue * @public * @type {String|Int|Date} */ defaultValue: undefined, /** * @description field의 정렬시 데이터 처리 Function * @property sortType * @public * @type {Function} */ sortType : function(s) {return s;} } /** * 데이터의 컬럼정보를 가지는 객체 * * @namespace DU.data * @module data * @requires DU * @requires event */ /** * Field utility. * @namespace DU.data * @class LRecord * @constructor LRecord * @param {Object} data 초기 데이터. * @param {Object} config The intial LDataSet. */ DU.namespace('DU.data'); DU.data.LRecord = function(data, config) { /** * @description Record의 데이터 객체 * @config data * @type {Array} * @default null */ /** * @description Record의 데이터 객체 * @property data * @private * @type {Array} */ this.data = data; /** * @description Record의 데이터 객체 * @config attrData * @type {Array} * @default null */ /** * @description Record의 데이터 객체 * @property attrData * @private * @type {Array} */ this.attrData = {}; /** * @description Field 정보를 가지는 배열 * @property fields * @public * @type {Array} */ this.fields = config.fields; /** * @description LDataSet 객체 * @property dataSet * @private * @type {DU.data.LDataSet} */ this.dataSet = config.dataSet; /** * @description Record의 변경 상태 정보 * @config state * @type {DU.data.LRecord.STATE_NORMAL|DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} * @default DU.data.LRecord.STATE_NORMAL */ /** * @description Record의 변경 상태 정보 * @property state * @public * @type {DU.data.LRecord.STATE_NORMAL|DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} */ this.state = DU.isUndefined(config.state) == false ? config.state : DU.data.LRecord.STATE_INSERT; /** * @description Config 생성시 마지막 상태 정보 * @property lastConfig * @public * @type {Object} */ this.lastConfig = config; /** * @description LRecord id * @config id * @type {String} * @default new record id */ /** * @description LRecord id * @property id * @public * @type {String} */ this.id = (config.id || config.id == 0) ? config.id : this.dataSet.getNewRecordId(); /** * @description 데이터를 초기화할지 여부를 설정 * @property isInitData * @private * @type {Boolean} */ this.isInitData = DU.isUndefined(config.isInitData) == false ? config.isInitData : false; this.initData(); this.onFieldChanged = null; if(this.state != DU.data.LRecord.STATE_NORMAL) { for(var i = 0 ; i < this.fields.length; i++) this.getOriginData().add(this.fields[i].id, this.data[this.fields[i].id]); } }; DU.data.LRecord.prototype = { /** * @description 객체의 문자열 * @property otype * @private * @type {String} */ otype : 'DU.data.LRecord', /** * @description 변경 Field정보를 가지는 Collection객체 * @property modified * @private * @type {DU.util.LCollection} */ modified: null, /** * @description 변경전 Field정보를 가지는 Collection객체 * @property originData * @private * @type {DU.util.LCollection} */ originData: null, /** * @description originData Collection객체 * @property getOriginData * @private * @type {DU.util.LCollection} */ getOriginData : function() { if(!this.originData) this.originData = new DU.util.LCollection(); return this.originData; }, /** * @description 데이터를 초기화하는 메소드 * @method initData * @public * @return {void} */ initData : function() { if(this.isInitData) { for(var i = 0 ; i < this.fields.length ; i++) { var value = this.data[this.fields[i].id]; value = DU.isUndefined(value) ? this.fields[i].defaultValue : value; this.data[this.fields[i].id] = value; } } }, /** * @description Record의 id를 리턴하는 메소드 * @method getId * @public * @return {String} */ getId : function() { return this.id; }, /** * @description key에 해당되는 데이터를 리턴하는 메소드 * @method get * @public * @param {String} key Field id * @return {Object} */ get : function(key) { return this.data[key]; }, /** * @description key에 해당되는 value를 저장하는 메소드 * @method set * @public * @param {String} key Field id * @param {Object} value Field에 대항되는 값 * @param {Object} option [optional] 환경정보 객체 *
* ignoreEvent {boolean} 이벤트 무시 *
* @return void */ set : function(key, value, option) { option = option || {}; var field = this.findField(key); if(field == null) return; var originData = this.getOriginData(); if(DU.isEmpty(value) == false) { if((field.type == 'number' && typeof parseFloat(value) != 'number') || (field.type == 'number' && isNaN(value)) || (field.type == 'date' && typeof value != 'object')) throw new DU.LException(key + ': Invalid type [' + field.type + ':' + value + ']'); value = (field.type == 'string' && typeof value != 'string') ? value + '' : value; } if(String(originData.get(key)) == String(value)) { originData.remove(key); this.updateState(); this.data[key] = value; } else { if (String(this.data[key]) == String(value)) { this.updateState(); return; } if (originData.indexOfKey(key) < 0) { originData.add(key, this.data[key]); } if(this._unDo !== true) { this.data[key] = value; if(this.state === DU.data.LRecord.STATE_NORMAL) this.state = DU.data.LRecord.STATE_UPDATE; } } if(option.ignoreEvent !== true) { var isEvent = false; if(this._unDo === true) { if(this._unDoKey[key] !== true) { this._unDoKey[key] = isEvent = true; } } else isEvent = true; if(isEvent === true) { this.onFieldChanged.call(this, { target: this, colId: key, value: value, originValue: originData.get(key) || value }); } } }, /** * @description ui용 attribute key에 해당되는 데이터를 리턴하는 메소드 * @method getAttribute * @public * @param {String} key Field id * @return {Object} */ getAttribute: function(key) { return this.attrData[key]; }, /** * @description ui용 attribute key에 해당되는 value를 저장하는 메소드 * @method setAttribute * @public * @param {String} key Field id * @param {Object} value Field에 대항되는 값 * @return void */ setAttribute: function(key, value) { this.attrData[key] = value; }, /** * @description state 정보를 갱신하는 메소드 * @method updateState * @private * @return void */ updateState : function() { if (this.state != DU.data.LRecord.STATE_INSERT) { if(this.lastConfig.state != DU.data.LRecord.STATE_NORMAL) this.state = this.lastConfig.state; else if(this.getOriginData().length > 0 && this.state == DU.data.LRecord.STATE_NORMAL) { this.state = DU.data.LRecord.STATE_UPDATE; } else if(this.getOriginData().length == 0){ this.state = DU.data.LRecord.STATE_NORMAL; } } }, /** * @description 현재 Record를 undo하는 메소드 * @method undo * @public * @param {Boolean} ignoreEvent [optional] 이벤트 무시 여부 * @return void */ undo : function(ignoreEvent) { var originData = this.getOriginData(); this._unDo = true; this._unDoKey = {}; if(this.state != DU.data.LRecord.STATE_INSERT) { this._unDo = true; var max = originData.length; while(max > 0) { var key = originData.getKey(0); var value = originData.getAt(0); this.set(key, value, { ignoreEvent: ignoreEvent }); max = originData.length; } this._unDo = false; } else { // insert일 경우 undo는 update 이벤트는 실행되지 않고 row 위치가 바뀌면서 rowPosChanged 이벤트로 처리 for(var i = 0 ; i < originData.length ; i++) { var key = originData.getKey(0); var value = originData.getAt(0); this.data[key] = value; } originData.clear(); } delete this._unDoKey; delete this._unDo; this.state = this.lastConfig.state; }, /** * @description 현재 Record의 상태를 변경한다. * @method setState * @public * @param {DU.data.LRecord.STATE_NORMAL|DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} state Record에 해당되는 상태값 * @return void */ setState : function(state) { this.state = state; }, /** * @description 현재 상태를 commit한다. * @method commit * @public * @return void */ commit : function() { this.state = DU.data.LRecord.STATE_NORMAL; this.getOriginData().clear(); }, /** * @description 변경 컬럼의 데이터만 리턴한다. * @method getModifiedData * @public * @return {DU.util.LCollection} */ getModifiedData : function() { var originData = this.getOriginData(); var newData = {}; for(var i = 0 ; i < originData.length ; i++) { var key = originData.getKey(i); newData[key] = this.get(key); } return newData; }, /** * @description 현재 변경상태 정보를 QueryString형 문자열로 리턴한다. * @method serialize * @private * @return {String} */ serialize : function(isOrigin) { isOrigin = isOrigin || false; var prefix = isOrigin ? '' : 'du_' + this.dataSet.id.toLowerCase() + '_'; var result = 'du_' + this.dataSet.id.toLowerCase() + '_Id=' + this.id + '_' + this.state + '&'; for(var i = 0 ; i < this.fields.length ; i++) { var value = this.get(this.fields[i].id); if(value == null) value = ''; result += prefix + this.fields[i].id + '=' + value + '&'; } result = DU.util.LString.lastCut(result, 1); return result; }, /** * @description Record의 값을 Object 객체로 리턴 * @method getValues * @public * @return {Object} */ getValues : function() { var o = {}; DU.util.LArray.each(this.fields, function(c) { o[c.id] = this.data[c.id]; }, this); return o; }, /** * @description id에 해당되는 fields의 객체를 찾는다. * @method findField * @private * @param {String} id 검색할 Field의 id * @return {Object} 검색된 Field객체 */ findField : function(id) { return this.dataSet.getFieldById(id); }, /** * @description Record에 Object의 객체 정보의 값을 반영한다. * @method setValues * @public * @param {Object} o Record에 반영할 Object 객체 * @return {Object} */ setValues : function(o) { for(id in o) { if(this.findField(id) != null) this.set(id, o[id]); } }, /** * @description Record에 id에 해당되는 Field가 변경 데이터인지 여부를 리턴한다. * @method isModifiedField * @public * @param {Object} id 검색할 Field의 id * @return {Boolean} 변경 여부 */ isModifiedField : function(id) { return (this.getOriginData().indexOfKey(id) < 0) ? false : true; }, /** * @description Record에 id에 해당되는 Field가 변경 데이터인지 여부를 리턴한다. * @method isModifiedField * @public * @param {Object} id 검색할 Field의 id * @return {Boolean} 변경 여부 */ isModified : function(id) { return ((this.getOriginData().length > 0) ? true : false) || (this.dataSet.lastOptions ? this.dataSet.lastOptions.state != this.state : this.state != 0); }, /** * @description Record를 newId객체로 복사하여 리턴한다. * @method clone * @public * @param {String} newId (optional) 신규 Record객체 Id * @return {DU.data.LRecord} */ clone : function(newId) { newId = newId || this.dataSet.getNewRecordId(); var newData = {}; DU.applyObject(newData, this.data); var newAttrData = {}; DU.applyObject(newAttrData, this.attrData); var record = new DU.data.LRecord(newData, { id: newId, fields: this.fields, dataSet: this.dataSet }); record.attrData = newAttrData; return record; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.data.LRecord ' + this.id; } } DU.data.LRecord.AUTO_ID = 1000; DU.data.LRecord.STATE_NORMAL = 0; DU.data.LRecord.STATE_INSERT = 1; DU.data.LRecord.STATE_UPDATE = 2; DU.data.LRecord.STATE_DELETE = 3; /** * Json 데이터의 컬럼정보를 가지는 객체 * * @namespace DU.data * @module data * @requires DU * @requires event */ /** * LJsonDataSet * @namespace DU.data * @class LJsonDataSet * @extends DU.data.LDataSet * @constructor LJsonDataSet * @param config {Object} The intial LJsonDataSet. * { id:'dataSet', * fields:[ * {id:'col1'} * ] * } */ DU.data.LJsonDataSet = function(config) { DU.data.LJsonDataSet.superclass.constructor.call(this, config); /** * @description 데이터 처리 방식 (1:Json); * @property dataSetType * @private * @type {Int} */ this.dataSetType = DU.data.LJsonDataSet.DATA_TYPE; }; DU.extend(DU.data.LJsonDataSet, DU.data.LDataSet, { /** * @description 결과 데이터를 Array로 변환하여 리턴 * @method getReadDataMulti * @private * @param {Object} dataSet LDataSet 객체 * @param {Object} conn 응답 객체 * @return void */ getReadDataMulti : function(dataSet, conn){ var data = null; try{ data = eval('(' + conn.responseText + ')'); } catch(e) { throw new Error(DU.getMessageManager().get('$.base.msg110')); } var dataIndex = dataSet.dataIndex; if(DU.isUndefined(dataIndex)) { for(var i = 0 ; i < data.length ; i++) { if(data[i].metaData && data[i].metaData.dataSetId == dataSet.id) { dataIndex = i; break; } } dataIndex = DU.isUndefined(dataIndex) ? 0 : dataIndex; } var rowData = []; var rootData = data[dataIndex].records; //총갯수 저장 this.totalCount = (data[dataIndex].metaData && data[dataIndex].metaData.totalCount) ? data[dataIndex].metaData.totalCount : rootData.length; for (var i = 0; i < rootData.length; i++) { var node = rootData[i]; var colData = {}; colData.node = node; for(var col = 0 ; col < dataSet.fields.length ; col++) { var field = dataSet.fields[col]; var value = node[field.id]; if(this.readFieldFormater) { var formater = this.readFieldFormater[field.type]; value = formater ? formater(value) : value; } colData[field.id] = value; } rowData.push(colData); } return rowData; }, /** * @description 데이터 정보를 문자열로 리턴한다. * @method serializeDataSet * @public * @return {String} 문자열 */ serializeDataSet: function() { var fields = this.fields; var result = {}; result['metaData'] = { dataSetId:this.id }; var records = []; for(var i = 0 ; i < this.getCount() ; i++) { var record = this.getAt(i); var r = { duistate : record.state }; for(var j = 0 ; j < fields.length ; j++) { var field = fields[j]; var value = record.get(field.id); if (this.writeFieldFormater) { var formater = this.writeFieldFormater[field.type]; value = formater ? formater(value) : value; } if(value == null) value = ''; r[fields[j].id] = value; } records.push(r); } result['records'] = records; return DU.util.LJson.encode(result); }, /** * @description 변경된 데이터 정보를 문자열로 리턴한다. * @method serializeModifiedDataSet * @public * @return {String} 변경된 문자열 */ serializeModifiedDataSet: function() { var fields = this.fields; var modifiedRecords = this.getModifiedRecords(); var result = {}; result['metaData'] = { dataSetId:this.id }; var records = []; for(var i = 0 ; i < modifiedRecords.length ; i++) { var record = modifiedRecords.getAt(i); var r = { duistate : record.state }; for(var j = 0 ; j < fields.length ; j++) { var field = fields[j]; var value = record.get(field.id); var formater = this.writeFieldFormater[field.type]; if(formater) value = formater(value); if(value == null) value = ''; r[fields[j].id] = value; } records.push(r); } result['records'] = records; return DU.util.LJson.encode(result); }, /** * @description LDataSet 배열의 데이터 정보를 리턴한다. * @method serializeDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeDataSetList: function(dataSets) { var fields = this.fields; var result = {}; result['metaData'] = { dataSetId:this.id }; var records = []; for(var i = 0 ; i < this.getCount() ; i++) { var record = this.getAt(i); var r = { duistate : record.state }; for(var j = 0 ; j < fields.length ; j++) { var field = fields[j]; var value = record.get(field.id); var formater = this.writeFieldFormater[field.type]; if(formater) value = formater(value); if(value == null) value = ''; r[fields[j].id] = value; } records.push(r); } result['records'] = records; return DU.util.LJson.encode(result); }, /** * @description LDataSet 배열의 변경된 데이터 정보를 리턴한다. * @method serializeModifiedDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeModifiedDataSetList : function(dataSets) { var buf = []; buf.push('['); for(var i = 0 ; i < dataSets.length ; i++) { var dataSetEl = DU.util.LDom.get(dataSets[i]); buf.push(dataSetEl.serializeModifiedDataSet(), ','); } if(buf[buf.length - 1] == ',') delete buf[buf.length - 1]; buf.push(']'); var params = buf.join(''); return params; } }); // json type DU.data.LJsonDataSet.DATA_TYPE = 1; /** * Xml 데이터의 컬럼정보를 가지는 객체 * * @namespace DU.data * @module data * @requires DU * @requires event */ /** * LXmlDataSet * @namespace DU.data * @class LXmlDataSet * @extends DU.data.LDataSet * @constructor LXmlDataSet * @param config {Object} The intial LXmlDataSet. */ DU.data.LXmlDataSet = function(config) { DU.data.LXmlDataSet.superclass.constructor.call(this, config); /** * @description 데이터 처리 방식 (2:Xml); * @property dataSetType * @private * @type {Int} */ this.dataSetType = DU.data.LXmlDataSet.DATA_TYPE; }; DU.extend(DU.data.LXmlDataSet, DU.data.LDataSet, { /** * @description 결과 데이터를 Array로 변환하여 리턴 * @method getReadDataMulti * @private * @param {Object} dataSet LDataSet 객체 * @param {Object} conn 응답 객체 * @return void */ getReadDataMulti : function(dataSet, conn){ var responseXML = conn.responseXML; var doc = responseXML; var root = dataSet.root; if(DU.isUndefined(root)) { root = this.id; } var rowData = []; var datasetNodes = doc.getElementsByTagName('dataset'); if (datasetNodes && datasetNodes.length > 0) { for (var i = 0; i < datasetNodes.length; i++) { var node = datasetNodes.item(i); var id = node.getAttribute('name'); if(id != root) continue; var rowNodes = doc.getElementsByTagName('row'); if (rowNodes && rowNodes.length > 0) { for (var i = 0; i < rowNodes.length; i++) { var rowNode = rowNodes.item(i); var colData = []; colData.node = rowNode; for (var j = 0; j < dataSet.fields.length; j++) { var field = dataSet.fields[j]; var val = this._getNamedValue(rowNode, dataSet.fields[j].id, ""); var formater = this.readFieldFormater[field.type]; if(formater) val = formater(val); colData[dataSet.fields[j].id] = val; } rowData.push(colData); } } } } return rowData; }, /** * @description data를 읽어온다. * @method _getNamedValue * @private * @param {Object} node Node객체 * @param {String} name 찾고자 하는 Attribute 이름 * @param {String} defaultValue 기본 값 * @return void */ _getNamedValue : function(node, name, defaultValue) { if (!node || !name) { return defaultValue; } var nodeValue = defaultValue; var attrNode = node.attributes.getNamedItem(name); if (attrNode) { nodeValue = attrNode.value; } else { var childNode = node.getElementsByTagName(name); if (childNode && childNode.item(0) && childNode.item(0).firstChild) { nodeValue = childNode.item(0).firstChild.nodeValue; } else { var index = name.indexOf(':'); if (index > 0) { return this.getNamedValue(node, name.substr(index + 1), defaultValue); } } } return nodeValue; }, /** * @description 데이터 정보를 문자열로 리턴한다. * @method serializeDataSet * @public * @return {String} 문자열 */ serializeDataSet: function() { throw new LException('구현 안됨.'); }, /** * @description 변경된 데이터 정보를 문자열로 리턴한다. * @method serializeModifiedDataSet * @public * @return {String} 변경된 문자열 */ serializeModifiedDataSet : function() { throw new LException('구현 안됨.'); }, /** * @description LDataSet 배열의 데이터 정보를 리턴한다. * @method serializeDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeDataSetList: function(dataSets) { throw new LException('구현 안됨.'); }, /** * @description LDataSet 배열의 변경된 데이터 정보를 리턴한다. * @method serializeModifiedDataSetList * @private * @param {Array} dataSets 데이터 객체 배열 * @return {String} */ serializeModifiedDataSetList : function(dataSets) { throw new LException('구현 안됨.'); } }); // xml type DU.data.LXmlDataSet.DATA_TYPE = 2; /** * Form * @module data * @title LBind * @requires DU */ DU.namespace("DU.data"); /** * LBind * @namespace DU.data * @class LBind * @extends DU.util.LEventProvider * @constructor LBind * @param {Object} oConfig The intial LBind. */ DU.data.LBind = function(oConfig){ DU.data.LBind.superclass.constructor.call(this); var config = oConfig || {}; /** * @description bindRow 위치 * @property bindRow * @protected * @type Int */ this.bindRow = 1; /** * @description LBind 여부 * @config bind * @type {Boolean} * @default true */ /** * @description LBind 여부 * @property bind * @protected * @type Boolean */ this.bind = true; /** * @description Group 객체 Id * @property groupId * @protected * @type String */ this.groupId = config.groupId; /** * @description Bind객체 정보 * @config bindInfo * @type {Object} * @default null */ /** * @description Bind객체 정보 * @property bindInfo * @protected * @type Object */ this.bindInfo = null; /** * @description validate를 처리할 지 여부 * @config isValidation * @type {boolean} * @default true */ /** * @description validate를 처리할 지 여부 * @property isValidation * @protected * @type boolean */ this.isValidation = true; /** * @description Group을 가지는 Element * @property groupEl * @protected * @type DU.LElement */ this.groupEl = DU.get(this.groupId); /** * @description Group 객체 * @property groupDom * @protected * @type Object */ this.groupDom = this.groupEl.dom; DU.applyObject(this, config, true); this.onChangeDelegate = DU.util.LFunction.createDelegate(this.onChangeEvent, this); this.init(); } DU.extend(DU.data.LBind, DU.util.LEventProvider, { /** * @description 객체 초기화 메소드 * @method init * @private * @return void */ init : function() { if(this.dataSet) { if(this.bind && this.bind == true) { if(this.dataSet.getCount() > 0) { this.load(this.dataSet.getRow()); } this.initEvent(); } } }, /** * @description dataSet객체의 load 이벤트가 발생하면 호출되는 메소드 * @method onLoad * @private * @param {Object} e Event 객체 * @return void */ initEvent : function() { this.children = this.children || this.getChildren(); this.each(this.children, function(child) { var bindObject = null; bindObject = this.getBindInfoByDom(child); if(bindObject == null) return; var field = this.dataSet.getFieldById(bindObject.id); if(field) { var fn = bindObject.fn || this.onChangeDelegate; var obj = child.instance; if (obj && obj.fieldObject === true) { obj.unOn('changed', fn); obj.on('changed', fn); } else { DU.util.LEvent.removeListener(child, bindObject.eventName, fn); DU.util.LEvent.addListener(child, bindObject.eventName, fn, child, true); var childEl = DU.get(child); this.bindSetValueInit(childEl, fn); } } }); this.dataSet.on('rowPosChanged', this.onRowPosChanged, this, true); this.dataSet.on('update', this.onUpdate, this, true); this.dataSet.on('invalid', this.onInvalid, this, true); this.dataSet.on('valid', this.onValid, this, true); }, /** * @description dataSet객체의 rowPosChanged 이벤트가 발생하면 호출되는 메소드 * @method onRowPosChanged * @private * @param {Object} e Event 객체 * @return void */ onRowPosChanged : function(e) { if(this.dataSet.isLoading === true) return; this.children = null; this.load(this.dataSet.getRow()); }, /** * @description dataSet객체의 update 이벤트가 발생하면 호출되는 메소드 * @method onUpdate * @private * @param {Object} e Event 객체 * @return void */ onUpdate : function(e) { if(this.bind == false) return; this.children = this.children || this.getChildren(); var bindObject = this.getBindInfoById(e.colId); if(bindObject == null) return; this.each(this.children, function(child) { if((child.name == bindObject.ctrlId) || (child.id == bindObject.ctrlId)) { this.doChangeSetValue(this.children, child, bindObject, e.value); } }); }, /** * @description 객체의 change 이벤트가 발생하면 호출되는 메소드 * @method onChangeEvent * @private * @param {Object} e Event 객체 * @return void */ onChangeEvent : function(e) { var childObject = e.target; var value = childObject.fieldObject === true ? childObject.getValue() : childObject.value; this.doChangeEvent(this, childObject, value); }, /** * @description invalid시 호출되는 이벤트 메소드 * @method onInvalid * @protected * @param {Object} e Event 객체 * @return {void} */ onInvalid: function(e) { if(this.isValidation == false || e.row != this.bindRow) return; this.children = this.children || this.getChildren(); var bindObject = this.getBindInfoById(e.colId); if(bindObject) { this.each(this.children, function(child) { if ((child.name == bindObject.ctrlId) || (child.id == bindObject.ctrlId)) { var obj = child.instance; if (obj && obj.fieldObject === true) { obj.invalid(e.message); } else { var childEl = DU.get(child); childEl.invalid(e.message); } } }); } }, /** * @description valid시 호출되는 이벤트 메소드 * @method onValid * @protected * @param {Object} e Event 객체 * @return {void} */ onValid: function(e) { if (this.isValidation == false || e.row != this.bindRow) return; this.children = this.children || this.getChildren(); var record = this.dataSet.getAt(e.row); var bindObject = this.getBindInfoById(e.colId); if(bindObject) { this.each(this.children, function(child) { if ((child.name == bindObject.ctrlId) || (child.id == bindObject.ctrlId)) { var obj = child.instance; if (obj && obj.fieldObject === true) { obj.valid(); } else { var childEl = DU.get(child); childEl.valid(); } } }); } }, /** * @description 객체의 change 이벤트를 수행하는 메소드 * @method doChangeEvent * @private * @param {Object} e Event 객체 * @return void */ doChangeEvent : function(bindObject, child, newValue) { if(bindObject.bind == false) return; if(bindObject.dataSet.getCount() - 1 < bindObject.bindRow) return; var ctrlObject = child; var bindInfoList = bindObject.getBindInfoByObject(ctrlObject); for(var i = 0 ; i < bindInfoList.length ; i++) { var bindInfo = bindInfoList[i]; var value = null; if(bindObject.bindRow > -1) { var record = bindObject.dataSet.getAt(bindObject.bindRow); if (ctrlObject && ctrlObject.fieldObject === true && bindInfo.value == 'text') { value = ctrlObject.getDisplayValue ? ctrlObject.getDisplayValue() : ctrlObject.getValue(); } else if(ctrlObject && ctrlObject.fieldObject === true) { value = ctrlObject.getValue(); } else if(ctrlObject.tagName.toLowerCase() == 'select' && bindInfo.value == 'text') { value = ctrlObject.options[ctrlObject.selectedIndex].text; } else { value = DU.isUndefined(ctrlObject[bindInfo.value]) == false ? ctrlObject[bindInfo.value] : null; if(ctrlObject.tagName.toLowerCase() == 'input' && ctrlObject.type == 'checkbox') { if(ctrlObject.checked == false) value = ''; } } var column = bindObject.dataSet.getFieldById(bindInfo.id); if(column && column.type == 'number' && typeof value == 'string') { value = value == '' ? 0 : parseInt(value, 10); } record.set(bindInfo.id, value); } } }, /** * @description Bind정보배열을 리턴 * @method getBindInfoByObject * @private * @param {Object} child Child 객체 * @return {Array} */ getBindInfoByObject : function(child) { this.bindInfoMap = this.bindInfoMap || {}; var bindInfoList = this.bindInfoMap[child.name] || this.bindInfoMap[child.id] ; if(!bindInfoList) { bindInfoList = []; for(var i = 0 ; i < this.bindInfo.length; i++) { if(this.bindInfo[i].ctrlId == child.name || this.bindInfo[i].ctrlId == child.id) { if(!this.bindInfo[i].eventName) { if (child.tagName == 'INPUT' || child.tagName == 'TEXTAREA' || child.tagName == 'SELECT') { if (child.tagName == 'INPUT' && (child.type == 'radio' || child.type == 'checkbox')) { this.bindInfo[i].eventName = 'click'; } else this.bindInfo[i].eventName = 'change'; } else { throw new Error('LBind.getBindInfoByObject() : Bind를 지원하는 객체가 아닙니다.'); } } bindInfoList[bindInfoList.length] = this.bindInfo[i]; } } this.bindInfoMap[child.name || child.id] = bindInfoList; } return bindInfoList; }, /** * @description bindInfoMap cache를 지운다. * @method clearBindInfoMap * @public * @return {void} */ clearBindInfoMap : function() { this.bindInfoMap = null; }, /** * @description Bind정보를 가지는 객체를 리턴 * @method getBindInfoByDom * @private * @param {Object} child Child 객체 * @return {Object} */ getBindInfoByDom : function(child) { var bindInfoList = this.getBindInfoByObject(child); return bindInfoList.length > 0 ? bindInfoList[0] : null; }, /** * @description Bind정보를 가지는 객체를 리턴 * @method getBindInfoById * @private * @param {Object} id dataSet 변경 id * @return {Object} */ getBindInfoById : function(id) { for (var i = 0; i < this.bindInfo.length; i++) { if(this.bindInfo[i].id == id) return this.bindInfo[i]; } return null; }, /** * @description DataSet의 row 위치에 해당되는 정보를 읽는 메소드 * @method load * @public * @param {Int} row 읽어올 위치 값 * @return void */ load : function(row) { if(DU.isUndefined(row)) row = 0; if(row < -1 || this.dataSet.getCount() - 1 < row) return; this.bindRow = row; this.children = this.children || this.getChildren(); var children = this.children; this.each(children, function(child) { var bindObject = this.getBindInfoByDom(child); if(bindObject == null) return; var field = this.dataSet.getFieldById(bindObject.id); if(field) { if(this.dataSet.getCount() - 1 < row) return; var value = (row > -1) ? this.dataSet.getAt(row).get(field.id) : null; this.doChangeSetValue(children, child, bindObject, value); } }); }, /** * @description * @method getChildren * @private * @return {Array} */ getChildren : function() { return DU.util.LDomSelector.query('input,select,textarea', this.groupDom); }, /** * @description 객체의 setValue를 재구현한다. * @method bindSetValueInit * @private * @param {childEl} childEl child element 객체 * @param {function} fn fn 객체 * @return void */ bindSetValueInit : function(childEl, fn) { if(childEl.setValue) { var setValueDelegate = function() { childEl.oldSetValue(arguments[0]); fn.call(childEl, {target:childEl.dom}); } childEl.oldSetValue = childEl.setValue; childEl.setValue = setValueDelegate; } }, /** * @description 객체에 값 데입 * @method doChangeSetValue * @private * @param {Object} children 배열 객체 리스트 * @param {Object} child 현재 child 객체 * @param {Object} bindObject bind 정보 객체 * @param {Object} value 대입할 값 * @return void */ doChangeSetValue : function(children, child, bindObject, value) { if(child.tagName == 'INPUT' && (child.type == 'radio' || child.type == 'checkbox')) { this.each(children, function(currentChild){ if((child.name || child.id) == (currentChild.name || currentChild.id)) { if(currentChild.value == value) { currentChild.checked = true; return; }else { currentChild.checked = false; } } }); } else { if(value == null) value = ''; var obj = child.instance; if (obj && obj.fieldObject === true && bindObject.value == 'value') obj.setValue(value); else child[bindObject.value] = value; } }, /** * @description 객체 배열을 Function객체로 반복 호출하는 메소드 * @method each * @private * @param {Object} children 배열 객체 리스트 * @param {Object} fn Function 객체 * @return void */ each : function(children, fn) { for (var i = 0; i < children.length; i++) { var id = children[i].id || children[i].name; if(DU.isUndefined(id) == false && DU.isNull(id) == false && id != '') { if(fn.call(this, children[i]) == false) break; } } }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.data.LBind ' + this.id; } }); /** * LTransactionManager * @module data * @title LTransactionManager * @requires DU */ DU.namespace("DU.data"); /** * LTransactionManager * @namespace DU.data * @class LTransactionManager * @extends DU.util.LEventProvider * @constructor LTransactionManager * @param {Object} oConfig The intial LTransactionManager. */ DU.data.LTransactionManager = function(oConfig){ DU.data.LTransactionManager.superclass.constructor.call(this); var config = oConfig || {}; config = DU.util.LDom.applyIfProperties(config, '$.base.transactionManager'); /** * @description field의 id * @property id * @public * @type {String} */ this.id = config.formId; DU.applyObject(this, config, true); /** * @description requset의 transaction id * @property transaction * @public * @type {String} */ this.transaction = null; /** * @description Update가 성공할 경우 발생하는 이벤트 * @event success * @param {XMLHttpRequest} conn ajax response 객체 */ this.createEvent('success'); /** * @description Update가 실패할 경우 발생하는 이벤트 * @event failure * @param {XMLHttpRequest} conn ajax response 객체 */ this.createEvent('failure'); /** * @description Update를 실행하기전에 실행여부를 판단하는 이벤트 * @event beforeUpdate * @param {Object} target this객체 * @param {String} url url 정보 * @param {HtmlElement} form form 객체 * @param {Object} params parameter 객체 * @param {DU.data.LDataSet} dataSets dataset 객체 */ this.createEvent('beforeUpdate'); /** * @description load이 발생하기전 수행하는 이벤트 * @event beforeLoad * @param {Object} target this객체 */ this.createEvent('beforeLoad'); /** * @description load이 발생한 후 * @event load * @param {Object} target this객체 */ this.createEvent('load'); /** * @description load딩시 에러가 발생했을 경우 * @event loadException * @param {Object} target this객체 * @param {Object} throwObject exception 객체 */ this.createEvent('loadException'); /** * @description success를 처리하는 Function delegate객체 * @property doSuccessDelegate * @private * @type Object */ this.doSuccessDelegate = DU.util.LFunction.createDelegate(this.doSuccess, this); /** * @description failure를 처리하는 Function delegate객체 * @property failureDelegate * @private * @type Object */ this.doFailureDelegate = DU.util.LFunction.createDelegate(this.doFailure, this); this.on('success', this.onSuccess); /** * @description du_config.js에 있는 defaultSuccessHandler를 사용할지 여부 * @config defaultSuccessHandler * @type {Boolean} * @default false */ /** * @description du_config.js에 있는 defaultSuccessHandler를 사용할지 여부 * @property defaultSuccessHandler * @type {Boolean} */ if (this.defaultSuccessHandler === true) { this.on('success', function(e) { DU.alert(DU.getMessageManager().get('$.base.msg100')); }); } /** * @description du_config.js에 있는 defaultFailureHandler를 사용할지 여부 * @config defaultFailureHandler * @type {Boolean} * @default false */ /** * @description du_config.js에 있는 defaultFailureHandler를 사용할지 여부 * @property defaultFailureHandler * @type {Boolean} */ if (this.defaultFailureHandler === true) { var failureHandler = DU.getConfig().getFirst("$.base.transactionManager.failureHandler"); this.on('failure', failureHandler); } /** * @description du_config.js에 있는 defaultLoadExceptionHandler를 사용할지 여부 * @config defaultLoadExceptionHandler * @type {Boolean} * @default false */ /** * @description du_config.js에 있는 defaultLoadExceptionHandler를 사용할지 여부 * @property defaultLoadExceptionHandler * @type {Boolean} */ if (this.defaultLoadExceptionHandler === true) { var loadExceptionHandler = DU.getConfig().getFirst("$.base.transactionManager.loadExceptionHandler"); this.on('loadException', loadExceptionHandler); } if(DU.widget.LWaitPanel) this.waitPanel = new DU.widget.LWaitPanel(); } DU.extend(DU.data.LTransactionManager, DU.util.LEventProvider, { /** * @description wait Panel 객체 * @property waitPanel * @private * @type {DU.widget.LWaitPanel} */ waitPanel : null, /** * @description URL에 대한 Cache 여부 * @config disableCaching * @type {Boolean} * @default true */ /** * @description URL에 대한 Cache 여부 * @property disableCaching * @public * @type {Boolean} */ disableCaching : true, /** * @description Request의 timeout값 * @config timeout * @type {Int} * @default 30 */ /** * @description Request의 timeout값 * @property timeout * @public * @type {Int} */ timeout : 30, /** * @description ssl에 해당되는 blank url * @property sslBlankUrl * @public * @type {String} */ sslBlankUrl : 'about:blank', /** * @description sync 여부 * @config sync * @type {Boolean} * @default true */ /** * @description sync 여부 * @property sync * @public * @type {Boolean} */ sync : true, /** * @description DataSet의 변경건을 확인할지 여부를 결정한다. * @config isCheckedUpdate * @type {Boolean} * @default true */ /** * @description DataSet의 변경건을 확인할지 여부를 결정한다. * @property isCheckedUpdate * @public * @type {Boolean} */ isCheckedUpdate : true, /** * @description params정보를 가지고 url로 서버를 호출하는 메소드 *


    *  var options = {

    *       url:'test.dev', 

    *       params:{}, 

    *       callback:{}, 

    *       discardUrl:false

    *   }

    * 
* @method update * @public * @param {Object} options 호출할때 전달할 Option정보 객체 *
* url {String} url
* params {Object} 파라미터 객체
* callback {Object} success, failure 콜백 메소드 *
* @return void */ update: function(options){ var config = options || {}; config = DU.applyIf(config, { url:null, params:{}, callback:{}, discardUrl:false }); var url = config.url; var params = config.params; var callback = config.callback; var discardUrl = config.discardUrl; if (this.fireEvent('beforeUpdate', { target: this.el, url: url, params: params }) === false) return; if (!discardUrl) { this.defaultUrl = url; } if (params && typeof params != 'string') { params = DU.util.LObject.serialize(params); } var callbackDelegate = { success: this.doSuccessDelegate, failure: this.doFailureDelegate, timeout: (this.timeout * 1000), cache: !this.disableCaching, argument: { 'url': url, 'form': null, 'callback': callback, 'params': params } }; this.waitePanelShow(); var method = params ? 'POST': 'GET'; if(this.sync || this.sync == true) { this.transaction = DU.LConnect.syncRequest(method, url, callbackDelegate, params); } else { this.transaction = DU.LConnect.asyncRequest(method, url, callbackDelegate, params); } }, /** * @description form의 element정보를 가지고 url로 서버를 호출하는 메소드 *


    *  var options = {

    *       url:'test.dev',

    *       form:'frm'

    *   }

    * 
* @method updateForm * @public * @param {Object} options 호출할때 전달할 Option정보 객체 *
* url {String} url
* form {HTMLElement} form 객체
* callback {Object} success, failure 콜백 메소드
* params {Object} 파라미터 객체
* reset {boolean} 호출후 reset 여부 *
* @return void */ updateForm : function(options) { var config = options || {}; config = DU.applyIf(config, { url:null, form:null, callback:{}, params:{}, reset:false }); var url = config.url; var form = config.form; var callback = config.callback; var reset = config.reset; if (this.fireEvent('beforeUpdate', { target: this.el, url: url, form:form }) === false) return; var formEl = DU.util.LDom.get(form); url = url || formEl.action; var callbackDelegate = { success: this.doSuccessDelegate, failure: this.doFailureDelegate, timeout: (this.timeout * 1000), argument: { 'url': url, 'form': form, 'callback': callback, 'reset': reset } }; var isUpload = false; var enctype = formEl.getAttribute('enctype'); if (enctype && enctype.toLowerCase() == 'multipart/form-data') { isUpload = true; } DU.LConnect.setForm(form, isUpload, this.sslBlankUrl); postData = DU.util.LObject.serialize(config.params); this.waitePanelShow(); if(this.sync || this.sync == true) { this.transaction = DU.LConnect.syncRequest('POST', url, callbackDelegate, postData); } else { this.transaction = DU.LConnect.asyncRequest('POST', url, callbackDelegate, postData); } }, /** * @description {DU.data.LDataSet}의 변경정보를 가지고 url로 서버를 호출하는 메소드 *


    *  var options = {

    *       dataSets:[dataSet],

    *       url:'test.dev',

    *       params:{},

    *       callback:{}

    *   }

    * 
*

Sample: 보기

* @method updateDataSet * @public * @param {Object} options 호출할때 전달할 Option정보 객체 *
* dataSets {Array} dataSet 배열
* url {String} url
* callback {Object} success, failure 콜백 메소드
* params {Object} 파라미터 객체
* isCheckedUpdate {boolean} 변경된 값이 존재하는지 체크할지 여부 *
* @return void */ updateDataSet : function(options) { var config = options || {}; config = DU.applyIf(config, { dataSets:[], url:null, params:{}, callback:{}, isCheckedUpdate:this.isCheckedUpdate }); var dataSets = config.dataSets; if(!this.filterTask) this.filterTask = new DU.util.LDelayedTask(DU.util.LFunction.createDelegate(function() {this.doUpdateDataSet(config, dataSets);}, this)); this.filterTask.delay(500); }, /** * @description dataSet의 update를 수행하는 메소드 * @method doUpdateDataSet * @protected * @param {Object} options 호출할때 전달할 Option정보 객체 * @param {Array} dataSets 서버에 전달할 데이터셋 리스트 * @return void */ doUpdateDataSet: function(config, dataSets) { if (this.fireEvent('beforeUpdate', { target: this.el, url: config.url, dataSets:dataSets }) === false) return; var isUpdate = false; for(var i = 0 ; i < dataSets.length ; i++) { var dataSetEl = DU.util.LDom.get(dataSets[i]); if(config.isCheckedUpdate == true) isUpdate = (isUpdate === false) ? dataSetEl.isUpdate() : isUpdate; if(isUpdate === true) break; } if(isUpdate === false) { alert(DU.getMessageManager().get('$.base.msg102')); return; } var url = config.url; var callback = config.callback; var params = this.serializeByModifiedDataSet(dataSets); if (typeof config.params == 'object') params += "&" + DU.util.LObject.serialize(config.params); else params += "&" + config.params; var callbackDelegate = { success: this.doSuccessDelegate, failure: this.doFailureDelegate, timeout: (this.timeout * 1000), argument: { 'url': config.url, 'dataSets': dataSets, 'callback': callback, 'params': config.params } }; // onSuccess 이벤트에서 처리할 수 있게 현재 dataSets 등록 (구조를 delegate구조로 바꾸고 싶으나 데이터 전달 방법의 구조를 잡기 어려움. ㅜㅜ) this.dataSets = dataSets; this.waitePanelShow(); if(this.sync || this.sync == true) { this.transaction = DU.LConnect.syncRequest('POST', url, callbackDelegate, params); } else { this.transaction = DU.LConnect.asyncRequest('POST', url, callbackDelegate, params); } }, /** * @description 여러개의 {DU.data.LDataSet}에 해당되는 queryString을 리턴한다. * @method serializeByModifiedDataSet * @public * @param {Array} dataSets 데이터셋 리스트 * @return void */ serializeByModifiedDataSet : function(dataSets) { var params = 'dui_datasetdata='; if(dataSets.length > 0) { var dataSetEl = DU.util.LDom.get(dataSets[0]); params += encodeURIComponent(dataSetEl.serializeModifiedDataSetList(dataSets)); params += '&dui_datasetdatatype=' + dataSetEl.dataSetType } return params; }, /** * @description 여러개의 {DU.data.LDataSet}에 해당되는 queryString을 리턴한다. * @method serializeByModifiedDataSet * @public * @param {Array} dataSets 데이터셋 리스트 * @return void */ serializeByDataSet : function(dataSets) { var params = 'dui_datasetdata='; if(dataSets.length > 0) { var dataSetEl = DU.util.LDom.get(dataSets[0]); params += encodeURIComponent(dataSetEl.serializeDataSetList(dataSets)); params += '&dui_datasetdatatype=' + dataSetEl.dataSetType } return params; }, /** * @description 여러개의 {DU.data.LDataSet}을 서버에서 load하는 메소드 *

Sample: 보기

* @method loadDataSet * @public * @param {Object} options 호출할때 전달할 Option정보 객체 *
* dataSets {Array} DataSet 배열
* url {String} 서버 호출 url
* params {Object} 서버에 전달할 파라미터 객체
* method {String} get or post
* sync {boolean} sync 여부 (default : false)
* state {DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} load시 record의 기본 상태 *
* @return void */ loadDataSet : function(option) { var config = option || {}; var dataSets = config.dataSets; config.dataSets = null; var me = this; config = DU.applyIf(config, { method : 'POST', callback : { success : function(conn) { me.loadDataResponse(dataSets, conn, config); }, failure : function(conn) { var e = new Error(conn.responseText); me.fireEvent('loadException', {target:me, throwObject:e, conn:conn}); throw e; } }, url : null, params:{} }); this.lastOptions = config; this.lastOptions.state = DU.isUndefined(this.lastOptions.state) == false ? this.lastOptions.state : DU.data.LRecord.STATE_NORMAL; var params = ''; if (typeof config.params == 'object') params = DU.util.LObject.serialize(config.params); else params = config.params; this.fireEvent('beforeLoad', {target:this}); if(config.sync && config.sync == true) { DU.LConnect.syncRequest(config.method, config.url, config.callback, params); } else { DU.LConnect.asyncRequest(config.method, config.url, config.callback, params); } }, /** * @description HttpResponse 결과를 여러개의 {DU.data.LDataSet}에 반영하는 메소드 *

Sample: 보기

* @method loadDataResponse * @public * @param {Array} dataSets DataSet 배열 * @param {Object} conn HttpResponse 객체 * @param {Object} config 호출할때 전달할 Option정보 객체 *
* state {DU.data.LRecord.STATE_INSERT|DU.data.LRecord.STATE_UPDATE|DU.data.LRecord.STATE_DELETE} load시 record의 기본 상태 *
* @return void */ loadDataResponse : function(dataSets, conn, config) { try { for(var i = 0 ; i < dataSets.length ; i++) { var dataSet = dataSets[i]; var dataSetData = dataSet.getReadData(conn); dataSet.loadData(dataSetData, config); } this.fireEvent('load', {target:this, conn: conn}); } catch(e) { this.fireEvent('loadException', {target:this, throwObject:e}); throw e; } }, /** * @description success를 처리하는 Function delegate객체 * @method doSuccess * @private * @param {Object} response response객체 * @return void */ doSuccess: function(response) { this.waitePanelHide(); this.transaction = null; if (response.argument.form && response.argument.reset) { try { response.argument.form.reset(); } catch(e) {} } response.target = this; this.fireEvent('success', response); if (typeof response.argument.callback == 'function') { response.argument.callback(this, true); } }, /** * @description failure를 처리하는 Function delegate객체 * @method doFailure * @private * @param {Object} response response객체 * @return void */ doFailure: function(response) { this.waitePanelHide(); this.transaction = null; response.target = this; this.fireEvent('failure', response); if (typeof response.argument.callback == 'function') { response.argument.callback(this, false); } }, /** * @description updateDataSet시 정상적으로 success일 경우 모든 데이터셋 commit 처리 * @method onSuccess * @private * @return void */ onSuccess : function() { for(var i = 0 ; this.dataSets != null && i < this.dataSets.length ; i++) { var dataSetEl = this.dataSets[i]; dataSetEl.commit(); } this.dataSets = null; }, /** * @description request를 중단하는 메소드 * @method abort * @public * @return void */ abort : function() { if (this.transaction) { DU.LConnect.abort(this.transaction); } }, /** * @description update를 호출했는지 판단하는 메소드 * @method isUpdating * @public * @return void */ isUpdating: function() { if (this.transaction) { return DU.LConnect.isCallInProgress(this.transaction); } return false; }, /** * @description wait panel을 출력하는 메소드 * @method waitePanelShow * @public * @return void */ waitePanelShow : function() { if(this.waitPanel) this.waitPanel.show(); }, /** * @description wait panel을 숨기는 메소드 * @method waitePanelHide * @public * @return void */ waitePanelHide : function() { if(this.waitPanel) this.waitPanel.hide(); }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.data.LTransactionManager ' + this.id; } }); /** * Validator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * Validator * @namespace DU.validate * @class LValidator * @constructor LValidator * @param {Object} oConfig The intial Validator. */ DU.validate.LValidator = function(id, oConfig){ this.config = oConfig || {}; this.id = id; } DU.validate.LValidator.prototype = { /** * @description validator id * @property id * @private * @type {String} */ id: null, /** * @description 출력할 컬럼명 * @property itemName * @private * @type {String} */ itemName: null, /** * @description object type 명 * @property otype * @private * @type {String} */ otype: 'DU.util.LValidator', /** * @description validate하는 메소드 * @method validate * @public * @param {Object} value 비교 값 * @return {Boolean} 비교 결과값 */ validate : function(value) { return true; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return (this.otype || 'DU.validate.LValidator') + ' ' + this.id; } } /** * Validator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LValidatorManager * @namespace DU.validate * @class LValidatorManager * @constructor LValidatorManager * @param {Object} oConfig The intial LValidatorManager. */ DU.validate.LValidatorManager = function(oConfig){ this.config = oConfig || {}; this.validators = new DU.util.LCollection(); if(this.config.validators) { this.parse(this.config.validators); } } DU.validate.LValidatorManager.prototype = { /** * @description invalid 된 객체들의 정보 배열 * @property invalidList * @private * @type {Array} */ invalidList : [], /** * validators 객체 정보 parse * @method parse * @private * @param {Array} validators validator Array객체 * @return {void} */ parse : function(validators) { for(var vi = 0 ; vi < validators.length ; vi++) { var validator = validators[vi]; var id = validator.id; var expression = validator.validExp; var columns = DU.util.LString.advancedSplit(expression, ":", "it"); var label = columns[0]; var required = columns[1] == "true" ? true : false; if(required) { var requiredValidator = new DU.validate.LRequiredValidator(id); requiredValidator.required = required; requiredValidator.label = label; this.add(id, requiredValidator); } if (DU.isNull(columns[2])) return; var vExps = DU.util.LString.advancedSplit(columns[2], "&", "it"); for (var i = 0; i < vExps.length; i++) { if (DU.isNull(vExps[i])) continue; var sArr = DU.util.LString.advancedSplit(vExps[i], "=", "it"); if (DU.isNull(sArr[0])) throw new DU.LException(label+" : "+DU.getMessageManager().get("$.base.msg050", vExps[i])); if (sArr.length>1 && DU.util.LString.trim(sArr[1]) == "") throw new DU.LException(label+" : "+DU.getMessageManager().get("$.base.msg050", vExps[i])); var firstUpperCase = DU.util.LString.firstUpperCase(sArr[0]); if (!eval("DU.validate.L"+firstUpperCase+"Validator")) throw new DU.LException(label+" : "+DU.getMessageManager().get("$.base.msg051", vExps[i])); try { var validatorObj = null; if (sArr[1]) validatorObj = eval("new DU.validate.L"+firstUpperCase+"Validator('"+id+"','"+sArr[1]+"')"); else validatorObj = eval("new DU.validate.L"+firstUpperCase+"Validator('"+id+"')"); validatorObj.required = required; validatorObj.fn = validator.fn; validatorObj.label = label; this.add(id, validatorObj); } catch (e) { if (e instanceof DU.LException) e.message = label+" : "+DU.getMessageManager().get("$.base.msg050", vExps[i]); throw e; } } } }, /** * Validator객체를 추가하는 메소드 * @method add * @param {String} id validation할 객체의 id * @param {DU.validate.LValidator} validator validator 객체 * @return {void} */ add : function(id, validator) { this.validators.add(id, validator); }, /** * validation를 dom을 지정하여 하위 모든 객체를 수행하는 메소드 * @method validateGroup * @param {String} id Validation할 group id * @return {Boolean} validate 여부 */ validateGroup : function(groupEl){ var isValid = true; this.invalidList = []; var groupEl = DU.get(groupEl); var itemMessageList = new Array(); var childList = DU.util.LDomSelector.query('input,select,textarea', groupEl.dom); DU.util.LArray.each(childList, function(f) { var child = DU.get(f); if(this.validateEl(child) == false) { var id = child.name || child.id; var validator = this.validators.get(id); var colLabel = (validator != null) ? validator.label : id; isValid = false; this.invalidList.push({ id: id, label: colLabel, value: child.getValue(), messageList: this.messageList }); itemMessageList = itemMessageList.concat(colLabel + ' : ' + this.messageList + '\r\n'); } }, this); this.messageList = itemMessageList; return isValid; }, /** * id에 해당되는 값만 validate를 수행하는 메소드 * @method validateField * @param {String} id Validation할 field * @param {Object} value Validation할 object * @return {Boolean} validate 여부 */ validateField : function(id, value) { var isValid = true; var label = id; var messageList = []; var validatorList = this.getValidatorList(id); for(var i = 0 ; i < validatorList.length ; i++) { var item = validatorList[i]; if(item) { if(!(item.required === false && DU.isEmpty(value))) { var currentValid = item.validate(value); if(currentValid == true && item.fn) currentValid = item.fn.call(item, value); if (currentValid == false) { label = item.label || id; isValid = currentValid; messageList.push(item.message); } } } } return { isValid: isValid, id: id, label: label, message: messageList.join('\r\n') }; }, /** * validation를 object에 가지고 있는 키,값으로 수행하는 메소드 * @method validate * @param {Object} obj Validation할 object * @return {Boolean} validate 여부 */ validate : function(obj) { var isValid = true; this.invalidList = []; var newMessageList = new Array(); for(var id in obj) { var value = obj[id]; var invalidInfo = this.validateField(id, value); if(invalidInfo.isValid == false) { this.invalidList.push({ id: invalidInfo.id, value: invalidInfo.value, label: invalidInfo.label, message: invalidInfo.message }) if(!DU.util.LArray.indexOf(newMessageList, invalidInfo.message) <= 0) { newMessageList = newMessageList.concat(invalidInfo.message); } isValid = false; } } this.messageList = newMessageList; return isValid; }, /** * validation을 수행하는 메소드 * @method validateEl * @param {DU.LElement} el Validation할 Element객체 * @return {Boolean} validate 여부 */ validateEl : function(el) { var isValid = true; var validatorManager = this; var message = '
    '; var objId = null; var obj = null; if(el.gridFieldId) { o = el; objId = el.gridFieldId; } else { el = DU.get(el); if(el == null) throw new DU.LException("Unknown element"); obj = el.dom.instance; var o = el; if (obj && obj.fieldObject === true) { o = obj objId = obj.id; } else { objId = el.dom.name || el.dom.id; } } var value = o.getValue(); this.messageList = []; var newMessageList = new Array(); var validatorList = this.getValidatorList(objId); for (var i = 0; i < validatorList.length; i++) { var item = validatorList[i]; if (item) { if (item.required === false && DU.isEmpty(value)) { o.valid(); return; } var currentValid = item.validate(value); if (currentValid == true && item.fn) currentValid = item.fn.call(item, value); if (currentValid == false && !DU.util.LArray.indexOf(newMessageList, item.message) <= 0) { message += '
  • ' + item.message + '
  • '; newMessageList.push(item.message); isValid = false; } } } message += '
'; this.messageList = newMessageList; isValid ? o.valid() : o.invalid(message); return isValid; }, /** * validateDataSet 수행하는 메소드 * @method validateDataSet * @param {DU.data.LDataSet} dataSet Validation할 dataSet객체 * @return {Boolean} validate 여부 */ validateDataSet : function(dataSet, row) { if(dataSet.getCount() < 1) return true; var isValid = true; var messageList = []; var isMulti = !DU.isUndefined(row) ? false : true; row = !DU.isUndefined(row) && row < 0 ? 0 : row; var count = !DU.isUndefined(row) ? row + 1 : dataSet.getCount(); var isValidMulti = false; for(var currRow = (row || 0) ; currRow < count ; currRow++) { var record = dataSet.getAt(currRow); for(var col = 0 ; col < dataSet.fields.length; col++) { var colId = dataSet.fields[col].id; var value = record.get(colId); if(dataSet.isModifiedRow(currRow)) { var invalidInfo = this.validateField(colId, value); if (invalidInfo.isValid == false) { isValid = false; dataSet.invalid(currRow, invalidInfo.id, invalidInfo.message, invalidInfo.value, isMulti); messageList.push('[' + (currRow + 1) + ' row : ' + invalidInfo.label + '] => ' + invalidInfo.message); } else { dataSet.valid(currRow, invalidInfo.id, isMulti); } } else { if(!isMulti) dataSet.valid(currRow, colId, isMulti); else if(isMulti && isValidMulti == false) { dataSet.valid(currRow, colId, isMulti); isValidMulti = true; } } } } this.messageList = messageList; return isValid; }, /** * validateGrid 수행하는 메소드 * @method validateGrid * @param {DU.widget.LGridPanel} gridPanel Validation할 gridPanel객체 * @return {Boolean} validate 여부 */ validateGrid : function(gridPanel) { var view = gridPanel.getView(); var dataSet = view.dataSet; var isValid = true; this.messageList = []; var newMessageList = new Array(); for(var row = 0 ; row < dataSet.getCount() ; row++) { if(dataSet.isModifiedRow(row)) { var record = dataSet.getAt(row); var currentValid = this.validate(record.getValues()); isValid = isValid ? currentValid : isValid; var invalidList = this.getInvalidList(); var alt = ''; var colMap = {}; for(var i = 0 ; i < invalidList.length ; i++) { var column = view.columnModel.getColumnById(invalidList[i].id); var colId = invalidList[i].id; var validator = this.validators.get(colId); colLabel = (validator != null) ? validator.label : (column != null) ? column.getLabel() : colLabel; if(column != null) { var col = view.columnModel.getIndexById(colId); view.addCellClass(row, col, 'L-invalid'); if(!colMap[colId]) view.removeCellAlt(row, col); var alt = view.getCellAlt(row, col); view.addCellAlt(row, col, (alt ? alt + '/' : alt) + invalidList[i].message); colMap[colId] = col; } var message = invalidList[i].messageList; newMessageList.push('[' + (row + 1) + ' row : ' + colLabel + '] => ' + message); } } } this.messageList = newMessageList; return isValid; }, /** * validator의 id에 해당되는 LValidator를 배열로 리턴한다. * @method getValidatorList * @param {String} id validator id * @return {ArrayList} */ getValidatorList: function(id) { var validatorList = []; for(var i = 0; i < this.validators.length ; i++) { var item = this.validators.getAt(i); if(id == item.id) validatorList.push(item); }; return validatorList; }, /** * invalid된 객체를 담아 배열로 리턴한다. * @method getInvalidList * @return {Array} invalid 배열 */ getInvalidList : function() { return this.invalidList; }, /** * 출력했던 메시지를 문자로 리턴한다. * @method getMessageList * @return {String} 출력했던 전체 메시지 */ getMessageList : function() { return this.messageList; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return 'DU.validate.LValidatorManager ' + this.id; } } /** * LLengthValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LLengthValidator 전체 글자의 길이를 체크하는 validator * { id: 'col2', validExp:'Col2:true:length=4'} * @namespace DU.validate * @class LLengthValidator * @extends DU.validate.LValidator * @constructor LLengthValidator * @param {Object} oConfig The intial LLengthValidator. */ DU.validate.LLengthValidator = function(id, length, oConfig){ DU.validate.LLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg003'; } DU.extend(DU.validate.LLengthValidator, DU.validate.LValidator, { otype: 'DU.validate.LLengthValidator', validate : function(value) { if(DU.isNull(value) || DU.isUndefined(value)) return true; value += ''; if (!this.checkCondition(value, this.length)) { this.message = DU.getMessageManager().get(this.msgId, [this.length]); return false; } return true; }, checkCondition : function (value, vValue) { return value.length == vValue; } }); /** * LDateValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LDateValidator 날짜의 유효성을 체크하는 validator * { id: 'col11', validExp:'Col11:true:date=YYYYMMDD'} * format문자 : YYYY, -> 4자리 년도 * YY, -> 2자리 년도. 2000년 이후. * MM, -> 2자리 숫자의 달. * LDD, -> 2자리 숫자의 일. * hh, -> 2자리 숫자의 시간. 12시 기준 * HH, -> 2자리 숫자의 시간. 24시 기준 * mm, -> 2자리 숫자의 분. * ss -> 2자리 숫자의 초. * * 예) * 'YYYYMMDD' -> '20020328' * 'YYYY/MM/LDD' -> '2002/03/28' * 'Today : YY-MM-LDD' -> 'Today : 02-03-28' * * 참고) * format문자가 중복해서 나오더라도 처음 나온 문자에 대해서만 * format문자로 인식된다. YYYY와 YY, hh와 HH 도 중복으로 본다. * 날짜는 년,월이 존재할 때만 정확히 체크하고 만일 년, 월이 없다면 * 1 ~ 31 사이인지만 체크한다. * * @namespace DU.validate * @class LDateValidator * @extends DU.validate.LValidator * @constructor LDateValidator * @param {Object} oConfig The intial LDateValidator. */ DU.validate.LDateValidator = function(id, dateExp, oConfig){ DU.validate.LDateValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.dateExp = dateExp; this.lastDateExp = this.dateExp; this.year = null; this.month = null; this.value = null; } DU.extend(DU.validate.LDateValidator, DU.validate.LValidator, { otype: 'DU.validate.LDateValidator', validate : function(value) { this.value = value; this.dateExp = this.lastDateExp; return ( this.checkLength(value) && this.checkYear(value) && this.checkMonth(value) && this.checkDay(value) && this.checkHour(value) && this.checkMin(value) && this.checkSec(value) && this.checkRest(value) ); }, checkLength : function () { if (this.value.length != this.dateExp.length) { this.message = DU.getMessageManager().get("$.base.msg037", [this.dateExp]); return false; } return true; }, checkYear : function () { var index = -1; if ( (index = this.dateExp.indexOf("YYYY")) != -1 ) { subValue = this.value.substr(index, 4); if ( !isNaN(subValue) && (subValue > 0) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 4); this.value = DU.util.LString.cut(this.value, index, 4); this.year = subValue; return true; } else { this.message = DU.getMessageManager().get("$.base.msg013"); return false; } } if ( (index = this.dateExp.indexOf("YY")) != -1 ) { subValue = "20" + this.value.substr(index, 2); if ( !isNaN(subValue) && (subValue > 0) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); this.year = subValue; return true; } else { this.message = DU.getMessageManager().get("$.base.msg013"); return false; } } return true; }, checkMonth : function () { var index = -1; if ( (index = this.dateExp.indexOf("MM")) != -1 ) { subValue = this.value.substr(index, 2); if ( !isNaN(subValue) && (subValue > 0) && (subValue <= 12) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); this.month = subValue; return true; } else { this.message = DU.getMessageManager().get("$.base.msg017"); return false; } } return true; }, checkDay : function () { var index = -1; var days = 0; if ( (index = this.dateExp.indexOf("DD")) != -1 ) { if ( (this.year) && (this.month) ) { days = (this.month != 2) ? DU.util.LDate.getDayInMonth(this.month-1) : (( (this.year % 4) == 0 && (this.year % 100) != 0 || (this.year % 400) == 0 ) ? 29 : 28 ); } else { days = 31; } subValue = this.value.substr(index, 2); if ( (!isNaN(subValue)) && (subValue > 0) && (subValue <= days) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); return true; } else { this.message = DU.getMessageManager().get("$.base.msg018"); return false; } } return true; }, checkHour : function () { var index = -1; if ( (index = this.dateExp.indexOf("hh")) != -1 ) { subValue = this.value.substr(index, 2); if ( !isNaN(subValue) && ((subValue=Number(subValue)) >= 0) && (subValue <= 12) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); return true; } else { this.message = DU.getMessageManager().get("$.base.msg019"); return false; } } if ( (index = this.dateExp.indexOf("HH")) != -1 ) { subValue = this.value.substr(index, 2); if ( !isNaN(subValue) && ((subValue=Number(subValue)) >= 0) && (subValue < 24) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); return true; } else { this.message = DU.getMessageManager().get("$.base.msg019"); return false; } } return true; }, checkMin : function () { var index = -1; if ( (index = this.dateExp.indexOf("mm")) != -1 ) { subValue = this.value.substr(index, 2); if ( !isNaN(subValue) && ((subValue=Number(subValue)) >= 0) && (subValue < 60 ) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); this.month = subValue; return true; } else { this.message = DU.getMessageManager().get("$.base.msg020"); return false; } } return true; }, checkSec : function () { var index = -1; if ( (index = this.dateExp.indexOf("ss")) != -1 ) { subValue = this.value.substr(index, 2); if ( (!isNaN(subValue)) && ((subValue=Number(subValue)) >= 0) && (subValue < 60 ) ) { this.dateExp = DU.util.LString.cut(this.dateExp, index, 2); this.value = DU.util.LString.cut(this.value, index, 2); this.month = subValue; return true; } else { this.message = DU.getMessageManager().get("$.base.msg021"); return false; } } return true; }, checkRest : function () { if (this.value == this.dateExp) return true; return false; } }); /** * LFormatValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LFormatValidator format에 맞는 값인지를 체크하는 validator * { id: 'col17', validExp:'Col17:true:format=abc'} * @namespace DU.validate * @class LFormatValidator * @extends DU.validate.LValidator * @constructor LFormatValidator * @param {Object} oConfig The intial LFormatValidator. */ DU.validate.LFormatValidator = function(id, format, oConfig){ DU.validate.LFormatValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.format = format; } DU.extend(DU.validate.LFormatValidator, DU.validate.LValidator, { otype: 'DU.validate.LFormatValidator', validate : function(value) { if (value.length != this.format.length) { this.message = DU.getMessageManager().get("$.base.msg024", [this.format]); return false; } var invalid = false; for (var i = 0; i < this.format.length; i++) { var chr = value.charAt(i); var cCode = value.charCodeAt(i); switch (this.format.charAt(i)) { case 'h': if ((chr == " ") || !((0xAC00 <= cCode && cCode <= 0xD7A3) || (0x3131 <= cCode && cCode <= 0x318E))) invalid = true; break; case 'H': var cCode = value.charCodeAt(i); if ((chr != " ") && !((0xAC00 <= cCode && cCode <= 0xD7A3) || (0x3131 <= cCode && cCode <= 0x318E))) invalid = true; break; case '0': if (isNaN(chr) || chr == " ") invalid = true; break; case '9': if (isNaN(chr)) { if (chr != " ") invalid = true; } break; case 'A': if ((chr == " ") || !isNaN(chr)) invalid = true; break; case 'Z': if ((chr != " ") && !isNaN(chr)) invalid = true; break; case '#': break; default: if (chr != this.format.charAt(i)) invalid = true; } if (invalid) { this.message = DU.getMessageManager().get("$.base.msg024", [this.format]); return false; } } } }); /** * LNumberValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LNumberValidator 숫자 여부를 판단하는 validator * { id: 'col6', validExp:'Col6:true:number'} * @namespace DU.validate * @class LNumberValidator * @extends DU.validate.LValidator * @constructor LNumberValidator * @param {Object} oConfig The intial LNumberValidator. */ DU.validate.LNumberValidator = function(id, format, oConfig){ this.config = oConfig || {}; DU.validate.LNumberValidator.superclass.constructor.call(this, id, oConfig); if (!format) return; var r = format.match(DU.validate.LNumberValidator.re); if (r) { this.iLength = Number(r[1]); this.dLength = Number(r[2]); } else { this.iLength = null; this.dLength = null; } } DU.extend(DU.validate.LNumberValidator, DU.validate.LValidator, { otype: 'DU.validate.LNumberValidator', validate : function(value) { if (isNaN(value)) { this.message = DU.getMessageManager().get("$.base.msg005"); return false; } try{ parseFloat(value); if(String(value).length > 0 && String(value).indexOf('-') > 0) throw new Error(); }catch(e) { this.message = DU.getMessageManager().get("$.base.msg005"); return false; } if (DU.isNull(this.iLength) || DU.isUndefined(this.iLength)) return true; var strValue = String(value); var idx = strValue.indexOf('.'); if (idx == -1) { this.message = DU.getMessageManager().get("$.base.msg036", [this.dLength]); return false; } var iNumStr = strValue.substr(0, idx); var dNumStr = strValue.substr(idx + 1); if (iNumStr.length > this.iLength) { this.message = DU.getMessageManager().get("$.base.msg035", [this.iLength]); return false; } else if (dNumStr.length != this.dLength) { this.message = DU.getMessageManager().get("$.base.msg036", [this.dLength]); return false; } return true; } }); DU.validate.LNumberValidator.re = /\s*(\d+)\s*.\s*(\d+)\s*/; /** * LRequiredValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LRequiredValidator 필수 여부 * @namespace DU.validate * @class LRequiredValidator * @extends DU.validate.LValidator * @constructor LRequiredValidator * @param {Object} oConfig The intial LRequiredValidator. */ DU.validate.LRequiredValidator = function(id, oConfig){ DU.validate.LRequiredValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.msgId = '$.base.msg001'; } DU.extend(DU.validate.LRequiredValidator, DU.validate.LValidator, { otype: 'DU.validate.LRequiredValidator', validate : function(value) { if (DU.isEmpty(value)) { this.message = DU.getMessageManager().get(this.msgId); return false; } return true; } }); /** * LByteLengthValidator byte로 길이를 체크하는 validator * { id: 'col4', validExp:'Col4:true:byteLength=4'} * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LByteLengthValidator * @namespace DU.validate * @class LByteLengthValidator * @extends DU.validate.LValidator * @constructor LByteLengthValidator * @param {Object} oConfig The intial LByteLengthValidator. */ DU.validate.LByteLengthValidator = function(id, length, oConfig){ DU.validate.LByteLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg029'; } DU.extend(DU.validate.LByteLengthValidator, DU.validate.LValidator, { otype: 'DU.validate.LByteLengthValidator', validate : function(value) { if(DU.isNull(value) || DU.isUndefined(value)) return true; value += ''; if (!this.checkCondition(this.getByteLength(value), this.length)) { this.message = DU.getMessageManager().get(this.msgId, [this.length, Math.round(this.length / 3)]); return false; } return true; }, getByteLength : function (value) { var byteLength = 0, c; for(var i = 0; i < value.length; i++) { c = escape(value.charAt(i)); if (c.length == 1) { byteLength ++; } else if (c.indexOf("%u") != -1) { // utf-8일 경우 3byte byteLength += 3; } else if (c.indexOf("%") != -1) { byteLength += c.length/3; } } return byteLength; }, checkCondition : function (value, vValue) { return value == vValue; } }); /** * LFilterValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * filterValidator : 지정된 문자가 들어있을 경우 유효하지 않은 것으로 판단한다. * { id: 'col16', validExp:'Col16:true:filter=%;<;\\h;\\;;haha'} * Wild 문자 * ; - \; * 한글 - \h * 영문 - \a * 숫자 - \n * @namespace DU.validate * @class LFilterValidator * @extends DU.validate.LValidator * @constructor LFilterValidator * @param {Object} oConfig The intial LFilterValidator. */ DU.validate.LFilterValidator = function(id, fStr, oConfig){ DU.validate.LFilterValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.fStrArr = DU.util.LString.advancedSplit(fStr, ";", "i"); for (var i = 0; i < this.fStrArr.length; i++) { if (this.fStrArr[i] == "\\h") { this.fStrArr[i] = "한글"; } else if (this.fStrArr[i] == "\\a") { this.fStrArr[i] = "영문"; } else if (this.fStrArr[i] == "\\n") { this.fStrArr[i] = "숫자"; } } this.msgId = '$.base.msg033'; } DU.extend(DU.validate.LFilterValidator, DU.validate.LValidator, { otype: 'DU.validate.LFilterValidator', validate : function(value) { for (var i = 0; i < this.fStrArr.length; i++) { var fStr = this.fStrArr[i]; if ( ((fStr == "한글" || fStr == "영문" || fStr == "숫자") && !this.checkWildChr(value, fStr)) || (value.indexOf(fStr) != -1) ) { this.message = DU.getMessageManager().get(this.msgId, fStr); return false; } } return true; }, checkWildChr : function (value, fChr) { for (var i = 0; i < value.length; i++) { var chr = value.charAt(i); var cCode = chr.charCodeAt(0); if ((fChr == "한글" && ((0xAC00 <= cCode && cCode <= 0xD7A3) || (0x3131 <= cCode && cCode <= 0x318E))) || (fChr == "영문" && ((0x61 <= cCode && cCode <= 0x7A) || (0x41 <= cCode && cCode <= 0x5A))) || (fChr == "숫자" && !isNaN(chr)) ) { return false; } } return true; } }); /** * LInNumberValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LInNumberValidator 범위안에 숫자가 존재하는지 체크하는 validator * { id: 'col10', validExp:'Col10:true:inNumber=90~100'} * @namespace DU.validate * @class LInNumberValidator * @extends DU.validate.LValidator * @constructor LInNumberValidator * @param {Object} oConfig The intial LInNumberValidator. */ DU.validate.LInNumberValidator = function(id, inNumber, oConfig){ var index = inNumber.indexOf("~"); this.minNumber = inNumber.substring(0, index); this.maxNumber = inNumber.substr(index+1); if (isNaN(this.minNumber) || isNaN(this.maxNumber)) throw new DU.LException(); DU.validate.LInNumberValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.minNumber = Number(this.minNumber); this.maxNumber = Number(this.maxNumber); } DU.extend(DU.validate.LInNumberValidator, DU.validate.LValidator, { otype: 'DU.validate.LInNumberValidator', validate : function(value) { if (isNaN(value)) { this.message = DU.getMessageManager().get("$.base.msg005"); return false; } value = Number(value); if (value < this.minNumber || value > this.maxNumber) { this.message = DU.getMessageManager().get("$.base.msg004", [this.minNumber,this.maxNumber]); return false; } return true; } }); /** * LMinByteLengthValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMinByteLengthValidator byte로 최소 길이를 체크하는 validator * { id: 'col5', validExp:'Col5:true:minByteLength=8'} * @namespace DU.validate * @class LMinByteLengthValidator * @extends DU.validate.LValidator * @constructor LMinByteLengthValidator * @param {Object} oConfig The intial LMinByteLengthValidator. */ DU.validate.LMinByteLengthValidator = function(id, length, oConfig){ DU.validate.LMinByteLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg030'; } DU.extend(DU.validate.LMinByteLengthValidator, DU.validate.LByteLengthValidator, { otype: 'DU.validate.LMinByteLengthValidator', checkCondition : function (value, vValue) { return value >= vValue; } }); /** * LMinDateValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMinDateValidator 최소 입력 날짜인지 확인하는 validator * { id: 'col12', validExp:'Col12:true:minDate=2008/11/11(YYYY/MM/DD)'} * @namespace DU.validate * @class LMinDateValidator * @extends DU.validate.LValidator * @constructor LMinDateValidator * @param {Object} oConfig The intial LMinDateValidator. */ DU.validate.LMinDateValidator = function(id, minDate, oConfig){ var index = minDate.indexOf("("); this.format = "YYYYMMDD"; this.minDate = minDate; if (index != -1) { this.minDate = minDate.substring(0, index); this.format = minDate.substring(index + 1, minDate.length - 1); } if (!(new DU.validate.LDateValidator(id, this.format).validate(this.minDate))) throw new DU.LException(); DU.validate.LMinDateValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.msgId = '$.base.msg016'; } DU.extend(DU.validate.LMinDateValidator, DU.validate.LValidator, { otype: 'DU.validate.LMinDateValidator', validate : function(value) { if (!(new DU.validate.LDateValidator(this.id, this.format).validate(value))) { this.message = DU.getMessageManager().get(this.msgId); return false; } if (!this.checkCondition(value, this.minDate)) { var msgParams = new Array(4); msgParams[0] = this.minDate.substring(0,4); msgParams[1] = this.minDate.substring(4,5) == "0" ? this.minDate.substring(5,6) : this.minDate.substring(4,6); msgParams[2] = this.minDate.substring(6,7) == "0" ? this.minDate.substring(7,8) : this.minDate.substring(6,8) this.message = DU.getMessageManager().get(this.msgId, msgParams); return false; } return true; }, checkCondition : function (value, vValue) { return (value >= vValue); } }); /** * LMinLengthValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMinLengthValidator 최소 길이를 확인하는 validator * { id: 'col3', validExp:'Col3:true:minLength=6'} * @namespace DU.validate * @class LMinLengthValidator * @extends DU.validate.LValidator * @constructor LMinLengthValidator * @param {Object} oConfig The intial LMinLengthValidator. */ DU.validate.LMinLengthValidator = function(id, length, oConfig){ DU.validate.LMinLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg009'; } DU.extend(DU.validate.LMinLengthValidator, DU.validate.LValidator, { otype: 'DU.validate.LMinLengthValidator', validate : function(value) { if(DU.isNull(value) || DU.isUndefined(value)) return true; value += ''; if (!this.checkCondition(value, this.length)) { this.message = DU.getMessageManager().get(this.msgId, [this.length]); return false; } return true; }, checkCondition : function (value, vValue) { return value.length >= vValue; } }); /** * LMinNumberValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMinNumberValidator 최소 숫자를 확인하는 validator * { id: 'col8', validExp:'Col8:true:minNumber=100'} * @namespace DU.validate * @class LMinNumberValidator * @extends DU.validate.LValidator * @constructor LMinNumberValidator * @param {Object} oConfig The intial LMinNumberValidator. */ DU.validate.LMinNumberValidator = function(id, minNumber, oConfig){ if (isNaN(minNumber)) throw new DU.LException(); DU.validate.LMinNumberValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.minNumber = Number(minNumber); } DU.extend(DU.validate.LMinNumberValidator, DU.validate.LValidator, { otype: 'DU.validate.LMinNumberValidator', validate : function(value) { if (isNaN(value)) { this.message = DU.getMessageManager().get("$.base.msg005"); return false; } this.minNumber = Number(this.minNumber); value = Number(value); if (value < this.minNumber) { this.message = DU.getMessageManager().get("$.base.msg011", [this.minNumber]); return false; } return true; } }); /** * LMaxByteLengthValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMaxByteLengthValidator byte로 최대 길이를 체크하는 validator * { id: 'col5', validExp:'Col5:true:maxByteLength=8'} * @namespace DU.validate * @class LMaxByteLengthValidator * @extends DU.validate.LValidator * @constructor LMaxByteLengthValidator * @param {Object} oConfig The intial LMaxByteLengthValidator. */ DU.validate.LMaxByteLengthValidator = function(id, length, oConfig){ DU.validate.LMaxByteLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg031'; } DU.extend(DU.validate.LMaxByteLengthValidator, DU.validate.LByteLengthValidator, { otype: 'DU.validate.LMaxByteLengthValidator', checkCondition : function (value, vValue) { return value <= vValue; } }); /** * LMaxDateValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMaxDateValidator 기준 날짜를 초과하는지 체크하는 validator * { id: 'col13', validExp:'Col13:true:maxDate=20081111'} * @namespace DU.validate * @class LMaxDateValidator * @extends DU.validate.LValidator * @constructor LMaxDateValidator * @param {Object} oConfig The intial LMaxDateValidator. */ DU.validate.LMaxDateValidator = function(id, inNumber, oConfig){ DU.validate.LMaxDateValidator.superclass.constructor.call(this, id, inNumber, oConfig); this.config = oConfig || {}; this.msgId = '$.base.msg023'; } DU.extend(DU.validate.LMaxDateValidator, DU.validate.LMinDateValidator, { otype: 'DU.validate.LMaxDateValidator', checkCondition : function (value, vValue) { return (value <= vValue); } }); /** * LMaxLengthValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMaxLengthValidator 최대 길이를 초과하는지 체크하는 validator * { id: 'col3', validExp:'Col3:true:maxLength=6'} * @namespace DU.validate * @class LMaxLengthValidator * @extends DU.validate.LValidator * @constructor LMaxLengthValidator * @param {Object} oConfig The intial LMaxLengthValidator. */ DU.validate.LMaxLengthValidator = function(id, length, oConfig){ DU.validate.LMaxLengthValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.length = length; this.msgId = '$.base.msg010'; } DU.extend(DU.validate.LMaxLengthValidator, DU.validate.LValidator, { otype: 'DU.validate.LMaxLengthValidator', validate : function(value) { if(DU.isNull(value) || DU.isUndefined(value)) return true; value += ''; if (!this.checkCondition(value, this.length)) { this.message = DU.getMessageManager().get(this.msgId, [this.length]); return false; } return true; }, checkCondition : function (value, vValue) { return (value+'').length <= vValue; } }); /** * LMaxNumberValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LMaxNumberValidator 최대 숫자를 초과하는지 체크하는 validator * { id: 'col9', validExp:'Col9:true:maxNumber=100'} * @namespace DU.validate * @class LMaxNumberValidator * @extends DU.validate.LValidator * @constructor LMaxNumberValidator * @param {Object} oConfig The intial LMaxNumberValidator. */ DU.validate.LMaxNumberValidator = function(id, maxNumber, oConfig){ if (isNaN(maxNumber)) throw new DU.LException(); DU.validate.LMaxNumberValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.maxNumber = Number(maxNumber); } DU.extend(DU.validate.LMaxNumberValidator, DU.validate.LValidator, { otype: 'DU.validate.LMaxNumberValidator', validate : function(value) { if (isNaN(value)) { this.message = DU.getMessageManager().get("$.base.msg005"); return false; } this.maxNumber = Number(this.maxNumber); value = Number(value); if (value > this.maxNumber) { this.message = DU.getMessageManager().get("$.base.msg012", [this.maxNumber]); return false; } return true; } }); /** * LSsnValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LSsnValidator 주민번호인지 체크하는 validator * @namespace DU.validate * @class LSsnValidator * @extends DU.validate.LValidator * @constructor LSsnValidator * @param {Object} oConfig The intial LSsnValidator. */ DU.validate.LSsnValidator = function(id, oConfig){ DU.validate.LSsnValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; } DU.extend(DU.validate.LSsnValidator, DU.validate.LValidator, { otype: 'DU.validate.LSsnValidator', validate : function(value) { if ( !value || (value+'').length != 13 || isNaN(value) ) { this.message = DU.getMessageManager().get("$.base.msg014"); 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) { this.message = DU.getMessageManager().get("$.base.msg014"); 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]) { this.message = DU.getMessageManager().get("$.base.msg014"); return false; } return true; } }); /** * LCsnValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LCsnValidator 사업자 번호인지 체크하는 validator * { id: 'col15', validExp:'Col15:true:csn'} * @namespace DU.validate * @class LCsnValidator * @extends DU.validate.LValidator * @constructor LCsnValidator * @param {Object} oConfig The intial LCsnValidator. */ DU.validate.LCsnValidator = function(id, oConfig){ DU.validate.LCsnValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; } DU.extend(DU.validate.LCsnValidator, DU.validate.LValidator, { otype: 'DU.validate.LCsnValidator', validate : function(value) { if (!value || (value+'').length != 10 || isNaN(value)) { this.message = DU.getMessageManager().get("$.base.msg015"); return false; } if (DU.util.LString.isCsn(value) == false) { this.message = DU.getMessageManager().get("$.base.msg015"); return false; } return true; } }); /** * LEmailValidator * @module validate * @title Validator * @requires DU */ DU.namespace("DU.validate"); /** * LEmailValidator 이메일인지 체크하는 validator * { id: 'col18', validExp:'Col18:true:email'} * @namespace DU.validate * @class LEmailValidator * @extends DU.validate.LValidator * @constructor LEmailValidator * @param {Object} oConfig The intial LEmailValidator. */ DU.validate.LEmailValidator = function(id, oConfig){ DU.validate.LEmailValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; } DU.extend(DU.validate.LEmailValidator, DU.validate.LValidator, { otype: 'DU.validate.LEmailValidator', validate : function(value) { if (DU.util.LString.isEmail(value) == false) { this.message = DU.getMessageManager().get("$.base.msg034"); return false; } return true; } }); /** * LAllowValidator * @module validate * @title LAllowValidator * @requires DU */ DU.namespace("DU.validate"); /** * AllowCharValidator : 지정된 문자가 들어있을 경우 유효한것으로 판단한다. * { id: 'col17', validExp:'Col17:true:allow=\\a;\\n'} * Wild 문자 * ; - \; * 한글 - \h * 영문 - \a * 숫자 - \n * @namespace DU.validate * @class LAllowValidator * @extends DU.validate.LValidator * @constructor LAllowValidator * @param {Object} oConfig The intial LAllowValidator. */ DU.validate.LAllowValidator = function(id, fStr, oConfig){ DU.validate.LAllowValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.fStrArr = DU.util.LString.advancedSplit(fStr, ";", "i"); this.msgId = '$.base.msg038'; var msgFStr = fStr.split(';'); for (var i = 0; i < msgFStr.length; i++) { if (msgFStr[i] == "\\h") { msgFStr[i] = DU.getMessageManager().get('$.core.kor'); // 한글 } else if(msgFStr[i] == "\\a") { msgFStr[i] = DU.getMessageManager().get('$.core.eng'); // 영어 } else if(msgFStr[i] == "\\n") { msgFStr[i] = DU.getMessageManager().get('$.core.num'); // 숫자 } } this.message = DU.getMessageManager().get(this.msgId, [msgFStr.join(',')]); } DU.extend(DU.validate.LAllowValidator, DU.validate.LValidator, { otype: 'DU.validate.LAllowValidator', validate : function(value) { value += ''; for (var i = 0; i < value.length; i++) { var chr = value.charAt(i); var cCode = chr.charCodeAt(0); if ((0xAC00 <= cCode && cCode <= 0xD7A3) || (0x3131 <= cCode && cCode <= 0x318E)) { if(DU.util.LArray.indexOf(this.fStrArr, '\\h') == -1) return false; } else if((0x61 <= cCode && cCode <= 0x7A) || (0x41 <= cCode && cCode <= 0x5A)) { if(DU.util.LArray.indexOf(this.fStrArr, '\\a') == -1) return false; } else if(!isNaN(chr)) { if(DU.util.LArray.indexOf(this.fStrArr, '\\n') == -1) return false; } else { var isValid = false; for(var j = 0 ; j < this.fStrArr.length; j++) { if(this.fStrArr[j] == '\\h' || this.fStrArr[j] == '\\a' || this.fStrArr[j] == '\\n') continue; isValid = true; if(this.fStrArr[j].indexOf(chr) < 0) { isValid = false; break; } } if(isValid == false) return false; } } return true; } }); /** * LGroupRequireValidator * @module validate * @title LGroupRequireValidator * @requires DU */ DU.namespace("DU.validate"); /** * Checkbox나 Radiobox의 필수 여부를 체크하는 validator * { id: 'col18', validExp:'Col18:true:groupName=col8'} * @namespace DU.validate * @class LGroupRequireValidator * @extends DU.validate.LValidator * @constructor LGroupRequireValidator * @param {Object} oConfig The intial LGroupRequireValidator. */ DU.validate.LGroupRequireValidator = function(id, groupName, oConfig){ DU.validate.LGroupRequireValidator.superclass.constructor.call(this, id, oConfig); this.config = oConfig || {}; this.groupName = groupName; this.message = DU.getMessageManager().get('$.base.msg039', [1]); } DU.extend(DU.validate.LGroupRequireValidator, DU.validate.LValidator, { otype: 'DU.validate.LGroupRequireValidator', validate : function(value) { if(DU.select('input[name="' + this.groupName + '"]:checked').length == 0) return false; return true; } }); DU.namespace("DU.widget"); /** * @description LUIComponent * @module widget * @namespace DU.widget * @class LUIComponent * @extends DU.util.LEventProvider * @constructor * @param {Object} oConfig The intial LUIComponent. */ DU.widget.LUIComponent = function(oConfig){ var config = oConfig || {}; this._events = this._events || {}; DU.widget.LUIComponent.superclass.constructor.call(this); DU.applyObject(this, config, true); this.deferOnBlurDelegate = DU.util.LFunction.createDelegate(this.deferOnBlur, this); /** * @description disable 메소드가 호출되면 수행하는 이벤트 * @event disable */ this.disableEvent = this.createEvent('disable'); /** * @description enable 메소드가 호출되면 수행하는 이벤트 * @event enable */ this.enableEvent = this.createEvent('enable'); /** * @description show 메소드가 호출되면 수행하는 이벤트 * @event show */ this.showEvent = this.createEvent('show'); /** * @description hide 메소드가 호출되면 수행하는 이벤트 * @event hide */ this.hideEvent = this.createEvent('hide'); /** * @description focus 메소드가 호출되면 수행하는 이벤트 * @event focus */ this.focusEvent = this.createEvent('focus'); /** * @description blur 메소드가 호출되면 수행하는 이벤트 * @event blur */ this.blurEvent = this.createEvent('blur'); /** * @description render 메소드가 호출되면 수행하는 이벤트 * @event render */ this.renderEvent = this.createEvent('render'); /** * @description destroy 메소드가 호출되면 수행하는 이벤트 * @event destroy */ this.destroyEvent = this.createEvent('destroy'); /** * @description resize 메소드가 호출되면 수행하는 이벤트 * @event resize */ this.resizeEvent = this.createEvent('resize'); /** * move 메소드가 호출되면 수행하는 이벤트 * @event move */ this.moveEvent = this.createEvent('move'); if(this.plugin) this.plugin.initPlugin(this); this.init(oConfig); if(this.applyTo){ this.applyToMarkup(this.applyTo); delete this.applyTo; }else if(this.renderTo){ this.render(this.renderTo); delete this.renderTo; } } DU.extend(DU.widget.LUIComponent, DU.util.LEventProvider, { /** * @description disabled 여부 * @config disabled * @type {Boolean} * @default false */ /** * @description disabled 여부 * @property disabled * @private * @type {Boolean} */ disabled : false, /** * @description blur시 contains 체크를 할지 여부 * @property isCheckContainBlur * @private * @type {Boolean} */ isCheckContainBlur : true, /** * @description 객체를 초기화하는 메소드 * @method init * @private * @param {Object} oConfig 환경정보 객체 * @return void */ init : function(oConfig) { this.initDefaultConfig(); if (oConfig) { this.cfg.applyConfig(oConfig, true); } this.initComponent(oConfig); this.initEvents(); if (!DU.widget.LConfig.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) { this.renderEvent.on(this.cfg.fireQueue, this.cfg, true); } }, /** * @description element Dom객체 생성 * @method createElement * @protected * @return {HTMLElement} */ createElement : function() { var el = document.createElement('div'); return el; }, /** * @description element Dom객체 생성 * @method createContainer * @protected * @param {HTMLElement} parentNode 부모 노드 * @return {HTMLElement} */ createContainer : function(parentNode) { if(!this.el) { this.el = DU.get(this.createElement().cloneNode(false)); this.id = this.id || this.el.id; } return this.el; }, /** * @description Dom객체 생성 및 초기화하는 메소드 * @method initComponent * @protected * @param {String|Object} el 객체의 아이디나 객체 * @param {Object} oConfig 환경정보 객체 * @return void */ initComponent : function(oConfig){ }, /** * @description 객체의 이벤트 초기화 메소드 * @method initEvents * @protected * @return void */ initEvents : function() { }, /** * @description 객체를 Config정보를 초기화하는 메소드 * @method initDefaultConfig * @protected * @return void */ initDefaultConfig : function() { this.cfg = new DU.widget.LConfig(this); this.cfg.addProperty("width", { handler: this._setWidth, value: this.width, validator: DU.isNumber }); this.cfg.addProperty("height", { handler: this._setHeight, value: this.height, validator: DU.isNumber }); this.cfg.addProperty("left", { handler: this._setLeft, validator: DU.isNumber }); this.cfg.addProperty("top", { handler: this._setTop, validator: DU.isNumber }); this.cfg.addProperty("disabled", { handler: this._setDisabled, value: false, validator: DU.isBoolean }); }, /** * @description 객체를 Render하는 메소드 * @method render * @public * @param {String|Object} appendToNode 객체를 붙이고자 하는 Node정보 * @return void */ render : function(appendToNode) { if (appendToNode) { this.createContainer(appendToNode); this.appendTo(appendToNode); this.doRender(appendToNode); } else { if (! DU.util.LDom.inDocument(this.el.dom)) { return false; } } this.dom = this.el.dom; this.renderEvent.fire(); this.afterRender(appendToNode); this._rendered = true; }, /** * @description render시 호출되는 메소드 * @method doRender * @public * @param {String|Object} container 부모객체 정보 * @return void */ doRender : function(container){ }, /** * @description render후 호출되는 메소드 * @method afterRender * @protected * @param {HTMLElement} container 부모 객체 * @return void */ afterRender : function(container) { if(this.el) { this.el.on('focus', this.onFocus, this, true); this.el.on('blur', this.onBlur, this, true); } }, /** * @description focus 여부를 확인하는 메소드 * @method onCheckFocus * @protected * @param {Object} e Event 객체 * @return {void} */ onCheckFocus : function(e) { if(!this.isFocus) { this.onFocus.call(this, e); if(this.isCheckContainBlur == true) { DU.util.LEvent.addListener(DU.browser.msie ? document.body : document, "mousedown", this.deferOnBlurDelegate, this, true); this.isFocus = true; } } }, /** * @description blur 이벤트를 취소후 재 등록하는 메소드 (이벤트 순서 변경) * @method reOnDeferOnBlur * @protected * @return {void} */ reOnDeferOnBlur: function() { DU.util.LEvent.removeListener(DU.browser.msie ? document.body : document, "mousedown", this.deferOnBlurDelegate, this); DU.util.LEvent.addListener(DU.browser.msie ? document.body : document, "mousedown", this.deferOnBlurDelegate, this, true); }, /** * @description blur 이벤트 발생시 defer를 연결하는 메소드 * @method deferOnBlur * @protected * @param {Object} e Event 객체 * @return {void} */ deferOnBlur : function(e) { var checkBlur = function(e) { if(e.deferCancelBubble == true) return; var target = e.target; if(!this.el.isAncestor(target)) { if(this.canBlur(e) === false) return; DU.util.LEvent.removeListener(DU.browser.msie ? document.body : document, "mousedown", this.deferOnBlurDelegate, this); this.onBlur.call(this, e); this.isFocus = null; } else e.deferCancelBubble = true; } DU.util.LFunction.defer(checkBlur, 10, this, [e]); }, /** * @description blur 이벤트 발생시 호출되는 메소드 * @method canBlur * @private * @param {Object} e Event 객체 * @return void */ canBlur: function(e) { }, /** * @description focus 이벤트 발생시 호출되는 메소드 * @method onFocus * @private * @param {Object} e Event 객체 * @return void */ onFocus : function(e) { this.focusEvent.fire(this); }, /** * @description blur 이벤트 발생시 호출되는 메소드 * @method onBlur * @private * @param {Object} e Event 객체 * @return void */ onBlur : function(e) { this.blurEvent.fire(this); }, /** * @description 객체를 destroy하는 메소드 * @method destroy * @public * @return void */ destroy : function() { this.destroyEvent.fire(); this.unOnAll(); if(this.el) { this.el.unOnAll(); this.el.remove(); this.el = null; } if(this.cfg) { this.cfg.destroy(); this.cfg = null; } }, /** * @description visMode가 true면 visibility에 설정 false거나 없으면 display에 설정한다. * @method setVisibilityMode * @param {Boolean} visMode visibility로 설정할지 display로 설정할지 결정하는 값 * @return {void} */ setVisibilityMode : function(visMode) { this.el.setVisibilityMode(visMode); }, /** * @description disabled 속성에 따른 실제 적용 메소드 * @method _setDisabled * @protected * @param {String} type 속성의 이름 * @param {Array} args 속성의 값 배열 * @param {Object} obj 적용된 객체 * @return {void} */ _setDisabled : function(type, args, obj) { args = args[0]; if(args === false) { this.disabled = false; if (this.enableMaskEl) { this.enableMaskEl.removeNode(); this.enableMaskEl = null; } this.enableEvent.fire(); } else { this.disabled = true; if(!this.enableMaskEl) { var enableMaskDom = document.createElement('div'); this.el.appendChild(enableMaskDom); this.enableMaskEl = DU.get(enableMaskDom); this.enableMaskEl.addClass('L-disable-mask'); this.enableMaskEl.addClass('L-disabled'); this.enableMaskEl.setRegion(this.el.getRegion()); this.enableMaskEl.on('click', function(e){ DU.util.LEvent.stopEvent(e); }, this, true) } this.disableEvent.fire(); } }, /** * @description width 속성에 따른 실제 적용 메소드 * @method _setWidth * @protected * @param {String} type 속성의 이름 * @param {Array} args 속성의 값 배열 * @param {Object} obj 적용된 객체 * @return void */ _setWidth : function(type, args, obj) { var width = args[0]; if(width < 0) return; this.el.setWidth(width); this.width = width; }, /** * @description height 속성에 따른 실제 적용 메소드 * @method _setHeight * @protected * @param {String} type 속성의 이름 * @param {Array} args 속성의 값 배열 * @param {Object} obj 적용된 객체 * @return void */ _setHeight: function(type, args, obj) { var height = args[0]; if(height < 0) return; this.el.setHeight(height); this.height = height; }, /** * @description left 속성에 따른 실제 적용 메소드 * @method _setLeft * @protected * @param {String} type 속성의 이름 * @param {Array} args 속성의 값 배열 * @param {Object} obj 적용된 객체 * @return void */ _setLeft : function(type, args, obj) { var left = args[0]; this.el.setLeft(left); }, /** * @description top 속성에 따른 실제 적용 메소드 * @method _setTop * @protected * @param {String} type 속성의 이름 * @param {Array} args 속성의 값 배열 * @param {Object} obj 적용된 객체 * @return void */ _setTop : function(type, args, obj) { var top = args[0]; this.el.setTop(top); }, /** * @description 객체를 사용 가능하게 하는 메소드 * @method enable * @public * @return {void} */ enable : function() { this.cfg.setProperty("disabled", false); }, /** * @description 객체를 사용 불가능하게 하는 메소드 * @method disable * @public * @return {void} */ disable : function() { this.cfg.setProperty("disabled", true); }, /** * @description 객체를 보이게 설정하는 메소드 * @method show * @public * @param {Boolean|DU.animation.LAnim} anim (optional) Animation 여부를 설정한다. Boolean값이면 디폴트 animation을 실행하고 객체면 해당 객체에 설정된 animation을 수행한다. * @return {void} */ show : function(anim) { this.el.show(anim); this.showEvent.fire(); }, /** * @description 객체를 안보이게 설정하는 메소드 * @method hide * @public * @param {Boolean|DU.animation.LAnim} anim (optional) Animation 여부를 설정한다. Boolean값이면 디폴트 animation을 실행하고 객체면 해당 객체에 설정된 animation을 수행한다. * @return {void} */ hide : function(anim) { this.el.hide(anim); this.hideEvent.fire(); }, /** * @description 객체를 focus한다. * @method focus * @public * @return {void} */ focus : function() { this.el.focus(); }, /** * @description 객체를 blur한다. * @method blur * @public * @return {void} */ blur : function() { this.el.blur(); }, /** * @description 엘리먼트의 오프셋 높이를 리턴한다. * @method getHeight * @public * @param {Boolean} contentHeight (optional) : border와 padding을 뺀 높이를 가져오게 하려면 true * @return {Number} The element's height */ getHeight : function(contentHeight){ return this.el.getHeight(contentHeight); }, /** * 엘리먼트의 높이를 설정한다. *


// 높이를 200px로 바꾸고 default configuration으로 동작한다.

DU.get('elementId').setHeight(200, true);



// 높이를 150px로 바꾸고 custom configuration으로 동작한다.

DU.get('elId').setHeight(150, {

duration : .5, // 동작이 .5초 동안 지속된다

// 내용을 "finished"로 변환한다

callback: function(){ this.{@link #update}("finished"); } 

});

     * 
* @method setHeight * @param {Mixed} height 새로운 높이. 다음 중 하나:
    *
  • Number : 엘리먼트의 {@link #defaultUnit}s (by default, pixels.)에서 새로운 높이를 나타냄
  • *
  • String : 높이와 관련된 CSS 스타일을 설정하는데 사용된다. 애니메이션은 사용되지 않을 수 있다.
  • *
* @return {void} */ setHeight : function(height){ this.cfg.setProperty("height", height); }, /** * @description 엘리먼트의 오프셋 넓이를 리턴한다. * @method getWidth * @public * @param {Boolean} contentWidth (optional) : border와 padding을 뺀 높이를 가져오게 하려면 true * @return {Number} The element's width */ getWidth : function(contentWidth){ return this.el.getWidth(contentWidth); }, /** * @description 엘리먼트의 넓이를 설정한다. * @method setWidth * @public * @param {Mixed} width 새로운 넓이. 다음 중 하나:
    *
  • Number : 엘리먼트의 {@link #defaultUnit}s (by default, pixels)에서 새로운 넓이를 나타냄
  • *
  • String : 넓이와 관련된 CSS 스타일을 설정하는 데 사용된다. 애니메이션은 사용되지 않을 수있다.
  • *
* @return {void} */ setWidth : function(width){ this.cfg.setProperty("width", width); }, /** * @description 엘리먼트의 오프셋 Left를 리턴한다. * @method getLeft * @public * @return {Number} The element's left */ getLeft : function(){ return this.el.getLeft(); }, /** * @description 엘리먼트의 Left를 설정한다. * @method setLeft * @public * @param {Int} left 새로운 Left * @return {void} */ setLeft : function(left){ this.cfg.setProperty("left", left); }, /** * @description 엘리먼트의 오프셋 top를 리턴한다. * @method getTop * @public * @return {Number} The element's top */ getTop : function(){ return this.el.getTop(); }, /** * @description 엘리먼트의 top를 설정한다. * @method setTop * @public * @param {Int} left 새로운 top * @return {void} */ setTop : function(top){ this.cfg.setProperty("top", top); }, /** * @description 엘리먼트의 parent node를 가져온다. 선택사항으로 parent node와 일치하는 selector를 줄 수 있음. * @method parent * @param {String} selector (optional) Find a parent node that matches the passed simple selector * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an DU.LElement * @return {DU.LElement|HTMLElement} The parent node or null */ parent: function(selector, returnDom){ return this.el.parent(selector, returnDom); }, /** * @description IE에서 동작이 멈추는 에러를 유발하는 모듈을 위해 DOM 구조를 만들 때 사용되는 protected helper. * 일반적인 DOM 구조로 사용되면 안된다. *

* parentNode가 document.body가 아니면 엘리먼트는 마지막 엘리먼트에 append 된다. *

++ *

* parentNode가 document.body이면 IE의 동작이 멈추는 에러를 방지하기 위해 * 엘리먼트는 첫번째 child로 추가된다. *

* * @method _addToParent * @protected * @param {parentNode} The HTML element to which the element will be added * @param {element} The HTML element to be added to parentNode's children */ _addToParent: function(parentNode, element) { if (parentNode === document.body && parentNode.firstChild) { parentNode.insertBefore(element, parentNode.firstChild); } else { parentNode.appendChild(element); } }, /** * @description parentNode 중 하나에 HTMLElement를 append한다. * @method appendTo * @param {HTMLElement | Element} parentNode The node to append to * @return {void} */ appendTo : function(parentNode) { if (typeof parentNode == "string") { parentNode = document.getElementById(parentNode); } if (parentNode && this.el && this.el.dom) { if(parentNode != this.el.dom.parentNode) this._addToParent(parentNode, this.el.dom); } }, /** * @description CSS Selector로 child 객체를 배열로 리턴한다. * @method select * @param {String} selector CSS selector 문자열 * @param {Boolean} firstOnly 찾은 객체의 무조건 첫번째 객체를 리턴한다. * @return {DU.LElementList} DU.LElementList 객체 리턴 */ select: function(selector, firstOnly){ return this.el.select(selector, firstOnly); }, /** * @description CSS Selector로 child 객체를 배열로 리턴한다. * @method query * @param {String} selector CSS selector 문자열 * @param {Boolean} firstOnly 찾은 객체의 무조건 첫번째 객체를 리턴한다. * @return {Array} Array 객체 리턴 */ query: function(selector, firstOnly){ return this.el.query(selector, firstOnly); }, /** * @description CSS Selector로 현재 node중 selector로 지정된 child node만 배열로 리턴한다. * @method filter * @param {String} selector CSS selector 문자열 * @return {Array} Array 객체 리턴 */ filter: function(selector){ return this.el.query(selector, firstOnly); }, /** * @description el위치에 render하는 메소드 * @method applyToMarkup * @private * @param {String} el 부모 객체 * @return {void} */ applyToMarkup : function(el) { this.el = DU.get(el); if(!this.id) this.id = this.el.id; this.render(this.el.dom.parentNode); }, /** * @description 부모 객체를 리턴하는 메소드 * @method getContainer * @return {DU.LElement} */ getContainer : function(){ return this.el; }, /** * @description 객체의 toString * @method toString * @public * @return {String} */ toString : function() { return (this.otype || 'DU.widget.LUIComponent(Unknown)') + ' ' + this.id; } }); (function () { /** * The LButton class creates a rich, graphical button. * @param {String} p_oElement String specifying the id attribute of the * <input>, <button>, * <a>, or <span> element to * be used to create the button. * @param {HTMLInputElement| * HTMLButtonElement|HTMLElement} p_oElement Object reference for the * <input>, <button>, * <a>, or <span> element to be * used to create the button. * @param {Object} p_oElement Object literal specifying a set of * configuration attributes used to create the button. * @param {Object} p_oAttributes Optional. Object literal specifying a set * of configuration attributes used to create the button. * @namespace DU.widget * @class LButton * @constructor * @extends DU.LElement */ // Shorthard for utilities var Dom = DU.util.LDom, Event = DU.util.LEvent, UA = DU.browser, // Private member variables m_oButtons = {}, // Collection of all LButton instances m_oSubmitTrigger = null; // The button that submitted the form /** * @method setAttributesFromSrcElement * @description Gets the values for all the attributes of the source element * (either <input> or <a>) that * map to LButton configuration attributes and sets them into a collection * that is passed to the LButton constructor. * @private * @param {HTMLInputElement|HTMLAnchorElement} p_oElement Object reference to the HTML * element (either <input> or <span> * ) used to create the button. * @param {Object} p_oAttributes Object reference for the collection of * configuration attributes used to create the button. */ function setAttributesFromSrcElement(p_oElement, p_oAttributes) { var sSrcElementNodeName = p_oElement.nodeName.toUpperCase(), me = this, oAttribute, oRootNode, sText; /** * @method setAttributeFromDOMAttribute * @description Gets the value of the specified DOM attribute and sets it * into the collection of configuration attributes used to configure * the button. * @private * @param {String} p_sAttribute String representing the name of the * attribute to retrieve from the DOM element. */ function setAttributeFromDOMAttribute(p_sAttribute) { if (!(p_sAttribute in p_oAttributes)) { /* Need to use "getAttributeNode" instead of "getAttribute" because using "getAttribute," IE will return the innerText of a <button> for the value attribute rather than the value of the "value" attribute. */ oAttribute = p_oElement.getAttributeNode(p_sAttribute); if (oAttribute && ("value" in oAttribute)) { p_oAttributes[p_sAttribute] = oAttribute.value; } } } /** * @method setFormElementProperties * @description Gets the value of the attributes from the form element * and sets them into the collection of configuration attributes used to * configure the button. * @private */ function setFormElementProperties() { setAttributeFromDOMAttribute("type"); if (p_oAttributes.type == "button") { p_oAttributes.type = "push"; } if (!("disabled" in p_oAttributes)) { p_oAttributes.disabled = p_oElement.disabled; } setAttributeFromDOMAttribute("name"); setAttributeFromDOMAttribute("value"); setAttributeFromDOMAttribute("title"); } switch (sSrcElementNodeName) { case "A": p_oAttributes.type = "link"; setAttributeFromDOMAttribute("href"); setAttributeFromDOMAttribute("target"); break; case "INPUT": setFormElementProperties(); break; case "BUTTON": setFormElementProperties(); oRootNode = p_oElement.parentNode.parentNode; if (Dom.hasClass(oRootNode, this.CSS_CLASS_NAME + "-disabled")) { p_oAttributes.disabled = true; } p_oElement.removeAttribute("value"); p_oElement.setAttribute("type", "button"); break; } p_oElement.removeAttribute("id"); p_oElement.removeAttribute("name"); if (!("tabindex" in p_oAttributes)) { p_oAttributes.tabindex = p_oElement.tabIndex; } if (!("label" in p_oAttributes)) { // Set the "label" property sText = sSrcElementNodeName == "INPUT" ? p_oElement.value : p_oElement.innerHTML; if (sText && sText.length > 0) { p_oAttributes.label = sText; } } } /** * @method initConfig * @description Initializes the set of configuration attributes that are * used to instantiate the button. * @private * @param {Object} Object representing the button's set of * configuration attributes. */ function initConfig(p_oConfig) { var oAttributes = p_oConfig.attributes, oSrcElement = oAttributes.srcelement, sSrcElementNodeName = oSrcElement.nodeName.toUpperCase(), me = this; if (sSrcElementNodeName == this.NODE_NAME) { p_oConfig.element = oSrcElement; p_oConfig.id = oSrcElement.id; Dom.getElementsBy(function (p_oElement) { switch (p_oElement.nodeName.toUpperCase()) { case "BUTTON": case "A": case "INPUT": setAttributesFromSrcElement.call(me, p_oElement, oAttributes); break; } }, "*", oSrcElement); } else { switch (sSrcElementNodeName) { case "BUTTON": case "A": case "INPUT": setAttributesFromSrcElement.call(this, oSrcElement, oAttributes); break; } } } // Constructor DU.widget.LButton = function (p_oElement, p_oAttributes) { var fnSuperClass = DU.widget.LButton.superclass.constructor, oConfig, oElement; if (arguments.length == 1 && !DU.isString(p_oElement) && !p_oElement.nodeName) { if (!p_oElement.id) { p_oElement.id = Dom.generateId(); } fnSuperClass.call(this, (this.createButtonElement(p_oElement.type)), p_oElement); } else { oConfig = { element: null, attributes: (p_oAttributes || {}) }; if (DU.isString(p_oElement)) { oElement = Dom.get(p_oElement); if (oElement) { if (!oConfig.attributes.id) { oConfig.attributes.id = p_oElement; oConfig.attributes.className = oElement.className; } oConfig.attributes.srcelement = oElement; if (!oConfig.element) { oConfig.element = this.createButtonElement(oConfig.attributes.type); } initConfig.call(this, oConfig); fnSuperClass.call(this, oConfig.element, oConfig.attributes); } } else if (p_oElement.nodeName) { if (!oConfig.attributes.id) { if (p_oElement.id) { oConfig.attributes.id = p_oElement.id; } else { oConfig.attributes.id = Dom.generateId(); } } oConfig.attributes.srcelement = p_oElement; if (!oConfig.element) { oConfig.element = this.createButtonElement(oConfig.attributes.type); } initConfig.call(this, oConfig); fnSuperClass.call(this, oConfig.element, oConfig.attributes); } } }; DU.extend(DU.widget.LButton, DU.LElement, { // Protected properties /** * @description Object reference to the button's internal * <a> or <button> element. * @property _button * @default null * @protected * @type HTMLAnchorElement|HTMLButtonElement */ _button: null, // Constants /** * @description The name of the node to be used for the button's * root element. * @property NODE_NAME * @default "SPAN" * @final * @type String */ NODE_NAME: "SPAN", /** * @description String representing the CSS class(es) to be applied to * the button's root element. * @property CSS_CLASS_NAME * @default "bu-button" * @final * @type String */ CSS_CLASS_NAME: "L-button", // Protected attribute setter methods /** * @description Sets the value of the button's "label" attribute. * @method _setLabel * @protected * @param {String} p_sLabel String indicating the value for the button's * "label" attribute. */ _setLabel: function (p_sLabel) { this._button.innerHTML = p_sLabel; /* Remove and add the default class name from the root element for Gecko to ensure that the button shrinkwraps to the label. Without this the button will not be rendered at the correct width when the label changes. The most likely cause for this bug is button's use of the Gecko-specific CSS display type of "-moz-inline-box" to simulate "inline-block" supported by IE, Safari and Opera. */ var sClass, nGeckoVersion = UA.gecko; if (nGeckoVersion && nGeckoVersion < 1.9 && Dom.inDocument(this.get("element"))) { sClass = this.CSS_CLASS_NAME; this.removeClass(sClass); DU.later(0, this, this.addClass, sClass); } }, /** * @description Sets the value of the button's "disabled" attribute. * @method _setDisabled * @protected * @param {Boolean} p_bDisabled Boolean indicating the value for * the button's "disabled" attribute. */ _setDisabled: function (p_bDisabled) { if (this.get("type") != "link") { if (p_bDisabled) { this.blur(); this._button.setAttribute("disabled", "disabled"); this.addStateCSSClasses("disabled"); this.removeStateCSSClasses("focus"); } else { this._button.removeAttribute("disabled"); this.removeStateCSSClasses("disabled"); } } }, // Protected methods // Protected event handlers /** * @method _setOnClick * @description Sets the value of the button's "onclick" attribute. * @protected * @param {Object} p_oObject Object indicating the value for the button's * "onclick" attribute. */ _setOnClick: function (p_oObject) { /* Remove any existing listeners if a "click" event handler has already been specified. */ if (this._onclickAttributeValue && (this._onclickAttributeValue != p_oObject)) { this.removeListener("click", this._onclickAttributeValue.fn); this._onclickAttributeValue = null; } if (!this._onclickAttributeValue && DU.isObject(p_oObject) && DU.isFunction(p_oObject.fn)) { this.on("click", p_oObject.fn, p_oObject.obj, p_oObject.scope); this._onclickAttributeValue = p_oObject; } }, /** * @description "click" event handler for the button. * @method _onClick * @protected * @param {Event} p_oEvent Object representing the DOM event object * passed back by the event utility (DU.util.LEvent). */ _onClick: function (p_oEvent) { var sType = this.get("type"), oForm, oSrcElement, bReturnVal; switch (sType) { case "submit": if (p_oEvent.returnValue !== false) { this.submitForm(); } break; case "reset": oForm = this.getForm(); if (oForm) { oForm.reset(); } break; } return bReturnVal; }, // Public methods /** * @description Creates the button's HTML elements. * @method createButtonElement * @param {String} p_sType String indicating the type of element * to create. * @return {HTMLElement} */ createButtonElement: function (p_sType) { var sNodeName = this.NODE_NAME, oElement = document.createElement(sNodeName); oElement.innerHTML = "<" + sNodeName + " class=\"first-child\">" + (p_sType == "link" ? "" : "") + ""; return oElement; }, /** * @description Appends state-specific CSS classes to the button's root * DOM element. * @method addStateCSSClasses * @private */ addStateCSSClasses: function (p_sState) { var sType = this.get("type"); if (DU.isString(p_sState)) { if (p_sState != "activeoption" && p_sState != "hoveroption") { this.addClass(this.CSS_CLASS_NAME + ("-" + p_sState)); } this.addClass("L-" + sType + ("-button-" + p_sState)); } }, /** * @description Removes state-specific CSS classes to the button's root * DOM element. * @method removeStateCSSClasses * @private */ removeStateCSSClasses: function (p_sState) { var sType = this.get("type"); if (DU.isString(p_sState)) { this.removeClass(this.CSS_CLASS_NAME + ("-" + p_sState)); this.removeClass("L-" + sType + ("-button-" + p_sState)); } }, /** * @description Submits the form to which the button belongs. Returns * true if the form was submitted successfully, false if the submission * was cancelled. * @method submitForm * @protected * @return {Boolean} */ submitForm: function () { var oForm = this.getForm(), oSrcElement = this.get("srcelement"), /* Boolean indicating if the event fired successfully (was not cancelled by any handlers) */ bSubmitForm = false, oEvent; if (oForm) { if (this.get("type") == "submit" || (oSrcElement && oSrcElement.type == "submit")) { m_oSubmitTrigger = this; } if (UA.msie) { bSubmitForm = oForm.fireEvent("onsubmit"); } else { // Gecko, Opera, and Safari oEvent = document.createEvent("HTMLEvents"); oEvent.initEvent("submit", true, true); bSubmitForm = oForm.dispatchEvent(oEvent); } /* In IE and Safari, dispatching a "submit" event to a form WILL cause the form's "submit" event to fire, but WILL NOT submit the form. Therefore, we need to call the "submit" method as well. */ if ((UA.msie || UA.webkit) && bSubmitForm) { oForm.submit(); } } return bSubmitForm; }, /** * @description The LButton class's initialization method. * @method init * @param {String} p_oElement String specifying the id attribute of the * <input>, <button>, * <a>, or <span> element to * be used to create the button. * @param {HTMLInputElement|HTMLButtonElement| * HTMLElement} p_oElement Object reference for the * <input>, <button>, * <a>, or <span> element to be * used to create the button. * @param {Object} p_oElement Object literal specifying a set of * configuration attributes used to create the button. * @param {Object} p_oAttributes Optional. Object literal specifying a * set of configuration attributes used to create the button. */ init: function (p_oElement, p_oAttributes) { var sNodeName = p_oAttributes.type == "link" ? "a" : "button", oSrcElement = p_oAttributes.srcelement, oButton = p_oElement.getElementsByTagName(sNodeName)[0], oInput; if (!oButton) { oInput = p_oElement.getElementsByTagName("input")[0]; if (oInput) { oButton = document.createElement("button"); oButton.setAttribute("type", "button"); oInput.parentNode.replaceChild(oButton, oInput); } } this._button = oButton; /* Capture if the button has a value for the title attribute. If so, we won't override it for type of "checkbox" or "radio". */ DU.widget.LButton.superclass.init.call(this, p_oElement, p_oAttributes); var sId = this.get("id"), sButtonId = sId + "-button"; oButton.id = sButtonId; var aLabels, oLabel; var hasLabel = function (element) { return (element.htmlFor === sId); }; var setLabel = function () { oLabel.setAttribute((UA.msie ? "htmlFor" : "for"), sButtonId); }; if (oSrcElement && this.get("type") != "link") { aLabels = Dom.getElementsBy(hasLabel, "label"); if (DU.isArray(aLabels) && aLabels.length > 0) { oLabel = aLabels[0]; } } m_oButtons[sId] = this; this.addClass(this.CSS_CLASS_NAME); this.addClass("L-" + this.get("type") + "-button"); this.on("click", this._onClick); this.disableDbClick = !DU.isUndefined(p_oAttributes.disableDbClick) ? p_oAttributes.disableDbClick : DU.getConfig().getFirst('$.base.button.disableDbClick'); this.disableDbClickInterval = p_oAttributes.disableDbClickInterval || DU.getConfig().getFirst('$.base.button.disableDbClickInterval'); if(this.disableDbClick) { this.on("click", function(){ if(this.disableDbClick == false) return; this.disable(); var bMe = this; setTimeout(function(){ bMe.enable(); }, bMe.disableDbClickInterval); }); } if (oLabel) { this.on("appendTo", setLabel); } var oContainer = this.get("container"), oElement = this.get("element"), bElInDoc = Dom.inDocument(oElement), oParentNode; if (oContainer) { if (oSrcElement && oSrcElement != oElement) { oParentNode = oSrcElement.parentNode; if (oParentNode) { oParentNode.removeChild(oSrcElement); } } if (DU.isString(oContainer)) { Event.onContentReady(oContainer, this.appendTo, oContainer, this); } else { this.on("init", function () { DU.later(0, this, this.appendTo, oContainer); }); } } else if (!bElInDoc && oSrcElement && oSrcElement != oElement) { oParentNode = oSrcElement.parentNode; if (oParentNode) { this.fireEvent("beforeAppendTo", { type: "beforeAppendTo", target: oParentNode }); oParentNode.replaceChild(oElement, oSrcElement); this.fireEvent("appendTo", { type: "appendTo", target: oParentNode }); } } else if (this.get("type") != "link" && bElInDoc && oSrcElement && oSrcElement == oElement) { this._addListenersToForm(); } this.fireEvent("init", { type: "init", target: this }); }, /** * @description Initializes all of the configuration attributes used to * create the button. * @method initAttributes * @param {Object} p_oAttributes Object literal specifying a set of * configuration attributes used to create the button. */ initAttributes: function (p_oAttributes) { var oAttributes = p_oAttributes || {}; DU.widget.LButton.superclass.initAttributes.call(this, oAttributes); /** * @description String specifying the button's text label * or innerHTML. * @attribute label * @default null * @type String */ this.setAttributeConfig("label", { value: oAttributes.label, validator: DU.isString, method: this._setLabel }); /** * @description String specifying the name for the button. * @attribute name * @default null * @type String */ this.setAttributeConfig("name", { value: oAttributes.name, validator: DU.isString }); /** * @description Boolean indicating if the button should be disabled. * (Disabled buttons are dimmed and will not respond to user input * or fire events. Does not apply to button's of type "link.") * @attribute disabled * @default false * @type Boolean */ this.setAttributeConfig("disabled", { value: (oAttributes.disabled || false), validator: DU.isBoolean, method: this._setDisabled }); /** * @attribute onclick * @description Object literal representing the code to be executed * when the button is clicked. Format:
{
* fn: Function, // The handler to call * when the event fires.
obj: Object, * // An object to pass back to the handler.
* scope: Object // The object to use * for the scope of the handler.
}
* @type Object * @default null */ this.setAttributeConfig("onclick", { value: oAttributes.onclick, method: this._setOnClick }); }, /** * @description Causes the button to receive the focus and fires the * button's "focus" event. * @method focus */ focus: function () { if (!this.get("disabled")) { this._button.focus(); } }, /** * @description Causes the button to lose focus and fires the button's * "blur" event. * @method blur */ blur: function () { if (!this.get("disabled")) { this._button.blur(); } }, /** * @description Returns a reference to the button's parent form. * @method getForm * @return {HTMLFormElement} */ getForm: function () { var oButton = this._button, oForm; if (oButton) { oForm = oButton.form; } return oForm; }, /** * @description Removes the button's element from its parent element and * removes all event handlers. * @method destroy */ destroy: function () { var oElement = this.get("element"), oParentNode = oElement.parentNode, aButtons; Event.purgeElement(oElement); Event.purgeElement(this._button); var oForm = this.getForm(); this.unOnAll(); if (oParentNode) { oParentNode.removeChild(oElement); } delete m_oButtons[this.get("id")]; }, fireEvent: function (p_sType , p_aArgs) { var sType = arguments[0]; // Disabled buttons should not respond to DOM events if (this.DOM_EVENTS[sType] && this.get("disabled")) { return false; } return DU.widget.LButton.superclass.fireEvent.apply(this, arguments); }, /** * @description Returns a string representing the button. * @method toString * @return {String} */ toString: function () { return ("LButton " + this.get("id")); }, /** * @description Button객체를 활성화 한다. * @method enable * @public * @return void */ enable : function() { this.set('disabled', false); }, /** * @description Button객체를 비활성화 한다. * @method disable * @public * @return void */ disable : function() { this.set('disabled', true); }, /** * @description Button의 label을 변경한다. * @method setLabel * @param {String} label 변경할 label * @public * @return void */ setLabel : function(label) { this.set('label', label); }, /** * @description Button의 label을 리턴한다. * @method getLabel * @public * @return {String} */ getLabel : function() { return this.get('label'); }, /** * @description Button의 Click한다. * @method click * @public * @return {void} */ click : function() { this._button.click(); }, /** * @description 객체를 Render하는 메소드 * @method render * @public * @param {String|Object} appendToNode 객체를 붙이고자 하는 Node정보 * @return void */ render : function(appendToNode) { var el = this.get("element"); if (appendToNode) { if(!el) { el = DU.get(this.createElement().cloneNode(false)); this.id = el.id; } this.appendTo(appendToNode); } else { if (! DU.util.LDom.inDocument(el.dom)) { return false; } } }, /** * @description This method is a protected helper, used when constructing the DOM structure for the module * to account for situations which may cause Operation Aborted errors in IE. It should not * be used for general DOM construction. *

* If the parentNode is not document.body, the element is appended as the last element. *

*

* If the parentNode is document.body the element is added as the first child to help * prevent Operation Aborted errors in IE. *

* * @param {parentNode} The HTML element to which the element will be added * @param {element} The HTML element to be added to parentNode's children * @method _addToParent * @protected */ _addToParent: function(parentNode, element) { if (parentNode === document.body && parentNode.firstChild) { parentNode.insertBefore(element, parentNode.firstChild); } else { parentNode.appendChild(element); } }, /** * @description Appends the HTMLElement into either the supplied parentNode. * @method appendTo * @param {HTMLElement | Element} parentNode The node to append to * @return {void} */ appendTo : function(parentNode) { var el = this.get("element"); if (typeof parentNode == "string") { parentNode = document.getElementById(parentNode); } if (parentNode && el) { this._addToParent(parentNode, el); } } }); /** * @description Returns a button with the specified id. * @method DU.widget.LButton.getButton * @param {String} p_sId String specifying the id of the root node of the * HTML element representing the button to be retrieved. * @return {DU.widget.LButton} */ DU.widget.LButton.getButton = function (p_sId) { return m_oButtons[p_sId]; }; // Events /** * @event focus * @description Fires when the menu item receives focus. Passes back a * single object representing the original DOM event object passed back by * the event utility (DU.util.LEvent) when the event was fired. See * Element.addListener * for more information on listening for this event. * @type DU.util.LCustomEvent */ /** * @event blur * @description Fires when the menu item loses the input focus. Passes back * a single object representing the original DOM event object passed back by * the event utility (DU.util.LEvent) when the event was fired. See * Element.addListener for * more information on listening for this event. * @type DU.util.LCustomEvent */ })(); (function () { /** * Config is a utility used within an Object to allow the implementer to * maintain a list of local configuration properties and listen for changes * to those properties dynamically using LCustomEvent. The initial values are * also maintained so that the configuration can be reset at any given point * to its initial state. * @namespace DU.widget * @class LConfig * @constructor * @param {Object} owner The owner Object to which this Config Object belongs */ DU.widget.LConfig = function (owner) { if (owner) { this.init(owner); } }; var LCustomEvent = DU.util.LCustomEvent, Config = DU.widget.LConfig; /** * Constant representing the LCustomEvent type for the config changed event. * @property DU.util.LConfig.CONFIG_CHANGED_EVENT * @private * @static * @final */ Config.CONFIG_CHANGED_EVENT = "configChanged"; /** * Constant representing the boolean type string * @property DU.DU.widget.LConfig.BOOLEAN_TYPE * @private * @static * @final */ Config.BOOLEAN_TYPE = "boolean"; Config.prototype = { /** * Object reference to the owner of this Config Object * @property owner * @type Object */ owner: null, /** * Boolean flag that specifies whether a queue is currently * being executed * @property queueInProgress * @type Boolean */ queueInProgress: false, /** * Maintains the local collection of configuration property objects and * their specified values * @property config * @private * @type Object */ config: null, /** * Maintains the local collection of configuration property objects as * they were initially applied. * This object is used when resetting a property. * @property initialConfig * @private * @type Object */ initialConfig: null, /** * Maintains the local, normalized LCustomEvent queue * @property eventQueue * @private * @type Object */ eventQueue: null, /** * Custom Event, notifying subscribers when Config properties are set * (setProperty is called without the silent flag * @event configChangedEvent */ configChangedEvent: null, /** * Initializes the configuration Object and all of its local members. * @method init * @param {Object} owner The owner Object to which this Config * Object belongs */ init: function (owner) { this.owner = owner; this.configChangedEvent = this.createEvent(Config.CONFIG_CHANGED_EVENT); this.configChangedEvent.signature = LCustomEvent.LIST; this.queueInProgress = false; this.config = {}; this.initialConfig = {}; this.eventQueue = []; }, /** * Validates that the value passed in is a Boolean. * @method checkBoolean * @param {Object} val The value to validate * @return {Boolean} true, if the value is valid */ checkBoolean: function (val) { return (typeof val == Config.BOOLEAN_TYPE); }, /** * Validates that the value passed in is a number. * @method checkNumber * @param {Object} val The value to validate * @return {Boolean} true, if the value is valid */ checkNumber: function (val) { return (!isNaN(val)); }, /** * Fires a configuration property event using the specified value. * @method fireEvent * @private * @param {String} key The configuration property's name * @param {value} value The value of the correct type for the property */ fireEvent: function ( key, value ) { var property = this.config[key]; if (property && property.event) { property.event.fire(value); } }, /** * Adds a property to the Config Object's private config hash. * @method addProperty * @param {String} key The configuration property's name * @param {Object} propertyObject The Object containing all of this * property's arguments */ addProperty: function ( key, propertyObject ) { key = key.toLowerCase(); this.config[key] = propertyObject; propertyObject.event = this.createEvent(key, { scope: this.owner }); propertyObject.event.signature = LCustomEvent.LIST; propertyObject.key = key; if (propertyObject.handler) { propertyObject.event.on(propertyObject.handler, this.owner); } this.setProperty(key, propertyObject.value, true); if (! propertyObject.suppressEvent) { this.queueProperty(key, propertyObject.value); } }, /** * Returns a key-value configuration map of the values currently set in * the Config Object. * @method getConfig * @return {Object} The current config, represented in a key-value map */ getConfig: function () { var cfg = {}, currCfg = this.config, prop, property; for (prop in currCfg) { if (DU.hasOwnProperty(currCfg, prop)) { property = currCfg[prop]; if (property && property.event) { cfg[prop] = property.value; } } } return cfg; }, /** * Returns the value of specified property. * @method getProperty * @param {String} key The name of the property * @return {Object} The value of the specified property */ getProperty: function (key) { var property = this.config[key.toLowerCase()]; if (property && property.event) { return property.value; } else { return undefined; } }, /** * Returns the value of specified property. * @method getOldProperty * @param {String} key The name of the property * @return {Object} The value of the specified property */ getOldProperty: function (key) { var property = this.config[key.toLowerCase()]; if (property && property.event) { return property.oldValue || property.value; } else { return undefined; } }, /** * Resets the specified property's value to its initial value. * @method resetProperty * @param {String} key The name of the property * @return {Boolean} True is the property was reset, false if not */ resetProperty: function (key) { key = key.toLowerCase(); var property = this.config[key]; if (property && property.event) { if (this.initialConfig[key] && !DU.isUndefined(this.initialConfig[key])) { this.setProperty(key, this.initialConfig[key]); return true; } } else { return false; } }, /** * Sets the value of a property. If the silent property is passed as * true, the property's event will not be fired. * @method setProperty * @param {String} key The name of the property * @param {String} value The value to set the property to * @param {Boolean} silent Whether the value should be set silently, * without firing the property event. * @return {Boolean} True, if the set was successful, false if it failed. */ setProperty: function (key, value, silent) { var property; key = key.toLowerCase(); if (this.queueInProgress && ! silent) { // Currently running through a queue... this.queueProperty(key,value); return true; } else { property = this.config[key]; if (property && property.event) { if (property.validator && !property.validator(value)) { return false; } else { property.oldValue = property.value; property.value = value; if (! silent) { this.fireEvent(key, value); this.configChangedEvent.fire([key, value]); } return true; } } else { return false; } } }, /** * Sets the value of a property and queues its event to execute. If the * event is already scheduled to execute, it is * moved from its current position to the end of the queue. * @method queueProperty * @param {String} key The name of the property * @param {String} value The value to set the property to * @return {Boolean} true, if the set was successful, false if * it failed. */ queueProperty: function (key, value) { key = key.toLowerCase(); var property = this.config[key], foundDuplicate = false, iLen, queueItem, queueItemKey, queueItemValue, sLen, supercedesCheck, qLen, queueItemCheck, queueItemCheckKey, queueItemCheckValue, i, s, q; if (property && property.event) { if (!DU.isUndefined(value) && property.validator && !property.validator(value)) { // validator return false; } else { if (!DU.isUndefined(value)) { property.value = value; } else { value = property.value; } foundDuplicate = false; iLen = this.eventQueue.length; for (i = 0; i < iLen; i++) { queueItem = this.eventQueue[i]; if (queueItem) { queueItemKey = queueItem[0]; queueItemValue = queueItem[1]; if (queueItemKey == key) { /* found a dupe... push to end of queue, null current item, and break */ this.eventQueue[i] = null; this.eventQueue.push( [key, (!DU.isUndefined(value) ? value : queueItemValue)]); foundDuplicate = true; break; } } } // this is a refire, or a new property in the queue if (! foundDuplicate && !DU.isUndefined(value)) { this.eventQueue.push([key, value]); } } if (property.supercedes) { sLen = property.supercedes.length; for (s = 0; s < sLen; s++) { supercedesCheck = property.supercedes[s]; qLen = this.eventQueue.length; for (q = 0; q < qLen; q++) { queueItemCheck = this.eventQueue[q]; if (queueItemCheck) { queueItemCheckKey = queueItemCheck[0]; queueItemCheckValue = queueItemCheck[1]; if (queueItemCheckKey == supercedesCheck.toLowerCase() ) { this.eventQueue.push([queueItemCheckKey, queueItemCheckValue]); this.eventQueue[q] = null; break; } } } } } return true; } else { return false; } }, /** * Fires the event for a property using the property's current value. * @method refireEvent * @param {String} key The name of the property */ refireEvent: function (key) { key = key.toLowerCase(); var property = this.config[key]; if (property && property.event && !DU.isUndefined(property.value)) { if (this.queueInProgress) { this.queueProperty(key); } else { this.fireEvent(key, property.value); } } }, /** * Applies a key-value Object literal to the configuration, replacing * any existing values, and queueing the property events. * Although the values will be set, fireQueue() must be called for their * associated events to execute. * @method applyConfig * @param {Object} userConfig The configuration Object literal * @param {Boolean} init When set to true, the initialConfig will * be set to the userConfig passed in, so that calling a reset will * reset the properties to the passed values. */ applyConfig: function (userConfig, init) { var sKey, oConfig; if (init) { oConfig = {}; for (sKey in userConfig) { if (DU.hasOwnProperty(userConfig, sKey)) { oConfig[sKey.toLowerCase()] = userConfig[sKey]; } } this.initialConfig = oConfig; } for (sKey in userConfig) { if (DU.hasOwnProperty(userConfig, sKey)) { this.queueProperty(sKey, userConfig[sKey]); } } }, /** * Refires the events for all configuration properties using their * current values. * @method refresh */ refresh: function () { var prop; for (prop in this.config) { if (DU.hasOwnProperty(this.config, prop)) { this.refireEvent(prop); } } }, /** * Fires the normalized list of queued property change events * @method fireQueue */ fireQueue: function () { var i, queueItem, key, value, property; this.queueInProgress = true; for (i = 0;i < this.eventQueue.length; i++) { queueItem = this.eventQueue[i]; if (queueItem) { key = queueItem[0]; value = queueItem[1]; property = this.config[key]; property.value = value; // Clear out queue entry, to avoid it being // re-added to the queue by any queueProperty/supercedes // calls which are invoked during fireEvent this.eventQueue[i] = null; this.fireEvent(key,value); } } this.queueInProgress = false; this.eventQueue = []; }, /** * Subscribes an external handler to the change event for any * given property. * @method subscribeToConfigEvent * @param {String} key The property name * @param {Function} handler The handler function to use on to * the property's event * @param {Object} obj The Object to use for scoping the event handler * (see LCustomEvent documentation) * @param {Boolean} override Optional. If true, will override "this" * within the handler to map to the scope Object passed into the method. * @return {Boolean} True, if the subscription was successful, * otherwise false. */ subscribeToConfigEvent: function (key, handler, obj, override) { var property = this.config[key.toLowerCase()]; if (property && property.event) { if (!Config.alreadySubscribed(property.event, handler, obj)) { property.event.on(handler, obj, override); } return true; } else { return false; } }, /** * Unsubscribes an external handler from the change event for any * given property. * @method unsubscribeFromConfigEvent * @param {String} key The property name * @param {Function} handler The handler function to use on to * the property's event * @param {Object} obj The Object to use for scoping the event * handler (see LCustomEvent documentation) * @return {Boolean} True, if the unsubscription was successful, * otherwise false. */ unsubscribeFromConfigEvent: function (key, handler, obj) { var property = this.config[key.toLowerCase()]; if (property && property.event) { return property.event.unOn(handler, obj); } else { return false; } }, /** * Returns a string representation of the Config object * @method toString * @return {String} The Config object in string format. */ toString: function () { var output = "Config"; if (this.owner) { output += " [" + this.owner.toString() + "]"; } return output; }, /** * Returns a string representation of the Config object's current * LCustomEvent queue * @method outputEventQueue * @return {String} The string list of CustomEvents currently queued * for execution */ outputEventQueue: function () { var output = "", queueItem, q, nQueue = this.eventQueue.length; for (q = 0; q < nQueue; q++) { queueItem = this.eventQueue[q]; if (queueItem) { output += queueItem[0] + "=" + queueItem[1] + ", "; } } return output; }, /** * Sets all properties to null, unsubscribes all listeners from each * property's change event and all listeners from the configChangedEvent. * @method destroy */ destroy: function () { var oConfig = this.config, sProperty, oProperty; for (sProperty in oConfig) { if (DU.hasOwnProperty(oConfig, sProperty)) { oProperty = oConfig[sProperty]; oProperty.event.unOnAll(); oProperty.event = null; } } this.configChangedEvent.unOnAll(); this.configChangedEvent = null; this.owner = null; this.config = null; this.initialConfig = null; this.eventQueue = null; } }; /** * Checks to determine if a particular function/Object pair are already * subscribed to the specified LCustomEvent * @method alreadySubscribed * @static * @param {DU.util.LCustomEvent} evt The LCustomEvent for which to check * the subscriptions * @param {Function} fn The function to look for in the subscribers list * @param {Object} obj The execution scope Object for the subscription * @return {Boolean} true, if the function/Object pair is already subscribed * to the LCustomEvent passed in */ Config.alreadySubscribed = function (evt, fn, obj) { var nSubscribers = evt.subscribers.length, subsc, i; if (nSubscribers > 0) { i = nSubscribers - 1; do { subsc = evt.subscribers[i]; if (subsc && subsc.obj == obj && subsc.fn == fn) { return true; } } while (i--); } return false; }; DU.applyPrototype(Config, DU.util.LEventProvider); }()); (function () { /** * Module is a JavaScript representation of the Standard Module Format. * Standard Module Format is a simple standard for markup containers where * child nodes representing the header, body, and footer of the content are * denoted using the CSS classes "hd", "bd", and "ft" respectively. * Module is the base class for all other classes in the du * Container package. * @namespace DU.widget * @class LModule * @constructor * @param {String|HTMLElement} el The element ID representing the Module ORel The element representing the Module * @param {Object} userConfig The configuration Object literal containing * the configuration that should be set for this module. See configuration * documentation for more details. */ DU.widget.LModule = function (el, userConfig) { if (el) { this.init(el, userConfig); } else { } }; var Dom = DU.util.LDom, Config = DU.widget.LConfig, Event = DU.util.LEvent, LCustomEvent = DU.util.LCustomEvent, Module = DU.widget.LModule, m_oModuleTemplate, m_oHeaderTemplate, m_oBodyTemplate, m_oFooterTemplate, /** * Constant representing the name of the Module's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "BEFORE_INIT": "beforeInit", "INIT": "init", "APPEND": "append", "BEFORE_RENDER": "beforeRender", "RENDER": "render", "CHANGE_HEADER": "changeHeader", "CHANGE_BODY": "changeBody", "CHANGE_FOOTER": "changeFooter", "CHANGE_CONTENT": "changeContent", "DESTORY": "destroy", "BEFORE_SHOW": "beforeShow", "SHOW": "show", "BEFORE_HIDE": "beforeHide", "HIDE": "hide" }, /** * Constant representing the Module's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "VISIBLE": { key: "visible", value: true, validator: DU.isBoolean }, "EFFECT": { key: "effect", suppressEvent: true, supercedes: ["visible"] }, "MONITOR_RESIZE": { key: "monitorresize", value: true }, "APPEND_TO_DOCUMENT_BODY": { key: "appendtodocumentbody", value: false } }; /** * Constant representing the prefix path to use for non-secure images * @property DU.widget.LModule.IMG_ROOT * @static * @final * @type String */ Module.IMG_ROOT = null; /** * Constant representing the prefix path to use for securely served images * @property DU.widget.LModule.IMG_ROOT_SSL * @static * @final * @type String */ Module.IMG_ROOT_SSL = null; /** * Constant for the default CSS class name that represents a Module * @property DU.widget.LModule.CSS_MODULE * @static * @final * @type String */ Module.CSS_MODULE = "L-module"; /** * Constant representing the module header * @property DU.widget.LModule.CSS_HEADER * @static * @final * @type String */ Module.CSS_HEADER = "hd"; /** * Constant representing the module body * @property DU.widget.LModule.CSS_BODY * @static * @final * @type String */ Module.CSS_BODY = "bd"; /** * Constant representing the module footer * @property DU.widget.LModule.CSS_FOOTER * @static * @final * @type String */ Module.CSS_FOOTER = "ft"; /** * Constant representing the url for the "src" attribute of the iframe * used to monitor changes to the browser's base font size * @property DU.widget.LModule.RESIZE_MONITOR_SECURE_URL * @static * @final * @type String */ Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;"; /** * Singleton LCustomEvent fired when the font size is changed in the browser. * Opera's "zoom" functionality currently does not support text * size detection. * @event DU.widget.LModule.textResizeEvent */ Module.textResizeEvent = new LCustomEvent("textResize"); function createModuleTemplate() { if (!m_oModuleTemplate) { m_oModuleTemplate = document.createElement("div"); m_oModuleTemplate.innerHTML = ("
" + "
"); m_oHeaderTemplate = m_oModuleTemplate.firstChild; m_oBodyTemplate = m_oHeaderTemplate.nextSibling; m_oFooterTemplate = m_oBodyTemplate.nextSibling; } return m_oModuleTemplate; } function createHeader() { if (!m_oHeaderTemplate) { createModuleTemplate(); } return (m_oHeaderTemplate.cloneNode(false)); } function createBody() { if (!m_oBodyTemplate) { createModuleTemplate(); } return (m_oBodyTemplate.cloneNode(false)); } function createFooter() { if (!m_oFooterTemplate) { createModuleTemplate(); } return (m_oFooterTemplate.cloneNode(false)); } Module.prototype = { /** * The class's constructor function * @property contructor * @type Function */ constructor: Module, /** * The main module element that contains the header, body, and footer * @property element * @type HTMLElement */ element: null, /** * The header element, denoted with CSS class "hd" * @property header * @type HTMLElement */ header: null, /** * The body element, denoted with CSS class "bd" * @property body * @type HTMLElement */ body: null, /** * The footer element, denoted with CSS class "ft" * @property footer * @type HTMLElement */ footer: null, /** * The id of the element * @property id * @type String */ id: null, /** * A string representing the root path for all images created by * a Module instance. * @deprecated It is recommend that any images for a Module be applied * via CSS using the "background-image" property. * @property imageRoot * @type String */ imageRoot: Module.IMG_ROOT, /** * Initializes the custom events for Module which are fired * automatically at appropriate times by the Module class. * @method initEvents */ initEvents: function () { var SIGNATURE = LCustomEvent.LIST; /** * LCustomEvent fired prior to class initalization. * @event beforeInitEvent * @param {class} classRef class reference of the initializing * class, such as this.beforeInitEvent.fire(Module) */ this.beforeInitEvent = this.createEvent(EVENT_TYPES.BEFORE_INIT); this.beforeInitEvent.signature = SIGNATURE; /** * LCustomEvent fired after class initalization. * @event initEvent * @param {class} classRef class reference of the initializing * class, such as this.beforeInitEvent.fire(Module) */ this.initEvent = this.createEvent(EVENT_TYPES.INIT); this.initEvent.signature = SIGNATURE; /** * LCustomEvent fired when the Module is appended to the DOM * @event appendEvent */ this.appendEvent = this.createEvent(EVENT_TYPES.APPEND); this.appendEvent.signature = SIGNATURE; /** * LCustomEvent fired before the Module is rendered * @event beforeRenderEvent */ this.beforeRenderEvent = this.createEvent(EVENT_TYPES.BEFORE_RENDER); this.beforeRenderEvent.signature = SIGNATURE; /** * LCustomEvent fired after the Module is rendered * @event renderEvent */ this.renderEvent = this.createEvent(EVENT_TYPES.RENDER); this.renderEvent.signature = SIGNATURE; /** * LCustomEvent fired when the header content of the Module * is modified * @event changeHeaderEvent * @param {String/HTMLElement} content String/element representing * the new header content */ this.changeHeaderEvent = this.createEvent(EVENT_TYPES.CHANGE_HEADER); this.changeHeaderEvent.signature = SIGNATURE; /** * LCustomEvent fired when the body content of the Module is modified * @event changeBodyEvent * @param {String/HTMLElement} content String/element representing * the new body content */ this.changeBodyEvent = this.createEvent(EVENT_TYPES.CHANGE_BODY); this.changeBodyEvent.signature = SIGNATURE; /** * LCustomEvent fired when the footer content of the Module * is modified * @event changeFooterEvent * @param {String/HTMLElement} content String/element representing * the new footer content */ this.changeFooterEvent = this.createEvent(EVENT_TYPES.CHANGE_FOOTER); this.changeFooterEvent.signature = SIGNATURE; /** * LCustomEvent fired when the content of the Module is modified * @event changeContentEvent */ this.changeContentEvent = this.createEvent(EVENT_TYPES.CHANGE_CONTENT); this.changeContentEvent.signature = SIGNATURE; /** * LCustomEvent fired when the Module is destroyed * @event destroyEvent */ this.destroyEvent = this.createEvent(EVENT_TYPES.DESTORY); this.destroyEvent.signature = SIGNATURE; /** * LCustomEvent fired before the Module is shown * @event beforeShowEvent */ this.beforeShowEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW); this.beforeShowEvent.signature = SIGNATURE; /** * LCustomEvent fired after the Module is shown * @event showEvent */ this.showEvent = this.createEvent(EVENT_TYPES.SHOW); this.showEvent.signature = SIGNATURE; /** * LCustomEvent fired before the Module is hidden * @event beforeHideEvent */ this.beforeHideEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE); this.beforeHideEvent.signature = SIGNATURE; /** * LCustomEvent fired after the Module is hidden * @event hideEvent */ this.hideEvent = this.createEvent(EVENT_TYPES.HIDE); this.hideEvent.signature = SIGNATURE; }, /** * Initializes the custom events for Module which are fired * automatically at appropriate times by the Module class. */ initDefaultConfig: function () { // Add properties // /** * Specifies whether the Module is visible on the page. * @config visible * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key, { handler: this.configVisible, value: DEFAULT_CONFIG.VISIBLE.value, validator: DEFAULT_CONFIG.VISIBLE.validator }); /** *

* Object or array of objects representing the ContainerEffect * classes that are active for animating the container. *

*

* NOTE: Although this configuration * property is introduced at the Module level, an out of the box * implementation is not shipped for the Module class so setting * the proroperty on the Module class has no effect. The Overlay * class is the first class to provide out of the box ContainerEffect * support. *

* @config effect * @type Object * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key, { suppressEvent: DEFAULT_CONFIG.EFFECT.suppressEvent, supercedes: DEFAULT_CONFIG.EFFECT.supercedes }); /** * Specifies whether to create a special proxy iframe to monitor * for user font resizing in the document * @config monitorresize * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key, { handler: this.configMonitorResize, value: DEFAULT_CONFIG.MONITOR_RESIZE.value }); /** * Specifies if the module should be rendered as the first child * of document.body or appended as the last child when render is called * with document.body as the "appendToNode". *

* Appending to the body while the DOM is still being constructed can * lead to Operation Aborted errors in IE hence this flag is set to * false by default. *

* * @config appendtodocumentbody * @type Boolean * @default false */ this.cfg.addProperty(DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.key, { value: DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.value }); }, /** * The Module class's initialization method, which is executed for * Module and all of its subclasses. This method is automatically * called by the constructor, and sets up all DOM references for * pre-existing markup, and creates required markup if it is not * already present. * @method init * @param {String} el The element ID representing the Module OR * @param {HTMLElement} el The element representing the Module * @param {Object} userConfig The configuration Object literal * containing the configuration that should be set for this module. * See configuration documentation for more details. */ init: function (el, userConfig) { var elId, child; this.initEvents(); this.beforeInitEvent.fire(Module); /** * The Module's Config object used for monitoring * configuration properties. * @property cfg * @type DU.widget.LConfig */ this.cfg = new Config(this); if (this.isSecure) { this.imageRoot = Module.IMG_ROOT_SSL; } if (typeof el == "string") { elId = el; el = document.getElementById(el); if (! el) { el = (createModuleTemplate()).cloneNode(false); el.id = elId; } } this.element = el; if (el.id) { this.id = el.id; } child = this.element.firstChild; if (child) { var fndHd = false, fndBd = false, fndFt = false; do { // We're looking for elements if (1 == child.nodeType) { if (!fndHd && Dom.hasClass(child, Module.CSS_HEADER)) { this.header = child; fndHd = true; } else if (!fndBd && Dom.hasClass(child, Module.CSS_BODY)) { this.body = child; fndBd = true; } else if (!fndFt && Dom.hasClass(child, Module.CSS_FOOTER)){ this.footer = child; fndFt = true; } } } while ((child = child.nextSibling)); } this.initDefaultConfig(); Dom.addClass(this.element, Module.CSS_MODULE); if (userConfig) { this.cfg.applyConfig(userConfig, true); } /* Subscribe to the fireQueue() method of Config so that any queued configuration changes are excecuted upon render of the Module */ if (!Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) { this.renderEvent.on(this.cfg.fireQueue, this.cfg, true); } this.initEvent.fire(Module); }, /** * Initialize an empty IFRAME that is placed out of the visible area * that can be used to detect text resize. * @method initResizeMonitor */ initResizeMonitor: function () { var isGeckoWin = (DU.browser.gecko && DU.platform.window); if (isGeckoWin) { // Help prevent spinning loading icon which // started with FireFox 2.0.0.8/Win var self = this; setTimeout(function(){self._initResizeMonitor();}, 0); } else { this._initResizeMonitor(); } }, /** * Create and initialize the text resize monitoring iframe. * * @protected * @method _initResizeMonitor */ _initResizeMonitor : function() { var oDoc, oIFrame, sHTML; function fireTextResize() { Module.textResizeEvent.fire(); } if (!DU.browser.opera) { oIFrame = Dom.get("_duResizeMonitor"); var supportsCWResize = this._supportsCWResize(); if (!oIFrame) { oIFrame = document.createElement("iframe"); if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && DU.browser.ie) { oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL; } if (!supportsCWResize) { // Can't monitor on contentWindow, so fire from inside iframe sHTML = ["