| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- (function(angular) {
- 'use strict';
- function isDnDsSupported() {
- return 'ondrag' in document.createElement('a');
- }
- function determineEffectAllowed(e) {
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- // Chrome doesn't set dropEffect, so we have to work it out ourselves
- if (typeof e.dataTransfer !== 'undefined' && e.dataTransfer.dropEffect === 'none') {
- if (e.dataTransfer.effectAllowed === 'copy' ||
- e.dataTransfer.effectAllowed === 'move') {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed;
- } else if (e.dataTransfer.effectAllowed === 'copyMove' || e.dataTransfer.effectAllowed === 'copymove') {
- e.dataTransfer.dropEffect = e.ctrlKey ? 'copy' : 'move';
- }
- }
- }
- if (!isDnDsSupported()) {
- angular.module('ang-drag-drop', []);
- return;
- }
- var module = angular.module('ang-drag-drop', []);
- module.directive('uiDraggable', ['$parse', '$rootScope', '$dragImage', function($parse, $rootScope, $dragImage) {
- return function(scope, element, attrs) {
- var isDragHandleUsed = false,
- dragHandleClass,
- draggingClass = attrs.draggingClass || 'on-dragging',
- dragTarget;
- element.attr('draggable', false);
- scope.$watch(attrs.uiDraggable, function(newValue) {
- if (newValue) {
- element.attr('draggable', newValue);
- element.bind('dragend', dragendHandler);
- element.bind('dragstart', dragstartHandler);
- }
- else {
- element.removeAttr('draggable');
- element.unbind('dragend', dragendHandler);
- element.unbind('dragstart', dragstartHandler);
- }
- });
- if (angular.isString(attrs.dragHandleClass)) {
- isDragHandleUsed = true;
- dragHandleClass = attrs.dragHandleClass.trim() || 'drag-handle';
- element.bind('mousedown', function(e) {
- dragTarget = e.target;
- });
- }
- function dragendHandler(e) {
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- setTimeout(function() {
- element.unbind('$destroy', dragendHandler);
- }, 0);
- var sendChannel = attrs.dragChannel || 'defaultchannel';
- $rootScope.$broadcast('ANGULAR_DRAG_END', e, sendChannel);
- determineEffectAllowed(e);
- if (e.dataTransfer && e.dataTransfer.dropEffect !== 'none') {
- if (attrs.onDropSuccess) {
- var onDropSuccessFn = $parse(attrs.onDropSuccess);
- scope.$evalAsync(function() {
- onDropSuccessFn(scope, {$event: e});
- });
- }
- }else if (e.dataTransfer && e.dataTransfer.dropEffect === 'none'){
- if (attrs.onDropFailure) {
- var onDropFailureFn = $parse(attrs.onDropFailure);
- scope.$evalAsync(function() {
- onDropFailureFn(scope, {$event: e});
- });
- }
- }
- element.removeClass(draggingClass);
- }
- function setDragElement(e, dragImageElementId) {
- var dragImageElementFn;
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- dragImageElementFn = $parse(dragImageElementId);
- scope.$apply(function() {
- var elementId = dragImageElementFn(scope, {$event: e}),
- dragElement;
- if (!(elementId && angular.isString(elementId))) {
- return;
- }
- dragElement = document.getElementById(elementId);
- if (!dragElement) {
- return;
- }
- e.dataTransfer.setDragImage(dragElement, 0, 0);
- });
- }
- function dragstartHandler(e) {
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);
- if (isDragAllowed) {
- var sendChannel = attrs.dragChannel || 'defaultchannel';
- var dragData = '';
- if (attrs.drag) {
- dragData = scope.$eval(attrs.drag);
- }
- var dragImage = attrs.dragImage || null;
- element.addClass(draggingClass);
- element.bind('$destroy', dragendHandler);
- //Code to make sure that the setDragImage is available. IE 10, 11, and Opera do not support setDragImage.
- var hasNativeDraggable = !(document.uniqueID || window.opera);
- //If there is a draggable image passed in, then set the image to be dragged.
- if (dragImage && hasNativeDraggable) {
- var dragImageFn = $parse(attrs.dragImage);
- scope.$apply(function() {
- var dragImageParameters = dragImageFn(scope, {$event: e});
- if (dragImageParameters) {
- if (angular.isString(dragImageParameters)) {
- dragImageParameters = $dragImage.generate(dragImageParameters);
- }
- if (dragImageParameters.image) {
- var xOffset = dragImageParameters.xOffset || 0,
- yOffset = dragImageParameters.yOffset || 0;
- e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
- }
- }
- });
- } else if (attrs.dragImageElementId) {
- setDragElement(e, attrs.dragImageElementId);
- }
- var offset = {x: e.offsetX, y: e.offsetY};
- var transferDataObject = {data: dragData, channel: sendChannel, offset: offset};
- var transferDataText = angular.toJson(transferDataObject);
- e.dataTransfer.setData('text', transferDataText);
- e.dataTransfer.effectAllowed = 'copyMove';
- $rootScope.$broadcast('ANGULAR_DRAG_START', e, sendChannel, transferDataObject);
- }
- else {
- e.preventDefault();
- }
- }
- };
- }
- ]);
- module.directive('uiOnDrop', ['$parse', '$rootScope', function($parse, $rootScope) {
- return function(scope, element, attr) {
- var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
- var dropChannel = attr.dropChannel || 'defaultchannel';
- var dragChannel = '';
- var dragEnterClass = attr.dragEnterClass || 'on-drag-enter';
- var dragHoverClass = attr.dragHoverClass || 'on-drag-hover';
- var customDragEnterEvent = $parse(attr.onDragEnter);
- var customDragLeaveEvent = $parse(attr.onDragLeave);
- function calculateDropOffset(e) {
- var offset = {
- x: e.offsetX,
- y: e.offsetY
- };
- var target = e.target;
- while (target !== element[0]) {
- offset.x = offset.x + target.offsetLeft;
- offset.y = offset.y + target.offsetTop;
- target = target.offsetParent;
- if (!target) {
- return null;
- }
- }
- return offset;
- }
- function onDragOver(e) {
- if (e.preventDefault) {
- e.preventDefault(); // Necessary. Allows us to drop.
- }
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- var uiOnDragOverFn = $parse(attr.uiOnDragOver);
- scope.$evalAsync(function() {
- uiOnDragOverFn(scope, {$event: e, $channel: dropChannel});
- });
- return false;
- }
- function onDragLeave(e) {
- if (e.preventDefault) {
- e.preventDefault();
- }
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- dragging--;
- if (dragging === 0) {
- scope.$evalAsync(function() {
- customDragLeaveEvent(scope, {$event: e, $channel: dropChannel});
- });
- element.addClass(dragEnterClass);
- element.removeClass(dragHoverClass);
- }
- var uiOnDragLeaveFn = $parse(attr.uiOnDragLeave);
- scope.$evalAsync(function() {
- uiOnDragLeaveFn(scope, {$event: e, $channel: dropChannel});
- });
- }
- function onDragEnter(e) {
- if (e.preventDefault) {
- e.preventDefault();
- }
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- if (dragging === 0) {
- scope.$evalAsync(function() {
- customDragEnterEvent(scope, {$event: e, $channel: dropChannel});
- });
- element.removeClass(dragEnterClass);
- element.addClass(dragHoverClass);
- }
- dragging++;
- var uiOnDragEnterFn = $parse(attr.uiOnDragEnter);
- scope.$evalAsync(function() {
- uiOnDragEnterFn(scope, {$event: e, $channel: dropChannel});
- });
- $rootScope.$broadcast('ANGULAR_HOVER', dragChannel);
- }
- function onDrop(e) {
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- if (e.preventDefault) {
- e.preventDefault(); // Necessary. Allows us to drop.
- }
- if (e.stopPropagation) {
- e.stopPropagation(); // Necessary. Allows us to drop.
- }
- var sendData = e.dataTransfer.getData('text');
- sendData = angular.fromJson(sendData);
- var dropOffset = calculateDropOffset(e);
- var position = dropOffset ? {
- x: dropOffset.x - sendData.offset.x,
- y: dropOffset.y - sendData.offset.y
- } : null;
- determineEffectAllowed(e);
- var uiOnDropFn = $parse(attr.uiOnDrop);
- scope.$evalAsync(function() {
- uiOnDropFn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel, $position: position});
- });
- element.removeClass(dragEnterClass);
- dragging = 0;
- }
- function isDragChannelAccepted(dragChannel, dropChannel) {
- if (dropChannel === '*') {
- return true;
- }
- var channelMatchPattern = new RegExp('(\\s|[,])+(' + dragChannel + ')(\\s|[,])+', 'i');
- return channelMatchPattern.test(',' + dropChannel + ',');
- }
- function preventNativeDnD(e) {
- if(e.originalEvent) {
- e.dataTransfer = e.originalEvent.dataTransfer;
- }
- if (e.preventDefault) {
- e.preventDefault();
- }
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- e.dataTransfer.dropEffect = 'none';
- return false;
- }
- var deregisterDragStart = $rootScope.$on('ANGULAR_DRAG_START', function(_, e, channel, transferDataObject) {
- dragChannel = channel;
- var valid = true;
- if (!isDragChannelAccepted(channel, dropChannel)) {
- valid = false;
- }
- if (valid && attr.dropValidate) {
- var validateFn = $parse(attr.dropValidate);
- valid = validateFn(scope, {
- $drop: {scope: scope, element: element},
- $event: e,
- $data: transferDataObject.data,
- $channel: transferDataObject.channel
- });
- }
- if (valid) {
- element.bind('dragover', onDragOver);
- element.bind('dragenter', onDragEnter);
- element.bind('dragleave', onDragLeave);
- element.bind('drop', onDrop);
- element.addClass(dragEnterClass);
- } else {
- element.bind('dragover', preventNativeDnD);
- element.bind('dragenter', preventNativeDnD);
- element.bind('dragleave', preventNativeDnD);
- element.bind('drop', preventNativeDnD);
- element.removeClass(dragEnterClass);
- }
- });
- var deregisterDragEnd = $rootScope.$on('ANGULAR_DRAG_END', function() {
- element.unbind('dragover', onDragOver);
- element.unbind('dragenter', onDragEnter);
- element.unbind('dragleave', onDragLeave);
- element.unbind('drop', onDrop);
- element.removeClass(dragHoverClass);
- element.removeClass(dragEnterClass);
- element.unbind('dragover', preventNativeDnD);
- element.unbind('dragenter', preventNativeDnD);
- element.unbind('dragleave', preventNativeDnD);
- element.unbind('drop', preventNativeDnD);
- });
- scope.$on('$destroy', function() {
- deregisterDragStart();
- deregisterDragEnd();
- });
- attr.$observe('dropChannel', function(value) {
- if (value) {
- dropChannel = value;
- }
- });
- };
- }
- ]);
- module.constant('$dragImageConfig', {
- height: 20,
- width: 200,
- padding: 10,
- font: 'bold 11px Arial',
- fontColor: '#eee8d5',
- backgroundColor: '#93a1a1',
- xOffset: 0,
- yOffset: 0
- });
- module.service('$dragImage', ['$dragImageConfig', function(defaultConfig) {
- var ELLIPSIS = '…';
- function fitString(canvas, text, config) {
- var width = canvas.measureText(text).width;
- if (width < config.width) {
- return text;
- }
- while (width + config.padding > config.width) {
- text = text.substring(0, text.length - 1);
- width = canvas.measureText(text + ELLIPSIS).width;
- }
- return text + ELLIPSIS;
- }
- this.generate = function(text, options) {
- var config = angular.extend({}, defaultConfig, options || {});
- var el = document.createElement('canvas');
- el.height = config.height;
- el.width = config.width;
- var canvas = el.getContext('2d');
- canvas.fillStyle = config.backgroundColor;
- canvas.fillRect(0, 0, config.width, config.height);
- canvas.font = config.font;
- canvas.fillStyle = config.fontColor;
- var title = fitString(canvas, text, config);
- canvas.fillText(title, 4, config.padding + 4);
- var image = new Image();
- image.src = el.toDataURL();
- return {
- image: image,
- xOffset: config.xOffset,
- yOffset: config.yOffset
- };
- };
- }
- ]);
- }(angular));
|