﻿/*
 * jQuery JavaScript Library v1.3.2 required
 *
 * Copyright (c) 2009 Jimbo
 * Dual licensed under the MIT and GPL licenses.
 * e-mail: jimboyeah@gmail.com
 *
 * Date: 2010-4-9 18:05 (Fri, 9 April 2010)
 * Revision: 0.1
 * 
 * Last Modify: 2010-5-5 10:45
 * Content: redesign nob's size mode and scrolling action effect
 * 
 * Last Modify: 2010-5-7 13:37
 * Content: implemtate mouse wheel and dragable rolling
 * 
 * Last Modify: 2010-7-28 11:25:38
 * Content: modify flexble feature about slider and it's nob background, border
 * Remark: In this modify, I spent a lot of time to finish it to be useful, now 16:26:49.
 *         And I achive back-forward compatible use for the old template.
 * 
 * TODO:Grid step -- this function provide sliding grid by grid
 */

      $.fn.anyscroll = function(options){
        var context = this;

        ////////////////////////AnyScroll Settings////////////////////////////
        this.settings = {
          width:800, height:600,//size of content 
          axis:"y", //direction of scrolling
          roll:true,//use mouse wheel for true
          dragable:false,//use mouse left button to scroll
          clickable:true,//click track to scroll
          tuneable:true,//track to scroll step by step when mousedown
          interval:24,//space between content and control
          tick:24,//space between each wheel
          spend:0, //delay for a scrolling
          head:0,//nob space of it head
          tail:0,//nob space of ti tail
          navhead:0,//track head space for some navigator
          navtail:0,//track tail space for some navigator
          spinsize:{small:8,big:64}, //size of the nob
          nobmap:"", //background picture for the nob
          trackmap:"",//background picture for the track
          nobstyle:"", //css for the nob
          trackstyle:"",//css for the track
          trackThick:0 //board width of track nob obey. BECAREFUL absolute original is where beyound border!
        };
        $.fn.extend(this.settings,options);
        var sets = this.settings;
          sets.outerWidth = sets.width+sets.interval+sets.spinsize.small+sets.trackThick*2;
          sets.outerHeight = sets.height+sets.interval+sets.spinsize.small+sets.trackThick*2;
          sets.nobmaxheight = sets.height-sets.head-sets.tail-sets.spinsize.big;
          sets.nobmaxwidth = sets.width-sets.head-sets.tail-sets.spinsize.big;
        ///////////////////////////////////////////////////////////////////////

        var nobcss, trackcss, innercss, outercss;
        nobcss = "position:absolute;background:#8b8a8f;left:0px;top:0px;border:0px solid #464;font-size:0px;";
        trackcss = "position:relative;border:solid #444 "+sets.trackThick+"px;";
        if(sets.nobmap){
          nobcss+="background:url("+sets.nobmap+") no-repeat right top;";
        }else{
          //nobcss+="background:url(../images/nob.jpg) no-repeat center;";
        }
        if(sets.trackmap){
          trackcss+="background:url("+sets.trackmap+") no-repeat center;";
        }else{
          trackcss+="background:url(../images/track.jpg) no-repeat center;";
        }
        if (sets.axis=="y") {
          nobcss += "width:"+sets.spinsize.small+"px;height:"+sets.spinsize.big+"px;top:"+sets.navhead+"px;";
          trackcss += 'float:right;width:'+sets.spinsize.small+'px;height:'+ (sets.height-sets.head-sets.tail)+'px;margin-top:'+sets.head+'px;';
          innercss = 'margin-right:'+sets.interval+"px;";
          outercss = 'width:'+(sets.outerWidth)+'px;height:'+ sets.height+'px;';
        }else if(sets.axis=="x"){
          nobcss += "width:"+sets.spinsize.big+"px;height:"+sets.spinsize.small+"px;left:"+sets.navhead+"px;";
          trackcss += 'height:'+sets.spinsize.small+'px;width:'+ (sets.width-sets.head-sets.tail)+'px;margin-left:'+sets.head+'px;';
          innercss = 'margin-bottom:'+sets.interval+"px;";
          outercss = 'width:'+(sets.width)+'px;height:'+ (sets.outerHeight)+'px;';
        }
        nobcss +=";"+sets.nobstyle;
        trackcss += ";"+sets.trackstyle;

        this.nob = $('<div style="'+nobcss+'" />')
          .attr("class","anynob")
          .bind("mousedown",function(event){drag.call(context,event);});
        //为了，使nob限定在track指定的区域，我须要一个shadow来作探测器。
        this.shadow = this.nob.clone()
          .attr("class","anyshadow")
          .css("visibility","hidden");//.css("left",sets.spinsize.big)
        this.track = $('<div style="'+trackcss+'" />')
          .attr("class","anytrack")
          .append(this.nob)
          .append(this.shadow);
        if(sets.clickable)this.track.click(click);
        if(sets.tuneable)this.track.mousedown(tuneHandler);
        this.road = $('<div style="overflow:hidden;text-align:left;background:white;'+
          'position:absolute;top:0px;left:0px;'+
          'cursor: url(../images/size4_r.cur), pointer;'+
          (sets.dragable?'filter:alpha(opacity=0);opacity:0;':'visibility:hidden;')+
          'width:'+sets.width+'px;height:'+sets.height+'px;" />')
          .attr("class","anyroad");
          //.bind("mouseover",over)
          //.bind("mouseout",out)
          //.bind("DOMMouseScroll",rolling)
          //.bind("mousewheel",rolling);
        this.inner = $('<div style="position:relative;overflow:hidden;border:0px solid yellow;'+
          innercss+'width:'+sets.width+'px;height:'+sets.height+'px;" />')
          .attr("class","anyinner")
          .append($(this[0]).html());
        //capture for dragable of left mouse button
        var onmousewheel = (document.addEventListener?"DOMMouseScroll":"onmousewheel");
        //alert("onmousewheel="+onmousewheel);
        if(sets.dragable) Capture(
          {name:'onmousedown',fun:over},
          {name:'onmousemove',fun:rolling},
          {name:'onmouseup',fun:out},
          this.road[0]
        );
        if(sets.dragable) bind(this.road[0],onmousewheel,rolling);
        /*capture for mouse wheel
        if(sets.roll) Capture(
          {name:'onmouseover',fun:over},
          {name:onmousewheel,fun:rolling},
          {name:'onmouseout',fun:out},
          this.road[0]
        );*/
        if(sets.roll) bind(this.inner[0],onmousewheel,rolling);

        this.outer = $('<div style="position:relative;border:0px solid blue;'+outercss+'" />')
          .attr("id","anyouter")
          .append(this.track)
          .append(this.inner)
          .append(this.road);
        if(sets.axis=="x")this.outer.append(this.track);
        $(this[0]).html("") //Clear inner HTML of current DOM object
          .append(this.outer); //Append this object create here

        //after execute DOM, then we can get/set attribute savely
        //this.track.offsetTop = this.track[0].offsetTop;
        //log.info(this.nob[0].offsetTop);

        //var baby = this.context;
        //for(var p in baby){
        //  var v = typeof(baby[p]) == "function"?"function":baby[p];
        //  log.info(p);
        //}

        //public methods 
        this.scrollTo = function(delta,type){
          if(typeof(chill)!="undefined") { //use Jany.js for animation
            var from = parseInt(sets.axis=="y"? this.nob[0].style.top:this.nob[0].style.left);
            var type = type||"linear";
            new chill(function(v){scrollTo(v,v);},{to:delta,from:from,type:type})
          }else
            scrollTo(delta,delta);
        };

        return this;

        //protected methods following
        
        function dbg(s){//method for debuging
          //return ;
          document.title = s;
        }
        function initTrackBound(){
          /*
          context.trackbound = context.track[0].getBoundingClientRect();
          //without consider track's border width 
          context.trackbound.top=context.trackbound.top+sets.trackThick;
          context.trackbound.bottom==context.trackbound.top-sets.trackThick;
          context.trackbound.left==context.trackbound.top+sets.trackThick;
          context.trackbound.right==context.trackbound.top-sets.trackThick;
          return context.trackbound;
          */
          var rect = new Object();
          var _rect = context.track[0].getBoundingClientRect();
          //without consider track's border width 
          rect.top=_rect.top+sets.trackThick;
          rect.bottom=_rect.bottom-sets.trackThick;
          rect.left=_rect.left+sets.trackThick;
          rect.right=_rect.right-sets.trackThick;
          //work out then navigator space
          rect.top=sets.axis=="y"?rect.top+sets.navhead:rect.top;
          rect.bottom=sets.axis=="y"?rect.bottom-sets.navtail:rect.bottom;
          rect.left=sets.axis=="x"?rect.left+sets.navhead:rect.left;
          rect.right=sets.axis=="x"?rect.right-sets.navtail:rect.right;
          //dbg(rect.right+" "+sets.navtail);
          context.trackbound = rect;
          //alert(rect.top+' '+rect.bottom+' '+rect.left+' '+rect.right)
          return rect;
        }
        function click(e){
          //document.title = "click";
          //jQuery's click not a click that I need infact, so I simulate the true one 
          //with LET (Last Event Time) attribute
          //which seted in mousedown event handler name tuneHandler.
          //alert('do something here');
          //this.innerHTML = "over "+new Date().toGMTString();
          //document.title="click ("+e.clientX+","+e.clientY+")"+/\d\d:\d\d:\d\d/.exec(new Date().toGMTString());
          //alert(this.getAttribute("anyLET"));
          var let = new Date().getTime()-(this.getAttribute("anyLET")); //return milliseconds
          //document.title=(new Date().getTime()+"||||"+this.getAttribute("anyLET")+"|||"+let);
          if(let<200){ //if let great then 300 I take it consider as a tuning action
            var track = initTrackBound();
            var width = e.clientX-track.left-(sets.spinsize.big)/2, height = e.clientY-track.top-(sets.spinsize.big)/2;
            scrollTo(width,height);
          }
        }
        function tuneHandler(e){
          //document.title = "tune "+new Date().toUTCString();
          this.setAttribute("anyLET",new Date().getTime());
          //var track = intiTrackBound();
          var nob = context.nob[0].getBoundingClientRect();
          var track = initTrackBound();
          var offset = 10;
          var dx = e.clientX>(nob.top+nob.bottom)/2? offset:-offset;
          var dy = e.clientY>(nob.top+nob.bottom)/2? offset:-offset;
          var isTuning = true;
          context.track.one("mouseup",stoptune);
          context.track.one("mouseout",stoptune);
          //context.track.bind("mousemove",function(){document.title=new Date();});
          function tuning(){
            //alert("tunig");
            var nob = context.nob[0].getBoundingClientRect();
            var sentinalY = (nob.top+nob.bottom)/2;
            var sentinalX = (nob.left+nob.right)/2;
            //dbg("e.clientY"+e.clientY+" "+sentinalY+" "+new Date().getSeconds());
            var cy = e.clientY;
            var cx = e.clientX;
            var noCross = (cy>sentinalY&&dy>0||cy<sentinalY&&dy<0)&&sets.axis=="y"
              || (cx>sentinalX&&dx>0||cx<sentinalX&&dx<0)&&sets.axis=="x";
            var noExtra = (nob.top-sets.navhead>track.top||nob.bottom+sets.navtail<track.bottom)&&sets.axis=="y"
              || (nob.left-sets.navhead>track.left||nob.right+sets.navtail<track.right)&&sets.axis=="x"
            if(isTuning&&noCross&&noExtra) {
              scrollBy(dx,dy);
              setTimeout(tuning,10);
            }
          }
          setTimeout(tuning,200);
          function stoptune(e){
            isTuning=false;
            //alert("stoptune"+this);
            //context.track.unbind("mouseup",stoptune);
          }
        }
        function over(e){
          //alert('do something here');
          //this.innerHTML = "over "+new Date().toGMTString();
          //document.title="start ("+e.clientX+","+e.clientY+")"+/\d\d:\d\d:\d\d/.exec(new Date().toGMTString());
        }
        function out(e){
          //alert('do something here');
          //this.innerHTML = "out "+new Date().toGMTString();
          //document.title+="end ("+e.clientX+","+e.clientY+")"+/\d\d:\d\d:\d\d/.exec(new Date().toGMTString());
        }
        function rolling(e){
          //alert('do something here');
          e=e||event;
          if(e.wheelDelta||e.detail){
            //alert("whell");
            var delta = (e.wheelDelta||e.detail)<0?sets.tick:-sets.tick;
            //reverse for MF;
            var ua = navigator.userAgent;
            delta = (ua.indexOf("Firefox")!=-1&&ua.indexOf("Navigator")==-1?-delta:delta);
            scrollBy(delta,delta);
          }else{
            var width = e.clientX - deltaX;
            var height = e.clientY - deltaY;
            scrollTo(width,height);
          }
          //document.title="capturing ("+e.clientX+","+e.clientY+")-("+deltaX+","+deltaY+")"+/\d\d:\d\d:\d\d/.exec(new Date().toGMTString());
          //this.innerHTML = "roling "+new Date().toGMTString()+"<br>"+delta;
          //for(var p in e)
          //this.innerHTML += "<br>"+p+"="+e[p];
          // We've handled this event. Don't let anybody else see it.

          if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
          else e.cancelBubble = true;                      // IE
          // Now prevent any default action.
          if (e.preventDefault) e.preventDefault( );   // DOM Level 2
          return false; //prevent default scroll for IE
        }
        function scrollBy(dx,dy){
          var top = parseInt(context.nob[0].style.top,10);
          var left = parseInt(context.nob[0].style.left,10);
          //context.road[0].innerHTML = new Date().toGMTString()+"<br>"+context.nob[0].style.top+"|"+delta;
          scrollTo(left+dx,top+dy);
        }

        function scrollTo(width,height){
          //var track = context.track[0].getBoundingClientRect();
          //var nob = context.nob[0].getBoundingClientRect();
          var track = (context.trackbound==null)? initTrackBound():context.trackbound;
          var nob = context.nobbound;
          var isAxisX = (sets.axis=="x"||sets.axis!="y");
          var isAxisY = (sets.axis=="y"||sets.axis!="x");

          //here is trick solution and the shadow act as pioneer to dectect situation.
          context.shadow[0].style.left = (width) + "px";
          context.shadow[0].style.top = (height) + "px";
          context.shadow.show();
          var shadow = context.shadow[0].getBoundingClientRect();
          context.shadow.hide();
          var canMoveX = shadow.left>=track.left && shadow.right<=track.right;
          var canMoveY = shadow.top>=track.top && shadow.bottom<=track.bottom;

          var bescrollHeight = context.inner.attr("scrollHeight")-sets.height;
          var bescrollWidth = context.inner.attr("scrollWidth")-sets.width;

          //var destX = parseInt(parseInt("0"+context.nob[0].style.left,10)*bescrollWidth/sets.nobmaxwidth);
          //var destY = parseInt(parseInt("0"+context.nob[0].style.top,10)*bescrollHeight/sets.nobmaxheight);
          //alert("d:"+destX+"|"+destY+"|"+context.nob[0].style.left);
          //if(isAxisX&&(dirX=="+"&&nob.right<=track.right||dirX=="-"&&nob.left>=track.left)){
          if(isAxisX&&canMoveX){
            context.nob[0].style.left = (width) + "px";
            var destX = parseInt(parseInt("0"+context.nob[0].style.left,10)*bescrollWidth/(sets.nobmaxwidth-sets.navhead-sets.navtail));
            context.inner.stop().animate({"scrollLeft":destX},sets.spend);
          }else if(isAxisX){
            context.nob[0].style.left = (shadow.left<track.left?sets.navhead:sets.nobmaxwidth-sets.navtail) + "px";
            context.inner.stop().animate({"scrollLeft":(shadow.left<track.left?0:bescrollWidth)},sets.spend);
          }
          //if(isAxisY&&(dirY=="+"&&nob.bottom<=track.bottom||dirY=="-"&&nob.top>=track.top)){
          if(isAxisY&&canMoveY){
            context.nob[0].style.top = (height) + "px";
            var destY = parseInt(parseInt("0"+context.nob[0].style.top,10)*bescrollHeight/(sets.nobmaxheight-sets.navhead-sets.navtail));
            context.inner.stop().animate({"scrollTop":destY},sets.spend);
          }else if(isAxisY){
            context.nob[0].style.top = (shadow.top<track.top?sets.navhead:sets.nobmaxheight-sets.navtail) + "px";
            context.inner.stop().animate({"scrollTop":(shadow.top<track.top?0:bescrollHeight)},sets.spend);
          }
          //document.title = canMoveX+"|"+canMoveY+"|"+nob.left+"|"+nob.right+"|";
          //document.title = shadow.left+"|"+shadow.right+"|"+track.left+"|"+track.right;
        }
        
        function proxy(tag,fun){
          return function(e){
            if(typeof(fun)=="function")
              return fun.call(tag,e);
            return null
          }
        }

        function bind(tag,event,handler,capture){
          if(typeof(handler)!="function")throw new Error("handler must a function");
          if(typeof(tag)!="object") throw new Error("tag must an DOM element");
          capture = capture?true:false;
          if (tag.addEventListener){
            tag.addEventListener(event.replace(/^on/,""),proxy(tag,handler),capture);
            if(event=="DOMMouseScroll") //Opera/Chrome/Safari
              tag.onmousewheel=handler;
              //tag.addEventListener("onmousewheel", handler, capture);
          }else if(tag.attachEvent){
            tag.attachEvent(event,proxy(tag,handler));
          }else{
            tag[event] = proxy(tag,handler);
          }
        }

        function loos(tag,event,handler,capture){
          if(typeof(handler)!="function")throw new Error("handler must a function");
          if(typeof(tag)!="object") throw new Error("tag must an DOM element");
          capture = capture?true:false;
          if (tag.addEventListener){
            tag.removeEventListener(event.replace(/^on/,""),handler,capture);
          }else if(tag.detachEvent){
            tag.detachEvent(event,handler);
          }else{
            tag[event] = null; //arbitary set null
          }
        }

        function Capture(begin,enter,end,tag){

          bind(tag,begin.name,capture);
          //alert(begin.name+"|"+enter.name+"|"+end.name);

          //var deltaX, deltaY; use external variables
          var enterfun = proxy(tag,enter.fun);
          var endfun = proxy(tag,end.fun);
          //bind(tag,enter.name,function(){if(enter.fun) return enter.fun();});

          function capture(e){
            e = e||event;
            var startX = e.clientX, startY = e.clientY; //事件:击点对视区左上角
            var origX = context.nob[0].offsetLeft, origY = context.nob[0].offsetTop; //对象:对象对body左上角
            deltaX = startX - origX, deltaY = startY - origY; //求出对象对client的原点坐标差
            if (document.addEventListener) {  // DOM Level 2 event model
              //alert("document.addEventListener="+document.addEventListener);
              if(enter.name=="DOMMouseScroll") //Opera/Chrome/Safari
                tag.onmousewheel=enterfun;
                //document.addEventListener("onmousewheel", enterfun, true);
              document.addEventListener(enter.name.replace(/^on/,""), enterfun, true);
              document.addEventListener(end.name.replace(/^on/,""), release, true);
              //bind(document,enter.name,enterfun,true);
              //bind(document,end.name,release,true);
            } else if (document.attachEvent) {  // IE 5+ Event Model
              tag.setCapture( );
              if(enter.fun) tag.attachEvent(enter.name, enterfun);
              tag.attachEvent(end.name, release);
              // Treat loss of mouse capture as a mouseup event.
              tag.attachEvent("onlosecapture", release);
              //bind(tag,enter.name,enterfun);
              //bind(tag,end.name,release);
              //bind(tag,"onlosecapture",release);
            } else {  // IE 4 Event Model
              // In IE 4 we can't use attachEvent( ) or setCapture( ), so we set
              document[enter.name] = enterfun;
              document[end.name]= endfun;
            }

            // We've handled this event. Don't let anybody else see it.
            if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
            else e.cancelBubble = true;                      // IE

            // Now prevent any default action.
            if (e.preventDefault) e.preventDefault( );   // DOM Level 2

            //if(begin.fun) return begin.fun.call(tag,e);
            return proxy(tag,begin.fun)(e);
          }

          function release(e){
            e = e||event;
            if (document.removeEventListener) {  // DOM event model
              document.removeEventListener(enter.name.replace(/^on/,""), enterfun, true);
              document.removeEventListener(end.name.replace(/^on/,""), release, true);
              if(enter.name=="DOMMouseScroll") //Opera/Chrome/Safari
                tag.onmousewheel=enterfun;
                //document.removeEventListener("onmousewheel", enterfun, true);
              //looe(document,enter.name, enterfun, true);
              //looe(document,end.name, release, true);
            }else if (document.detachEvent) {  // IE 5+ Event Model
              tag.detachEvent(enter.name, enterfun);
              tag.detachEvent(end.name, release);
              tag.detachEvent("onlosecapture", release);
              //loos(tag,enter.name,enterfun);
              //loos(tag,end.name,release);
              //loos(tag,"onlosecapture", release);
              tag.releaseCapture( );
            }else {  // IE 4 Event Model
              document[enter.name] = null;
              document[end.name] = null;
            }
            if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
            else e.cancelBubble = true; // IE
            return endfun(e);
          }
        }

        function HitTest(rect, p){
          var left = p.x?(rect.left>p.x):false;
          var right = p.x?(rect.right<p.x):false;
          var hitX = !(left||right);
          var top = p.y?(rect.top>p.y):false;
          var bottom = p.y?(rect.bottom<p.y):false;
          var hitY = !(top||bottom);
          return {y:hitY,x:hitX,both:hitX&&hitY,left:left,right:right,bottom:bottom,top:top};
        }

        function BoundTest(dest,src,dir){
          var top = dest.top>src.top;
          var bottom = dest.bottom<src.bottom;
          var hitY = !(top||bottom);
          var left = dest.left>src.left;
          var right = dest.right < src.rigth;
          var hitX = !(left||right);
          return {y:hitY,x:hitX,both:hitX&&hitY,left:left,right:right,bottom:bottom,top:top};
        }

        //Thanks for JavaScript: The Definitive Guide, 5th Edition by David Flanagan
        //感谢David的示例代码，代码如诗，为我的二次开发节省了大量时间。
        //虽然可直接使用已实现的Capture方法，但在此仍直接引用。
        var oldmovehandler, olduphandler, deltaX, deltaY;
        //var oldX,oldY;
        function moveHandler(e) {
          //var r = context.track[0].getBoundingClientRect(); //value take considered border-width
          //document.title = (r.top +" "+r.bottom);
          //if(oldX==undefined) oldX = e.screenX;
          //if(oldY==undefined) oldY = e.screenY;
          if (!e) e = window.event; // IE Event Model
          //var isAxisX = HitTest(track,{x:e.clientX-deltaX}) && (sets.axis=="x"||sets.axis!="y");
          //var isAxisY = HitTest(track,{y:e.clientY-deltaY}) && (sets.axis=="y"||sets.axis!="x");
          //document.title = deltaX+"|"+deltaY+"|"+e.clientX+"|"+e.clientX;
          var width = e.clientX - deltaX;
          var height = e.clientY - deltaY;
          //var dirX = oldX>e.screenX?"-":"+";
          //var dirY = oldY>e.screenY?"-":"+";
          //oldX = e.screenX;
          //oldY = e.screenY;
          //document.title = e.screenX+"|"+dirX+"|"+dirY+"|"+track.left+"|"+track.right;
          //document.title = oldX+"|"+oldY+"|"+nob.left+"|"+nob.right+"|"+track.left+"|"+track.right;
          
          //log.info(true && BoundTest(track,nob,"y"));
          //log.info(nob.top+'|'+nob.bottom+'|'+nob.left+'|'+nob.right);
          //log.info(track.top+'|'+track.bottom+'|'+track.left+'|'+track.right);
          //log.info(e.clientY+'|'+deltaY);

          scrollTo(width,height);

          if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
          else e.cancelBubble = true;                  // IE
        }
        function upHandler(e) {
            context.trackbound = context.track[0].getBoundingClientRect();
            context.nobbound = context.nob[0].getBoundingClientRect();
            if (!e) e = window.event;  // IE Event Model
            if (document.removeEventListener) {  // DOM event model
                document.removeEventListener("mouseup", upHandler, true);
                document.removeEventListener("mousemove", moveHandler, true);
            }
            else if (document.detachEvent) {  // IE 5+ Event Model
                context.nob[0].detachEvent("onlosecapture", upHandler);
                context.nob[0].detachEvent("onmouseup", upHandler);
                context.nob[0].detachEvent("onmousemove", moveHandler);
                context.nob[0].releaseCapture( );
            }
            else {  // IE 4 Event Model
                // Restore the original handlers, if any
                document.onmouseup = olduphandler;
                document.onmousemove = oldmovehandler;
            }
            if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2
            else e.cancelBubble = true;                  // IE
            
            if(sets.axis=="y" || sets.axis!="x"){
              if(context.nobbound.bottom>context.trackbound.bottom) context.nob.animate({top:sets.nobmaxheight},200);
              if(context.nobbound.top<context.trackbound.top) context.nob.animate({top:0},200);
            }
            if(sets.axis=="x" || sets.axis!="y"){
              if(context.nobbound.right>context.trackbound.right) context.nob.animate({left:sets.nobmaxwidth},200);
              if(context.nobbound.left<context.trackbound.left) context.nob.animate({left:0},200);
            }

        }
        function drag(event) {
            context.nobbound = context.nob[0].getBoundingClientRect();

            var startX = event.clientX+sets.trackThick, startY = event.clientY+sets.trackThick; //事件:击点对视区左上角

            //var track = context.track[0].getBoundingClientRect();
            var track = initTrackBound();

            // The original position (in document coordinates) of the
            // element that is going to be dragged. Since elementToDrag is
            // absolutely positioned, we assume that its offsetParent is the
            // document body.
            var origX = context.nob[0].offsetLeft, origY = context.nob[0].offsetTop; //对象:对象对body左上角

            // Even though the coordinates are computed in different
            // coordinate systems, we can still compute the difference between them
            // and use it in the moveHandler( ) function. This works because
            // the scrollbar position never changes during the drag.
            deltaX = startX - origX, deltaY = startY - origY; //求出对象对client的原点坐标差
            //log.info("startX="+startX+"|"+"startY="+startY);
            //log.info("origX="+origX+"|"+"origY="+origY);
            //log.info("deltaX="+deltaX+"|"+"deltaY="+deltaY);
            //log.info('|delta|'+deltaX+'|'+deltaY+"|rect|"+track.top+'|'+track.bottom+'|'+track.left+'|'+track.right+'|client|'+startX+'|'+startY+'|'+origX+'|'+origY);
            //log.info('|XY|'+event.X+'|'+event.Y+'|delta|'+deltaX+'|'+deltaY+'|client|'+startX+'|'+startY+'|'+origX+'|'+origY);

            if (document.addEventListener) {  // DOM Level 2 event model
                document.addEventListener("mousemove", moveHandler, true);
                document.addEventListener("mouseup", upHandler, true);
            }
            else if (document.attachEvent) {  // IE 5+ Event Model
                context.nob[0].setCapture( );
                context.nob[0].attachEvent("onmousemove", moveHandler);
                context.nob[0].attachEvent("onmouseup", upHandler);
                // Treat loss of mouse capture as a mouseup event.
                context.nob[0].attachEvent("onlosecapture", upHandler);
            }
            else {  // IE 4 Event Model
                // In IE 4 we can't use attachEvent( ) or setCapture( ), so we set
                oldmovehandler = document.onmousemove; // used by upHandler( )
                olduphandler = document.onmouseup;
                document.onmousemove = moveHandler;
                document.onmouseup = upHandler;
            }

            // We've handled this event. Don't let anybody else see it.
            if (event.stopPropagation) event.stopPropagation( );  // DOM Level 2
            else event.cancelBubble = true;                      // IE

            // Now prevent any default action.
            if (event.preventDefault) event.preventDefault( );   // DOM Level 2
        }

      }

