/** * @fileoverview * XComp 관련 event add, remove 할 때 사용하는 helper */ if ( !JsNamespace.exist("Eco.XComp.Event") ) { /** * * @description * XComp 관련 event add, remove 할 때 사용하는 helper
* 그 외 dragging 처리를 용이하게 하는 makeDraggable, repeat처리를 용이하게 하는 makeRepeatable 과
* 부드러운 animation 처리를 위한 requestAnimationFrame, cancelAnimationFrame 기능을 포함한다. * * @namespace * @name Eco.XComp.Event * @memberof! */ JsNamespace.declare("Eco.XComp.Event", { /** * 주어진 XComp을 가지고 주어진 events 정보로 eventHandler함수들을 추가한다.
* 두번째 events는 이벤트명=eventHandler함수로 이루어진 object collection이다.
* event가 fire할 때 eventHandler함수가 호출되는데 그 함수 내부의 this는 주어진 scope가 된다.
* @example * * this.onLbuttonDownHandler = function(obj, e) * { * trace(this)//Form * } * this.onLbuttonUpHandler = function(obj, e) * { * trace(this)//Form * } * * var events = {"onlbuttondown": this.onLbuttonDownHandler, "onlbuttonup": this.onLbuttonUpHandler}; * * Eco.XComp.Event.add(this.st_sample01, events, this); * * @param {XComp} XComp events 설정할 대상 XComp. * @param {object} events 이벤트명=eventHandler함수로 정의된 object collection. * @param {*} scope scope로 설정할 대상으로 eventHandler 내부 루틴에 this에 해당하는 값 * @memberOf Eco.XComp.Event */ "add": function(XComp, events, scope) { for (var type in events) { if ( events.hasOwnProperty(type) ) { XComp.addEventHandler(type, events[type], scope); } } }, /** * 주어진 XComp을 가지고 주어진 events 정보로 eventHandler함수들을 제거한다.
* 두번째 events는 이벤트명=eventHandler함수로 이루어진 object collection이다. * @example * * this.onLbuttonDownHandler = function(obj, e) * { * trace(this)//Form * } * this.onLbuttonUpHandler = function(obj, e) * { * trace(this)//Form * } * * var events = {"onlbuttondown": this.onLbuttonDownHandler, "onlbuttonup": this.onLbuttonUpHandler}; * * Eco.XComp.Event.remove(this.st_sample01, events, this); * * @param {XComp} XComp events 설정할 대상 XComp. * @param {object} events 이벤트명=eventHandler함수로 정의된 object collection. * @param {*} scope scope로 설정할 대상으로 eventHandler 내부 루틴에 this에 해당하는 값 * @memberOf Eco.XComp.Event **/ "remove": function(XComp, events, scope) { for (var type in events) { if ( events.hasOwnProperty(type) ) { XComp.removeEventHandler(type, events[type], scope); } } }, /** * 주어진 XComp에 drag 기능를 설정한다.
* 두번째 param 값은 function이거나, object type으로 값이 주어져야 한다.
* function이면 dragging 되는 시점에 호출되는 함수로 설정된다.
* object이면 object.start, object.end, object.dragging 으로 각각 함수값이 주어지는데.
* start는 drag시작되는 시점에 호출되는 함수로 return 값이 false 일 경우 드래그를 실행하지 않는다.
* end는 drag종료되는 시점에 호출되는 함수
* dragging는 dragging 하는 시점에 계속 호출되는 함수이다.
* 네번째 param인 addArgs로 주어지는 array는 함수 호출시에 추가되는 arguments이다.
* 각 함수의 arguments은 다음과 같다.
* ------------------------------------------------------------------------------------------------------
* 1. start
* ------------------------------------------------------------------------------------------------------
* addArgs[0], addArg[1], ... ,clientX, clientY, shiftKey, ctrlKey
* ------------------------------------------------------------------------------------------------------
* 2. end
* ------------------------------------------------------------------------------------------------------
* addArgs[0], addArg[1], ... ,clientX, clientY, shiftKey, ctrlKey
* ------------------------------------------------------------------------------------------------------
* 3. dragging
* ------------------------------------------------------------------------------------------------------
* offsetX, offsetY, addArgs[0], addArg[1], ... , shiftKey, ctrlKey
*
* 세번째 param인 scope는 호출되는 함수 내부의 this 로 지정되며 만약 scope를 생략하면 this는 form이 된다.
* offsetX, offsetY param 값은 dragging이 발생하는 시점의 x, y기준으로 그 다음에 발생되는 dragging 시점의 x, y 값의 차이가 주어지는데
* 만약 다섯번째 isOffsetFromStart 값을 true로 주어지면 offsetX, offsetY 는 최초 drag가 시작되는 시점의 x, y 기준으로 dragging 발생하는 x, y 값의 차이가 주어진다.
* 여섯 번째 delayTask 값을 true로 주어지면 dragging 함수 호출 하여 루틴이 실행되고 있는 중에 다시 dragging 함수가 호출하게 되면 호출를 생략하는 하는 처리이다. * @example * * // 1) 2번째 인자를 function 으로 선언 시 drag 진행 시점에 정의된 함수가 호출된다. * Eco.XComp.Event.makeDraggable(this.st_sample02, this.onDragging, this, [this.st_sample02]); * * // 2) 2번째 인자를 object 로 선언 시 drag 시작, 진행, 종료 시점에 정의된 함수가 호출된다. * var draggingFunc = { * 'start': this.onDragStart, // 사용자가 정의한 arguments + [clientX, clientY, shiftKey, ctrlKey] 추가함(2015/3/12) * 'dragging': this.onDragging, // [offsetX, offsetY] + 사용자가 정의한 arguments + [shiftKey, ctrlKey] 추가함(2015/3/12) * 'end': this.onDragEnd // 사용자가 정의한 arguments + [clientX, clientY, shiftKey, ctrlKey] 추가함(2015/3/12) * }; * Eco.XComp.Event.makeDraggable(this.st_sample02, draggingFunc, this, [this.st_sample02]); * * // drag start 시점에 처리할 함수 * this.onDragStart = function(comp) * { * trace(comp.name + " Drag Start !!"); * comp.set_text("Drag Start !!"); * } * * // dragging 시점에 처리할 루틴을 정의한 함수 * this.onDragging = function(offsetX, offsetY, obj) * { * var x = obj.getOffsetLeft() + offsetX, * y = obj.getOffsetTop() + offsetY; * * obj.move(x, y); * } * * @param {XComp} XComp draggable하고자 하는 xcomp. * @param {object|function} draggingFunc dragging 처리 루틴에 해당하는 함수들. * @param {*=} scope scope로 설정할 대상. * @param {array=} addArgs 설정된 함수 호출시 추가할 arguments을 array로 설정. * @param {boolean=} isOffsetFromStart offsetX, offsetY arguments의 drag시작 시점을 기준할 것인지 여부. * @param {boolean=} delayTask 반복되어지는 dragging함수 호출을 중간에 겹치면 delay할 것인지 여부 * @memberOf Eco.XComp.Event **/ "makeDraggable": function(XComp, draggingFunc, scope, addArgs, isOffsetFromStart, delayTask) { if ( Eco.isObject(draggingFunc) ) { XComp._dragFuncs = { "draggingFunc": draggingFunc.dragging, "draggingStartFunc": draggingFunc.start, "draggingEndFunc": draggingFunc.end, "args": addArgs, "mouseEventInfo": draggingFunc.mouseEventInfo, "isOffsetFromStart": isOffsetFromStart }; } else { XComp._dragFuncs = { "draggingFunc": draggingFunc, "args": addArgs, "mouseEventInfo": draggingFunc.mouseEventInfo, "isOffsetFromStart": isOffsetFromStart }; } Eco.XComp.Event.add(XComp, { "onlbuttondown": Eco.XComp.Event._dragDownHandler, "onlbuttonup": Eco.XComp.Event._dragUpHandler }, scope); XComp.__scope = scope; if ( delayTask ) { XComp._delayDragProc = true; } else { XComp._delayDragProc = null; } }, /** * 주어진 XComp에 drag 기능를 해제한다. * @example * * Eco.XComp.Event.clearDraggable(this.st_sample02); * * @param {XComp} XComp draggable기능을 해제하는 xcomp. * @memberOf Eco.XComp.Event **/ "clearDraggable": function(XComp) { if ( XComp._dragFuncs ) { XComp._dragFuncs = null; XComp._delayDragProc = null; Eco.XComp.Event.remove(XComp, { "onlbuttondown": Eco.XComp.Event._dragDownHandler, "onlbuttonup": Eco.XComp.Event._dragUpHandler }); var topForm = Eco.XComp.getTopLevelForm(XComp.parent); var uniqueId = XComp._unique_id; var hasDragComp = false; var dragComps = topForm.__makeDraggableTargets; for (var id in dragComps) { if ( dragComps.hasOwnProperty(id) ) { if ( id == uniqueId ) { delete dragComps[id]; } else { hasDragComp = true; } } } // top form에 drag comp가 없다면 핸들러 및 속성 제거 if ( !hasDragComp && topForm.findEventHandler("onmousemove", Eco.XComp.Event._dragMoveHandler) > -1 ) { topForm.__makeDraggableTargets = null; topForm.__makeDraggableCurComp = null; delete topForm.__makeDraggableTargets; delete topForm.__makeDraggableCurComp; topForm.removeEventHandler("onmousemove", Eco.XComp.Event._dragMoveHandler); } } }, /** * dragging 기능을 처리하기 위해 내부적으로 설정하는 onlbuttondown event의 handler함수 * @param {XComp} obj 발생한 event의 XComp. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_dragDownHandler": function(obj, e) { var topForm = Eco.XComp.getTopLevelForm(obj.parent); if ( topForm.findEventHandler("onmousemove", Eco.XComp.Event._dragMoveHandler) < 0 ) { topForm.addEventHandler("onmousemove", Eco.XComp.Event._dragMoveHandler); } // top form 에 drag 대상 컴포넌트로 지정하기 위해 추가 if ( !topForm.__makeDraggableTargets ) { topForm.__makeDraggableTargets = {}; } topForm.__makeDraggableTargets[obj._unique_id] = true; var pt = {x:e.screenX, y:e.screenY}; obj._drag = { "startPt": pt, "offsetX": 0, "offsetY": 0, "isOffsetFromStart": false }; // drag 대상으로 지정 topForm.__makeDraggableCurComp = obj; // scope 가 없으면 topForm 지정 if ( !obj.__scope ) { obj.__scope = topForm; } var func = obj._dragFuncs, mouseEventInfo, addArgs; if ( func ) { if ( func.isOffsetFromStart === true ) obj._drag.isOffsetFromStart = true; addArgs = func.args; mouseEventInfo = func.mouseEventInfo; func = func.draggingStartFunc; } if ( func ) { var args = []; if ( addArgs ) { args = args.concat(addArgs); } if ( mouseEventInfo ) { args[args.length] = e; } else { // [2015.03.12] arguments 끝 부분에 추가 args[args.length] = e.clientX; args[args.length] = e.clientY; args[args.length] = e.shiftKey; args[args.length] = e.ctrlKey; } // [2013.11.13] 리턴값에 따라 드래그 실행 중지 var ret = func.apply(this, args); if ( ret === false ) { obj._drag = null; obj.__scope = null; delete obj._drag; delete obj.__scope; delete topForm.__makeDraggableTargets[obj._unique_id]; return; } } // 버튼의 mousemove 이벤트를 topform 에 발생하도록 // (Form.Div.Button 과 같은 중첩의 경우 버블링이 안된다.) Eco.XComp.Event._lockMouseEvent(topForm); }, /** * dragging 기능을 처리하기 위해 내부적으로 설정하는 onlbuttonup event의 handler함수 * @param {XComp} obj 발생한 event의 XComp. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_dragUpHandler": function(obj, e) { var drag = obj._drag; // drag 대상 초기화 var topForm = Eco.XComp.getTopLevelForm(obj.parent); topForm.__makeDraggableCurComp = null; if ( drag ) { Eco.XComp.Event.cancelAnimationFrame(obj._reqDragId);//func obj._reqDragId = null; obj._drag = null; var func = obj._dragFuncs, addArgs, mouseEventInfo; if ( func ) { addArgs = func.args; mouseEventInfo = func.mouseEventInfo; func = func.draggingEndFunc; } if ( func ) { var args = []; if ( addArgs ) { args = args.concat(addArgs); } if ( mouseEventInfo ) { args[args.length] = e; } else { // [2015.03.12] arguments 끝 부분에 추가 args[args.length] = e.clientX; args[args.length] = e.clientY; args[args.length] = e.shiftKey; args[args.length] = e.ctrlKey; } func.apply(this, args); } } }, /** * dragging 기능을 처리하기 위해 내부적으로 설정하는 onmousemove event의 handler함수 * @param {Form} form mouse move가 발생한 top form. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_dragMoveHandler": function(form, e) { // 현재 drag 중인 대상 comp var obj = form.__makeDraggableCurComp; if ( obj ) { var drag = obj._drag; if ( drag ) { // [2015.03.12] drag 중에 shiftKey, ctrlKey 정보 추가함. obj._dragCurPt = {x:e.screenX, y:e.screenY, shiftkey:e.shiftKey, ctrlkey:e.ctrlKey}; if ( obj._delayDragProc ) { Eco.XComp.Event.cancelAnimationFrame(obj._reqDragId); //func obj._reqDragId = null; } obj._reqDragId = Eco.XComp.Event.requestAnimationFrame(Eco.XComp.Event._dragProcess, obj.__scope, obj, e); //func, scope(default: topForm), func's arguments } } }, /** * dragging 기능을 처리하기 위해 내부적으로 설정하는 함수 * @param {XComp} obj dragging 발생한 XComp. * @private * @memberOf Eco.XComp.Event **/ "_dragProcess": function(obj, e) { var pt = obj._dragCurPt, drag = obj._drag; if ( drag == null ) return; drag.offsetX = pt.x - drag.startPt.x; drag.offsetY = pt.y - drag.startPt.y; if ( !drag.isOffsetFromStart ) { drag.startPt = pt; } var func = obj._dragFuncs, mouseEventInfo, addArgs; if ( func ) { addArgs = func.args; mouseEventInfo = func.mouseEventInfo; func = func.draggingFunc; } if ( func ) { var args = [drag.offsetX, drag.offsetY]; if ( addArgs ) { args = args.concat(addArgs); } if ( mouseEventInfo ) { args[args.length] = e; } else { // [2015.03.12] drag 중에 shiftKey, ctrlKey 정보 추가함. args[args.length] = drag.shiftkey; args[args.length] = drag.ctrlkey; } func.apply(this, args); } }, /** * 전체 화면상에 마우스를 locking 하여 강제로 대상 컴포넌트의 마우스 이벤트로 호출하게 한다. * * @param {XComp} XComp 마우스이벤트를 대체하고자 하는 xcomp. * @private * @memberOf Eco.XComp.Event */ "_lockMouseEvent": function(comp, onlyLbtnUp) { var win = comp._getWindow(); win._mouseLockComp = comp; win._on_sys_lbuttonup = Eco.XComp.Event._on_default_sys_lbuttonup; if ( !onlyLbtnUp ) win._on_sys_mousemove = Eco.XComp.Event._on_default_sys_mousemove; }, /** * locking시에 window _on_sys_lbuttonup 함수를 이것으로 대처함. * @private * @memberOf Eco.XComp.Event */ "_on_default_sys_lbuttonup": function(elem, strButton, altKey, ctrlKey, shiftKey, windowX, windowY, screenX, screenY) { var lockComp = this._mouseLockComp, enableChanged = false, tmpFunc, visibleChanged = false; elem = lockComp._control_element; if ( !lockComp._isEnable() ) { enableChanged = true; tmpFunc = lockComp._isEnable; lockComp._isEnable = function() { return true }; } if ( !lockComp.visible ) { visibleChanged = true; lockComp.visible = true; } nexacro.Window.prototype._on_default_sys_lbuttonup.call(this, elem, strButton, altKey, ctrlKey, shiftKey, windowX, windowY, screenX, screenY); this._on_sys_lbuttonup = nexacro.Window.prototype._on_default_sys_lbuttonup; this._on_sys_mousemove = nexacro.Window.prototype._on_default_sys_mousemove; if ( enableChanged ) { lockComp._isEnable = tmpFunc; } if ( visibleChanged ) { lockComp.visible = false; } this._mouseLockComp = null; }, /** * locking시에 window _on_sys_mousemove 함수를 이것으로 대처함. * @private * @memberOf Eco.XComp.Event */ "_on_default_sys_mousemove": function(elem, strButton, altKey, ctrlKey, shiftKey, windowX, windowY, screenX, screenY) { elem = this._mouseLockComp._control_element; nexacro.Window.prototype._on_default_sys_mousemove.call(this, elem, strButton, altKey, ctrlKey, shiftKey, windowX, windowY, screenX, screenY); }, /** * 주어진 XComp에 repeat 기능를 설정한다.
* 두번째 param 값은 function이거나, object type으로 값이 주어져야 한다.
* function이면 repeating 되는 시점에 호출되는 함수로 설정된다.
* object이면 object.start, object.end, object.repeating, object.repeatingStop으로 각각 함수값이 주어지는데.
* start는 mouse down되는 시점에 호출되는 함수
* end는 mouse up되는 시점에 호출되는 함수
* repeating는 repeating 하는 시점에 계속 호출되는 함수이다.
* repeatingStop는 mouse 누른 상태에서 마우스가 XComp 영역을 벗어나면 repeating 멈추게 되는데 이 멈추는 시점에 호출되는 함수이다.
* 네번째 param인 addArgs로 주어지는 array는 함수 호출시에 추가되는 arguments이다.
* 각 함수의 arguments은 다음과 같다.
* start | end | repeating | repeatingStop
* ----------------------------------------------------------------------------------------------------------------------------
* x, y, addArgs[0], addArg[1], ... |addArgs[0], addArg[1], ... |x, y, addArgs[0], addArg[1], ... |addArgs[0], addArg[1], ...
*
* 세번째 param인 scope는 호출되는 함수 내부의 this값에 해당한다.
* 만약 scope를 생략하면 this는 form이 된다.
* x, y param 값은 start, repeating이 발생하는 시점의 마우스 x, y값인데 좌표기준은 XComp.parent 기준으로 처리된다. * @example * * // 1) 2번째 인자를 function 으로 선언 시 repeat 진행 시점에 정의된 함수가 호출된다. * Eco.XComp.Event.makeRepeatable(this.btn_repeat, this.onRepeating, this, [ds, this.grd_sample]); * * // 2) 2번째 인자를 object 로 선언 시 repeat 시작, 진행, 중단, 종료 시점에 정의된 함수가 호출된다. * var repeatFunc = { * 'start': this.onRepeatStart, * 'repeating': this.onRepeating, * 'end': this.onRepeatEnd * }; * Eco.XComp.Event.makeRepeatable(this.btn_repeat, repeatFunc, this, [ds, this.grd_sample]); * * // repeating 시작 시점에 처리할 루틴을 정의한 함수 * this.onRepeatStart = function(comp) * { * trace("onRepeatStart"); * } * * // repeating 시점에 처리할 루틴을 정의한 함수 * this.onRepeating = function(offsetX, offsetY, obj) * { * trace("onRepeating"); * } * * // repeating 중단 시점에 처리할 루틴을 정의한 함수 * this.onRepeatingStop = function(offsetX, offsetY, obj) * { * trace("onRepeatingStop"); * } * * // repeating 종료 시점에 처리할 루틴을 정의한 함수 * this.onRepeatEnd = function(offsetX, offsetY, obj) * { * trace("onRepeatEnd"); * } * * @param {XComp} XComp repeatable하고자 하는 xcomp. * @param {object|function} repeatFunc repeating 처리 루틴에 해당하는 함수들. * @param {*=} scope scope로 설정할 대상. * @param {array=} args 설정된 함수 호출시 추가할 arguments을 array로 설정. * @memberOf Eco.XComp.Event **/ "makeRepeatable": function(XComp, repeatFunc, scope, args) { if ( Eco.isObject(repeatFunc) ) { XComp._repeatFuncs = { "repeatStartFunc": repeatFunc.start, "repeatEndFunc": repeatFunc.end, "repeatingFunc": repeatFunc.repeating, "repeatStopFunc": repeatFunc.repeatingStop, "firstInterval": repeatFunc.firstInterval||500, "interval": repeatFunc.interval||100, "args": args }; } else { XComp._repeatFuncs = { "repeatingFunc": repeatFunc, "firstInterval": 500, "interval": 100, "args": args }; } Eco.XComp.Event.add(XComp, { "onlbuttondown": Eco.XComp.Event._repeatDownHandler, // "onmouseenter": Eco.XComp.Event._repeatEnterHandler, // "onmouseleave": Eco.XComp.Event._repeatLeaveHandler, "onlbuttonup": Eco.XComp.Event._repeatUpHandler }, scope); }, /** * 주어진 XComp에 repeatable 기능를 해제한다. * @param {XComp} XComp repeatable기능을 해제하는 xcomp. * @example * * Eco.XComp.Event.clearRepeatable(this.btn_repeat); * * @memberOf Eco.XComp.Event **/ "clearRepeatable": function(XComp) { if ( XComp._repeatFuncs ) { Eco.XComp.Event._cancelRepeatable(XComp); Eco.XComp.Event.remove(XComp, {"onmousemove": Eco.XComp.Event._repeatMoveHandler}, this); XComp._repeatStatus = null; XComp._repeatFuncs = null; Eco.XComp.Event.remove(XComp, { "onlbuttondown": Eco.XComp.Event._repeatDownHandler, // "onmouseenter": Eco.XComp.Event._repeatEnterHandler, // "onmouseleave": Eco.XComp.Event._repeatLeaveHandler, "onlbuttonup": Eco.XComp.Event._repeatUpHandler }); } }, /** * repeating 기능을 취소하기 위해 내부적으로 사용하는 함수. * @param {XComp} XComp repeating 기능을 취소하고자 하는 XComp. * @private * @memberOf Eco.XComp.Event **/ "_cancelRepeatable": function(XComp) { if ( XComp._timeId != null ) { nexacro._clearSystemTimer(XComp._repeatFuncs._win_handle, XComp._timeId); XComp._timeId = null; } if ( XComp._reqId != null ) { Eco.XComp.Event.cancelAnimationFrame(XComp._reqId); XComp._reqId = null; } var curPoint = XComp._curPoint; if ( curPoint ) { XComp._curPoint = null; } }, /** * repeating 기능을 처리하기 위해 내부적으로 설정하는 onlbuttondown event의 handler함수 * @param {XComp} obj 발생한 event의 XComp. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_repeatDownHandler": function(obj, e) { //Eco.XComp.Event._lockMouseEvent(obj, true); Eco.XComp.Event._lockMouseEvent(obj); Eco.XComp.Event.add(obj, {"onmousemove": Eco.XComp.Event._repeatMoveHandler}, this); obj._curPoint = {x:e.clientX, y:e.clientY}; var func = obj._repeatFuncs, args, addArgs; if ( func ) { addArgs = func.args; func = func.repeatStartFunc; if ( !func ) { func = null; } } if ( func ) { var pt = obj._curPoint; args = [pt.x, pt.y]; if ( addArgs ) { args = args.concat(addArgs); } func.apply(this, args); } var repeatConf = obj._repeatFuncs, pThis = this; repeatConf._win_handle = obj._getReferenceContext()._getWindowHandle(); obj._repeatStatus = "firstRepeat"; obj._timeId = nexacro._setSystemTimer(repeatConf._win_handle, function() { Eco.XComp.Event._repeatProcess.call(pThis, obj); //func, scope(default: topForm), func's arguments }, repeatConf.firstInterval); //Eco.XComp.Event._repeatProcess.call(this, obj); }, /** * repeating 기능을 처리하기 위해 내부적으로 설정하는 onlbuttonup event의 handler함수 * @param {XComp} obj 발생한 event의 XComp. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_repeatUpHandler": function(obj, e) { //console.log("_repeatUpHandler", obj); Eco.XComp.Event.remove(obj, {"onmousemove": Eco.XComp.Event._repeatMoveHandler}, this); var repeatConfig = obj._repeatFuncs, args, addArgs; Eco.XComp.Event._cancelRepeatable(obj); if ( e.clientX > 0 && e.clientY > 0 && e.clientX < obj.getOffsetWidth() && e.clientY < obj.getOffsetHeight() ) { if ( obj._repeatStatus != "repeat" ) { var repeatingFunc, repeatingArg; if ( repeatConfig ) { addArgs = repeatConfig.args; repeatingFunc = repeatConfig.repeatingFunc; if ( !repeatingFunc ) { repeatingFunc = null; } } if ( repeatingFunc ) { repeatingArg = [e.clientX, e.clientY]; if ( addArgs ) { repeatingArg = repeatingArg.concat(addArgs); } repeatingFunc.apply(this, repeatingArg); } } } var repeatEndFunc; addArgs = null; if ( repeatConfig ) { addArgs = repeatConfig.args; repeatEndFunc = repeatConfig.repeatEndFunc; if ( !repeatEndFunc ) { repeatEndFunc = null; } } if ( repeatEndFunc ) { if ( !addArgs ) { addArgs = []; } repeatEndFunc.apply(this, addArgs); } }, /** * repeating 기능을 처리하기 위해 내부적으로 설정하는 onmousemove event의 handler함수 * @param {XComp} obj 발생한 event의 XComp. * @param {EventInfo} e EventInfo 객체. * @private * @memberOf Eco.XComp.Event **/ "_repeatMoveHandler": function(obj, e) { if ( e.clientX < 0 || e.clientY < 0 || e.clientX > obj.getOffsetWidth() || e.clientY > obj.getOffsetHeight() ) { if ( obj._repeatStatus == "firstRepeat" || obj._repeatStatus == "repeat" ) { obj._repeatStatus = "stop"; Eco.XComp.Event._cancelRepeatable(obj); var func = obj._repeatFuncs, addArgs; if ( func ) { addArgs = func.args; func = func.repeatStopFunc; if ( !func ) { func = null; } } if ( func ) { if ( !addArgs ) { addArgs = []; } func.apply(this, addArgs); } } } else { var curPoint = obj._curPoint; if ( !curPoint ) { curPoint = {x: e.clientX, y: e.clientY}; obj._curPoint = curPoint; } else { curPoint.x = e.clientX; curPoint.y = e.clientY; } if ( obj._repeatStatus == "stop" ) { var repeatConf = obj._repeatFuncs, pThis = this; obj._repeatStatus = "firstRepeat"; obj._timeId = nexacro._setSystemTimer(repeatConf._win_handle, function() { Eco.XComp.Event._repeatProcess.call(pThis, obj); //func, scope(default: topForm), func's arguments }, repeatConf.firstInterval); } } }, /** * repeating 기능을 처리하기 위해 내부적으로 설정하는 함수
* 이 함수는 XCompEvent.requestAnimationFrame을 통해 호출되는데 내부적으로 timer 호출이 된다.
* 이렇게 호출하는 것은 repeating 중간에 화면 render가 존재하면 smooth하게 처리되는 이점이 있다. * @param {XComp} obj repeating 발생한 XComp. * @private * @memberOf Eco.XComp.Event **/ "_repeatProcess": function(obj) { var status = obj._repeatStatus; //firstRepeat, repeat, stop if ( obj._reqId != null ) { Eco.XComp.Event.cancelAnimationFrame(obj._reqId); obj._reqId = null; } if ( status == "stop" ) { nexacro._clearSystemTimer(obj._repeatFuncs._win_handle, obj._timeId); obj._timeId = null; return; } //obj._reqRepeatId = Eco.XComp.Event.requestAnimationFrame(Eco.XComp.Event._repeatProcess, this, obj); //func, scope(default: topForm), func's arguments if ( status == "firstRepeat" ) { nexacro._clearSystemTimer(obj._repeatFuncs._win_handle, obj._timeId); obj._timeId = null; } var func = obj._repeatFuncs, args, addArgs; if ( func ) { addArgs = func.args; func = func.repeatingFunc; if ( !func ) { func = null; } } if ( func ) { var curPoint = obj._curPoint; if ( curPoint ) { args = [curPoint.x, curPoint.y]; } else { args = [-1, -1]; } if ( addArgs ) { args = args.concat(addArgs); } obj._reqId = Eco.XComp.Event.requestAnimationFrame(function() { func.apply(this, args); }, this); } if ( status == "firstRepeat" ) { var pThis = this; obj._repeatStatus = "repeat"; obj._timeId = nexacro._setSystemTimer(obj._repeatFuncs._win_handle, function() { Eco.XComp.Event._repeatProcess.call(pThis, obj); //func, scope(default: topForm), func's arguments }, obj._repeatFuncs.interval); } }, /** * requestAnimationFrame 기능
* callback 함수 내부의 this는 주어진 scope가 된다.
* func 내부 루틴에서 화면 render가 존재하면 smooth하게 처리되는 이점이 있다. * 실행하고자 하는 루프 function 에서 requestAnimationFrame 을 호출하고 callback 함수로 * 자기 자신을 호출하는 recursive 방식을 사용한다. * @example * * this.renderLoop = function() * { * // something animation code * * var reqId = Eco.XComp.Event.requestAnimationFrame(this.renderLoop, this); * } * * this.renderLoop(); * * @param {function} callback 콜백 함수 * @param {*} scope callback 함수 내부에서 this 로 사용할 개체. * @param {...} 호출하는 함수의 arguments * @return {number} request id. * @memberOf Eco.XComp.Event **/ "requestAnimationFrame": function(callback, scope) { var args; if ( arguments.length > 2 ) //callback, scope, .... { args = Eco.array.toArray(arguments, 2); } else { args = []; } var isRuntime = nexacro._init_platform_runtime; var useSetTimeout = Eco.XComp.Event._requestAnimationFrameUseSetTimer; if( useSetTimeout === undefined ) useSetTimeout = false; var rAF = Eco.XComp.Event._requestAnimationFrame; if ( !rAF ) { // Runtime if ( isRuntime ) { // 현재 Runtime 에는 requestAnimationFrame 이 없으므로 timer 를 이용한다. // (현재 성능이 썩 좋지 않다... 엔진에 기능 추가될 예정임) rAF = function(form, callback, lastTimeRef) { var lastTime = lastTimeRef.lastTime; var currTime = (Date.now ? Date.now() : (new Date).getTime()); var interval = (1000/60) + lastTime - currTime; if ( interval < 0 ) interval = 0; var id = nexacro.OnceCallbackTimer.callonce(form, function(){ lastTimeRef.lastTime = (Date.now ? Date.now() : (new Date).getTime()); callback(); }, interval); return id; }; } else // HTML { // HTML - 브라우저별로 requestAnimationFrame 가 다를 수 있으므로 체크한다. var context = JsNamespace.getGlobalContext(); rAF = context.requestAnimationFrame; if ( !rAF ) { var vendors = ['ms', 'moz', 'webkit', 'o']; for(var x = 0; x < vendors.length && !rAF; ++x) { rAF = context[vendors[x]+'RequestAnimationFrame']; } if ( !rAF ) { rAF = function(callback, lastTimeRef) { var lastTime = lastTimeRef.lastTime; var currTime = (Date.now ? Date.now() : (new Date).getTime()); var interval = (1000/60) + lastTime - currTime; if ( interval < 0 ) interval = 0; var id = context.setTimeout(function(){ lastTimeRef.lastTime = (Date.now ? Date.now() : (new Date).getTime()); callback(); }, interval); return id; }; Eco.XComp.Event._requestAnimationFrameUseSetTimer = true; useSetTimeout = true; } } } Eco.XComp.Event._requestAnimationFrame = rAF; } /* RequestAnimationFrame 이 없는 브라우저(런타임 포함)에 유사 기능을 사용하기 위해 timer 를 사용하는데 시간 계산을 위한 lastTime 이 필요하다. 동일한 callback에 대해 하나의 lastTime 값이 필요하므로 속성으로 추가하여 사용하고 cancelAnimationFrame 에서 삭제한다. */ if ( !Eco.XComp.Event._requestAnimationFrameLastTimeInfo ) { Eco.XComp.Event._requestAnimationFrameLastTimeInfo = {}; } var id; if ( isRuntime ) { var form = Eco.XComp.Event._getRequestAnimationFrameForm(scope); var callbackString = callback.toString(); var lastTimeRef = Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callbackString]; if ( lastTimeRef === undefined ) { Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callback.toString()] = {'lastTime': 0, 'timers':[]}; lastTimeRef = Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callbackString]; } var timer = rAF(form, function() { callback.apply(scope, args); }, lastTimeRef); id = timer._handle; // cancel 시 제거할 대상을 위해 지정 lastTimeRef.id = id; lastTimeRef.timers.push(timer); } else if ( useSetTimeout ) { var callbackString = callback.toString(); var lastTimeRef = Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callback.toString()]; if ( lastTimeRef === undefined ) { Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callbackString] = {'lastTime': 0}; lastTimeRef = Eco.XComp.Event._requestAnimationFrameLastTimeInfo[callbackString]; } id = rAF(function() { callback.apply(scope, args); }, lastTimeRef); // cancel 시 제거할 대상을 위해 지정 lastTimeRef.id = id; } else { id = rAF(function() { callback.apply(scope, args); }); } return id; }, /** * requsetAnimationFrame 기능이 없는 런타임을 위한 것으로
* scope 의 ReferenceContext 통해 form 을 찾고 없으면 mainframe의 첫번째 폼을 찾아서 반환. * @param {*} scope requestAnimationFrame 호출시 지정한 scope. * @private * @memberOf Eco.XComp.Event **/ "_getRequestAnimationFrameForm": function(scope) { var form; if ( scope._getReferenceContext ) { // scope ==> Form, Component form = scope._getReferenceContext(); } else { var c = application.mainframe.all[0]; do { if ( c instanceof ChildFrame ) { break; } c = c.all[0]; } while ( true ) form = c.form; } return form; }, /** * requestAnimationFrame 호출한 것을 중지하고자 할때 사용하는 함수.
* requestAnimationFrame의 return 값으로 id값이 나온다. 이것을 이 함수 argument로 넘겨준다. * @example * * Eco.XComp.Event.cancelAnimationFrame(reqId); * * @param {number} id requestAnimationFrame id. * @memberOf Eco.XComp.Event **/ "cancelAnimationFrame": function(id) { var cAF = Eco.XComp.Event._cancelAnimationFrame; if ( !cAF ) { // Runtime if ( nexacro._init_platform_runtime ) { cAF = function(id) { var lastTimeInfo = Eco.XComp.Event._requestAnimationFrameLastTimeInfo; if ( lastTimeInfo ) { for (var p in lastTimeInfo) { if ( lastTimeInfo.hasOwnProperty(p) ) { if ( id == lastTimeInfo[p].id ) { var timers = lastTimeInfo[p].timers; for (var i=0,len=timers.length; i