Source: node/DraggableNodeMixin.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This module provides a set of attributes that define topic names for publications
 * and subscriptions. This should be mixed into any widget that wishes to use those topics
 * to ensure consistency. It also allows the actual values to be managed from a single file. 
 * 
 * @module alfresco/node/DraggableNodeMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/core/Core",
        "dojo/dnd/Moveable",
        "dojo/_base/event",
        "dojo/dom-attr",
        "dojo/on",
        "dojo/_base/lang",
        "dojo/fx", 
        "dojo/aspect",
        "dojo/dom-style",
        "dojo/dom-geometry"],
        function(declare, AlfCore, Moveable, event, domAttr, on, lang, fx, aspect, domStyle, domGeom) {
   
   return declare([AlfCore], {
      
      /**
       * The DOM node to be dragged. If this is left as null then the "domNode" attribute will be used (assuming
       * there is one).
       * 
       * @instance
       * @type {element}
       */
      draggableNode: null,
      
      /**
       * Indicates whether or not the mixing module should take advantage of the capabilities. This makes
       * it possible to "opt-out" through configuration or extension.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.39
       */
      isDraggable: false,

      /**
       * This will be assigned a reference to the "dojo/dnd/Moveable" instance created by the 
       * [postCreate function]{@link module:alfresco/node/DraggableNodeMixin#onDragStart}
       * 
       * @instance
       * @type {object}
       */
      moveable: null,
      
      /**
       * This will be assigned a "dojo/dnd/Mover" instance when a drag is in-flight. It will be assigned in the 
       * call to [onDragStart]{@link module:alfresco/node/DraggableNodeMixin#onDragStart} as this is the first time
       * that a reference to the object is made available. A new "mover" is created on each move. The reference
       * is required to work around an unresolved bug in the Dojo DND code that causes the 2nd move event to 
       * allow "click" events to bubble. If the dragged DOM node is wrapped in an anchor element then this will
       * cause the link to be processed.
       * 
       * @instance
       * @type {object}
       */
      mover: null,
      
      /**
       * @instance
       */
      postCreate: function alfresco_node_DraggableNodeMixin__postCreate() {
         this.inherited(arguments);
         if (this.isDraggable)
         {
            if (!this.draggableNode && this.domNode)
            {
               this.draggableNode = this.domNode;
            }
            if (this.draggableNode)
            {
               // It's important to set a delay (this is a measure of pixels NOT time) so that
               // regular click events can be processed...
               this.moveable = new Moveable(this.draggableNode, {
                  delay: 20
               });
               // Use aspect to hitch some events to work around 2nd drag event bugs...
               aspect.before(this.moveable, "onMoveStart", lang.hitch(this, "onDragStart"));
               aspect.after(this.moveable, "onMoveStop", lang.hitch(this, "onDragEnd"), true);
               on(this.moveable.handle, "mousedown", lang.hitch(this, "onMouseDown"));
               on(this.moveable.handle, "mouseup", lang.hitch(this, "onMouseUp"));
               on(this.moveable.handle, "click", lang.hitch(this, "onDraggableClick"));
            }
         }
      },
      
      onMouseDown: function alfresco_node_DraggableNodeMixin__onMouseDown(evt) {
         this.alfLog("log", "On mouse down", evt);
         var output = domGeom.position(this.draggableNode);
         this.origX = output.x;
         this.origY = output.y;
      },
      
      /**
       * This function is called on any mouseup events that occur on the dragged node. This is
       * required because on the 2nd drag of any node the events are fired in the wrong order
       * (this is a bug in Dojo 1.9.0 that should be periodically checked for a fix) and would
       * result in a click event not being suppressed (this would allow links to be processed
       * at the end of the drag). If [mover]{@link module:alfresco/node/DraggableNodeMixin#mover}
       * as been assigned then this has been called at the end of a drag. Therefore we can 
       * call the "onMouseUp" function of the mover to get events processing correctly again.
       * 
       * @instance
       * @param {object} e The mouse up event
       */
      onMouseUp: function alfresco_node_DraggableNodeMixin__onMouseUp(e) {
         this.alfLog("log", "On Mouse Up", e);
         if (this.mover)
         {
            this.mover.onMouseUp(e);
            this.mover = null;
            event.stop(e);
         }
      },
      
      /**
       * Suppresses click events at the end of a drag.
       * 
       * @instance
       * @param {object} e The click event
       */
      onDraggableClick: function alfresco_node_DraggableNodeMixin__onDraggableClick(e) {
         this.alfLog("log", "On draggable click", e, this.dragInFlight);
         if (this.dragInFlight)
         {
            this.alfLog("log", "Cancelling in flight");
            event.stop(e);
            this.dragInFlight = false;
         }
      },
      
      /**
       * @instance
       * @type {number}
       * @default
       */
      origX: 0,
      
      /**
       * @instance
       * @type {number}
       * @default
       */
      origY: 0,
      
      /**
       * Captures the start of a new drag and sets the [dragInFlight]{@link module:alfresco/node/DraggableNodeMixin#dragInFlight}
       * flag to indicate that a drag has begun and sets the [mover]{@link module:alfresco/node/DraggableNodeMixin#mover}
       * attribute with the newly created dojo/dnd/Mover instance.
       * 
       * @instance
       * @param {object} mover The newly created dojo/dnd/Mover instance
       * @param {object} evt The event created.
       */
      onDragStart: function alfresco_node_DraggableNodeMixin__onDragStart(mover, evt) {
         this.alfLog("log", "Drag started", mover, evt);
         this.dragInFlight = true;
         this.mover = mover;
         
         // Set the current item on the dojo/dnd/Mover object so that drop targets know what it represents...
         this.mover.alfCurrentItem = this.currentItem;
      },
      
      /**
       * Returns the dragged node to its starting position if it hasn't been dropped onto a valid target.
       * 
       * @instance
       * @type {object}
       */
      onDragEnd: function alfresco_node_DraggableNodeMixin__onDragEnd(mover) {
         this.alfLog("log", "Drag ended", mover);
         if (mover.alfNodeDropClaimed)
         {
            this.alfLog("log", "Drop claimed!");
         }
         else
         {
            fx.slideTo({
               node: this.draggableNode,
               top: this.origY,
               left: this.origX,
               units: "px",
               onEnd: lang.hitch(this, "resetDraggableNodeStyle")
            }).play();
         }
      },
      
      /**
       * @instance
       */
      resetDraggableNodeStyle: function alfresco_node_DraggableNodeMixin__resetDraggableNodeStyle() {
         domStyle.set(this.draggableNode, {
            position: "inherit",
            top: "inherit",
            left: "inherit"
         });
      }
   });
});