(function(){
    /**
     *  mouse state variable.
     *     -1: uninitialized
     *      0: mouse is up
     *      1: mouse is down
     *      2: mouse is down and moving(drag)
     */
    var mousestate = -1;

    /**
     *  mouse position & move size variables.
     */
    var mouseX = 0;
    var mouseY = 0;
    var moveX = 0;
    var moveY = 0;

    /**
     *  glide variables.
     */
    var glideTimer;

    /**
     *  settings.
     */
    var dragSpeed = 1.5;
    var glideMultiple = 30;
    var glideFactor = 30;
    var glideEndPixel = 5;
    var timerInterval = 7;

    /**
     *  other variable.
     */
    var lastTouchedTime = (new Date()).getTime();

    /**
     *  init Touch interface
     */
    function chromeTouch() {
        if (mousestate == -1) {
            // For Sample Use FROM
            if (typeof document.addEventListener == "undefined") {
                return;
            }
            document.removeEventListener("mousedown", handleMousedown, false);
            document.removeEventListener("mouseup",   handleMouseup, false);
            document.removeEventListener("mousemove", handleMousemove, false);
            // For Sample Use TO

            document.addEventListener("mousedown", handleMousedown, false);
            document.addEventListener("mouseup",   handleMouseup, false);
            document.addEventListener("mousemove", handleMousemove, false);
        }
    }

    /**
     *  'mousedown' event handler
     */
    function handleMousedown(event) {
        endGlide();

        // Check draggable
        var nodeName = event.target.nodeName.toUpperCase();
        var style = getComputedStyle(event.target, '');

        if (nodeName == "SELECT") {
            // close pulldown options.
            event.target.addEventListener("change", unselect, false);
        }
        if (event.ctrlKey
                || event.shiftKey
                || style.cursor.toUpperCase() != "AUTO"
                || event.target.nodeType == Node.TEXT_NODE
                || nodeName == "INPUT"
                || nodeName == "TEXTAREA"
                || nodeName == "SELECT"
                || nodeName == "OPTION"
                || nodeName == "EMBED"
                || nodeName == "OBJECT"
                || nodeName == "APPLET"
                || nodeName == "HTML") {
            return true;
        }

        // Check text node
        if (event.target.childNodes) {
            var isText = false;

            for (var i = 0; i < event.target.childNodes.length; i++) {
                var node = event.target.childNodes[i];
                // ignore non-TEXT_NODE
                if (node.nodeType != Node.TEXT_NODE
                        || node.textContent.replace(/^[\s\t\r\n]+|[\s\t\r\n]+$/g, "") == "") {
                    continue;
                }

                // clicked point is TEXT_NODE?
                // * chrome(weblit) has not event.explicitOriginalTarget, so check step-by-step..
                var wrap = document.createElement("span");
                wrap.appendChild(node.cloneNode());
                wrap.style.position = "absolute";
                wrap = node.parentNode.insertBefore(wrap, node);
                var mx = event.clientX + window.pageXOffset;
                var my = event.clientY + window.pageYOffset;
                var pos = position(wrap);
                var padding = 10;
                if (pos.x - padding > mx || pos.x + wrap.offsetWidth + padding < mx
                        || pos.y - padding > my || pos.y + wrap.offsetHeight + padding < my) {
                    node.parentNode.removeChild(wrap);
                    continue;
                }
                node.parentNode.removeChild(wrap);

                isText = true;
                break;
            }
            if (isText) {
                return true;
            }
        }

        mousestate = 1;
        mouseX = event.clientX;
        mouseY = event.clientY;
        event.stopPropagation();
        event.preventDefault();
    }

    /**
     *  'mouseup' event handler
     */
    function handleMouseup(event) {
        if (mousestate == 2) {
            endDrag(event);
        } else if (mousestate == 1) {
            window.getSelection().removeAllRanges();
        }
        mousestate = 0;
    }

    /**
     *  'mousemove' event handler
     */
    function handleMousemove(event) {
        if (mousestate <= 0) {
            return;
        } else if (mousestate == 1) {
            mousestate = 2;
        }
        dragging(event);
        event.stopPropagation();
        event.preventDefault();
    }

    /**
     *  mouse dragging (called by handleMousemove)
     */
    function dragging(event) {
        moveX = (mouseX - event.clientX) * dragSpeed;
        moveY = (mouseY - event.clientY) * dragSpeed;
        window.scrollBy(moveX, moveY);
        mouseX = event.clientX;
        mouseY = event.clientY;
    }

    /**
     *  end drag (called by handleMouseup)
     */
    function endDrag(event) {
        // Glide away(only for Y)
        var glideY = moveY * glideMultiple;
        glideWindow(glideY, 0);
    }

    /**
     *  gride after mousedrag and mouseup
     */
    function glideWindow(lastY, currentY) {
        var diff = Math.pow(lastY - currentY, 2);
        var moved = 0;
        glideTimer = setTimeout(function(){
            var nextY = nextGlide(lastY, currentY);
            currentY += nextY;
            moved += nextY;
            if (Math.sqrt(Math.pow(nextY, 2)) <= glideEndPixel) {
                endGlide();
                return;
            }
            glideTimer = setTimeout(arguments.callee, timerInterval);
        }, timerInterval);
    }

    /**
     *  gride a bit
     */
    function nextGlide(lastY, currentY) {
        var nextY = (lastY - currentY) / glideFactor;
        window.scrollBy(0, nextY);
        return nextY;
    }

    /**
     *  finish gride
     */
    function endGlide() {
        if (glideTimer) {
            clearTimeout(glideTimer);
            glideTimer = null;
        }
    }

    /**
     *  event handler for closing select options.
     */
    function unselect(event) {
        this.size = 2;
        this.multiple = "multiple";
        this.size = 1;
        this.multiple = false;
        this.removeEventListener(arguments.callee);
    }

    /**
     *  get element's position utility
     */
    function position(element) {
        var x = 0, y = 0;
        for (var e = element; e; e = e.offsetParent) {
            x += e.offsetLeft;
            y += e.offsetTop;
        }
        for (e = element.parentNode; e && e != document.body; e = e.parentNode) {
            if (e.scrollLeft) {
                x -= e.scrollLeft;
            }
            if (e.scrollTop) {
                y -= e.scrollTop;
            }
        }
        return {x: x, y: y};
    }

    // initialize
    chromeTouch();
})();
