11/**
2- * angular-drag-and-drop-lists v1.2 .0
2+ * angular-drag-and-drop-lists v1.3 .0
33 *
44 * Copyright (c) 2014 Marcel Juenemann mail@marcel-juenemann.de
55 * Copyright (c) 2014-2015 Google Inc.
@@ -33,11 +33,16 @@ angular.module('dndLists', [])
3333 * remove your element from the original list in this callback, since the
3434 * directive is not doing that for you automatically. The original dragend
3535 * event will be provided in the local event variable.
36+ * - dnd-canceled Callback that is invoked if the element was dragged, but the operation was
37+ * canceled and the element was not dropped. The original dragend event will
38+ * be provided in the local event variable.
3639 * - dnd-copied Same as dnd-moved, just that it is called when the element was copied
3740 * instead of moved. The original dragend event will be provided in the local
3841 * event variable.
3942 * - dnd-dragstart Callback that is invoked when the element was dragged. The original
4043 * dragstart event will be provided in the local event variable.
44+ * - dnd-dragend Callback that is invoked when the drag operation ended. Available local
45+ * variables are event and dropEffect.
4146 * - dnd-type Use this attribute if you have different kinds of items in your
4247 * application and you want to limit which items can be dropped into which
4348 * lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
@@ -123,16 +128,19 @@ angular.module('dndLists', [])
123128 case "move" :
124129 $parse ( attr . dndMoved ) ( scope , { event : event } ) ;
125130 break ;
126-
127131 case "copy" :
128132 $parse ( attr . dndCopied ) ( scope , { event : event } ) ;
129133 break ;
134+ case "none" :
135+ $parse ( attr . dndCanceled ) ( scope , { event : event } ) ;
136+ break ;
130137 }
138+ $parse ( attr . dndDragend ) ( scope , { event : event , dropEffect : dropEffect } ) ;
131139 } ) ;
132140
133141 // Clean up
134142 element . removeClass ( "dndDragging" ) ;
135- element . removeClass ( "dndDraggingSource" ) ;
143+ $timeout ( function ( ) { element . removeClass ( "dndDraggingSource" ) ; } , 0 ) ;
136144 dndDragTypeWorkaround . isDragging = false ;
137145 event . stopPropagation ( ) ;
138146 } ) ;
@@ -142,12 +150,14 @@ angular.module('dndLists', [])
142150 * specified with the dnd-selected attribute.
143151 */
144152 element . on ( 'click' , function ( event ) {
145- event = event . originalEvent || event ;
153+ if ( ! attr . dndSelected ) return ;
146154
155+ event = event . originalEvent || event ;
147156 scope . $apply ( function ( ) {
148157 $parse ( attr . dndSelected ) ( scope , { event : event } ) ;
149158 } ) ;
150159
160+ // Prevent triggering dndSelected in parant elements.
151161 event . stopPropagation ( ) ;
152162 } ) ;
153163
@@ -156,7 +166,6 @@ angular.module('dndLists', [])
156166 */
157167 element . on ( 'selectstart' , function ( ) {
158168 if ( this . dragDrop ) this . dragDrop ( ) ;
159- return false ;
160169 } ) ;
161170 } ;
162171 } ] )
@@ -194,6 +203,11 @@ angular.module('dndLists', [])
194203 * - index: The position in the list at which the element would be dropped.
195204 * - item: The transferred object.
196205 * - type: The dnd-type set on the dnd-draggable, or undefined if unset.
206+ * - dnd-inserted Optional expression that is invoked after a drop if the element was
207+ * actually inserted into the list. The same local variables as for
208+ * dnd-drop will be available. Note that for reorderings inside the same
209+ * list the old element will still be in the list due to the fact that
210+ * dnd-moved was not called yet.
197211 * - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts
198212 * drops from sources outside of the current browser tab. This allows to
199213 * drag and drop accross different browser tabs. Note that this will allow
@@ -205,17 +219,19 @@ angular.module('dndLists', [])
205219 * CSS classes:
206220 * - dndPlaceholder When an element is dragged over the list, a new placeholder child
207221 * element will be added. This element is of type li and has the class
208- * dndPlaceholder set.
222+ * dndPlaceholder set. Alternatively, you can define your own placeholder
223+ * by creating a child element with dndPlaceholder class.
209224 * - dndDragover Will be added to the list while an element is dragged over the list.
210225 */
211226 . directive ( 'dndList' , [ '$parse' , '$timeout' , 'dndDropEffectWorkaround' , 'dndDragTypeWorkaround' ,
212227 function ( $parse , $timeout , dndDropEffectWorkaround , dndDragTypeWorkaround ) {
213228 return function ( scope , element , attr ) {
214229 // While an element is dragged over the list, this placeholder element is inserted
215230 // at the location where the element would be inserted after dropping
216- var placeholder = angular . element ( "<li class='dndPlaceholder'></li>" ) ;
231+ var placeholder = getPlaceholderElement ( ) ;
217232 var placeholderNode = placeholder [ 0 ] ;
218233 var listNode = element [ 0 ] ;
234+ placeholder . remove ( ) ;
219235
220236 var horizontal = attr . dndHorizontalList && scope . $eval ( attr . dndHorizontalList ) ;
221237 var externalSources = attr . dndExternalSources && scope . $eval ( attr . dndExternalSources ) ;
@@ -277,7 +293,7 @@ angular.module('dndLists', [])
277293
278294 // At this point we invoke the callback, which still can disallow the drop.
279295 // We can't do this earlier because we want to pass the index of the placeholder.
280- if ( attr . dndDragover && ! invokeCallback ( attr . dndDragover , event ) ) {
296+ if ( attr . dndDragover && ! invokeCallback ( attr . dndDragover , event , getPlaceholderIndex ( ) ) ) {
281297 return stopDragover ( ) ;
282298 }
283299
@@ -312,8 +328,9 @@ angular.module('dndLists', [])
312328 }
313329
314330 // Invoke the callback, which can transform the transferredObject and even abort the drop.
331+ var index = getPlaceholderIndex ( ) ;
315332 if ( attr . dndDrop ) {
316- transferredObject = invokeCallback ( attr . dndDrop , event , transferredObject ) ;
333+ transferredObject = invokeCallback ( attr . dndDrop , event , index , transferredObject ) ;
317334 if ( ! transferredObject ) {
318335 return stopDragover ( ) ;
319336 }
@@ -322,8 +339,9 @@ angular.module('dndLists', [])
322339 // Retrieve the JSON array and insert the transferred object into it.
323340 var targetArray = scope . $eval ( attr . dndList ) ;
324341 scope . $apply ( function ( ) {
325- targetArray . splice ( getPlaceholderIndex ( ) , 0 , transferredObject ) ;
342+ targetArray . splice ( index , 0 , transferredObject ) ;
326343 } ) ;
344+ invokeCallback ( attr . dndInserted , event , index , transferredObject ) ;
327345
328346 // In Chrome on Windows the dropEffect will always be none...
329347 // We have to determine the actual effect manually from the allowed effects
@@ -380,6 +398,21 @@ angular.module('dndLists', [])
380398 return mousePointer < targetPosition + targetSize / 2 ;
381399 }
382400
401+ /**
402+ * Tries to find a child element that has the dndPlaceholder class set. If none was found, a
403+ * new li element is created.
404+ */
405+ function getPlaceholderElement ( ) {
406+ var placeholder ;
407+ angular . forEach ( element . children ( ) , function ( childNode ) {
408+ var child = angular . element ( childNode ) ;
409+ if ( child . hasClass ( 'dndPlaceholder' ) ) {
410+ placeholder = child ;
411+ }
412+ } ) ;
413+ return placeholder || angular . element ( "<li class='dndPlaceholder'></li>" ) ;
414+ }
415+
383416 /**
384417 * We use the position of the placeholder node to determine at which position of the array the
385418 * object needs to be inserted
@@ -426,10 +459,10 @@ angular.module('dndLists', [])
426459 /**
427460 * Invokes a callback with some interesting parameters and returns the callbacks return value.
428461 */
429- function invokeCallback ( expression , event , item ) {
462+ function invokeCallback ( expression , event , index , item ) {
430463 return $parse ( expression ) ( scope , {
431464 event : event ,
432- index : getPlaceholderIndex ( ) ,
465+ index : index ,
433466 item : item || undefined ,
434467 external : ! dndDragTypeWorkaround . isDragging ,
435468 type : dndDragTypeWorkaround . isDragging ? dndDragTypeWorkaround . dragType : undefined
@@ -451,6 +484,42 @@ angular.module('dndLists', [])
451484 } ;
452485 } ] )
453486
487+ /**
488+ * Use the dnd-nodrag attribute inside of dnd-draggable elements to prevent them from starting
489+ * drag operations. This is especially useful if you want to use input elements inside of
490+ * dnd-draggable elements or create specific handle elements.
491+ */
492+ . directive ( 'dndNodrag' , function ( ) {
493+ return function ( scope , element , attr ) {
494+ // Set as draggable so that we can cancel the events explicitly
495+ element . attr ( "draggable" , "true" ) ;
496+
497+ /**
498+ * Since the element is draggable, the browser's default operation is to drag it on dragstart.
499+ * We will prevent that and also stop the event from bubbling up.
500+ */
501+ element . on ( 'dragstart' , function ( event ) {
502+ event = event . originalEvent || event ;
503+
504+ // If a child element already reacted to dragstart and set a dataTransfer object, we will
505+ // allow that. For example, this is the case for user selections inside of input elements.
506+ if ( ! ( event . dataTransfer . types && event . dataTransfer . types . length ) ) {
507+ event . preventDefault ( ) ;
508+ }
509+ event . stopPropagation ( ) ;
510+ } ) ;
511+
512+ /**
513+ * Stop propagation of dragend events, otherwise dnd-moved might be triggered and the element
514+ * would be removed.
515+ */
516+ element . on ( 'dragend' , function ( event ) {
517+ event = event . originalEvent || event ;
518+ event . stopPropagation ( ) ;
519+ } ) ;
520+ } ;
521+ } )
522+
454523 /**
455524 * This workaround handles the fact that Internet Explorer does not support drag types other than
456525 * "Text" and "URL". That means we can not know whether the data comes from one of our elements or
0 commit comments