static/dragdrop.js
changeset 148 2a2d2d6f6f80
parent 147 77330e43c855
child 149 176a656031cb
equal deleted inserted replaced
147:77330e43c855 148:2a2d2d6f6f80
     1 // script.aculo.us dragdrop.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
       
     2 
       
     3 // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
       
     4 //           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
       
     5 // 
       
     6 // script.aculo.us is freely distributable under the terms of an MIT-style license.
       
     7 // For details, see the script.aculo.us web site: http://script.aculo.us/
       
     8 //
       
     9 // Modified by Tero Marttila to strip unused code
       
    10 
       
    11 var Draggables = {
       
    12   drags: [],
       
    13   observers: [],
       
    14   
       
    15   register: function(draggable) {
       
    16     if(this.drags.length == 0) {
       
    17       this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
       
    18       this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
       
    19       this.eventKeypress  = this.keyPress.bindAsEventListener(this);
       
    20       
       
    21       Event.observe(document, "mouseup", this.eventMouseUp);
       
    22       Event.observe(document, "mousemove", this.eventMouseMove);
       
    23       Event.observe(document, "keypress", this.eventKeypress);
       
    24     }
       
    25     this.drags.push(draggable);
       
    26   },
       
    27   
       
    28   unregister: function(draggable) {
       
    29     this.drags = this.drags.reject(function(d) { return d==draggable });
       
    30     if(this.drags.length == 0) {
       
    31       Event.stopObserving(document, "mouseup", this.eventMouseUp);
       
    32       Event.stopObserving(document, "mousemove", this.eventMouseMove);
       
    33       Event.stopObserving(document, "keypress", this.eventKeypress);
       
    34     }
       
    35   },
       
    36   
       
    37   activate: function(draggable) {
       
    38     if(draggable.options.delay) { 
       
    39       this._timeout = setTimeout(function() { 
       
    40         Draggables._timeout = null; 
       
    41         window.focus(); 
       
    42         Draggables.activeDraggable = draggable; 
       
    43       }.bind(this), draggable.options.delay); 
       
    44     } else {
       
    45       window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
       
    46       this.activeDraggable = draggable;
       
    47     }
       
    48   },
       
    49   
       
    50   deactivate: function() {
       
    51     this.activeDraggable = null;
       
    52   },
       
    53   
       
    54   updateDrag: function(event) {
       
    55     if(!this.activeDraggable) return;
       
    56     var pointer = [Event.pointerX(event), Event.pointerY(event)];
       
    57     // Mozilla-based browsers fire successive mousemove events with
       
    58     // the same coordinates, prevent needless redrawing (moz bug?)
       
    59     if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
       
    60     this._lastPointer = pointer;
       
    61     
       
    62     this.activeDraggable.updateDrag(event, pointer);
       
    63   },
       
    64   
       
    65   endDrag: function(event) {
       
    66     if(this._timeout) { 
       
    67       clearTimeout(this._timeout); 
       
    68       this._timeout = null; 
       
    69     }
       
    70     if(!this.activeDraggable) return;
       
    71     this._lastPointer = null;
       
    72     this.activeDraggable.endDrag(event);
       
    73     this.activeDraggable = null;
       
    74   },
       
    75   
       
    76   keyPress: function(event) {
       
    77     if(this.activeDraggable)
       
    78       this.activeDraggable.keyPress(event);
       
    79   },
       
    80   
       
    81   addObserver: function(observer) {
       
    82     this.observers.push(observer);
       
    83     this._cacheObserverCallbacks();
       
    84   },
       
    85   
       
    86   removeObserver: function(element) {  // element instead of observer fixes mem leaks
       
    87     this.observers = this.observers.reject( function(o) { return o.element==element });
       
    88     this._cacheObserverCallbacks();
       
    89   },
       
    90   
       
    91   notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
       
    92     if(this[eventName+'Count'] > 0)
       
    93       this.observers.each( function(o) {
       
    94         if(o[eventName]) o[eventName](eventName, draggable, event);
       
    95       });
       
    96     if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
       
    97   },
       
    98   
       
    99   _cacheObserverCallbacks: function() {
       
   100     ['onStart','onEnd','onDrag'].each( function(eventName) {
       
   101       Draggables[eventName+'Count'] = Draggables.observers.select(
       
   102         function(o) { return o[eventName]; }
       
   103       ).length;
       
   104     });
       
   105   }
       
   106 }
       
   107 
       
   108 /*--------------------------------------------------------------------------*/
       
   109 
       
   110 var Draggable = Class.create({
       
   111   initialize: function(element) {
       
   112     var defaults = {
       
   113       handle: false,
       
   114       zindex: 1000,
       
   115       quiet: false,
       
   116       delay: 0
       
   117     };
       
   118     
       
   119     var options = Object.extend(defaults, arguments[1] || { });
       
   120 
       
   121     this.element = $(element);
       
   122     
       
   123     if(options.handle && Object.isString(options.handle))
       
   124       this.handle = this.element.down('.'+options.handle, 0);
       
   125     
       
   126     if(!this.handle) this.handle = $(options.handle);
       
   127     if(!this.handle) this.handle = this.element;
       
   128     
       
   129     Element.makePositioned(this.element); // fix IE    
       
   130 
       
   131     this.options  = options;
       
   132     this.dragging = false;   
       
   133 
       
   134     this.eventMouseDown = this.initDrag.bindAsEventListener(this);
       
   135     Event.observe(this.handle, "mousedown", this.eventMouseDown);
       
   136     
       
   137     Draggables.register(this);
       
   138   },
       
   139   
       
   140   destroy: function() {
       
   141     Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
       
   142     Draggables.unregister(this);
       
   143   },
       
   144   
       
   145   currentDelta: function() {
       
   146     return([
       
   147       parseInt(Element.getStyle(this.element,'left') || '0'),
       
   148       parseInt(Element.getStyle(this.element,'top') || '0')]);
       
   149   },
       
   150   
       
   151   initDrag: function(event) {
       
   152     if(!Object.isUndefined(Draggable._dragging[this.element]) &&
       
   153       Draggable._dragging[this.element]) return;
       
   154     if(Event.isLeftClick(event)) {    
       
   155       // abort on form elements, fixes a Firefox issue
       
   156       var src = Event.element(event);
       
   157       if((tag_name = src.tagName.toUpperCase()) && (
       
   158         tag_name=='INPUT' ||
       
   159         tag_name=='SELECT' ||
       
   160         tag_name=='OPTION' ||
       
   161         tag_name=='BUTTON' ||
       
   162         tag_name=='TEXTAREA')) return;
       
   163         
       
   164       var pointer = [Event.pointerX(event), Event.pointerY(event)];
       
   165       var pos     = Position.cumulativeOffset(this.element);
       
   166       this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
       
   167       
       
   168       Draggables.activate(this);
       
   169       Event.stop(event);
       
   170     }
       
   171   },
       
   172   
       
   173   startDrag: function(event) {
       
   174     this.dragging = true;
       
   175     if(!this.delta)
       
   176       this.delta = this.currentDelta();
       
   177     
       
   178     if(this.options.zindex) {
       
   179       this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
       
   180       this.element.style.zIndex = this.options.zindex;
       
   181     }
       
   182     
       
   183     Draggables.notify('onStart', this, event);
       
   184   },
       
   185   
       
   186   updateDrag: function(event, pointer) {
       
   187     if(!this.dragging) this.startDrag(event);
       
   188     
       
   189     Draggables.notify('onDrag', this, event);
       
   190     
       
   191     this.draw(pointer);
       
   192     if(this.options.change) this.options.change(this);
       
   193     
       
   194     // fix AppleWebKit rendering
       
   195     if(Prototype.Browser.WebKit) window.scrollBy(0,0);
       
   196     
       
   197     Event.stop(event);
       
   198   },
       
   199   
       
   200   finishDrag: function(event, success) {
       
   201     this.dragging = false;
       
   202     
       
   203     Draggables.notify('onEnd', this, event);
       
   204 
       
   205     var d = this.currentDelta();
       
   206     this.delta = d;
       
   207 
       
   208     if(this.options.zindex)
       
   209       this.element.style.zIndex = this.originalZ;
       
   210 
       
   211     Draggables.deactivate(this);
       
   212   },
       
   213   
       
   214   keyPress: function(event) {
       
   215     if(event.keyCode!=Event.KEY_ESC) return;
       
   216     this.finishDrag(event, false);
       
   217     Event.stop(event);
       
   218   },
       
   219   
       
   220   endDrag: function(event) {
       
   221     if(!this.dragging) return;
       
   222     this.finishDrag(event, true);
       
   223     Event.stop(event);
       
   224   },
       
   225   
       
   226   draw: function(point) {
       
   227     var pos = Position.cumulativeOffset(this.element);
       
   228     
       
   229     var d = this.currentDelta();
       
   230     pos[0] -= d[0]; pos[1] -= d[1];
       
   231     
       
   232     var p = [0,1].map(function(i){ 
       
   233       return (point[i]-pos[i]-this.offset[i]) 
       
   234     }.bind(this));
       
   235     
       
   236     var style = this.element.style;
       
   237     if((!this.options.constraint) || (this.options.constraint=='horizontal'))
       
   238       style.left = p[0] + "px";
       
   239     if((!this.options.constraint) || (this.options.constraint=='vertical'))
       
   240       style.top  = p[1] + "px";
       
   241     
       
   242     if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
       
   243   }
       
   244 });
       
   245 
       
   246 Draggable._dragging = { };
       
   247