/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ (function() { /* jshint validthis: true */ 'use strict'; /* * sgDraggable - Make an element (usually a folder of elements) draggable. * @memberof SOGo.Common * @restrict attribute * @param {Object=} sgDraggable - the object to be exposed to the droppable target. * @param {expression} sgDragStart - dragging will only start if this expression returns true. * @param {expression} sgDragCount - the number of items being dragged; this number appears inside * the sg-draggable-helper element that follows the mouse cursor. * * @example: email */ sgDraggable.$inject = ['$parse', '$rootScope', '$document', '$timeout', '$log']; function sgDraggable($parse, $rootScope, $document, $timeout, $log) { return { restrict: 'A', link: link }; function link(scope, element, attrs) { var o; $timeout(function() { var folder, dragStart, count; folder = $parse(attrs.sgDraggable)(scope); dragStart = attrs.sgDragStart? $parse(attrs.sgDragStart) : null; count = attrs.sgDragCount? $parse(attrs.sgDragCount) : null; o = new sgDraggableObject(element, folder, dragStart, count); }); scope.$on('$destroy', function() { o.$destroy(); }); function sgDraggableObject($element, folder, dragStart, count) { this.$element = $element; this.folder = folder; this.dragStart = dragStart; this.count = count; this.helper = $document.find('sg-draggable-helper'); if (!this.helper) { throw Error('sg-draggable requires a sg-draggable-helper element.'); } this.bindedOnDragDetect = angular.bind(this, this.onDragDetect); this.bindedOnDrag = angular.bind(this, this.onDrag); // Register the mousedown event that can trigger the dragging action this.$element.on('mousedown', this.bindedOnDragDetect); } /** * sgDraggableObject is an object that wraps the logic to emit the folder:dragstart and * folder:dragend custom events. */ sgDraggableObject.prototype = { dragHasStarted: false, $destroy: function() { this.$element.off('mousedown', this.bindedOnDragDetect); }, getDistanceFromStart: function(event) { var delta = { x: this.startPosition.clientX - event.clientX, y: this.startPosition.clientY - event.clientY }; return Math.sqrt(delta.x * delta.x + delta.y * delta.y); }, // Start dragging on mousedown onDragDetect: function(ev) { ev.stopPropagation(); if (!this.dragStart || this.dragStart(scope)) { // Listen to mousemove and start dragging when mouse has moved from at least 3 pixels $document.on('mousemove', this.bindedOnDrag); // Stop dragging on the next "mouseup" $document.one('mouseup', angular.bind(this, this.onDragEnd)); } }, // onDrag: function(ev) { var counter; if (!this.startPosition) { this.startPosition = { clientX: ev.clientX, clientY: ev.clientY }; } else if (!this.dragHasStarted && this.getDistanceFromStart(ev) > 10) { counter = this.helper.find('sg-draggable-helper-counter'); this.dragHasStarted = true; this.helper.removeClass('ng-hide'); if (this.count && this.count(scope) > 1) counter.text(this.count(scope)).removeClass('ng-hide'); else counter.addClass('ng-hide'); $log.debug('emit folder:dragstart'); $rootScope.$emit('folder:dragstart', this.folder); } if (this.dragHasStarted) { if (ev.shiftKey || this.folder.isRemote) this.helper.addClass('sg-draggable-helper--copy'); else this.helper.removeClass('sg-draggable-helper--copy'); this.helper.css({ top: (ev.pageY + 5) + 'px', left: (ev.pageX + 5) + 'px' }); } }, onDragEnd: function(ev) { var action = 'move'; this.startPosition = null; $document.off('mousemove', this.bindedOnDrag); if (this.dragHasStarted) { if (ev.shiftKey || this.folder.isRemote) action = 'copy'; $log.debug('emit folder:dragend'); $rootScope.$emit('folder:dragend', this.folder, action); this.dragHasStarted = false; this.helper.addClass('ng-hide'); } } }; } } angular .module('SOGo.Common') .directive('sgDraggable', sgDraggable); })();