Changeset 491

Show
Ignore:
Timestamp:
08/22/06 07:37:33
Author:
piv
Message:

ie compatibility

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • qPloneTabs/trunk/skins/qPloneTabs/javascripts/controls.js

    r490 r491  
    142142      } 
    143143     else  
    144       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||  
    145         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; 
     144      if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)  
     145        return; 
    146146 
    147147    this.changed = true; 
     
    151151      this.observer =  
    152152        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 
    153   }, 
    154  
    155   activate: function() { 
    156     this.changed = false; 
    157     this.hasFocus = true; 
    158     this.getUpdatedChoices(); 
    159153  }, 
    160154 
     
    228222      return; 
    229223    } 
    230     var value = ''; 
    231     if (this.options.select) { 
    232       var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; 
    233       if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); 
    234     } else 
    235       value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 
    236      
     224 
     225    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 
    237226    var lastTokenPos = this.findLastToken(); 
    238227    if (lastTokenPos != -1) { 
     
    317306Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { 
    318307  initialize: function(element, update, url, options) { 
    319     this.baseInitialize(element, update, options); 
     308         this.baseInitialize(element, update, options); 
    320309    this.options.asynchronous  = true; 
    321310    this.options.onComplete    = this.onComplete.bind(this); 
     
    460449 
    461450    this.options = Object.extend({ 
    462       okButton: true, 
    463451      okText: "ok", 
    464       cancelLink: true, 
    465452      cancelText: "cancel", 
    466453      savingText: "Saving...", 
     
    484471      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, 
    485472      highlightendcolor: "#FFFFFF", 
    486       externalControl: null, 
    487       submitOnBlur: false, 
    488       ajaxOptions: {}, 
    489       evalScripts: false 
     473      externalControl:  null, 
     474      ajaxOptions: {} 
    490475    }, options || {}); 
    491476 
     
    552537    } 
    553538 
    554     if (this.options.okButton) { 
    555       okButton = document.createElement("input"); 
    556       okButton.type = "submit"; 
    557       okButton.value = this.options.okText; 
    558       okButton.className = 'editor_ok_button'; 
    559       this.form.appendChild(okButton); 
    560     } 
    561  
    562     if (this.options.cancelLink) { 
    563       cancelLink = document.createElement("a"); 
    564       cancelLink.href = "#"; 
    565       cancelLink.appendChild(document.createTextNode(this.options.cancelText)); 
    566       cancelLink.onclick = this.onclickCancel.bind(this); 
    567       cancelLink.className = 'editor_cancel';       
    568       this.form.appendChild(cancelLink); 
    569     } 
     539    okButton = document.createElement("input"); 
     540    okButton.type = "submit"; 
     541    okButton.value = this.options.okText; 
     542    this.form.appendChild(okButton); 
     543 
     544    cancelLink = document.createElement("a"); 
     545    cancelLink.href = "#"; 
     546    cancelLink.appendChild(document.createTextNode(this.options.cancelText)); 
     547    cancelLink.onclick = this.onclickCancel.bind(this); 
     548    this.form.appendChild(cancelLink); 
    570549  }, 
    571550  hasHTMLLineBreaks: function(string) { 
     
    583562      text = this.getText(); 
    584563    } 
    585  
    586     var obj = this; 
    587564     
    588565    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { 
    589566      this.options.textarea = false; 
    590567      var textField = document.createElement("input"); 
    591       textField.obj = this; 
    592568      textField.type = "text"; 
    593569      textField.name = "value"; 
    594570      textField.value = text; 
    595571      textField.style.backgroundColor = this.options.highlightcolor; 
    596       textField.className = 'editor_field'; 
    597572      var size = this.options.size || this.options.cols || 0; 
    598573      if (size != 0) textField.size = size; 
    599       if (this.options.submitOnBlur) 
    600         textField.onblur = this.onSubmit.bind(this); 
    601574      this.editField = textField; 
    602575    } else { 
    603576      this.options.textarea = true; 
    604577      var textArea = document.createElement("textarea"); 
    605       textArea.obj = this; 
    606578      textArea.name = "value"; 
    607579      textArea.value = this.convertHTMLLineBreaks(text); 
    608580      textArea.rows = this.options.rows; 
    609581      textArea.cols = this.options.cols || 40; 
    610       textArea.className = 'editor_field';       
    611       if (this.options.submitOnBlur) 
    612         textArea.onblur = this.onSubmit.bind(this); 
    613582      this.editField = textArea; 
    614583    } 
     
    661630    this.onLoading(); 
    662631     
    663     if (this.options.evalScripts) { 
    664       new Ajax.Request( 
    665         this.url, Object.extend({ 
    666           parameters: this.options.callback(form, value), 
    667           onComplete: this.onComplete.bind(this), 
    668           onFailure: this.onFailure.bind(this), 
    669           asynchronous:true,  
    670           evalScripts:true 
    671         }, this.options.ajaxOptions)); 
    672     } else  { 
    673       new Ajax.Updater( 
    674         { success: this.element, 
    675           // don't update on failure (this could be an option) 
    676           failure: null },  
    677         this.url, Object.extend({ 
    678           parameters: this.options.callback(form, value), 
    679           onComplete: this.onComplete.bind(this), 
    680           onFailure: this.onFailure.bind(this) 
    681         }, this.options.ajaxOptions)); 
    682     } 
     632    new Ajax.Updater( 
     633      {  
     634        success: this.element, 
     635         // don't update on failure (this could be an option) 
     636        failure: null 
     637      }, 
     638      this.url, 
     639      Object.extend({ 
     640        parameters: this.options.callback(form, value), 
     641        onComplete: this.onComplete.bind(this), 
     642        onFailure: this.onFailure.bind(this) 
     643      }, this.options.ajaxOptions) 
     644    ); 
    683645    // stop the event to avoid a page refresh in Safari 
    684646    if (arguments.length > 1) { 
     
    761723  } 
    762724}; 
    763  
    764 Ajax.InPlaceCollectionEditor = Class.create(); 
    765 Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); 
    766 Object.extend(Ajax.InPlaceCollectionEditor.prototype, { 
    767   createEditField: function() { 
    768     if (!this.cached_selectTag) { 
    769       var selectTag = document.createElement("select"); 
    770       var collection = this.options.collection || []; 
    771       var optionTag; 
    772       collection.each(function(e,i) { 
    773         optionTag = document.createElement("option"); 
    774         optionTag.value = (e instanceof Array) ? e[0] : e; 
    775         if(this.options.value==optionTag.value) optionTag.selected = true; 
    776         optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); 
    777         selectTag.appendChild(optionTag); 
    778       }.bind(this)); 
    779       this.cached_selectTag = selectTag; 
    780     } 
    781  
    782     this.editField = this.cached_selectTag; 
    783     if(this.options.loadTextURL) this.loadExternalText(); 
    784     this.form.appendChild(this.editField); 
    785     this.options.callback = function(form, value) { 
    786       return "value=" + encodeURIComponent(value); 
    787     } 
    788   } 
    789 }); 
    790725 
    791726// Delayed observer, like Form.Element.Observer,  
  • qPloneTabs/trunk/skins/qPloneTabs/javascripts/dragdrop.js

    r490 r491  
    11// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
    2 //           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) 
    32//  
    43// See scriptaculous.js for full license. 
     
    1716    var options = Object.extend({ 
    1817      greedy:     true, 
    19       hoverclass: null, 
    20       tree:       false 
     18      hoverclass: null   
    2119    }, arguments[1] || {}); 
    2220 
     
    4038    this.drops.push(options); 
    4139  }, 
    42    
    43   findDeepestChild: function(drops) { 
    44     deepest = drops[0]; 
    45        
    46     for (i = 1; i < drops.length; ++i) 
    47       if (Element.isParent(drops[i].element, deepest.element)) 
    48         deepest = drops[i]; 
    49      
    50     return deepest; 
    51   }, 
    5240 
    5341  isContained: function(element, drop) { 
    54     var containmentNode; 
    55     if(drop.tree) { 
    56       containmentNode = element.treeNode;  
    57     } else { 
    58       containmentNode = element.parentNode; 
    59     } 
    60     return drop._containers.detect(function(c) { return containmentNode == c }); 
    61   }, 
    62    
    63   isAffected: function(point, element, drop) { 
     42    var parentNode = element.parentNode; 
     43    return drop._containers.detect(function(c) { return parentNode == c }); 
     44  }, 
     45 
     46  isAffected: function(pX, pY, element, drop) { 
    6447    return ( 
    6548      (drop.element!=element) && 
     
    6952        (Element.classNames(element).detect(  
    7053          function(v) { return drop.accept.include(v) } ) )) && 
    71       Position.within(drop.element, point[0], point[1]) ); 
     54      Position.within(drop.element, pX, pY) ); 
    7255  }, 
    7356 
     
    7962 
    8063  activate: function(drop) { 
     64    if(this.last_active) this.deactivate(this.last_active); 
    8165    if(drop.hoverclass) 
    8266      Element.addClassName(drop.element, drop.hoverclass); 
     
    8468  }, 
    8569 
    86   show: function(point, element) { 
     70  show: function(event, element) { 
    8771    if(!this.drops.length) return; 
    88     var affected = []; 
     72    var pX = Event.pointerX(event); 
     73    var pY = Event.pointerY(event); 
     74    Position.prepare(); 
     75 
     76    var i = this.drops.length-1; do { 
     77      var drop = this.drops[i]; 
     78      if(this.isAffected(pX, pY, element, drop)) { 
     79        if(drop.onHover) 
     80           drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); 
     81        if(drop.greedy) {  
     82          this.activate(drop); 
     83          return; 
     84        } 
     85      } 
     86    } while (i--); 
    8987     
    9088    if(this.last_active) this.deactivate(this.last_active); 
    91     this.drops.each( function(drop) { 
    92       if(Droppables.isAffected(point, element, drop)) 
    93         affected.push(drop); 
    94     }); 
    95          
    96     if(affected.length>0) { 
    97       drop = Droppables.findDeepestChild(affected); 
    98       Position.within(drop.element, point[0], point[1]); 
    99       if(drop.onHover) 
    100         drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); 
    101        
    102       Droppables.activate(drop); 
    103     } 
    10489  }, 
    10590 
     
    10893    Position.prepare(); 
    10994 
    110     if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) 
     95    if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active)) 
    11196      if (this.last_active.onDrop)  
    11297        this.last_active.onDrop(element, this.last_active.element, event); 
     
    120105 
    121106var Draggables = { 
    122   drags: [], 
    123107  observers: [], 
    124    
    125   register: function(draggable) { 
    126     if(this.drags.length == 0) { 
    127       this.eventMouseUp   = this.endDrag.bindAsEventListener(this); 
    128       this.eventMouseMove = this.updateDrag.bindAsEventListener(this); 
    129       this.eventKeypress  = this.keyPress.bindAsEventListener(this); 
    130        
    131       Event.observe(document, "mouseup", this.eventMouseUp); 
    132       Event.observe(document, "mousemove", this.eventMouseMove); 
    133       Event.observe(document, "keypress", this.eventKeypress); 
    134     } 
    135     this.drags.push(draggable); 
    136   }, 
    137    
    138   unregister: function(draggable) { 
    139     this.drags = this.drags.reject(function(d) { return d==draggable }); 
    140     if(this.drags.length == 0) { 
    141       Event.stopObserving(document, "mouseup", this.eventMouseUp); 
    142       Event.stopObserving(document, "mousemove", this.eventMouseMove); 
    143       Event.stopObserving(document, "keypress", this.eventKeypress); 
    144     } 
    145   }, 
    146    
    147   activate: function(draggable) { 
    148     window.focus(); // allows keypress events if window isn't currently focused, fails for Safari 
    149     this.activeDraggable = draggable; 
    150   }, 
    151    
    152   deactivate: function() { 
    153     this.activeDraggable = null; 
    154   }, 
    155    
    156   updateDrag: function(event) { 
    157     if(!this.activeDraggable) return; 
    158     var pointer = [Event.pointerX(event), Event.pointerY(event)]; 
    159     // Mozilla-based browsers fire successive mousemove events with 
    160     // the same coordinates, prevent needless redrawing (moz bug?) 
    161     if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; 
    162     this._lastPointer = pointer; 
    163     this.activeDraggable.updateDrag(event, pointer); 
    164   }, 
    165    
    166   endDrag: function(event) { 
    167     if(!this.activeDraggable) return; 
    168     this._lastPointer = null; 
    169     this.activeDraggable.endDrag(event); 
    170     this.activeDraggable = null; 
    171   }, 
    172    
    173   keyPress: function(event) { 
    174     if(this.activeDraggable) 
    175       this.activeDraggable.keyPress(event); 
    176   }, 
    177    
    178108  addObserver: function(observer) { 
    179109    this.observers.push(observer); 
    180110    this._cacheObserverCallbacks(); 
    181111  }, 
    182    
    183112  removeObserver: function(element) {  // element instead of observer fixes mem leaks 
    184113    this.observers = this.observers.reject( function(o) { return o.element==element }); 
    185114    this._cacheObserverCallbacks(); 
    186115  }, 
    187    
    188116  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag' 
    189117    if(this[eventName+'Count'] > 0) 
     
    192120      }); 
    193121  }, 
    194    
    195122  _cacheObserverCallbacks: function() { 
    196123    ['onStart','onEnd','onDrag'].each( function(eventName) { 
     
    209136    var options = Object.extend({ 
    210137      handle: false, 
    211       starteffect: function(element) { 
    212         element._opacity = Element.getOpacity(element);  
    213         new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});  
     138      starteffect: function(element) {  
     139        new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});  
    214140      }, 
    215141      reverteffect: function(element, top_offset, left_offset) { 
    216142        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; 
    217         element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur}); 
     143        new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur}); 
    218144      }, 
    219       endeffect: function(element) { 
    220         var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0 
    221         new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity});  
     145      endeffect: function(element) {  
     146         new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});  
    222147      }, 
    223148      zindex: 1000, 
    224149      revert: false, 
    225       scroll: false, 
    226       scrollSensitivity: 20, 
    227       scrollSpeed: 15, 
    228150      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] } 
    229151    }, arguments[1] || {}); 
    230152 
    231     this.element = $(element); 
    232      
    233     if(options.handle && (typeof options.handle == 'string')) { 
    234       var h = Element.childrenWithClassName(this.element, options.handle, true); 
    235       if(h.length>0) this.handle = h[0]; 
    236     } 
     153    this.element      = $(element); 
     154    if(options.handle && (typeof options.handle == 'string')) 
     155      this.handle = Element.childrenWithClassName(this.element, options.handle)[0]; 
     156       
    237157    if(!this.handle) this.handle = $(options.handle); 
    238158    if(!this.handle) this.handle = this.element; 
    239      
    240     if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) 
    241       options.scroll = $(options.scroll); 
    242159 
    243160    Element.makePositioned(this.element); // fix IE     
    244161 
    245     this.delta    = this.currentDelta(); 
    246     this.options  = options; 
    247     this.dragging = false;    
    248  
    249     this.eventMouseDown = this.initDrag.bindAsEventListener(this); 
    250     Event.observe(this.handle, "mousedown", this.eventMouseDown); 
    251      
    252     Draggables.register(this); 
    253   }, 
    254    
     162    this.offsetX      = 0; 
     163    this.offsetY      = 0; 
     164    this.originalLeft = this.currentLeft(); 
     165    this.originalTop  = this.currentTop(); 
     166    this.originalX    = this.element.offsetLeft; 
     167    this.originalY    = this.element.offsetTop; 
     168 
     169    this.options      = options; 
     170 
     171    this.active       = false; 
     172    this.dragging     = false;    
     173 
     174    this.eventMouseDown = this.startDrag.bindAsEventListener(this); 
     175    this.eventMouseUp   = this.endDrag.bindAsEventListener(this); 
     176    this.eventMouseMove = this.update.bindAsEventListener(this); 
     177    this.eventKeypress  = this.keyPress.bindAsEventListener(this); 
     178     
     179    this.registerEvents(); 
     180  }, 
    255181  destroy: function() { 
    256182    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); 
    257     Draggables.unregister(this); 
    258   }, 
    259    
    260   currentDelta: function() { 
    261     return([ 
    262       parseInt(Element.getStyle(this.element,'left') || '0'), 
    263       parseInt(Element.getStyle(this.element,'top') || '0')]); 
    264   }, 
    265    
    266   initDrag: function(event) { 
    267     if(Event.isLeftClick(event)) {     
     183    this.unregisterEvents(); 
     184  }, 
     185  registerEvents: function() { 
     186    Event.observe(document, "mouseup", this.eventMouseUp); 
     187    Event.observe(document, "mousemove", this.eventMouseMove); 
     188    Event.observe(document, "keypress", this.eventKeypress); 
     189    Event.observe(this.handle, "mousedown", this.eventMouseDown); 
     190  }, 
     191  unregisterEvents: function() { 
     192    //if(!this.active) return; 
     193    //Event.stopObserving(document, "mouseup", this.eventMouseUp); 
     194    //Event.stopObserving(document, "mousemove", this.eventMouseMove); 
     195    //Event.stopObserving(document, "keypress", this.eventKeypress); 
     196  }, 
     197  currentLeft: function() { 
     198    return parseInt(this.element.style.left || '0'); 
     199  }, 
     200  currentTop: function() { 
     201    return parseInt(this.element.style.top || '0') 
     202  }, 
     203  startDrag: function(event) { 
     204    if(Event.isLeftClick(event)) { 
     205       
    268206      // abort on form elements, fixes a Firefox issue 
    269207      var src = Event.element(event); 
     
    271209        src.tagName=='INPUT' || 
    272210        src.tagName=='SELECT' || 
    273         src.tagName=='OPTION' || 
    274211        src.tagName=='BUTTON' || 
    275212        src.tagName=='TEXTAREA')) return; 
    276          
    277       if(this.element._revert) { 
    278         this.element._revert.cancel(); 
    279         this.element._revert = null; 
    280       } 
    281213       
     214      // this.registerEvents(); 
     215      this.active = true; 
    282216      var pointer = [Event.pointerX(event), Event.pointerY(event)]; 
    283       var pos     = Position.cumulativeOffset(this.element); 
    284       this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); 
    285        
    286       Draggables.activate(this); 
     217      var offsets = Position.cumulativeOffset(this.element); 
     218      this.offsetX =  (pointer[0] - offsets[0]); 
     219      this.offsetY =  (pointer[1] - offsets[1]); 
    287220      Event.stop(event); 
    288221    } 
    289222  }, 
    290    
    291   startDrag: function(event) { 
    292     this.dragging = true; 
    293      
    294     if(this.options.zindex) { 
    295       this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); 
    296       this.element.style.zIndex = this.options.zindex; 
    297     } 
    298      
    299     if(this.options.ghosting) { 
    300       this._clone = this.element.cloneNode(true); 
    301       Position.absolutize(this.element); 
    302       this.element.parentNode.insertBefore(this._clone, this.element); 
    303     } 
    304      
    305     if(this.options.scroll) { 
    306       if (this.options.scroll == window) { 
    307         var where = this._getWindowScroll(this.options.scroll); 
    308         this.originalScrollLeft = where.left; 
    309         this.originalScrollTop = where.top; 
    310       } else { 
    311         this.originalScrollLeft = this.options.scroll.scrollLeft; 
    312         this.originalScrollTop = this.options.scroll.scrollTop; 
    313       } 
    314     } 
    315      
    316     Draggables.notify('onStart', this, event); 
    317     if(this.options.starteffect) this.options.starteffect(this.element); 
    318   }, 
    319    
    320   updateDrag: function(event, pointer) { 
    321     if(!this.dragging) this.startDrag(event); 
    322     Position.prepare(); 
    323     Droppables.show(pointer, this.element); 
    324     Draggables.notify('onDrag', this, event); 
    325     this.draw(pointer); 
    326     if(this.options.change) this.options.change(this); 
    327      
    328     if(this.options.scroll) { 
    329       this.stopScrolling(); 
    330        
    331       var p; 
    332       if (this.options.scroll == window) { 
    333         with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } 
    334       } else { 
    335         p = Position.page(this.options.scroll); 
    336         p[0] += this.options.scroll.scrollLeft; 
    337         p[1] += this.options.scroll.scrollTop; 
    338         p.push(p[0]+this.options.scroll.offsetWidth); 
    339         p.push(p[1]+this.options.scroll.offsetHeight); 
    340       } 
    341       var speed = [0,0]; 
    342       if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); 
    343       if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); 
    344       if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); 
    345       if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); 
    346       this.startScrolling(speed); 
    347     } 
    348      
    349     // fix AppleWebKit rendering 
    350     if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
    351      
    352     Event.stop(event); 
    353   }, 
    354    
    355223  finishDrag: function(event, success) { 
     224    // this.unregisterEvents(); 
     225 
     226    this.active = false; 
    356227    this.dragging = false; 
    357228 
     
    367238    var revert = this.options.revert; 
    368239    if(revert && typeof revert == 'function') revert = revert(this.element); 
    369      
    370     var d = this.currentDelta(); 
     240 
    371241    if(revert && this.options.reverteffect) { 
    372242      this.options.reverteffect(this.element,  
    373         d[1]-this.delta[1], d[0]-this.delta[0]); 
     243      this.currentTop()-this.originalTop, 
     244      this.currentLeft()-this.originalLeft); 
    374245    } else { 
    375       this.delta = d; 
     246      this.originalLeft = this.currentLeft(); 
     247      this.originalTop  = this.currentTop(); 
    376248    } 
    377249 
     
    382254      this.options.endeffect(this.element); 
    383255 
    384     Draggables.deactivate(this); 
     256 
    385257    Droppables.reset(); 
    386258  }, 
    387    
    388259  keyPress: function(event) { 
    389     if(event.keyCode!=Event.KEY_ESC) return; 
    390     this.finishDrag(event, false); 
    391     Event.stop(event); 
    392   }, 
    393    
     260    if(this.active) { 
     261      if(event.keyCode==Event.KEY_ESC) { 
     262        this.finishDrag(event, false); 
     263        Event.stop(event); 
     264      } 
     265    } 
     266  }, 
    394267  endDrag: function(event) { 
    395     if(!this.dragging) return; 
    396     this.stopScrolling(); 
    397     this.finishDrag(event, true); 
    398     Event.stop(event); 
    399   }, 
    400    
    401   draw: function(point) { 
    402     var pos = Position.cumulativeOffset(this.element); 
    403     var d = this.currentDelta(); 
    404     pos[0] -= d[0]; pos[1] -= d[1]; 
    405      
    406     if(this.options.scroll && (this.options.scroll != window)) { 
    407       pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; 
    408       pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; 
    409     } 
    410      
    411     var p = [0,1].map(function(i){  
    412       return (point[i]-pos[i]-this.offset[i])  
    413     }.bind(this)); 
     268    if(this.active && this.dragging) { 
     269      this.finishDrag(event, true); 
     270      Event.stop(event); 
     271    } 
     272    this.active = false; 
     273    this.dragging = false; 
     274  }, 
     275  draw: function(event) { 
     276    var pointer = [Event.pointerX(event), Event.pointerY(event)]; 
     277    var offsets = Position.cumulativeOffset(this.element); 
     278    offsets[0] -= this.currentLeft(); 
     279    offsets[1] -= this.currentTop(); 
     280    var style = this.element.style; 
     281     
     282    var pos = [ 
     283      (pointer[0] - offsets[0] - this.offsetX), 
     284      (pointer[1] - offsets[1] - this.offsetY)]; 
    414285     
    415286    if(this.options.snap) { 
    416287      if(typeof this.options.snap == 'function') { 
    417         p = this.options.snap(p[0],p[1],this); 
     288        pos = this.options.snap(pos[0],pos[1]); 
    418289      } else { 
     290      var draggable = this; 
    419291      if(this.options.snap instanceof Array) { 
    420         p = p.map( function(v, i) { 
    421           return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)
     292        pos = pos.collect( function(v, i) { 
     293          return Math.round(v/draggable.options.snap[i])*draggable.options.snap[i] }
    422294      } else { 
    423         p = p.map( function(v) { 
    424           return Math.round(v/this.options.snap)*this.options.snap }.bind(this)
     295        pos = pos.collect( function(v) { 
     296          return Math.round(v/draggable.options.snap)*draggable.options.snap }
    425297      } 
    426298    }} 
    427299     
    428     var style = this.element.style; 
    429300    if((!this.options.constraint) || (this.options.constraint=='horizontal')) 
    430       style.left = p[0] + "px"; 
     301      style.left = pos[0] + "px"; 
    431302    if((!this.options.constraint) || (this.options.constraint=='vertical')) 
    432       style.top  = p[1] + "px"; 
     303      style.top  = pos[1] + "px"; 
    433304    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering 
    434305  }, 
    435    
    436   stopScrolling: function() { 
    437     if(this.scrollInterval) { 
    438       clearInterval(this.scrollInterval); 
    439       this.scrollInterval = null; 
    440       Draggables._lastScrollPointer = null; 
    441     } 
    442   }, 
    443    
    444   startScrolling: function(speed) { 
    445     this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; 
    446     this.lastScrolled = new Date(); 
    447     this.scrollInterval = setInterval(this.scroll.bind(this), 10); 
    448   }, 
    449    
    450   scroll: function() { 
    451     var current = new Date(); 
    452     var delta = current - this.lastScrolled; 
    453     this.lastScrolled = current; 
    454     if(this.options.scroll == window) { 
    455       with (this._getWindowScroll(this.options.scroll)) { 
    456         if (this.scrollSpeed[0] || this.scrollSpeed[1]) { 
    457           var d = delta / 1000; 
    458           this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); 
     306  update: function(event) { 
     307   if(this.active) { 
     308      if(!this.dragging) { 
     309        var style = this.element.style; 
     310        this.dragging = true; 
     311         
     312        if(Element.getStyle(this.element,'position')=='')  
     313          style.position = "relative"; 
     314         
     315        if(this.options.zindex) { 
     316          this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); 
     317          style.zIndex = this.options.zindex; 
    459318        } 
    460       } 
    461     } else { 
    462       this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; 
    463       this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000; 
    464     } 
    465      
    466     Position.prepare(); 
    467     Droppables.show(Draggables._lastPointer, this.element); 
    468     Draggables.notify('onDrag', this); 
    469     Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); 
    470     Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; 
    471     Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; 
    472     if (Draggables._lastScrollPointer[0] < 0) 
    473       Draggables._lastScrollPointer[0] = 0; 
    474     if (Draggables._lastScrollPointer[1] < 0) 
    475       Draggables._lastScrollPointer[1] = 0; 
    476     this.draw(Draggables._lastScrollPointer); 
    477      
    478     if(this.options.change) this.options.change(this); 
    479   }, 
    480    
    481   _getWindowScroll: function(w) { 
    482     var T, L, W, H; 
    483     with (w.document) { 
    484       if (w.document.documentElement && documentElement.scrollTop) { 
    485         T = documentElement.scrollTop; 
    486         L = documentElement.scrollLeft; 
    487       } else if (w.document.body) { 
    488         T = body.scrollTop; 
    489         L = body.scrollLeft; 
    490       } 
    491       if (w.innerWidth) { 
    492         W = w.innerWidth; 
    493         H = w.innerHeight; 
    494       } else if (w.document.documentElement && documentElement.clientWidth) { 
    495         W = documentElement.clientWidth; 
    496         H = documentElement.clientHeight; 
    497       } else { 
    498         W = body.offsetWidth; 
    499         H = body.offsetHeight 
    500       } 
    501     } 
    502     return { top: T, left: L, width: W, height: H }; 
     319 
     320        if(this.options.ghosting) { 
     321          this._clone = this.element.cloneNode(true); 
     322          Position.absolutize(this.element); 
     323          this.element.parentNode.insertBefore(this._clone, this.element); 
     324        } 
     325 
     326        Draggables.notify('onStart', this, event); 
     327        if(this.options.starteffect) this.options.starteffect(this.element); 
     328      } 
     329 
     330      Droppables.show(event, this.element); 
     331      Draggables.notify('onDrag', this, event); 
     332      this.draw(event); 
     333      if(this.options.change) this.options.change(this); 
     334 
     335      // fix AppleWebKit rendering 
     336      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);  
     337 
     338      Event.stop(event); 
     339   } 
    503340  } 
    504341} 
     
    513350    this.lastValue = Sortable.serialize(this.element); 
    514351  }, 
    515    
    516352  onStart: function() { 
    517353    this.lastValue = Sortable.serialize(this.element); 
    518354  }, 
    519    
    520355  onEnd: function() { 
    521356    Sortable.unmark(); 
     
    526361 
    527362var Sortable = { 
    528   sortables: {}, 
    529    
    530   _findRootElement: function(element) { 
    531     while (element.tagName != "BODY") {   
    532       if(element.id && Sortable.sortables[element.id]) return element; 
    533       element = element.parentNode; 
    534     } 
    535   }, 
    536  
    537   options: function(element) { 
    538     element = Sortable._findRootElement($(element)); 
    539     if(!element) return; 
    540     return Sortable.sortables[element.id]; 
    541   }, 
    542    
     363  sortables: new Array(), 
     364  options: function(element){ 
     365    element = $(element); 
     366    return this.sortables.detect(function(s) { return s.element == element }); 
     367  }, 
    543368  destroy: function(element){ 
    544     var s = Sortable.options(element); 
    545      
    546     if(s) { 
     369    element = $(element); 
     370    this.sortables.findAll(function(s) { return s.element == element }).each(function(s){ 
    547371      Draggables.removeObserver(s.element); 
    548372      s.droppables.each(function(d){ Droppables.remove(d) }); 
    549373      s.draggables.invoke('destroy'); 
    550        
    551       delete Sortable.sortables[s.element.id]; 
    552     } 
    553   }, 
    554  
     374    }); 
     375    this.sortables = this.sortables.reject(function(s) { return s.element == element }); 
     376  }, 
    555377  create: function(element) { 
    556378    element = $(element); 
     
    559381      tag:         'li',       // assumes li children, override with tag: 'tagname' 
    560382      dropOnEmpty: false, 
    561       tree:        false, 
    562       treeTag:     'ul', 
     383      tree:        false,      // fixme: unimplemented 
    563384      overlap:     'vertical', // one of 'vertical', 'horizontal' 
    564385      constraint:  'vertical', // one of 'vertical', 'horizontal', false 
     
    568389      hoverclass:  null, 
    569390      ghosting:    false, 
    570       scroll:      false, 
    571       scrollSensitivity: 20, 
    572       scrollSpeed: 15, 
    573       format:      /^[^_]*_(.*)$/, 
     391      format:      null, 
    574392      onChange:    Prototype.emptyFunction, 
    575393      onUpdate:    Prototype.emptyFunction 
     
    582400    var options_for_draggable = { 
    583401      revert:      true, 
    584       scroll:      options.scroll, 
    585       scrollSpeed: options.scrollSpeed, 
    586       scrollSensitivity: options.scrollSensitivity, 
    587402      ghosting:    options.ghosting, 
    588403      constraint:  options.constraint, 
     
    610425      overlap:     options.overlap, 
    611426      containment: options.containment, 
    612       tree:        options.tree, 
    613427      hoverclass:  options.hoverclass, 
    614       onHover:     Sortable.onHover 
    615       //greedy:      !options.dropOnEmpty 
    616     } 
    617      
    618     var options_for_tree = { 
    619       onHover:      Sortable.onEmptyHover, 
    620       overlap:      options.overlap, 
    621       containment:  options.containment, 
    622       hoverclass:   options.hoverclass 
     428      onHover:     Sortable.onHover, 
     429      greedy:      !options.dropOnEmpty 
    623430    } 
    624431 
     
    629436    options.droppables = []; 
    630437 
     438    // make it so 
     439 
    631440    // drop on empty handling 
    632     if(options.dropOnEmpty || options.tree) { 
    633       Droppables.add(element, options_for_tree); 
     441    if(options.dropOnEmpty) { 
     442      Droppables.add(element, 
     443        {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false}); 
    634444      options.droppables.push(element); 
    635445    } 
     
    642452        new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); 
    643453      Droppables.add(e, options_for_droppable); 
    644       if(options.tree) e.treeNode = element; 
    645454      options.droppables.push(e);       
    646455    }); 
    647      
    648     if(options.tree) { 
    649       (Sortable.findTreeElements(element, options) || []).each( function(e) { 
    650         Droppables.add(e, options_for_tree); 
    651         e.treeNode = element; 
    652         options.droppables.push(e); 
    653       }); 
    654     } 
    655456 
    656457    // keep reference 
    657     this.sortables[element.id] = options
     458    this.sortables.push(options)
    658459 
    659460    // for onupdate 
     
    664465  // return all suitable-for-sortable elements in a guaranteed order 
    665466  findElements: function(element, options) { 
    666     return Element.findChildren( 
    667       element, options.only, options.tree ? true : false, options.tag); 
    668   }, 
    669    
    670   findTreeElements: function(element, options) { 
    671     return Element.findChildren( 
    672       element, options.only, options.tree ? true : false, options.treeTag); 
     467    if(!element.hasChildNodes()) return null; 
     468    var elements = []; 
     469    $A(element.childNodes).each( function(e) { 
     470      if(e.tagName && e.tagName==options.tag.toUpperCase() && 
     471        (!options.only || (Element.hasClassName(e, options.only)))) 
     472          elements.push(e); 
     473      if(options.tree) { 
     474        var grandchildren = this.findElements(e, options); 
     475        if(grandchildren) elements.push(grandchildren); 
     476      } 
     477    }); 
     478 
     479    return (elements.length>0 ? elements.flatten() : null); 
    673480  }, 
    674481 
    675482  onHover: function(element, dropon, overlap) { 
    676     if(Element.isParent(dropon, element)) return; 
    677  
    678     if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { 
    679       return; 
    680     } else if(overlap>0.5) { 
     483    if(overlap>0.5) { 
    681484      Sortable.mark(dropon, 'before'); 
    682485      if(dropon.previousSibling != element) { 
     
    701504    } 
    702505  }, 
    703    
    704   onEmptyHover: function(element, dropon, overlap) { 
    705     var oldParentNode = element.parentNode; 
    706     var droponOptions = Sortable.options(dropon); 
    707          
    708     if(!Element.isParent(dropon, element)) { 
    709       var index; 
    710        
    711       var children = Sortable.findElements(dropon, {tag: droponOptions.tag}); 
    712       var child = null; 
    713              
    714       if(children) { 
    715         var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); 
    716          
    717         for (index = 0; index < children.length; index += 1) { 
    718           if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { 
    719             offset -= Element.offsetSize (children[index], droponOptions.overlap); 
    720           } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { 
    721             child = index + 1 < children.length ? children[index + 1] : null; 
    722             break; 
    723           } else { 
    724             child = children[index]; 
    725             break; 
    726           } 
    727         } 
    728       } 
    729        
    730       dropon.insertBefore(element, child); 
    731        
     506 
     507  onEmptyHover: function(element, dropon) { 
     508    if(element.parentNode!=dropon) { 
     509      var oldParentNode = element.parentNode; 
     510      dropon.appendChild(element); 
    732511      Sortable.options(oldParentNode).onChange(element); 
    733       droponOptions.onChange(element); 
     512      Sortable.options(dropon).onChange(element); 
    734513    } 
    735514  }, 
     
    763542    Element.show(Sortable._marker); 
    764543  }, 
    765    
    766   _tree: function(element, options, parent) { 
    767     var children = Sortable.findElements(element, options) || []; 
    768    
    769     for (var i = 0; i < children.length; ++i) { 
    770       var match = children[i].id.match(options.format); 
    771  
    772       if (!match) continue; 
    773        
    774       var child = { 
    775         id: encodeURIComponent(match ? match[1] : null), 
    776         element: element, 
    777         parent: parent, 
    778         children: new Array, 
    779         position: parent.children.length, 
    780         container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase()) 
    781       } 
    782        
    783       /* Get the element containing the children and recurse over it */ 
    784       if (child.container) 
    785         this._tree(child.container, options, child) 
    786        
    787       parent.children.push (child); 
    788     } 
    789  
    790     return parent;  
    791   }, 
    792  
    793   /* Finds the first element of the given tag type within a parent element. 
    794     Used for finding the first LI[ST] within a L[IST]I[TEM].*/ 
    795   _findChildrenElement: function (element, containerTag) { 
    796     if (element && element.hasChildNodes) 
    797 &