[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
05122024
/
htdocs
/
simpeg
/
zapatec
/
zptime
/
utils
/
[
Home
]
File: draggable.js
//$Id: draggable.js 7453 2007-06-16 18:05:03Z vkulov $ /** * This is a set of functionality used for dragging object * and is implemented in interface (mixin) manner. */ Zapatec.Draggable = {}; /** * Makes the element draggable. This means attaching events, * and making some preparations. * @return {boolean} true if success, otherwise false. */ Zapatec.Draggable.makeDraggable = function() { if (!this.requireInterface("Zapatec.Movable")) { return false; } if (!this.requireInterface("Zapatec.CommandEvent")) { return false; } var draggables = Zapatec.Array(this.getDraggableElements()); var hooks = Zapatec.Array(this._getDraggableHooks()); var self = this, result = false; var config = this.getDragConfig(); //if method is not "cut" we can not imitate event capture if (config.method != "cut") { this.setDragConfig({eventCapture : false}); } //we need to save references to anonymous event listeners var listenersObj = this._getRestorer().getSavedProps()["dragListeners"] = { mousedown : function(ev) { return self.dragStart(ev); }, mousemove : function(ev) { if (self.isDragging()) { return self.dragMove(ev); } }, mouseup : function(ev) { if (self.isDragging()) { return self.dragEnd(ev); } } }; hooks.each(function(index, hook) { if (!Zapatec.isHtmlElement(hook)) { return; } //if we are here than there is at least one hook :) result = true; //this for disabling text selection on drag if (Zapatec.is_gecko) { hook.style.setProperty("-moz-user-select", "none", ""); } //adding drag start event Zapatec.Utils.addEvent(hook, 'mousedown', listenersObj.mousedown); //imitating capturing of some events. This is used when mouse event //is involved with hook element, cause there won't be a click event for //that element if cover will appear over it. if (config.eventCapture) { Zapatec.Utils.addEvent(hook, 'mousemove', listenersObj.mousemove); Zapatec.Utils.addEvent(hook, 'mouseup', listenersObj.mouseup); } }); draggables.each(function(index, draggable) { if (!Zapatec.isHtmlElement(draggable)) { return; } //this is for backward compability and to be able to //get draggable object with only a reference to HTML element self.createProperty(draggable, "dragObj", self); }); //returning result return result; }; /** * This is an event handler for the mouse down event * of the hook element. * @param ev {MouseEvent} this is mouse event triggered by * mouse down action. * @return {false} false for stoping events in some browsers. */ Zapatec.Draggable.dragStart = function(ev) { if (!this.canDrag()) { return true; } //getting event object ev = ev || window.event; //check mouse button, only left one is valid var iButton = ev.button || ev.which; if (iButton > 1) { return false; } var self = this; var config = this.getDragConfig(); //calling the handler if (this.fireEvent("beforeDragInit", ev) === false) { return true; } //calling global event handler if (Zapatec.GlobalEvents.fireEvent("beforeDragInit", ev, this) === false) { return true; } //now we started dragging! this._setDragging(true); //workaround for dummy and copy methods to capture events correctly if (config.eventCapture && (config.method == "dummy" || config.method == "copy")) { var draggables = Zapatec.Array(this.getDraggableElements()); draggables.each(function(index, draggable) { draggable.restorer.saveProp("style.zIndex"); draggable.style.zIndex = 2000001; }); } //preparing elements this._proceedDraggableElements("dragStart"); //lets get the starting position of the mouse var oPos = Zapatec.Utils.getMousePos(ev); var mouseX = oPos.pageX; var mouseY = oPos.pageY; //making object movable this.makeMovable(); //starting movement this.startMove(); //preparing movable elements var elements = Zapatec.Array(this.getMovableElements()); elements.each(function(index, movable) { if (Zapatec.isHtmlElement(movable)) { //giving z-index movable.restorer.saveProp("style.zIndex"); movable.style.zIndex = 1000000 + (parseInt(movable.style.zIndex, 10) || 0); //applying styles self._proceedDragStyles(movable, "dragStart"); //assigning proper cursor if event capture if (config.eventCapture) { movable.restorer.saveProp("style.cursor"); movable.style.cursor = "move"; } } else if (Zapatec.isMovableObj(movable)) { //making recursive iterating through all child movables var elems = Zapatec.Array(movable.getMovableElements()); elems.each(arguments.calee); } }); Zapatec.Utils.cover.show( config.eventCapture ? 999999 : 2000000, "move", function(ev) { return self.dragMove(ev); }, function(ev) { return self.dragEnd(ev); } ); this._setMovingPoint(mouseX, mouseY); //calling the handler this.fireEvent("onDragInit", ev); //calling the global handler Zapatec.GlobalEvents.fireEvent("onDragInit", ev, this); if (config.stopEvent) { return Zapatec.Utils.stopEvent(ev); } else { return true; } }; /** * This is an event handler for the mouse move event * of the cover element. * @param ev {MouseEvent} this is mouse event triggered by * mouse move action. * @return {false} false for stoping events in some browsers. */ Zapatec.Draggable.dragMove = function(ev){ if (!this.isDragging()) { return true; } //getting event object ev = ev || window.event; //calling the handler if (this.fireEvent("beforeDragMove", ev) === false) { return true; } //calling global event handler if (Zapatec.GlobalEvents.fireEvent("beforeDragMove", ev, this) === false) { return true; } if (Zapatec.Utils.cover.style.zIndex != 2000000) { var config = this.getDragConfig(); //workaround for dummy and copy methods to handle eventCapture corectly if (config.eventCapture && (config.method == "dummy" || config.method == "copy")) { var draggables = Zapatec.Array(this.getDraggableElements()); draggables.each(function(index, draggable) { draggable.restorer.restoreProp("style.zIndex"); }); } //putting cover over everything Zapatec.Utils.cover.style.zIndex = 2000000; } //mouse position var oPos = Zapatec.Utils.getMousePos(ev); var mouseX = oPos.pageX; var mouseY = oPos.pageY; var movePoint = this.getMovingPoint(); this.moveFor(mouseX - movePoint.x, mouseY - movePoint.y); this._setMovingPoint(mouseX, mouseY); //calling the handler this.fireEvent("onDragMove", ev); //calling global event handler Zapatec.GlobalEvents.fireEvent("onDragMove", ev, this); // Stop event return Zapatec.Utils.stopEvent(ev); }; /** * This is an event handler for the mouse up event * of the cover element. * @param ev {MouseEvent} this is mouse event triggered by * mouse up action. * @return {false} false for stoping events in some browsers. */ Zapatec.Draggable.dragEnd = function(ev){ if (!this.isDragging()) { return true; } //getting event object ev = ev || window.event; var self = this; //calling the handler if (this.fireEvent("beforeDragEnd", ev) === false) { return true; } //calling global event handler if (Zapatec.GlobalEvents.fireEvent("beforeDragEnd", ev, this) === false) { return true; } var config = this.getDragConfig(); //workaround for dummy and copy methods to handle eventCapture corectly if (config.eventCapture && (config.method == "dummy" || config.method == "copy")) { var draggables = Zapatec.Array(this.getDraggableElements()); draggables.each(function(index, draggable) { draggable.restorer.restoreProp("style.zIndex", true); }); } //restoring movable elements var elements = Zapatec.Array(this.getMovableElements()); elements.each(function(index, movable) { if (Zapatec.isHtmlElement(movable)) { movable.restorer.restoreProp("style.zIndex"); movable.restorer.restoreProp("style.cursor"); //clearing styles self._proceedDragStyles(movable, "dragEnd"); } else if (Zapatec.isMovableObj(movable)) { //making recursive iterating through all child movables var elems = Zapatec.Array(movable.getMovableElements()); elems.each(arguments.calee); } }); //unprepareing elements this._proceedDraggableElements("dragEnd"); //hiding the cover Zapatec.Utils.cover.hide(); //clearing moving point this._setMovingPoint(null, null); //now we ended dragging! this._setDragging(false); //ending movement this.endMove(); //calling the handler this.fireEvent("onDragEnd", ev); //calling global event handler Zapatec.GlobalEvents.fireEvent("onDragEnd", ev, this); // Stop event return Zapatec.Utils.stopEvent(ev); }; /** * returns element to its original place where it was taken from. * Applicable only for the cut method! */ Zapatec.Draggable.restorePos = function(){ this.restoreOfMove(); }; /** * Restores object from dragging. */ Zapatec.Draggable.restoreOfDrag = function() { //we need to save references to anonymous event listeners var listenersObj = this._getRestorer().getSavedProps()["dragListeners"]; if (!listenersObj) { return false; } this.restoreOfMove(); var hooks = Zapatec.Array(this._getDraggableHooks()); var draggables = Zapatec.Array(this.getDraggableElements()); var self = this; var config = this.getDragConfig(); hooks.each(function(index, hook) { if (!Zapatec.isHtmlElement(hook)) { return; } if (Zapatec.is_gecko) { hook.style.setProperty("-moz-user-select", "", ""); } Zapatec.Utils.removeEvent(hook, 'mousedown', listenersObj.mousedown); if (config.eventCapture) { Zapatec.Utils.removeEvent(hook, 'mousemove', listenersObj.mousemove); Zapatec.Utils.removeEvent(hook, 'mouseup', listenersObj.mouseup); } }); draggables.each(function(index, draggable) { if (!Zapatec.isHtmlElement(draggable)) { return; } draggable.dragObj = null; }); return true; }; /** * This method should return the array of elements that * are to be draggable. These are not always elements * that are actually moved. This is almost empty method * that should be overwritten in implementing class. * @return {array} array of draggable elements. */ Zapatec.Draggable.getDraggableElements = function() { return this.getContainer(); }; /** * This method should return the array of elements that * are handles for dragging. These are elements that * trigger drag start. This is almost empty method * that should be overwritten in implementing class. * @return {array} array of hook elements. */ Zapatec.Draggable._getDraggableHooks = function() { return this.getContainer(); }; /** * Returns container for this object. */ Zapatec.Draggable.getContainer = function() { return this.getDragConfig().container; }; /** * This method returns true if object started * dragging, otherwise false. * @return {boolean} true if object is dragging, * otherwise false. */ Zapatec.Draggable.isDragging = function() { return this.dragging; }; /** * This method returns true if dragging is enabled, * otherwise it should return false. * @return {boolean} true if dragging enabled, otherwise false. */ Zapatec.Draggable.canDrag = function() { return this.canDrag; }; /** * Turns on or off dragging. * @param on {boolean} whether we want to * turn on or off dragging. */ Zapatec.Draggable._setCanDrag = function(on) { this.canDrag = on; }; /** * Should returns the dragging configuration. * @return {object} configuration object. */ Zapatec.Draggable.getDragConfig = function() { return this.getConfiguration(); }; /** * Sets the dragging configuration. * @param config {object} a set of new configuration. */ Zapatec.Draggable.setDragConfig = function(config) { this.reconfigure(config); }; /** * Sets the flag of dragging to on or off. * @param on {boolean} true to turn on, otherwise false. */ Zapatec.Draggable._setDragging = function(on) { this.dragging = on; }; /** * This method handles overflow of the dragging coordinate. * @param limit {number} limit that was overflowed. * @param dimension {string} dimension of coordinate. * @return {number or boolean} new coordinate or false. */ Zapatec.Draggable._handleCoordOverflow = function(limit, dimension) { if (!this.isDragging()) { Zapatec.Movable._handleCoordOverflow.call(this, limit, dimension); } return limit; }; /** * Returns the Zapatec.SRProp object. * It is used for saving and restoring * object properties. * @return {object} Zapatec.SRProp object. */ Zapatec.Draggable._getRestorer = function() { if (!this.restorer) { this.restorer = new Zapatec.SRProp(this); } return this.restorer; }; /** * Proceeds the array of draggable elements, to create * a separate array for elements that are actually moving. * This is used to handle method of dragging. * @param dragState {string} "dragStart" or "dragEnd" string * to point the state of dragging. */ Zapatec.Draggable._proceedDraggableElements = function(dragState) { //drag configuration var config = this.getDragConfig(), restorer = this._getRestorer(), copies = null, measurement = null, self = this, listenersObj = restorer.getProp("dragListeners"); function toggleEvents(action, hooks, listenersObj) { hooks.each(function(index, hook) { Zapatec.Utils[action + "Event"](hook, "mousedown", listenersObj.mousedown); if (config.eventCapture) { Zapatec.Utils[action + "Event"](hook, 'mousemove', listenersObj.mousemove); Zapatec.Utils[action + "Event"](hook, 'mouseup', listenersObj.mouseup); } }); } switch (config.method) { case "copy" : case "dummy" : { if (dragState == "dragStart") { var elements = Zapatec.Array(this.getDraggableElements()); var hooks = Zapatec.Array(this._getDraggableHooks()); copies = Zapatec.Array(); //removing listeners so the elements will be cloned //without them. toggleEvents("remove", hooks, listenersObj); //copying elements elements.each(function(index, movable) { if (Zapatec.isHtmlElement(movable)) { //cloning a node var newNode = movable.cloneNode(config.copyChilds); newNode.dragObj = self; //adding event listeners if (config.eventCapture) { Zapatec.Utils.addEvent(newNode, 'mousemove', listenersObj.mousemove); Zapatec.Utils.addEvent(newNode, 'mouseup', listenersObj.mouseup); } //inserting it to the place where the original one is movable.parentNode.insertBefore(newNode, movable); //fix for unknown problem in IE6 newNode.style.visibility = "visible"; //adding copy to array copies.push(newNode); //assigning new measurement if (movable == self.getMovableMeasurement()) { measurement = newNode; } } else if (Zapatec.isMovableObj(movable)) { //making recursive iterating through all child movables var elems = Zapatec.Array(movable.getMovableElements()); elems.each(arguments.calee); } }); //adding listeners back toggleEvents("add", hooks, listenersObj); //if there was no measurement copied we take the //original one if (!measurement) { measurement = this.getMovableMeasurement(); } //we substitute methods of Zapatec.Movable //interface to get needed result restorer.saveProp("getMovableElements"); restorer.saveProp("isMovableSafely()"); //new elements need to be movable safely this._setMovableSafely(false); //we need to move copies this.getMovableElements = function(resetArray) { var arr = copies; copies = resetArray ? null : copies; return arr; }; restorer.saveProp("getMovableMeasurement"); //we need to use copied measurement if such exists this.getMovableMeasurement = function() { return measurement; }; } else if (dragState == "dragEnd") { //destroying elements if needed var elements = Zapatec.Array(this.getMovableElements(true)); elements.each(function(index, movable) { if (config.method == "dummy") { movable.parentNode.removeChild(movable); } movable.dragObj = null; //removing event listeners if (config.eventCapture) { Zapatec.Utils.removeEvent(movable, 'mousemove', listenersObj.mousemove); Zapatec.Utils.removeEvent(movable, 'mouseup', listenersObj.mouseup); } }); this.restoreOfMove(); //returning old methods and values restorer.restoreProp("getMovableElements"); this._setMovableSafely(restorer.getProp("isMovableSafely()")); restorer.restoreProp("isMovableSafely()"); restorer.restoreProp("getMovableMeasurement"); } break; } default : { break; } } }; /** * Proceeds each movable element with some * styling routines on drag start and end. * @param movable {HTML element} element to proceed. * @param dragState {string} string pointing to drag state. */ Zapatec.Draggable._proceedDragStyles = function(movable, dragState) { var config = this.getDragConfig(); //overwriting class with new one if needed if (config.overwriteCSS) { if (dragState == "dragStart") { movable.restorer.saveProp("className"); movable.className = config.overwriteCSS; } else if (dragState == "dragEnd") { movable.restorer.restoreProp("className"); } } //adding drag class if needed if (config.dragCSS) { if (dragState == "dragStart") { Zapatec.Utils.addClass(movable, config.dragCSS); } else if (dragState == "dragEnd") { Zapatec.Utils.removeClass(movable, config.dragCSS); } } }; /** * Sets the coordinates of the point which is thought as * the center of movement of the object. * @param x {number} x coordinate of the point. * @param y {number} y coordinate of the point. */ Zapatec.Draggable._setMovingPoint = function(x, y) { var movingPoint = this._getMovingPointObject(); //this means drag end, so we should reset the object if (x === null || y === null) { movingPoint.x = null; movingPoint.y = null; movingPoint.offsetX = null; movingPoint.offsetY = null; return; } //this means drag start, so we fill object if (movingPoint.x === null || movingPoint.y === null) { var pos = this.getPagePosition(); movingPoint.x = x; movingPoint.y = y; movingPoint.offsetX = x - pos.x; movingPoint.offsetY = y - pos.y; } else {//changing point coordinates as this is drag move var pos = this.getPagePosition(); movingPoint.x = pos.x + movingPoint.offsetX; movingPoint.y = pos.y + movingPoint.offsetY; } return; }; /** * Returns the coordinate of the moving point. * Its the point which is thought to be the * center of movement. * @return {object} object with x and y properties. */ Zapatec.Draggable.getMovingPoint = function() { var movingPoint = this._getMovingPointObject(); return {x : movingPoint.x, y : movingPoint.y}; }; /** * Returns the object which holds the information about moving point. * @return {object} object with x, y, offsetX and offsetY properties. */ Zapatec.Draggable._getMovingPointObject = function() { if (!this.movingPoint || typeof this.movingPoint != "object") { this.movingPoint = {x : null, y : null, offsetX : null, offsetY : null}; } return this.movingPoint; }; /** * This is Zapatec.Utils.Draggable object definition. * It represents most of the routines and * events connected to dragging of the object. * @param config {object} - all parameters are passed as the properties of this object. * * Constructor recognizes the following properties of the config object (rest options are inherited * from Zapatec.Utils.Movable object, see movable.js comments) * \code * prop. name | description * ------------------------------------------------------------------------------------------------- * method | {string} Method of dragging, can be "cut", "copy" or "dummy" (default "cut"). * stopEvent | {boolean} Should we stop mousedown event of the handler (default true). * eventCapture | {boolean} This is a workaround for capturing some events like click or double * | click for the element that triggers dragging. Otherwise there will happen no * | such event at all (default false). * handler | {HTML element} Handle of dragging, otherwise container will be used (default null). * dragCSS | {string} Class name that will be added to the element while it is dragging (default ""). * overwriteCSS | {string} Class name that will overwrite all the classes of the element (default ""). * * \endcode * There have been made some changes into the structure of DnD routines, so many options were changed. * They are still working for keeping backward compability, but are depricated for the future use. * Here is the list of the options and their equivalents: * \code * prop. name | description * ------------------------------------------------------------------------------------------------- * left, top, right,| Use "limit" option of the Zapatec.Utils.Movable object! * bottom | * dragLayer | Use "moveLayer" option of the Zapatec.Utils.Movable object! * beforeDragInit, | Use standart Zapatec.Widget option "listeners" * beforeDragMove, | * beforeDragEnd, | * onDragInit, | * onDragMove, | * onDragEnd | * stopEv | Use "stopEvent" form instead. * * \endcode */ Zapatec.Utils.Draggable = function(config) { if (arguments.length > 1) { var args = arguments[1]; args.container = config; config = args; } //for backward compability if (typeof config.left != "undefined" || typeof config.right != "undefined" || typeof config.top != "undefined" || typeof config.bottom != "undefined") { config.limit = { minX : config.left, maxX : config.right, minY : config.top, maxY : config.bottom }; } if (config.dragLayer) {config.moveLayer = config.dragLayer;} if (!config.eventListeners) { config.eventListeners = {}; } if (config.beforeDragInit) {config.eventListeners.beforeDragInit = config.beforeDragInit;} if (config.beforeDragMove) {config.eventListeners.beforeDragMove = config.beforeDragMove;} if (config.beforeDragEnd) {config.eventListeners.beforeDragEnd = config.beforeDragEnd;} if (config.onDragInit) {config.eventListeners.onDragInit = config.onDragInit;} if (config.onDragMove) {config.eventListeners.onDragMove = config.onDragMove;} if (config.onDragEnd) {config.eventListeners.onDragEnd = config.onDragEnd;} if (config.stopEv) {config.stopEvent = config.stopEv;} config = Zapatec.Hash.remove(config, "left", "top", "right", "bottom", "dragLayer", "beforeDragInit", "beforeDragMove", "beforeDragEnd", "onDragInit", "onDragMove", "onDragEnd", "stopEv" ); Zapatec.Utils.Draggable.SUPERconstructor.call(this, config); }; Zapatec.Utils.Draggable.id = "Zapatec.Utils.Draggable"; Zapatec.inherit(Zapatec.Utils.Draggable, Zapatec.Utils.Movable); Zapatec.implement(Zapatec.Utils.Draggable, "Zapatec.Draggable"); /** * Inits the object with set of config options. * @param config {object} configuration parameters. */ Zapatec.Utils.Draggable.prototype.init = function(config) { //calling parent init Zapatec.Utils.Draggable.SUPERclass.init.call(this, config); //making draggable this.makeDraggable(); }; /** * Sets the default configuration of the object and * inits it with user defined values. * @param config {object} configuration parameters. */ Zapatec.Utils.Draggable.prototype.configure = function(config) { //method of dragging this.defineConfigOption("method", "cut"); //should we stop mouse down event! this.defineConfigOption("stopEvent", true); //should we capture mouse down event! this.defineConfigOption("eventCapture", false); //the handler for dragging this.defineConfigOption("handler", null); //dragging class name, will be added to all present classes this.defineConfigOption("dragCSS", null); //dragging overwrite class name, will overwrite all present //classes, but they will be restored on drag end this.defineConfigOption("overwriteCSS", null); //this option is only useful for dummy or copy methods //and determines whether we copy child content of the elements this.defineConfigOption("copyChilds", true); //redefining the makeMovable option to cancel the action //defined in Zapatec.Movable class this.defineConfigOption("makeMovable", false); // Call parent method Zapatec.Utils.Draggable.SUPERclass.configure.call(this, config); config = this.getConfiguration(); //Opera has strange behaviour - when you just click element starts draging, //although mouseup should have been called. if (Zapatec.is_opera) { config.eventCapture = true; } //correcting handler option config.handler = Zapatec.Widget.getElementById(config.handler); config.handler = Zapatec.Utils.img2div(config.handler); if (!Zapatec.isHtmlElement(config.handler)) { config.handler = config.container; } }; /** * Reconfigures the object with new parameters. * @param config {object} new configuration parameters. */ Zapatec.Utils.Draggable.prototype.reconfigure = function(config) { // Call parent method Zapatec.Utils.Draggable.SUPERclass.reconfigure.call(this, config); }; /** * Returns the array of all draggable elements. * @return {array} array of draggable elements. */ Zapatec.Utils.Draggable.prototype.getDraggableElements = function() { return this.movableElements; }; /** * Returns the handler. * @return {HTML element} handler element. */ Zapatec.Utils.Draggable.prototype._getDraggableHooks = function() { return this.getConfiguration().handler; }; /** * searches elements with className=class and makes them draggable(useful to call it on document load) * @param class {string} searcable element's CSSclassname. * @param el {HTMLElement} reference to the element. * @param recursive {boolean} searches in childs. * @param config {object} config object for draggable. * @return {array} array of draggable objects */ Zapatec.Utils.initDragObjects = function(className, el, recursive, config){ //only by class name :) if (!className) return; //array of elements to make draggable var elements = Zapatec.Utils.getElementsByAttribute('className', className, el, recursive, true); //returning result return Zapatec.Utils.applyToElements(Zapatec.Utils.Draggable, elements, config); }