alfirin/sept-web-radio

View on GitHub
app/scripts/libs/jquery-ui/jquery.event.drop-2.2.js

Summary

Maintainability
B
6 hrs
Test Coverage
/*! 
 * jquery.event.drop - v 2.2
 * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
 * Open Source MIT License - http://threedubmedia.com/code/license
 */
// Created: 2008-06-04 
// Updated: 2012-05-21
// REQUIRES: jquery 1.7.x, event.drag 2.2

;(function($){ // secure $ jQuery alias

// Events: drop, dropstart, dropend

// add the jquery instance method
  $.fn.drop = function( str, arg, opts ){
    // figure out the event type
    var type = typeof str == "string" ? str : "",
    // figure out the event handler...
      fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
    // fix the event type
    if ( type.indexOf("drop") !== 0 )
      type = "drop"+ type;
    // were options passed
    opts = ( str == fn ? arg : opts ) || {};
    // trigger or bind event handler
    return fn ? this.bind( type, opts, fn ) : this.trigger( type );
  };

// DROP MANAGEMENT UTILITY
// returns filtered drop target elements, caches their positions
  $.drop = function( opts ){
    opts = opts || {};
    // safely set new options...
    drop.multi = opts.multi === true ? Infinity :
      opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi;
    drop.delay = opts.delay || drop.delay;
    drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance :
      opts.tolerance === null ? null : drop.tolerance;
    drop.mode = opts.mode || drop.mode || 'intersect';
  };

// local refs (increase compression)
  var $event = $.event,
    $special = $event.special,
// configure the drop special event
    drop = $.event.special.drop = {

      // these are the default settings
      multi: 1, // allow multiple drop winners per dragged element
      delay: 20, // async timeout delay
      mode: 'overlap', // drop tolerance mode

      // internal cache
      targets: [],

      // the key name for stored drop data
      datakey: "dropdata",

      // prevent bubbling for better performance
      noBubble: true,

      // count bound related events
      add: function( obj ){
        // read the interaction data
        var data = $.data( this, drop.datakey );
        // count another realted event
        data.related += 1;
      },

      // forget unbound related events
      remove: function(){
        $.data( this, drop.datakey ).related -= 1;
      },

      // configure the interactions
      setup: function(){
        // check for related events
        if ( $.data( this, drop.datakey ) )
          return;
        // initialize the drop element data
        var data = {
          related: 0,
          active: [],
          anyactive: 0,
          winner: 0,
          location: {}
        };
        // store the drop data on the element
        $.data( this, drop.datakey, data );
        // store the drop target in internal cache
        drop.targets.push( this );
      },

      // destroy the configure interaction
      teardown: function(){
        var data = $.data( this, drop.datakey ) || {};
        // check for related events
        if ( data.related )
          return;
        // remove the stored data
        $.removeData( this, drop.datakey );
        // reference the targeted element
        var element = this;
        // remove from the internal cache
        drop.targets = $.grep( drop.targets, function( target ){
          return ( target !== element );
        });
      },

      // shared event handler
      handler: function( event, dd ){
        // local vars
        var results, $targets;
        // make sure the right data is available
        if ( !dd )
          return;
        // handle various events
        switch ( event.type ){
          // draginit, from $.event.special.drag
          case 'mousedown': // DROPINIT >>
          case 'touchstart': // DROPINIT >>
            // collect and assign the drop targets
            $targets =  $( drop.targets );
            if ( typeof dd.drop == "string" )
              $targets = $targets.filter( dd.drop );
            // reset drop data winner properties
            $targets.each(function(){
              var data = $.data( this, drop.datakey );
              data.active = [];
              data.anyactive = 0;
              data.winner = 0;
            });
            // set available target elements
            dd.droppable = $targets;
            // activate drop targets for the initial element being dragged
            $special.drag.hijack( event, "dropinit", dd );
            break;
          // drag, from $.event.special.drag
          case 'mousemove': // TOLERATE >>
          case 'touchmove': // TOLERATE >>
            drop.event = event; // store the mousemove event
            if ( !drop.timer )
            // monitor drop targets
              drop.tolerate( dd );
            break;
          // dragend, from $.event.special.drag
          case 'mouseup': // DROP >> DROPEND >>
          case 'touchend': // DROP >> DROPEND >>
            drop.timer = clearTimeout( drop.timer ); // delete timer
            if ( dd.propagates ){
              $special.drag.hijack( event, "drop", dd );
              $special.drag.hijack( event, "dropend", dd );
            }
            break;

        }
      },

      // returns the location positions of an element
      locate: function( elem, index ){
        var data = $.data( elem, drop.datakey ),
          $elem = $( elem ),
          posi = $elem.offset() || {},
          height = $elem.outerHeight(),
          width = $elem.outerWidth(),
          location = {
            elem: elem,
            width: width,
            height: height,
            top: posi.top,
            left: posi.left,
            right: posi.left + width,
            bottom: posi.top + height
          };
        // drag elements might not have dropdata
        if ( data ){
          data.location = location;
          data.index = index;
          data.elem = elem;
        }
        return location;
      },

      // test the location positions of an element against another OR an X,Y coord
      contains: function( target, test ){ // target { location } contains test [x,y] or { location }
        return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right
          && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom );
      },

      // stored tolerance modes
      modes: { // fn scope: "$.event.special.drop" object
        // target with mouse wins, else target with most overlap wins
        'intersect': function( event, proxy, target ){
          return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor
            1e9 : this.modes.overlap.apply( this, arguments ); // check overlap
        },
        // target with most overlap wins
        'overlap': function( event, proxy, target ){
          // calculate the area of overlap...
          return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) )
            * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) );
        },
        // proxy is completely contained within target bounds
        'fit': function( event, proxy, target ){
          return this.contains( target, proxy ) ? 1 : 0;
        },
        // center of the proxy is contained within target bounds
        'middle': function( event, proxy, target ){
          return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0;
        }
      },

      // sort drop target cache by by winner (dsc), then index (asc)
      sort: function( a, b ){
        return ( b.winner - a.winner ) || ( a.index - b.index );
      },

      // async, recursive tolerance execution
      tolerate: function( dd ){
        // declare local refs
        var i, drp, drg, data, arr, len, elem,
        // interaction iteration variables
          x = 0, ia, end = dd.interactions.length,
        // determine the mouse coords
          xy = [ drop.event.pageX, drop.event.pageY ],
        // custom or stored tolerance fn
          tolerance = drop.tolerance || drop.modes[ drop.mode ];
        // go through each passed interaction...
        do if ( ia = dd.interactions[x] ){
          // check valid interaction
          if ( !ia )
            return;
          // initialize or clear the drop data
          ia.drop = [];
          // holds the drop elements
          arr = [];
          len = ia.droppable.length;
          // determine the proxy location, if needed
          if ( tolerance )
            drg = drop.locate( ia.proxy );
          // reset the loop
          i = 0;
          // loop each stored drop target
          do if ( elem = ia.droppable[i] ){
            data = $.data( elem, drop.datakey );
            drp = data.location;
            if ( !drp ) continue;
            // find a winner: tolerance function is defined, call it
            data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp )
              // mouse position is always the fallback
              : drop.contains( drp, xy ) ? 1 : 0;
            arr.push( data );
          } while ( ++i < len ); // loop
          // sort the drop targets
          arr.sort( drop.sort );
          // reset the loop
          i = 0;
          // loop through all of the targets again
          do if ( data = arr[ i ] ){
            // winners...
            if ( data.winner && ia.drop.length < drop.multi ){
              // new winner... dropstart
              if ( !data.active[x] && !data.anyactive ){
                // check to make sure that this is not prevented
                if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){
                  data.active[x] = 1;
                  data.anyactive += 1;
                }
                // if false, it is not a winner
                else
                  data.winner = 0;
              }
              // if it is still a winner
              if ( data.winner )
                ia.drop.push( data.elem );
            }
            // losers...
            else if ( data.active[x] && data.anyactive == 1 ){
              // former winner... dropend
              $special.drag.hijack( drop.event, "dropend", dd, x, data.elem );
              data.active[x] = 0;
              data.anyactive -= 1;
            }
          } while ( ++i < len ); // loop
        } while ( ++x < end ) // loop
        // check if the mouse is still moving or is idle
        if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY )
          delete drop.timer; // idle, don't recurse
        else  // recurse
          drop.timer = setTimeout(function(){
            drop.tolerate( dd );
          }, drop.delay );
        // remember event, to compare idleness
        drop.last = drop.event;
      }

    };

// share the same special event configuration with related events...
  $special.dropinit = $special.dropstart = $special.dropend = drop;

})(jQuery); // confine scope