Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._dragFactory = {
        createSimpleGroup : function(element, handle) {
                handle = handle ? handle : element
                var group = this.createGroup(element)
                group.setHandle(handle)
                group.transparentDrag()
                group.onTopWhileDragging()
                return group
        },

        createGroup : function(element) {
                var group = new _ToolManDragGroup(this, element)

                var position = ToolMan.css().readStyle(element, 'position')
                if (position == 'static') {
                        element.style["position"] = 'relative'
                } else if (position == 'absolute') {
                        /* for Safari 1.2 */
                        ToolMan.coordinates().topLeftOffset(element).reposition(element)
                }

                // TODO: only if ToolMan.isDebugging()
                group.register('draginit', this._showDragEventStatus)
                group.register('dragmove', this._showDragEventStatus)
                group.register('dragend', this._showDragEventStatus)

                return group
        },

        _showDragEventStatus : function(dragEvent) {
                window.status = dragEvent.toString()
        },

        constraints : function() {
                return this._constraintFactory
        },

        _createEvent : function(type, event, group) {
                return new _ToolManDragEvent(type, event, group)
        }
}

function _ToolManDragGroup(factory, element) {
        this.factory = factory
        this.element = element
        this._handle = null
        this._thresholdDistance = 0
        this._transforms = new Array()
        // TODO: refactor into a helper object, move into events.js
        this._listeners = new Array()
        this._listeners['draginit'] = new Array()
        this._listeners['dragstart'] = new Array()
        this._listeners['dragmove'] = new Array()
        this._listeners['dragend'] = new Array()
}

_ToolManDragGroup.prototype = {
        /*
         * TODO:
         *   - unregister(type, func)
         *   - move custom event listener stuff into Event library
         *   - keyboard nudging of "selected" group
         */

        setHandle : function(handle) {
                var events = ToolMan.events()

                handle.toolManDragGroup = this
                events.register(handle, 'mousedown', this._dragInit)
                handle.onmousedown = function() { return false }

                if (this.element != handle)
                        events.unregister(this.element, 'mousedown', this._dragInit)
        },

        register : function(type, func) {
                this._listeners[type].push(func)
        },

        addTransform : function(transformFunc) {
                this._transforms.push(transformFunc)
        },

        verticalOnly : function() {
                this.addTransform(this.factory.constraints().vertical())
        },

        horizontalOnly : function() {
                this.addTransform(this.factory.constraints().horizontal())
        },

        setThreshold : function(thresholdDistance) {
                this._thresholdDistance = thresholdDistance
        },

        transparentDrag : function(opacity) {
                var opacity = typeof(opacity) != "undefined" ? opacity : 0.75;
                var originalOpacity = ToolMan.css().readStyle(this.element, "opacity")

                this.register('dragstart', function(dragEvent) {
                        var element = dragEvent.group.element
                        element.style.opacity = opacity
                        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')'
                })
                this.register('dragend', function(dragEvent) {
                        var element = dragEvent.group.element
                        element.style.opacity = originalOpacity
                        element.style.filter = 'alpha(opacity=100)'
                })
        },

        onTopWhileDragging : function(zIndex) {
                var zIndex = typeof(zIndex) != "undefined" ? zIndex : 100000;
                var originalZIndex = ToolMan.css().readStyle(this.element, "z-index")

                this.register('dragstart', function(dragEvent) {
                        dragEvent.group.element.style.zIndex = zIndex
                })
                this.register('dragend', function(dragEvent) {
                        dragEvent.group.element.style.zIndex = originalZIndex
                })
        },

        _dragInit : function(event) {
                event = ToolMan.events().fix(event)
                var group = document.toolManDragGroup = this.toolManDragGroup
                var dragEvent = group.factory._createEvent('draginit', event, group)

                group._isThresholdExceeded = false
                group._initialMouseOffset = dragEvent.mouseOffset
                group._grabOffset = dragEvent.mouseOffset.minus(dragEvent.topLeftOffset)
                ToolMan.events().register(document, 'mousemove', group._drag)
                document.onmousemove = function() { return false }
                ToolMan.events().register(document, 'mouseup', group._dragEnd)

                group._notifyListeners(dragEvent)
        },

        _drag : function(event) {
                event = ToolMan.events().fix(event)
                var coordinates = ToolMan.coordinates()
                var group = this.toolManDragGroup
                if (!group) return
                var dragEvent = group.factory._createEvent('dragmove', event, group)

                var newTopLeftOffset = dragEvent.mouseOffset.minus(group._grabOffset)

                // TODO: replace with DragThreshold object
                if (!group._isThresholdExceeded) {
                        var distance = 
                                        dragEvent.mouseOffset.distance(group._initialMouseOffset)
                        if (distance < group._thresholdDistance) return
                        group._isThresholdExceeded = true
                        group._notifyListeners(
                                        group.factory._createEvent('dragstart', event, group))
                }

                for (i in group._transforms) {
                        var transform = group._transforms[i]
                        newTopLeftOffset = transform(newTopLeftOffset, dragEvent)
                }

                var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset)
                var newTopLeftPosition = dragEvent.topLeftPosition.plus(dragDelta)
                newTopLeftPosition.reposition(group.element)
                dragEvent.transformedMouseOffset = newTopLeftOffset.plus(group._grabOffset)

                group._notifyListeners(dragEvent)

                var errorDelta = newTopLeftOffset.minus(coordinates.topLeftOffset(group.element))
                if (errorDelta.x != 0 || errorDelta.y != 0) {
                        coordinates.topLeftPosition(group.element).plus(errorDelta).reposition(group.element)
                }
        },

        _dragEnd : function(event) {
                event = ToolMan.events().fix(event)
                var group = this.toolManDragGroup
                var dragEvent = group.factory._createEvent('dragend', event, group)

                group._notifyListeners(dragEvent)

                this.toolManDragGroup = null
                ToolMan.events().unregister(document, 'mousemove', group._drag)
                document.onmousemove = null
                ToolMan.events().unregister(document, 'mouseup', group._dragEnd)
        },

        _notifyListeners : function(dragEvent) {
                var listeners = this._listeners[dragEvent.type]
                for (i in listeners) {
                        listeners[i](dragEvent)
                }
        }
}

function _ToolManDragEvent(type, event, group) {
        this.type = type
        this.group = group
        this.mousePosition = ToolMan.coordinates().mousePosition(event)
        this.mouseOffset = ToolMan.coordinates().mouseOffset(event)
        this.transformedMouseOffset = this.mouseOffset
        this.topLeftPosition = ToolMan.coordinates().topLeftPosition(group.element)
        this.topLeftOffset = ToolMan.coordinates().topLeftOffset(group.element)
}

_ToolManDragEvent.prototype = {
        toString : function() {
                return "mouse: " + this.mousePosition + this.mouseOffset + "    " +
                                "xmouse: " + this.transformedMouseOffset + "    " +
                                "left,top: " + this.topLeftPosition + this.topLeftOffset
        }
}

ToolMan._dragFactory._constraintFactory = {
        vertical : function() {
                return function(coordinate, dragEvent) {
                        var x = dragEvent.topLeftOffset.x
                        return coordinate.x != x
                                        ? coordinate.factory.create(x, coordinate.y) 
                                        : coordinate
                }
        },

        horizontal : function() {
                return function(coordinate, dragEvent) {
                        var y = dragEvent.topLeftOffset.y
                        return coordinate.y != y
                                        ? coordinate.factory.create(coordinate.x, y) 
                                        : coordinate
                }
        }
}