text: /**
   * DraggableElement is a mixin class used to create elements that can be clicked
   * and dragged by a mouse to a new position within a group. This class must be used
   * in conjunction with OO.ui.mixin.DraggableGroupElement, which provides a container for
   * the draggable elements.
   *
   * @abstract
   * @class
   *
   * @constructor
   * @param {Object} [config] Configuration options
   * @cfg {jQuery} [$handle] The part of the element which can be used for dragging, defaults to
   *  the whole element
   * @cfg {boolean} [draggable] The items are draggable. This can change with #toggleDraggable
   *  but the draggable state should be called from the DraggableGroupElement, which updates
   *  the whole group
   */
  OO.ui.mixin.DraggableElement = function OoUiMixinDraggableElement( config ) {
  	config = config || {};
  
  	// Properties
  	this.index = null;
  	this.$handle = config.$handle || this.$element;
  	this.wasHandleUsed = null;
  
  	// Initialize and events
  	this.$element
  		.addClass( 'oo-ui-draggableElement' )
  		.on( {
  			mousedown: this.onDragMouseDown.bind( this ),
  			dragstart: this.onDragStart.bind( this ),
  			dragover: this.onDragOver.bind( this ),
  			dragend: this.onDragEnd.bind( this ),
  			drop: this.onDrop.bind( this )
  		} );
  	this.$handle.addClass( 'oo-ui-draggableElement-handle' );
  	this.toggleDraggable( config.draggable === undefined ? true : !!config.draggable );
  };
  
  OO.initClass( OO.ui.mixin.DraggableElement );
  
  /* Events */
  
  /**
   * @event dragstart
   *
   * A dragstart event is emitted when the user clicks and begins dragging an item.
   * @param {OO.ui.mixin.DraggableElement} item The item the user has clicked and is dragging with
   *  the mouse.
   */
  
  /**
   * @event dragend
   * A dragend event is emitted when the user drags an item and releases the mouse,
   * thus terminating the drag operation.
   */
  
  /**
   * @event drop
   * A drop event is emitted when the user drags an item and then releases the mouse button
   * over a valid target.
   */
  
  /* Static Properties */
  
  /**
   * @inheritdoc OO.ui.mixin.ButtonElement
   */
  OO.ui.mixin.DraggableElement.static.cancelButtonMouseDownEvents = false;
  
  /* Methods */
  
  /**
   * Change the draggable state of this widget.
   * This allows users to temporarily halt the dragging operations.
   *
   * @param {boolean} isDraggable Widget supports draggable operations
   * @fires draggable
   */
  OO.ui.mixin.DraggableElement.prototype.toggleDraggable = function ( isDraggable ) {
  	isDraggable = isDraggable !== undefined ? !!isDraggable : !this.draggable;
  
  	if ( this.draggable !== isDraggable ) {
  		this.draggable = isDraggable;
  
  		this.$handle.toggleClass( 'oo-ui-draggableElement-undraggable', !this.draggable );
  
  		// We make the entire element draggable, not just the handle, so that
  		// the whole element appears to move. wasHandleUsed prevents drags from
  		// starting outside the handle
  		this.$element.prop( 'draggable', this.draggable );
  	}
  };
  
  /**
   * Check the draggable state of this widget.
   *
   * @return {boolean} Widget supports draggable operations
   */
  OO.ui.mixin.DraggableElement.prototype.isDraggable = function () {
  	return this.draggable;
  };
  
  /**
   * Respond to mousedown event.
   *
   * @private
   * @param {jQuery.Event} e Drag event
   */
  OO.ui.mixin.DraggableElement.prototype.onDragMouseDown = function ( e ) {
  	if ( !this.isDraggable() ) {
  		return;
  	}
  
  	this.wasHandleUsed =
  		// Optimization: if the handle is the whole element this is always true
  		this.$handle[ 0 ] === this.$element[ 0 ] ||
  		// Check the mousedown occurred inside the handle
  		OO.ui.contains( this.$handle[ 0 ], e.target, true );
  };
  
  /**
   * Respond to dragstart event.
   *
   * @private
   * @param {jQuery.Event} e Drag event
   * @return {boolean} False if the event is cancelled
   * @fires dragstart
   */
  OO.ui.mixin.DraggableElement.prototype.onDragStart = function ( e ) {
  	var element = this,
  		dataTransfer = e.originalEvent.dataTransfer;
  
  	if ( !this.wasHandleUsed || !this.isDraggable() ) {
  		return false;
  	}
  
  	// Define drop effect
  	dataTransfer.dropEffect = 'none';
  	dataTransfer.effectAllowed = 'move';
  	// Support: Firefox
  	// We must set up a dataTransfer data property or Firefox seems to
  	// ignore the fact the element is draggable.
  	try {
  		dataTransfer.setData( 'application-x/OOUI-draggable', this.getIndex() );
  	} catch ( err ) {
  		// The above is only for Firefox. Move on if it fails.
  	}
  	// Briefly add a 'clone' class to style the browser's native drag image
  	this.$element.addClass( 'oo-ui-draggableElement-clone' );
  	// Add placeholder class after the browser has rendered the clone
  	setTimeout( function () {
  		element.$element
  			.removeClass( 'oo-ui-draggableElement-clone' )
  			.addClass( 'oo-ui-draggableElement-placeholder' );
  	} );
  	// Emit event
  	this.emit( 'dragstart', this );
  	return true;
  };
  
  /**
   * Respond to dragend event.
   *
   * @private
   * @fires dragend
   */
  OO.ui.mixin.DraggableElement.prototype.onDragEnd = function () {
  	this.$element.removeClass( 'oo-ui-draggableElement-placeholder' );
  	this.emit( 'dragend' );
  };
  
  /**
   * Handle drop event.
   *
   * @private
   * @param {jQuery.Event} e Drop event
   * @fires drop
   */
  OO.ui.mixin.DraggableElement.prototype.onDrop = function ( e ) {
  	e.preventDefault();
  	this.emit( 'drop', e );
  };
  
  /**
   * In order for drag/drop to work, the dragover event must
   * return false and stop propogation.
   *
   * @param {jQuery.Event} e Drag event
   * @private
   */
  OO.ui.mixin.DraggableElement.prototype.onDragOver = function ( e ) {
  	e.preventDefault();
  };
  
  /**
   * Set item index.
   * Store it in the DOM so we can access from the widget drag event.
   *
   * @private
   * @param {number} index Item index
   */
  OO.ui.mixin.DraggableElement.prototype.setIndex = function ( index ) {
  	if ( this.index !== index ) {
  		this.index = index;
  		this.$element.data( 'index', index );
  	}
  };
  
  /**
   * Get item index.
   *
   * @private
   * @return {number} Item index
   */
  OO.ui.mixin.DraggableElement.prototype.getIndex = function () {
  	return this.index;
  };
  