var oDrag = (function() {
    var bEnabled = false;
    var bResetCanvas = false;
    var sDraggableObjId = null;
    var fnReleaseCallback = null;
    var oCanvas = null;
    var bIsTracking = false;
    var oCanvasDim;
    var oDragStartMousePos = {
        iPageX: 0,
        iPageY: 0
    };

    var oDragDist = {
      iDistX: 0,
      iDistY: 0
    };

    var oPrevMouseDown = null;
    var oPrevMouseMove = null;
    var oPrevMouseUp = null;

        function enable(sId, fnCallback, bShouldReset) {
        sDraggableObjId = sId;
        if (sDraggableObjId) {
            oCanvas = getCanvas(sDraggableObjId);
            if (oCanvas) {
                var sPos = (oCanvas.currentStyle
                            ?                               oCanvas.currentStyle['position']
                            : (window.getComputedStyle
                               ?                                  window
                                 .getComputedStyle(oCanvas, null)
                                 .getPropertyValue('position')
                               : null));
                if (sPos && (sPos != 'absolute')) {
                    alert('Fatal Error(Drag): The draggable element must be enclosed within an absolutely positioned div.');
                    return;
                }
                oCanvasDim = getObjDim(oCanvas);
                bResetCanvas = bShouldReset;
            } else {
                alert('Fatal Error(oDrag): Cannot locate the DIV enclosing the element ' +
                      sDraggableObjId);
                return;
            }
        } else {
            alert('Fatal Error(oDrag): No image id specified.');
            return;
        }

        if (fnCallback) {
            fnReleaseCallback = fnCallback;
        } else {
            alert('Fatal Error(oDrag): No JavaScript callback function specified.');
            return;
        }

        if (document.captureEvents) {
                        document.captureEvents(Event.MOUSEDOWN |
                                   Event.MOUSEMOVE |
                                   Event.MOUSEUP);
        }

        oPrevMouseDown = document.onmousedown;
        oPrevMouseMove = document.onmousemove;
        oPrevMouseUp = document.onmouseup;

        document.onmousedown = engageMouse;
        document.onmousemove = trackMouse;
        document.onmouseup = releaseMouse;
        bEnabled = true;
        return;
    }

    function suspend() {
        if (bEnabled) {
            document.onmousedown = null;
            document.onmousemove = null;
            document.onmouseup = null;
        }
    }

    function resume() {
        if (bEnabled) {
            document.onmousedown = engageMouse;
            document.onmousemove = trackMouse;
            document.onmouseup = releaseMouse;
        }
    }

        function engageMouse(oEvt) {
        oEvt = (oEvt
                ? oEvt
                : event);
        if (oCanvas && (getEvtTargetId(oEvt) == sDraggableObjId)) {
            oCanvas.style.zIndex = 100;
            if (document.body && document.body.setCapture) {
                document.body.setCapture();
            }
            oDragStartMousePos = getMousePagePos(oEvt);
            bIsTracking = true;
            return false;
        }
    }

        function trackMouse(oEvt) {
        if (oCanvas && bIsTracking) {
            oEvt = (oEvt
                    ? oEvt
                    : event);
            var oMousePos = getMousePagePos(oEvt);
            if (oMousePos.iPageX < oCanvasDim.iOrigPageLeft) {
                oMousePos.iPageX = oCanvasDim.iOrigPageLeft;
            }
            if (oMousePos.iPageX >= (oCanvasDim.iOrigPageLeft + oCanvasDim.iWidth)) {
                oMousePos.iPageX = oCanvasDim.iOrigPageLeft + oCanvasDim.iWidth - 1;
            }
            if (oMousePos.iPageY < oCanvasDim.iOrigPageTop) {
                oMousePos.iPageY = oCanvasDim.iOrigPageTop;
            }
            if (oMousePos.iPageY >= (oCanvasDim.iOrigPageTop + oCanvasDim.iHeight)) {
                oMousePos.iPageY = oCanvasDim.iOrigPageTop + oCanvasDim.iHeight - 1;
            }
            oDragDist = clipCanvas(oMousePos);

            var oNewPos = {
                iPageX: oCanvasDim.iOrigPageLeft + oMousePos.iPageX -
                        oDragStartMousePos.iPageX,
                iPageY: oCanvasDim.iOrigPageTop  + oMousePos.iPageY -
                        oDragStartMousePos.iPageY
            };
            if (oNewPos.iPageX >= (oCanvasDim.iOrigPageLeft + oCanvasDim.iWidth)) {
                oNewPos.iPageX = oCanvasDim.iOrigPageLeft + oCanvasDim.iWidth - 1;
            }
            if (oNewPos.iPageX <= (oCanvasDim.iOrigPageLeft - oCanvasDim.iWidth)) {
                oNewPos.iPageX = oCanvasDim.iOrigPageLeft - oCanvasDim.iWidth + 1;
            }
            if (oNewPos.iPageY >= (oCanvasDim.iOrigPageTop + oCanvasDim.iHeight)) {
                oNewPos.iPageY = oCanvasDim.iOrigPageTop + oCanvasDim.iHeight - 1;
            }
            if (oNewPos.iPageY <= (oCanvasDim.iOrigPageTop - oCanvasDim.iHeight)) {
                oNewPos.iPageY = oCanvasDim.iOrigPageTop - oCanvasDim.iHeight + 1;
            }
            moveCanvas(oNewPos);

            oEvt.cancelBubble = true;
        }
        return false;
    }

        function releaseMouse(oEvt) {
        if (oCanvas && bIsTracking) {
                        bIsTracking = false;
            oCanvas.style.zIndex = 0;
            if (document.body && document.body.releaseCapture) {
                document.body.releaseCapture();
            }
            if (bResetCanvas) {
                resetCanvas();
            }
            if (fnReleaseCallback) {
                fnReleaseCallback(oDragDist.iDistX, oDragDist.iDistY);
            }
        }
    }

        function disable() {
        if (bEnabled) {
            document.onmousedown = oPrevMouseDown
            document.onmousemove = oPrevMouseMove;
            document.onmouseup = oPrevMouseUp;
            bEnabled = false;
        }
    }

        function moveCanvas(oPos) {
        if (document.body && document.body.style) {
            oCanvas.style.left = (oPos.iPageX - oCanvasDim.iOffsetX) + "px";
            oCanvas.style.top = (oPos.iPageY - oCanvasDim.iOffsetY) + "px";
        } else if (document.layers) {
            oCanvas.moveTo(oPos.iPageX, oPos.iPageY);
        }
    }

        function clipCanvas(oMousePos) {
        var iDistX = oMousePos.iPageX - oDragStartMousePos.iPageX;
        var iDistY = oMousePos.iPageY - oDragStartMousePos.iPageY;
        var iClipLeft = ((iDistX > 0)
                         ? 0
                         : - iDistX);
        var iClipRight = ((iDistX > 0)
                          ? oCanvasDim.iWidth - iDistX
                          : oCanvasDim.iWidth);
        var iClipTop = ((iDistY > 0)
                        ? 0
                        : - iDistY);
        var iClipBot = ((iDistY > 0)
                        ? oCanvasDim.iHeight - iDistY
                        : oCanvasDim.iHeight);
        oCanvas.style.clip =
            'rect(' +
                  iClipTop + 'px, ' +
                  iClipRight + 'px, ' +
                  iClipBot + 'px, ' +
                  iClipLeft + 'px' +
                ')';
        return {iDistX: iDistX,
                iDistY: iDistY};
    }

        function resetCanvas() {
        clipCanvas(oDragStartMousePos);
        moveCanvas({iPageX: oCanvasDim.iOrigPageLeft,
                    iPageY: oCanvasDim.iOrigPageTop});
    }

        function getCanvas(sObjId) {
        var oAnchor = null;
        if (sObjId) {
            var sAnchorId = sObjId + "Canvas";
            oAnchor = (document.layers
                       ? document.layers[sAnchorId]
                       : ((document.all && !window.opera)
                          ?                             document.all[sAnchorId]
                          : (document.getElementById
                             ? document.getElementById(sAnchorId)
                             : null)));
        }
        return oAnchor;
    }

        function getEvtTargetId(oEvt) {
        var oTarget = ((oEvt.target)
                       ? oEvt.target
                       : oEvt.srcElement);
        return (sDraggableObjId
                ? ((sDraggableObjId == oTarget.id)
                   ? sDraggableObjId
                   : null)
                : oTarget.id);
    }

        function getMousePagePos(oEvt) {
        oEvt = (oEvt
                ? oEvt
                : event);

        var oPos = {
            iPageX: 0,
            iPageY: 0
        };

        if (typeof(oEvt.clientX) != "undefined") {
                        oPos.iPageX = oEvt.clientX;
            oPos.iPageY = oEvt.clientY;
        } else if (typeof(oEvt.pageX) != "undefined") {
                        oPos.iPageX = oEvt.pageX;
            oPos.iPageY = oEvt.pageY;
        } else if (typeof(oEvt.offsetX) != "undefined") {
                        oPos.iPageX = oEvt.offsetX;
            oPos.iPageY = oEvt.offsetY;
        }
                return oPos;
    }

        function getObjDim(oObj) {
        var oDim = {
            iWidth: getObjWidth(oObj),
            iHeight: getObjHeight(oObj),
            iPageLeft: oObj.offsetLeft,
            iPageRight: 0,
            iPageTop: oObj.offsetTop,
            iPageBot: 0,
            iOffsetX: 0,
            iOffsetY: 0,
            iOrigPageLeft: 0,
            iOrigPageTop: 0
        };

        var iTotalOffsetLeft = 0;
        var iTotalOffsetTop = 0;
        if (oObj.offsetParent) {
                        var oParent = oObj.offsetParent;
            while (oParent) {
                iTotalOffsetLeft += oParent.offsetLeft;
                iTotalOffsetTop += oParent.offsetTop;
                oDim.iPageLeft += oParent.offsetLeft;
                oDim.iPageTop += oParent.offsetTop;
                oParent = oParent.offsetParent;
            }
                    }
        oDim.iOffsetX = iTotalOffsetLeft;
        oDim.iOffsetY = iTotalOffsetTop;
        oDim.iPageRight = oDim.iPageLeft + oDim.iWidth - 1;
        oDim.iPageBot = oDim.iPageTop + oDim.iHeight - 1;
        oDim.iOrigPageLeft = oDim.iPageLeft;
        oDim.iOrigPageTop = oDim.iPageTop;
        return oDim;
    }

        function getObjWidth(oObj) {
        return ((oObj.offsetWidth)
                ?                   oObj.offsetWidth
                : ((oObj.style && oObj.style.pixelWidth)
                   ?                      oObj.style.pixelWidth
                   : ((oObj.clip && oObj.clip.right)
                      ?                         oObj.clip.right
                      : '0')));
    }

        function getObjHeight(oObj) {
        return ((oObj.offsetHeight)
                ?                   oObj.offsetHeight
                : ((oObj.style && oObj.style.pixelHeight)
                   ?                      oObj.style.pixelHeight
                   : ((oObj.clip && oObj.clip.bottom)
                      ?                         oObj.clip.bottom
                      : '0')));
    }

        return {
        'enable': enable,
        'disable': disable,
        'suspend': suspend,
        'resume': resume
    }
})();
