/* * */ /* * @(#) 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
* 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개 까지이다.
* * 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
* var options = {
* url:'test.dev',
* params:{},
* callback:{},
* discardUrl:false
* }
*
* @method update
* @public
* @param {Object} options 호출할때 전달할 Option정보 객체
*
* @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정보 객체
*
* @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정보 객체 * * @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정보 객체 * * @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정보 객체 * * @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 = '
// 높이를 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 새로운 높이. 다음 중 하나:* 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" ? "" :
"") + "" + sNodeName + ">";
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 = ["