text: /*!
   * jquery.event.drag - 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
  
  ;
  (function($) {
  
      // add the jquery instance method
      $.fn.drag = 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("drag") !== 0)
              type = "drag" + type;
          // were options passed
          opts = (str == fn ? arg : opts) || {};
          // trigger or bind event handler
          return fn ? this.bind(type, opts, fn) : this.trigger(type);
      };
  
      // local refs (increase compression)
      var $event = $.event,
          $special = $event.special,
          // configure the drag special event
          drag = $special.drag = {
  
              // these are the default settings
              defaults: {
                  which: 1, // mouse button pressed to start drag sequence
                  distance: 0, // distance dragged before dragstart
                  not: ':input', // selector to suppress dragging on target elements
                  handle: null, // selector to match handle target elements
                  relative: false, // true to use "position", false to use "offset"
                  drop: true, // false to suppress drop events, true or selector to allow
                  click: false // false to suppress click events after dragend (no proxy)
              },
  
              // the key name for stored drag data
              datakey: "dragdata",
  
              // prevent bubbling for better performance
              noBubble: true,
  
              // count bound related events
              add: function(obj) {
                  // read the interaction data
                  var data = $.data(this, drag.datakey),
                      // read any passed options
                      opts = obj.data || {};
                  // count another realted event
                  data.related += 1;
                  // extend data options bound with this event
                  // don't iterate "opts" in case it is a node
                  $.each(drag.defaults, function(key, def) {
                      if (opts[key] !== undefined)
                          data[key] = opts[key];
                  });
              },
  
              // forget unbound related events
              remove: function() {
                  $.data(this, drag.datakey).related -= 1;
              },
  
              // configure interaction, capture settings
              setup: function() {
                  // check for related events
                  if ($.data(this, drag.datakey))
                      return;
                  // initialize the drag data with copied defaults
                  var data = $.extend({
                      related: 0
                  }, drag.defaults);
                  // store the interaction data
                  $.data(this, drag.datakey, data);
                  // bind the mousedown event, which starts drag interactions
                  $event.add(this, "touchstart mousedown", drag.init, data);
                  // prevent image dragging in IE...
                  if (this.attachEvent)
                      this.attachEvent("ondragstart", drag.dontstart);
              },
  
              // destroy configured interaction
              teardown: function() {
                  var data = $.data(this, drag.datakey) || {};
                  // check for related events
                  if (data.related)
                      return;
                  // remove the stored data
                  $.removeData(this, drag.datakey);
                  // remove the mousedown event
                  $event.remove(this, "touchstart mousedown", drag.init);
                  // enable text selection
                  drag.textselect(true);
                  // un-prevent image dragging in IE...
                  if (this.detachEvent)
                      this.detachEvent("ondragstart", drag.dontstart);
              },
  
              // initialize the interaction
              init: function(event) {
                  // sorry, only one touch at a time
                  if (drag.touched)
                      return;
                  // the drag/drop interaction data
                  var dd = event.data,
                      results;
                  // check the which directive
                  if (event.which != 0 && dd.which > 0 && event.which != dd.which)
                      return;
                  // check for suppressed selector
                  if ($(event.target).is(dd.not))
                      return;
                  // check for handle selector
                  if (dd.handle && !$(event.target).closest(dd.handle, event.currentTarget).length)
                      return;
  
                  drag.touched = event.type == 'touchstart' ? this : null;
                  dd.propagates = 1;
                  dd.mousedown = this;
                  dd.interactions = [drag.interaction(this, dd)];
                  dd.target = event.target;
                  dd.pageX = event.pageX;
                  dd.pageY = event.pageY;
                  dd.dragging = null;
                  // handle draginit event...
                  results = drag.hijack(event, "draginit", dd);
                  // early cancel
                  if (!dd.propagates)
                      return;
                  // flatten the result set
                  results = drag.flatten(results);
                  // insert new interaction elements
                  if (results && results.length) {
                      dd.interactions = [];
                      $.each(results, function() {
                          dd.interactions.push(drag.interaction(this, dd));
                      });
                  }
                  // remember how many interactions are propagating
                  dd.propagates = dd.interactions.length;
                  // locate and init the drop targets
                  if (dd.drop !== false && $special.drop)
                      $special.drop.handler(event, dd);
                  // disable text selection
                  drag.textselect(false);
                  // bind additional events...
                  if (drag.touched)
                      $event.add(drag.touched, "touchmove touchend", drag.handler, dd);
                  else
                      $event.add(document, "mousemove mouseup", drag.handler, dd);
                  // helps prevent text selection or scrolling
                  if (!drag.touched || dd.live)
                      return false;
              },
  
              // returns an interaction object
              interaction: function(elem, dd) {
                  var offset = $(elem)[dd.relative ? "position" : "offset"]() || {
                      top: 0,
                      left: 0
                  };
                  return {
                      drag: elem,
                      callback: new drag.callback(),
                      droppable: [],
                      offset: offset
                  };
              },
  
              // handle drag-releatd DOM events
              handler: function(event) {
                  // read the data before hijacking anything
                  var dd = event.data;
                  // handle various events
                  switch (event.type) {
                      // mousemove, check distance, start dragging
                      case !dd.dragging && 'touchmove':
                          event.preventDefault();
                      case !dd.dragging && 'mousemove':
                          //  drag tolerance, x≤ + y≤ = distance≤
                          if (Math.pow(event.pageX - dd.pageX, 2) + Math.pow(event.pageY - dd.pageY, 2) < Math.pow(dd.distance, 2))
                              break; // distance tolerance not reached
                          event.target = dd.target; // force target from "mousedown" event (fix distance issue)
                          drag.hijack(event, "dragstart", dd); // trigger "dragstart"
                          if (dd.propagates) // "dragstart" not rejected
                              dd.dragging = true; // activate interaction
                          // mousemove, dragging
                      case 'touchmove':
                          event.preventDefault();
                      case 'mousemove':
                          if (dd.dragging) {
                              // trigger "drag"
                              drag.hijack(event, "drag", dd);
                              if (dd.propagates) {
                                  // manage drop events
                                  if (dd.drop !== false && $special.drop)
                                      $special.drop.handler(event, dd); // "dropstart", "dropend"
                                  break; // "drag" not rejected, stop
                              }
                              event.type = "mouseup"; // helps "drop" handler behave
                          }
                          // mouseup, stop dragging
                      case 'touchend':
                      case 'mouseup':
                      default:
                          if (drag.touched)
                              $event.remove(drag.touched, "touchmove touchend", drag.handler); // remove touch events
                          else
                              $event.remove(document, "mousemove mouseup", drag.handler); // remove page events
                          if (dd.dragging) {
                              if (dd.drop !== false && $special.drop)
                                  $special.drop.handler(event, dd); // "drop"
                              drag.hijack(event, "dragend", dd); // trigger "dragend"
                          }
                          drag.textselect(true); // enable text selection
                          // if suppressing click events...
                          if (dd.click === false && dd.dragging)
                              $.data(dd.mousedown, "suppress.click", new Date().getTime() + 5);
                          dd.dragging = drag.touched = false; // deactivate element
                          break;
                  }
              },
  
              // re-use event object for custom events
              hijack: function(event, type, dd, x, elem) {
                  // not configured
                  if (!dd)
                      return;
                  // remember the original event and type
                  var orig = {
                      event: event.originalEvent,
                      type: event.type
                  },
                      // is the event drag related or drog related?
                      mode = type.indexOf("drop") ? "drag" : "drop",
                      // iteration vars
                      result, i = x || 0,
                      ia, $elems, callback,
                      len = !isNaN(x) ? x : dd.interactions.length;
                  // modify the event type
                  event.type = type;
                  // remove the original event
                  event.originalEvent = null;
                  // initialize the results
                  dd.results = [];
                  // handle each interacted element
                  do
                      if (ia = dd.interactions[i]) {
                          // validate the interaction
                          if (type !== "dragend" && ia.cancelled)
                              continue;
                          // set the dragdrop properties on the event object
                          callback = drag.properties(event, dd, ia);
                          // prepare for more results
                          ia.results = [];
                          // handle each element
                          $(elem || ia[mode] || dd.droppable).each(function(p, subject) {
                              // identify drag or drop targets individually
                              callback.target = subject;
                              // force propagtion of the custom event
                              event.isPropagationStopped = function() {
                                  return false;
                              };
                              // handle the event
                              result = subject ? $event.dispatch.call(subject, event, callback) : null;
                              // stop the drag interaction for this element
                              if (result === false) {
                                  if (mode == "drag") {
                                      ia.cancelled = true;
                                      dd.propagates -= 1;
                                  }
                                  if (type == "drop") {
                                      ia[mode][p] = null;
                                  }
                              }
                              // assign any dropinit elements
                              else if (type == "dropinit")
                                  ia.droppable.push(drag.element(result) || subject);
                              // accept a returned proxy element
                              if (type == "dragstart")
                                  ia.proxy = $(drag.element(result) || ia.drag)[0];
                              // remember this result
                              ia.results.push(result);
                              // forget the event result, for recycling
                              delete event.result;
                              // break on cancelled handler
                              if (type !== "dropinit")
                                  return result;
                          });
                          // flatten the results
                          dd.results[i] = drag.flatten(ia.results);
                          // accept a set of valid drop targets
                          if (type == "dropinit")
                              ia.droppable = drag.flatten(ia.droppable);
                          // locate drop targets
                          if (type == "dragstart" && !ia.cancelled)
                              callback.update();
                      }
                  while (++i < len)
                  // restore the original event & type
                      event.type = orig.type;
                  event.originalEvent = orig.event;
                  // return all handler results
                  return drag.flatten(dd.results);
              },
  
              // extend the callback object with drag/drop properties...
              properties: function(event, dd, ia) {
                  var obj = ia.callback;
                  // elements
                  obj.drag = ia.drag;
                  obj.proxy = ia.proxy || ia.drag;
                  // starting mouse position
                  obj.startX = dd.pageX;
                  obj.startY = dd.pageY;
                  // current distance dragged
                  obj.deltaX = event.pageX - dd.pageX;
                  obj.deltaY = event.pageY - dd.pageY;
                  // original element position
                  obj.originalX = ia.offset.left;
                  obj.originalY = ia.offset.top;
                  // adjusted element position
                  obj.offsetX = obj.originalX + obj.deltaX;
                  obj.offsetY = obj.originalY + obj.deltaY;
                  // assign the drop targets information
                  obj.drop = drag.flatten((ia.drop || []).slice());
                  obj.available = drag.flatten((ia.droppable || []).slice());
                  return obj;
              },
  
              // determine is the argument is an element or jquery instance
              element: function(arg) {
                  if (arg && (arg.jquery || arg.nodeType == 1))
                      return arg;
              },
  
              // flatten nested jquery objects and arrays into a single dimension array
              flatten: function(arr) {
                  return $.map(arr, function(member) {
                      return member && member.jquery ? $.makeArray(member) :
                          member && member.length ? drag.flatten(member) : member;
                  });
              },
  
              // toggles text selection attributes ON (true) or OFF (false)
              textselect: function(bool) {
                  $(document)[bool ? "unbind" : "bind"]("selectstart", drag.dontstart)
                      .css("MozUserSelect", bool ? "" : "none");
                  // .attr("unselectable", bool ? "off" : "on" )
                  document.unselectable = bool ? "off" : "on";
              },
  
              // suppress "selectstart" and "ondragstart" events
              dontstart: function() {
                  return false;
              },
  
              // a callback instance contructor
              callback: function() {}
  
          };
  
      // callback methods
      drag.callback.prototype = {
          update: function() {
              if ($special.drop && this.available.length)
                  $.each(this.available, function(i) {
                      $special.drop.locate(this, i);
                  });
          }
      };
  
      // patch $.event.$dispatch to allow suppressing clicks
      var $dispatch = $event.dispatch;
      $event.dispatch = function(event) {
          if ($.data(this, "suppress." + event.type) - new Date().getTime() > 0) {
              $.removeData(this, "suppress." + event.type);
              return;
          }
          return $dispatch.apply(this, arguments);
      };
  
      // event fix hooks for touch events...
      var touchHooks =
          $event.fixHooks.touchstart =
          $event.fixHooks.touchmove =
          $event.fixHooks.touchend =
          $event.fixHooks.touchcancel = {
              props: "clientX clientY pageX pageY screenX screenY".split(" "),
              filter: function(event, orig) {
                  if (orig) {
                      var touched = (orig.touches && orig.touches[0]) || (orig.changedTouches && orig.changedTouches[0]) || null;
                      // iOS webkit: touchstart, touchmove, touchend
                      if (touched)
                          $.each(touchHooks.props, function(i, prop) {
                              event[prop] = touched[prop];
                          });
                  }
                  return event;
              }
      };
  
      // share the same special event configuration with related events...
      $special.draginit = $special.dragstart = $special.dragend = drag;
  
  })(jQuery);