UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

424 lines (403 loc) 11.2 kB
/** * @fileoverview * Implementation of resize gripper for other widgets or HTML elements. * @author Partridge Jiang */ (function(){ "use strict"; var EU = Kekule.HtmlElementUtils; var EV = Kekule.X.Event; var CNS = Kekule.Widget.HtmlClassNames; /** @ignore */ Kekule.Widget.HtmlClassNames = Object.extend(Kekule.Widget.HtmlClassNames, { RESIZEGRIPPER: 'K-Resize-Gripper' }); var PS = Class.PropertyScope; /** * An gripper widget at the bottom right corner of parent to change the parent's dimension. * @class * @augments Kekule.Widget.BaseWidget * * @property {Object} target Target HTML element or widget to be resized. * @property {Bool} retainAspectRatio Whether retain width/height ratio when resizing. */ Kekule.Widget.ResizeGripper = Class.create(Kekule.Widget.BaseWidget, /** @lends Kekule.Widget.ResizeGripper# */ { /** @private */ CLASS_NAME: 'Kekule.Widget.ResizeGripper', /** @private */ BINDABLE_TAG_NAMES: ['div', 'span'], /** @constructs */ initialize: function(/*$super, */parentOrElementOrDocument) { this.reactMousemoveBind = this.reactMousemove.bind(this); this.reactMouseupBind = this.reactMouseup.bind(this); this.reactTouchmoveBind = this.reactTouchmove.bind(this); this.reactTouchendBind = this.reactTouchend.bind(this); this.tryApplySuper('initialize', [parentOrElementOrDocument]) /* $super(parentOrElementOrDocument) */; if (!this.getTarget()) { if (this.getParent()) this.setTarget(this.getParent()); else { var parentElem = this.getElement().parentNode; if (parentElem) this.setTarget(parentElem); } } }, /** @private */ initProperties: function() { this.defineProp('target', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': function(value) { if (this.getTarget() !== value) { this.setPropStoreFieldValue('target', value); this.targetChanged(value); } } }); this.defineProp('retainAspectRatio', {'dataType': DataType.BOOL}); // private properties this.defineProp('isUnderResizing', {'dataType': DataType.BOOL, 'serializable': false, 'setter': null, 'scope': PS.PRIVATE}); this.defineProp('baseCoord', {'dataType': DataType.HASH, 'serializable': false, 'scope': PS.PRIVATE}); //this.defineProp('currCoord', {'dataType': DataType.HASH, 'serializable': false, 'scope': PS.PRIVATE}); this.defineProp('baseDimension', {'dataType': DataType.HASH, 'serializable': false, 'scope': PS.PRIVATE}); this.defineProp('baseAspectRatio', {'dataType': DataType.FLOAT, 'serializable': false, 'scope': PS.PRIVATE}); }, /** @ignore */ doGetWidgetClassName: function(/*$super*/) { return this.tryApplySuper('doGetWidgetClassName') /* $super() */ + ' ' + CNS.RESIZEGRIPPER; }, /** @ignore */ doGetDefaultTabIndex: function() { return null; }, /** @ignore */ doCreateRootElement: function(doc) { var result = doc.createElement('span'); return result; }, /** * Called when target object is changed. * @param {Object} value * @private */ targetChanged: function(value) { var elem; // change parent of curr widget if (this.targetIsWidget(value)) { elem = value.getResizerElement? value.getResizerElement(): value.getChildrenHolderElement(); } else // HTML element { elem = value; } if (elem) this.appendToElem(elem); }, /** * Check if target object is a widget. * @param {Object} target * @returns {Bool} * @private */ targetIsWidget: function(target) { return target instanceof Kekule.Widget.BaseWidget; }, /** * Returns dimension of target object. * @param {Object} obj * @returns {Hash} * @private */ getTargetDimension: function(obj) { var target = obj || this.getTarget(); if (this.targetIsWidget(target)) return target.getDimension(); else //return Kekule.HtmlElementUtils.getElemBoundingClientRect(target, false); return Kekule.HtmlElementUtils.getElemPageRect(target, true); }, /** * Set dimension (in px) of target object. * @param {Object} obj * @param {Int} width * @param {Int} height */ setTargetDimension: function(obj, width, height) { var target = obj || this.getTarget(); if (this.targetIsWidget(target)) target.setDimension(width, height); else // HTML element { var style = target.style; style.width = width + 'px'; style.height = height + 'px'; } }, /** * Prepare to resize. * @private */ prepareResizing: function(startingCoord, event) { if (!this.getIsUnderResizing()) { var eventReceiver = this._getMoveEventReceiverElem(this.getElement()); this._installEventHandlers(eventReceiver); this._setMouseCapture(this.getElement(), true, event); this.setBaseCoord(startingCoord); var dim = this.getTargetDimension(); this.setBaseDimension(dim); this.setBaseAspectRatio(dim.width / dim.height); //this.getElement().setCapture(true); this.setPropStoreFieldValue('isUnderResizing', true); //this.setMouseCapture(true); } }, /** * Resizing process is over. */ doneResizing: function(event) { this._setMouseCapture(this.getElement(), false, event); var eventReceiver = this._getMoveEventReceiverElem(this.getElement()); this._uninstallEventHandlers(eventReceiver); //this.getElement().setCapture(false); //this.setMouseCapture(false); this.setBaseCoord(null); this.setBaseDimension(null); this.setPropStoreFieldValue('isUnderResizing', false); }, /** * Resize to a proper size according to starting coord and curr coord. * @param {Hash} currCoord * @private */ resizeTo: function(currCoord) { var delta = Kekule.CoordUtils.substract(currCoord, this.getBaseCoord()); var baseDim = this.getBaseDimension(); var w = baseDim.width + delta.x; var h = baseDim.height + delta.y; if (this.getRetainAspectRatio()) { var baseRatio = this.getBaseAspectRatio(); if (w / h > baseRatio) w = baseRatio * h; else if (w / h < baseRatio) h = w / baseRatio; } this.setTargetDimension(this.getTarget(), w, h); }, // event reactors /** @private */ reactMousemove: function(e) { if (this.getIsUnderResizing()) { var coord = {'x': e.getScreenX(), 'y': e.getScreenY()}; this.resizeTo(coord); e.stopPropagation(); e.preventDefault(); } }, /** @private */ reactMouseup: function(e) { if (e.getButton() === Kekule.X.Event.MouseButton.LEFT) { if (this.getIsUnderResizing()) { this.doneResizing(e); e.preventDefault(); } } }, /** @private */ reactTouchmove: function(e) { if (this.getIsUnderResizing()) { var coord = {'x': e.getScreenX(), 'y': e.getScreenY()}; this.resizeTo(coord); e.stopPropagation(); e.preventDefault(); } }, /** @private */ reactTouchend: function(e) { if (this.getIsUnderResizing()) { this.doneResizing(e); e.preventDefault(); } }, /** @private */ _elemSupportCapture: function(elem) { return !!elem.setCapture; }, /** @private */ _setMouseCapture: function(elem, capture, event) { if (elem) { if (capture) { if (elem.setCapture) elem.setCapture(true); if (elem.setPointerCapture) { if (Kekule.ObjUtils.notUnset(event.pointerId)) elem.setPointerCapture(event.pointerId) } } else { if (elem.releaseCapture) elem.releaseCapture(); if (elem.releasePointerCapture) { if (Kekule.ObjUtils.notUnset(event.pointerId)) elem.releasePointerCapture(event.pointerId) } } } }, /** @private */ _getMoveEventReceiverElem: function(gripperElem) { return (this._elemSupportCapture(gripperElem))? gripperElem: gripperElem.ownerDocument.documentElement; }, /** @private */ _installEventHandlers: function(receiver) { EV.addListener(receiver, 'mousemove', this.reactMousemoveBind); EV.addListener(receiver, 'touchmove', this.reactTouchmoveBind); EV.addListener(receiver, 'mouseup', this.reactMouseupBind); EV.addListener(receiver, 'touchend', this.reactTouchendBind); }, /** @private */ _uninstallEventHandlers: function(receiver) { EV.removeListener(receiver, 'mousemove', this.reactMousemoveBind); EV.removeListener(receiver, 'touchmove', this.reactTouchmoveBind); EV.removeListener(receiver, 'mouseup', this.reactMouseupBind); EV.removeListener(receiver, 'touchend', this.reactTouchendBind); }, /** @ignore */ //react_pointerdown: function(e) doReactActiviting: function(/*$super, */e) { this.tryApplySuper('doReactActiviting', [e]) /* $super(e) */; //var evType = e.getType(); { var coord = {'x': e.getScreenX(), 'y': e.getScreenY()}; this.prepareResizing(coord, e); } } /** @ignore */ //react_pointerup: function(e) /* doReactDeactiviting: function($super, e) { $super(e); //if (e.getButton() === Kekule.X.Event.MouseButton.LEFT) this.doneResizing(); }, */ /** @ignore */ /* react_pointermove: function(e) { if (this.getIsUnderResizing()) { var coord = {'x': e.getScreenX(), 'y': e.getScreenY()}; this.resizeTo(coord); e.stopPropagation(); e.preventDefault(); } }, */ /** @ignore */ /* react_touchmove: function(e) { if (this.getIsUnderResizing()) { var coord = {'x': e.getScreenX(), 'y': e.getScreenY()}; this.resizeTo(coord); e.stopPropagation(); e.preventDefault(); } } */ }); // extend Kekule.Widget.BaseWidget, add resize ability to all widgets ClassEx.extend(Kekule.Widget.BaseWidget, { resizableChanged: function() { if (this.getResizable()) // add new gripper widget { var gripper = new Kekule.Widget.ResizeGripper(this); gripper.setRetainAspectRatio(this.getResizeWithAspectRatio()); this.setPropStoreFieldValue('resizeGripper', gripper); } else { var gripper = this.getResizeGripper(); if (gripper) gripper.finalize(); this.setPropStoreFieldValue('resizeGripper', null); } } }); ClassEx.defineProps(Kekule.Widget.BaseWidget, [ { 'name': 'resizable', 'dataType': DataType.BOOL, 'setter': function(value) { if (value !== this.getResizable()) { this.setPropStoreFieldValue('resizable', value); this.resizableChanged(); } } }, { 'name': 'resizeWithAspectRatio', 'dataType': DataType.BOOL, 'setter': function(value) { this.setPropStoreFieldValue('resizeWithAspectRatio', value); if (this.getResizeGripper()) this.getResizeGripper().setRetainAspectRatio(value); } }, { 'name': 'isResizing', 'dataType': DataType.BOOL, 'serializable': false, 'scope': PS.PRIVATE, 'setter': null, 'getter': function() { var resizer = this.getResizeGripper(); return resizer? resizer.getIsUnderResizing(): false; } }, {'name': 'resizeGripper', 'dataType': 'Kekule.Widget.ResizeGripper', 'serializable': false, 'setter': null, 'scope': PS.PRIVATE} ]); })();