|
18 | 18 |
|
19 | 19 | // All valid HTML5 drop effects, in the order in which we prefer to use them.
|
20 | 20 | var ALL_EFFECTS = ['move', 'copy', 'link'];
|
21 |
| -// TODO: Update description of advanced demo. |
22 | 21 |
|
23 | 22 | /**
|
24 | 23 | * Use the dnd-draggable attribute to make your element draggable
|
|
27 | 26 | * - dnd-draggable Required attribute. The value has to be an object that represents the data
|
28 | 27 | * of the element. In case of a drag and drop operation the object will be
|
29 | 28 | * serialized and unserialized on the receiving end.
|
30 |
| - * - dnd-selected Callback that is invoked when the element was clicked but not dragged. |
31 |
| - * The original click event will be provided in the local event variable. |
32 | 29 | * - dnd-effect-allowed Use this attribute to limit the operations that can be performed. Valid
|
33 | 30 | * options are "move", "copy" and "link", as well as "all", "copyMove",
|
34 | 31 | * "copyLink" and "linkMove". The semantics of these operations are up to you
|
35 | 32 | * and have to be implemented using the callbacks described below. If you
|
36 | 33 | * allow multiple options, the user can choose between them by using the
|
37 |
| - * modifier keys and the cursor will be changed accordingly (expect for IE |
38 |
| - * and Edge, where this is not supported). |
39 |
| - TODO: list keys (Mac/Windows) |
40 |
| - TODO: link design doc |
| 34 | + * modifier keys (OS specific). The cursor will be changed accordingly, |
| 35 | + * expect for IE and Edge, where this is not supported. |
| 36 | + * - dnd-type Use this attribute if you have different kinds of items in your |
| 37 | + * application and you want to limit which items can be dropped into which |
| 38 | + * lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute |
| 39 | + * must be a lower case string. Upper case characters can be used, but will |
| 40 | + * be converted to lower case automatically. |
| 41 | + * - dnd-disable-if You can use this attribute to dynamically disable the draggability of the |
| 42 | + * element. This is useful if you have certain list items that you don't want |
| 43 | + * to be draggable, or if you want to disable drag & drop completely without |
| 44 | + * having two different code branches (e.g. only allow for admins). |
| 45 | + * |
| 46 | + * Callbacks: |
41 | 47 | * - dnd-dragstart Callback that is invoked when the element was dragged. The original
|
42 | 48 | * dragstart event will be provided in the local event variable.
|
43 | 49 | * - dnd-moved Callback that is invoked when the element was moved. Usually you will
|
|
53 | 59 | * be provided in the local event variable.
|
54 | 60 | * - dnd-dragend Callback that is invoked when the drag operation ended. Available local
|
55 | 61 | * variables are event and dropEffect.
|
56 |
| - * - dnd-type Use this attribute if you have different kinds of items in your |
57 |
| - * application and you want to limit which items can be dropped into which |
58 |
| - * lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute |
59 |
| - * must be a lower case string. Upper case characters can be used, but will |
60 |
| - * be converted to lower case automatically. |
61 |
| - * - dnd-disable-if You can use this attribute to dynamically disable the draggability of the |
62 |
| - * element. This is useful if you have certain list items that you don't want |
63 |
| - * to be draggable, or if you want to disable drag & drop completely without |
64 |
| - * having two different code branches (e.g. only allow for admins). |
| 62 | + * - dnd-selected Callback that is invoked when the element was clicked but not dragged. |
| 63 | + * The original click event will be provided in the local event variable. |
65 | 64 | *
|
66 | 65 | * CSS classes:
|
67 | 66 | * - dndDragging This class will be added to the element while the element is being
|
|
96 | 95 | if (element.attr('draggable') == 'false') return true;
|
97 | 96 |
|
98 | 97 | // Initialize global state.
|
99 |
| - dndState.dropEffect = "none"; |
100 |
| - dndState.effectAllowed = attr.dndEffectAllowed || ALL_EFFECTS[0]; |
101 | 98 | dndState.isDragging = true;
|
102 | 99 | dndState.itemType = attr.dndType && scope.$eval(attr.dndType).toLowerCase();
|
103 | 100 |
|
104 |
| - event.dataTransfer.effectAllowed = dndState.effectAllowed; // Special IE handling below. |
| 101 | + // Set the allowed drop effects. See below for special IE handling. |
| 102 | + dndState.dropEffect = "none"; |
| 103 | + dndState.effectAllowed = attr.dndEffectAllowed || ALL_EFFECTS[0]; |
| 104 | + event.dataTransfer.effectAllowed = dndState.effectAllowed; |
105 | 105 |
|
106 | 106 | // Internet Explorer and Microsoft Edge don't support custom mime types, see design doc:
|
107 | 107 | // https://github.com/marceljuenemann/angular-drag-and-drop-lists/wiki/Data-Transfer-Design
|
|
203 | 203 | * - dnd-allowed-types Optional array of allowed item types. When used, only items that had a
|
204 | 204 | * matching dnd-type attribute will be dropable. Upper case characters will
|
205 | 205 | * automatically be converted to lower case.
|
| 206 | + * - dnd-effect-allowed Optional string expression that limits the drop effects that can be |
| 207 | + * performed in the list. See dnd-effect-allowed on dnd-draggable for more |
| 208 | + * details on allowed options. The default value is all. |
206 | 209 | * - dnd-disable-if Optional boolean expresssion. When it evaluates to true, no dropping
|
207 | 210 | * into the list is possible. Note that this also disables rearranging
|
208 | 211 | * items inside the list.
|
209 | 212 | * - dnd-horizontal-list Optional boolean expresssion. When it evaluates to true, the positioning
|
210 | 213 | * algorithm will use the left and right halfs of the list items instead of
|
211 | 214 | * the upper and lower halfs.
|
| 215 | + * - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts |
| 216 | + * drops from sources outside of the current browser tab. This allows to |
| 217 | + * drag and drop accross different browser tabs. The only major browser |
| 218 | + * that does not support this is currently Microsoft Edge. |
| 219 | + * |
| 220 | + * Callbacks: |
212 | 221 | * - dnd-dragover Optional expression that is invoked when an element is dragged over the
|
213 | 222 | * list. If the expression is set, but does not return true, the element is
|
214 | 223 | * not allowed to be dropped. The following variables will be available:
|
|
217 | 226 | * - type: The dnd-type set on the dnd-draggable, or undefined if non was
|
218 | 227 | * set. Will be null for drops from external sources in IE and Edge,
|
219 | 228 | * since we don't know the type in those cases.
|
| 229 | + * - dropEffect: One of move, copy or link, see dnd-effect-allowed. |
220 | 230 | * - external: Whether the element was dragged from an external source.
|
221 | 231 | * - dnd-drop Optional expression that is invoked when an element is dropped on the
|
222 | 232 | * list. The same variables as for dnd-dragover will be available, with the
|
|
234 | 244 | * dnd-drop will be available. Note that for reorderings inside the same
|
235 | 245 | * list the old element will still be in the list due to the fact that
|
236 | 246 | * dnd-moved was not called yet.
|
237 |
| - * - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts |
238 |
| - * drops from sources outside of the current browser tab. This allows to |
239 |
| - * drag and drop accross different browser tabs. The only major browser |
240 |
| - * that does not support this is currently Microsoft Edge. |
241 | 247 | *
|
242 | 248 | * CSS classes:
|
243 | 249 | * - dndPlaceholder When an element is dragged over the list, a new placeholder child
|
|
322 | 328 | isFirstHalf ? listItemNode : listItemNode.nextSibling);
|
323 | 329 | }
|
324 | 330 | }
|
325 |
| -// TODO: design doc |
326 | 331 |
|
327 |
| -// TODO: documentation |
| 332 | + // In IE we set a fake effectAllowed in dragstart to get the correct cursor. For drops from |
| 333 | + // the same document we therefore ignore the effectAllowed passed in dataTransfer. |
328 | 334 | var ignoreDataTransfer = mimeType == MSIE_MIME_TYPE && dndState.isDragging;
|
329 | 335 | var dropEffect = getDropEffect(event, ignoreDataTransfer);
|
330 | 336 | if (dropEffect == 'none') return stopDragover();
|
331 |
| - // TODO: pass to callback |
332 | 337 |
|
333 | 338 | // At this point we invoke the callback, which still can disallow the drop.
|
334 | 339 | // We can't do this earlier because we want to pass the index of the placeholder.
|
335 |
| - if (attr.dndDragover && !invokeCallback(attr.dndDragover, event, dropEfect, itemType)) { |
| 340 | + if (attr.dndDragover && !invokeCallback(attr.dndDragover, event, dropEffect, itemType)) { |
336 | 341 | return stopDragover();
|
337 | 342 | }
|
338 | 343 |
|
339 |
| -// Set dropEffect to modify the cursor shown by the browser. Only works after preventDefault. |
| 344 | + // Set dropEffect to modify the cursor shown by the browser, unless we're in IE, where this |
| 345 | + // is not supported. This must be done after preventDefault in Firefox. |
340 | 346 | event.preventDefault();
|
341 |
| - if (!ignoreDataTransfer) { // Not for IE, because we might set the wrong dropEffect |
| 347 | + if (!ignoreDataTransfer) { |
342 | 348 | event.dataTransfer.dropEffect = dropEffect;
|
343 | 349 | }
|
344 | 350 |
|
|
378 | 384 | if (!isDropAllowed(itemType)) return stopDragover();
|
379 | 385 | }
|
380 | 386 |
|
381 |
| -// TODO: documentation |
| 387 | + // Special handling for internal IE drops, see dragover handler. |
382 | 388 | var ignoreDataTransfer = mimeType == MSIE_MIME_TYPE && dndState.isDragging;
|
383 | 389 | var dropEffect = getDropEffect(event, ignoreDataTransfer);
|
384 | 390 | if (dropEffect == 'none') return stopDragover();
|
385 |
| - if (!ignoreDataTransfer) { // Not for IE, because we might set the wrong dropEffect |
| 391 | + if (!ignoreDataTransfer) { |
386 | 392 | event.dataTransfer.dropEffect = dropEffect;
|
387 | 393 | }
|
388 |
| - // TODO: pass to callback documentaion |
389 |
| - |
390 | 394 |
|
391 | 395 | // Invoke the callback, which can transform the transferredObject and even abort the drop.
|
392 | 396 | var index = getPlaceholderIndex();
|
|
463 | 467 | return itemType && listSettings.allowedTypes.indexOf(itemType) != -1;
|
464 | 468 | }
|
465 | 469 |
|
466 |
| -// TODO: docs |
| 470 | + /** |
| 471 | + * Determines which drop effect to use for the given event. In Internet Explorer we have to |
| 472 | + * ignore the effectAllowed field on dataTransfer, since we set a fake value in dragstart. |
| 473 | + * In those cases we rely on dndState to filter effects. Read the design doc for more details: |
| 474 | + * https://github.com/marceljuenemann/angular-drag-and-drop-lists/wiki/Data-Transfer-Design |
| 475 | + */ |
467 | 476 | function getDropEffect(event, ignoreDataTransfer) {
|
468 | 477 | var effects = ALL_EFFECTS;
|
469 |
| - // don't use effectAllowed for internet explorer, otherwise we'd remove the |
470 |
| - // wrong ones. For externals we still do it though, because the above filter did not work. |
471 | 478 | if (!ignoreDataTransfer) {
|
472 | 479 | effects = filterEffects(effects, event.dataTransfer.effectAllowed);
|
473 | 480 | }
|
474 |
| - // TODO: add lots of comments |
475 | 481 | if (dndState.isDragging) {
|
476 | 482 | effects = filterEffects(effects, dndState.effectAllowed);
|
477 | 483 | }
|
478 |
| -// TODO: add to dnd-list docs |
479 | 484 | if (attr.dndEffectAllowed) {
|
480 | 485 | effects = filterEffects(effects, attr.dndEffectAllowed);
|
481 | 486 | }
|
| 487 | + // MacOS automatically filters dataTransfer.effectAllowed depending on the modifier keys, |
| 488 | + // therefore the following modifier keys will only affect other operating systems. |
482 | 489 | if (!effects.length) {
|
483 | 490 | return 'none';
|
484 | 491 | } else if (event.ctrlKey && effects.indexOf('copy') != -1) {
|
|
598 | 605 | };
|
599 | 606 | });
|
600 | 607 |
|
601 |
| -// TODO: jsdoc |
| 608 | + /** |
| 609 | + * Filters an array of drop effects using a HTML5 effectAllowed string. |
| 610 | + */ |
602 | 611 | function filterEffects(effects, effectAllowed) {
|
603 | 612 | if (effectAllowed == 'all') return effects;
|
604 | 613 | return effects.filter(function(effect) {
|
605 | 614 | return effectAllowed.toLowerCase().indexOf(effect) != -1;
|
606 | 615 | });
|
607 | 616 | }
|
608 | 617 |
|
609 |
| -// TODO: effectAllowed |
610 | 618 | /**
|
611 | 619 | * For some features we need to maintain global state. This is done here, with these fields:
|
612 | 620 | * - dropEffect: Set in dragstart to "none" and to the actual value in the drop handler. We don't
|
613 | 621 | * rely on the dropEffect passed by the browser, since there are various bugs in Chrome and
|
614 | 622 | * Safari, and Internet Explorer defaults to copy if effectAllowed is copyMove.
|
| 623 | + * - effectAllowed: Set in dragstart based on dnd-effect-allowed. This is needed for IE because |
| 624 | + * setting effectAllowed on dataTransfer might result in an undesired cursor. |
615 | 625 | * - isDragging: True between dragstart and dragend. Falsy for drops from external sources.
|
616 | 626 | * - itemType: The item type of the dragged element set via dnd-type. This is needed because IE
|
617 | 627 | * and Edge don't support custom mime types that we can use to transfer this information.
|
|
0 commit comments