bm-core-ui 2.11.9-beta.2 → 2.12.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/build/@types/index.d.ts +2194 -94
  3. package/build/BMCollectionView/BMCollectionView.js +977 -196
  4. package/build/BMCollectionView/BMCollectionViewFlowLayout.js +11 -4
  5. package/build/BMCoreUI.css +133 -2
  6. package/build/BMView/BMDragSession.js +1150 -0
  7. package/build/BMView/BMDragSessionActions.js +121 -0
  8. package/build/BMView/BMDragSessionPreview.js +1631 -0
  9. package/build/BMView/BMLayoutConstraint_v2.5.js +14 -59
  10. package/build/BMView/BMView_v2.5.js +34 -3
  11. package/build/BMWindow/BMPopover/BMPopover.js +35 -20
  12. package/build/BMWindow/BMToolWindow.js +29 -1
  13. package/build/BMWindow/BMWindow.js +128 -65
  14. package/build/images/AlignBottom.png +0 -0
  15. package/build/images/AlignCenterX.png +0 -0
  16. package/build/images/AlignCenterY.png +0 -0
  17. package/build/images/AlignLeading.png +0 -0
  18. package/build/images/AlignTop.png +0 -0
  19. package/build/images/AlignTrailing.png +0 -0
  20. package/build/images/AllConstraints.png +0 -0
  21. package/build/images/BottomConstraint.png +0 -0
  22. package/build/images/CenterXConstraint.png +0 -0
  23. package/build/images/CenterYConstraint.png +0 -0
  24. package/build/images/CoreUI2.png +0 -0
  25. package/build/images/CoreUI2@2x.png +0 -0
  26. package/build/images/Desktop.png +0 -0
  27. package/build/images/DesktopMini.png +0 -0
  28. package/build/images/EqualHeight.png +0 -0
  29. package/build/images/EqualHorizontalSpacing.png +0 -0
  30. package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
  31. package/build/images/EqualVerticalSpacing.png +0 -0
  32. package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
  33. package/build/images/EqualWidth.png +0 -0
  34. package/build/images/HeightConstraint.png +0 -0
  35. package/build/images/InactiveConstraints.png +0 -0
  36. package/build/images/Layout.png +0 -0
  37. package/build/images/LayoutVariables.png +0 -0
  38. package/build/images/LeftConstraint.png +0 -0
  39. package/build/images/OwnConstraints.png +0 -0
  40. package/build/images/Phone.png +0 -0
  41. package/build/images/PhoneLandscape.png +0 -0
  42. package/build/images/PhoneLandscapeMini.png +0 -0
  43. package/build/images/PhoneMini.png +0 -0
  44. package/build/images/PhonePortrait.png +0 -0
  45. package/build/images/PhonePortraitMini.png +0 -0
  46. package/build/images/Properties.png +0 -0
  47. package/build/images/RightConstraint.png +0 -0
  48. package/build/images/SubviewConstraints.png +0 -0
  49. package/build/images/Tablet.png +0 -0
  50. package/build/images/TabletLandscape.png +0 -0
  51. package/build/images/TabletLandscapeMini.png +0 -0
  52. package/build/images/TabletMini.png +0 -0
  53. package/build/images/TabletPortrait.png +0 -0
  54. package/build/images/TabletPortraitMini.png +0 -0
  55. package/build/images/TopConstraint.png +0 -0
  56. package/build/images/WidthConstraint.png +0 -0
  57. package/build/index.js +4 -0
  58. package/lib/@types/BMCoreUI.min.d.ts +2194 -94
  59. package/lib/BMCoreUI.min.js +1 -1
  60. package/package.json +2 -2
@@ -0,0 +1,1150 @@
1
+ import { YES, NO, BMExtend } from '../Core/BMCoreUI';
2
+ import { BMPointMake } from '../Core/BMPoint';
3
+ import { BMRectMakeWithNodeFrame } from '../Core/BMRect';
4
+ import { BMDropSessionActionKind, BMDragSessionActionKind, _BMDragSessionIconMap, _BMDragSessionDropActionMap, _BMDragDropSessionAction } from './BMDragSessionActions';
5
+ import { _BMDragPreviewSet, BMDropPreview } from './BMDragSessionPreview';
6
+
7
+ // @type BMDragTransferKind
8
+
9
+ /**
10
+ * A list of constants describing what happens to source objects when they are transferred
11
+ * as part of drag session a different target than the one they were dragged from.
12
+ */
13
+ export var BMDragTransferKind = Object.freeze({ // <enum>
14
+ /**
15
+ * Indicates that the items should be moved from the source to the target. This must
16
+ * be specified by both the source and the target for the movement to take place.
17
+ */
18
+ Move: 'Move', // <enum>
19
+
20
+ /**
21
+ * Indicates that a copy of the items will be transferred to the target. If either the
22
+ * target or the source of the transfer specifies this transfer kind, a copy is performed
23
+ * regardless of the transfer kind specified by the other.
24
+ */
25
+ Copy: 'Copy', // <enum>
26
+ });
27
+
28
+ // @endtype
29
+
30
+ // @type BMDragSessionAction
31
+
32
+ /**
33
+ * An object that describes the action that should be performed for a drag session when it ends.
34
+ */
35
+ export function BMDragSessionAction() {} // <constructor>
36
+
37
+ BMDragSessionAction.prototype = BMExtend(Object.create(_BMDragDropSessionAction.prototype), {
38
+
39
+ /**
40
+ * The action to perform at the end of the drag session.
41
+ */
42
+ get action() { // <BMDragSessionActionKind>
43
+ return this._action;
44
+ },
45
+
46
+ /**
47
+ * The message to display on the drag indicator describing the action.
48
+ */
49
+ get message() {
50
+ return this._message;
51
+ },
52
+
53
+ /**
54
+ * The HTML message to display on the drag indicator describing the action.
55
+ */
56
+ get messageHTML() {
57
+ return this._messageHTML;
58
+ },
59
+
60
+ /**
61
+ * Initializes this drag action with the specified drop action kind. Optionally,
62
+ * a message and an item count override may be provided.
63
+ * @param action <BMDragSessionActionKind> The action to perform at the end of the drag session.
64
+ * {
65
+ * @param message <String, nullable> If specified, an optional message to display on the
66
+ * drag indicator describing the drop action.
67
+ * @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
68
+ * drag indicator describing the drop action. If `message` is
69
+ * also specified, this parameter is not used.
70
+ * }
71
+ * @return <BMDragAction> This drag action.
72
+ */
73
+ initWithAction(action, args) {
74
+ this._action = action;
75
+ this._message = args?.message;
76
+ if (!args?.message) {
77
+ this._messageHTML = args?.messageHTML;
78
+ }
79
+
80
+ return this;
81
+ },
82
+ });
83
+
84
+
85
+
86
+ /**
87
+ * Creates and initializes a drag action with the specified drag action kind. Optionally,
88
+ * a message and an item override may be provided.
89
+ * @param action <BMDragSessionDropActionKind> The action to perform at the end of the drag session.
90
+ * {
91
+ * @param message <String, nullable> If specified, an optional message to display on the
92
+ * drag indicator describing the drop action.
93
+ * @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
94
+ * drag indicator describing the drop action. If `message` is
95
+ * also specified, this parameter is not used.
96
+ * }
97
+ * @return <BMDragSessionAction> A drag action.
98
+ */
99
+ BMDragSessionAction.actionWithKind = function (action, args) {
100
+ return new this().initWithAction(action, args);
101
+ };
102
+
103
+ // @endtype
104
+
105
+ // @type BMDropSessionAction
106
+
107
+ /**
108
+ * An object that describes the action that should be performed for a drop session when it ends.
109
+ */
110
+ export function BMDropSessionAction() {} // <constructor>
111
+
112
+ BMDropSessionAction.prototype = BMExtend(Object.create(_BMDragDropSessionAction.prototype), {
113
+
114
+ /**
115
+ * The action to perform at the end of the drag session.
116
+ */
117
+ get action() { // <BMDragSessionActionKind>
118
+ return this._action;
119
+ },
120
+
121
+ /**
122
+ * The message to display on the drag indicator describing the action.
123
+ */
124
+ get message() {
125
+ return this._message;
126
+ },
127
+
128
+ /**
129
+ * The HTML message to display on the drag indicator describing the action.
130
+ */
131
+ get messageHTML() {
132
+ return this._messageHTML;
133
+ },
134
+
135
+ /**
136
+ * If specified when the action is `.AcceptPartially`, the drag items that are actually acceptable.
137
+ */
138
+ get acceptableItems() {
139
+ return this._acceptableItems;
140
+ },
141
+
142
+ /**
143
+ * Initializes this drop action with the specified drop action kind. Optionally,
144
+ * a message and an item count override may be provided.
145
+ * @param action <BMDropSessionActionKind> The action to perform at the end of the drop session.
146
+ * {
147
+ * @param message <String, nullable> If specified, an optional message to display on the
148
+ * drag indicator describing the drop action.
149
+ * @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
150
+ * drag indicator describing the drop action. If `message` is
151
+ * also specified, this parameter is not used.
152
+ * @param items <[BMDragItem], nullable> If specified, the items that are acceptable for the current drop target.
153
+ * Requires the `action` to be set to `.AcceptPartially` to take effect.
154
+ * These items must be part of the drag or drop session.
155
+ * }
156
+ * @return <BMDragAction> This drop action.
157
+ */
158
+ initWithAction(action, args) {
159
+ this._action = action;
160
+ this._message = args?.message;
161
+ if (!args?.message) {
162
+ this._messageHTML = args?.messageHTML;
163
+ }
164
+ if (action == BMDropSessionActionKind.AcceptPartially) {
165
+ this._acceptableItems = args?.items;
166
+ }
167
+
168
+ return this;
169
+ },
170
+ });
171
+
172
+
173
+
174
+ /**
175
+ * Creates and initializes a drop action with the specified drop action kind. Optionally,
176
+ * a message and an item override may be provided.
177
+ * @param action <BMDragSessionDropActionKind> The action to perform at the end of the drop session.
178
+ * {
179
+ * @param message <String, nullable> If specified, an optional message to display on the
180
+ * drag indicator describing the drop action.
181
+ * @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
182
+ * drag indicator describing the drop action. If `message` is
183
+ * also specified, this parameter is not used.
184
+ * @param items <[BMDragItem], nullable> If specified, the items that are acceptable for the current drop target.
185
+ * Requires the `action` to be set to `.AcceptPartially` to take effect.
186
+ * These items must be part of the drop session.
187
+ * }
188
+ * @return <BMDropSessionAction> A drop action.
189
+ */
190
+ BMDropSessionAction.actionWithKind = function (action, args) {
191
+ return new this().initWithAction(action, args);
192
+ };
193
+
194
+ // @endtype
195
+
196
+ // @type BMDragSession implements EventHandlerObject
197
+
198
+ /**
199
+ * An object that describes an in-progress drag session. Drag sessions are automatically created by
200
+ * views that begin drag operations and supplied to potential drop targets when the drag gesture
201
+ * intersects their frame.
202
+ *
203
+ * To start a drag session, use the static `beginDragWithEvent` method, passing in the starting
204
+ * event and a delegate object providing the contents of the drag session.
205
+ *
206
+ * The session can be used to obtain the items that participate in the drag gesture and to update
207
+ * the state and messaging presented to the user.
208
+ */
209
+ export function BMDragSession() {} // <constructor>
210
+
211
+ BMDragSession.prototype = {
212
+
213
+ /**
214
+ * The drag delegate of hte object that initiated this drag session.
215
+ */
216
+ _dragDelegate: undefined, // <BMDragDelegate>
217
+
218
+ /**
219
+ * The view that initiated this drag session.
220
+ */
221
+ _sourceView: undefined, // <BMView>
222
+
223
+ /**
224
+ * The items participating in this drag session.
225
+ */
226
+ _items: undefined, // [BMDragItem]
227
+
228
+ get items() {
229
+ return this._items;
230
+ },
231
+
232
+ /**
233
+ * An array of nodes corresponding to the previews of the items being dragged.
234
+ * There may be fewer previews than items, and the preview positions in this array
235
+ * correspond
236
+ */
237
+ _itemPreviews: undefined, // [DOMNode]
238
+
239
+ /**
240
+ * The node displaying the items in this drag session.
241
+ */
242
+ _itemCountNode: undefined, // <DOMNode>
243
+
244
+ /**
245
+ * The current drop action as specified by the source view.
246
+ */
247
+ _sourceDropAction: undefined, // <BMDragSessionAction>
248
+
249
+ /**
250
+ * The current drop action as specified by the target view.
251
+ */
252
+ _targetDropAction: undefined, // <BMDropSessionAction, nullable>
253
+
254
+ /**
255
+ * The outcome of ending the drag session at the current position.
256
+ */
257
+ _dropAction: BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Reject), // <_BMDragDropSessionAction>
258
+
259
+ /**
260
+ * The final drop action at the end of the session. This property is only accessible since the
261
+ * `dragSessionWillFinish` delegate method is invoked.
262
+ */
263
+ _action: undefined, // <BMDragSessionAction | BMDropSessionAction | undefined>
264
+
265
+ get action() {
266
+ return this._action;
267
+ },
268
+
269
+ /**
270
+ * The event that started this drag session.
271
+ */
272
+ _startEvent: undefined, // <MouseEvent or TouchEvent>
273
+
274
+ /**
275
+ * The touch identifier tracked for this drag session if the session
276
+ * was started via a touch event.
277
+ */
278
+ _touchIdentifier: undefined, // <Number, nullable>
279
+
280
+ /**
281
+ * The drag delegate object.
282
+ */
283
+ _delegate: undefined, // <BMDragDelegate>
284
+
285
+ /**
286
+ * Initializes this drag session with the specified initial mouse or touch event
287
+ * and drag delegate object.
288
+ * @param event <MouseEvent or TouchEvent> The initial event that starts this drag session.
289
+ * {
290
+ * @param dragDelegate <BMDragDelegate> The drag delegate object providing information about
291
+ * the items in this drag session.
292
+ * @param view <BMView> The view initiating this drag session.
293
+ * @param touchIdentifier <Number, nullable> If the event is a touch event, the identifier of the
294
+ * touch that should be tracked for the drag session.
295
+ * If not specified, the first touch will be used instead.
296
+ * }
297
+ * @return <BMDragSession> This drag session.
298
+ */
299
+ _initWithEvent(event, {dragDelegate, view, touchIdentifier}) {
300
+ this._dropSessions = new Map();
301
+
302
+ this._startEvent = event;
303
+ this._delegate = dragDelegate;
304
+ this._sourceView = view;
305
+
306
+ if ('TouchEvent' in window && event instanceof TouchEvent) {
307
+ this._touchIdentifier = touchIdentifier ?? event.changedTouches[0].identifier;
308
+ }
309
+
310
+ return this;
311
+ },
312
+
313
+ /**
314
+ * The current position of the drag session, relative to the viewport.
315
+ */
316
+ _position: BMPointMake(), // <BMPoint>
317
+
318
+ get position() {
319
+ return this._position;
320
+ },
321
+
322
+ /**
323
+ * The drag indicator displaying information about the drag and drop session.
324
+ */
325
+ _dragIndicator: undefined, // <_BMDragIndicator, nullable>
326
+
327
+ /**
328
+ * The current drop target view, if any.
329
+ */
330
+ _dropTarget: undefined, // <BMView, nullable>
331
+
332
+ /**
333
+ * A map containing the views that represent valid drop targets for this session as keys
334
+ * and the drop sessions that were created for each as the associated values.
335
+ */
336
+ _dropSessions: undefined, // <Map<BMView, BMDropSession | BMDragSession>>
337
+
338
+ /**
339
+ * For touch drag sessions, an event handler used to determine when a new drop target
340
+ * has been reached as the touch pointer moves over a new drop target.
341
+ */
342
+ _touchDropTargetHandler: undefined, // <void ^(TouchEvent), nullable>
343
+
344
+ /**
345
+ * A dictionary of event handlers that have been attached to the source view for which this
346
+ * drag session was created.
347
+ */
348
+ _eventHandlers: undefined, // <Dictionary<unknown>>
349
+
350
+ /**
351
+ * The preview set managing the drag previews.
352
+ */
353
+ _previewSet: undefined, // <_BMDragPreviewSet>
354
+
355
+ /**
356
+ * An array of drop previews that can be used to customize the drop animation. This property is only
357
+ * initialized at the end of the drag session if the delegate object implemented the
358
+ * `dragSessionRequiresCustomDropAnimationForItems` method and returned `YES` from it when it was
359
+ * invoked.
360
+ */
361
+ _dropPreviews: undefined, // <[BMDropPreview], nullable>
362
+
363
+ get dropPreviews() {
364
+ return this._dropPreviews;
365
+ },
366
+
367
+ /**
368
+ * The kind of transfer that will be performed. Only set after the `dragSessionWillFinish` delegate
369
+ * method returns if the outcome of the drag and drop gesture is transfer to a different view.
370
+ * `undefined` in all other cases.
371
+ */
372
+ _transferKind: undefined, // <BMDragTransferKind, nullable>
373
+
374
+ get transferKind() {
375
+ return this._transferKind;
376
+ },
377
+
378
+ /**
379
+ * Sets up the drag previews and indicator and appropriate event handlers and starts tracking
380
+ * a drag originating from the event with which this drag session was initialized.
381
+ */
382
+ _beginDrag() {
383
+ // Notify the delegate that this drag session started
384
+ this._delegate.dragSessionWillBegin?.(this);
385
+
386
+ const touchIdentifier = this._touchIdentifier;
387
+
388
+ // Create an indicator that shows how many items are being dragged
389
+
390
+ // Determine where the drag started from
391
+ const event = this._startEvent;
392
+ if (touchIdentifier !== undefined) {
393
+ for (var i = 0; i < event.changedTouches.length; i++) {
394
+ if (event.changedTouches[i].identifier == touchIdentifier) {
395
+ this._position = BMPointMake(
396
+ event.changedTouches[i].clientX,
397
+ event.changedTouches[i].clientY,
398
+ );
399
+ break;
400
+ }
401
+ }
402
+ }
403
+ else {
404
+ this._position = BMPointMake(event.clientX, event.clientY);
405
+ }
406
+
407
+ // Request the items that participate in this drag session
408
+ this._items = this._delegate.dragSessionInitialItems(this);
409
+
410
+ this._dropSessions.set(this._sourceView, this);
411
+
412
+ // Determine the valid drop targets for these items and create the associated
413
+ // drop session for each view that can accept them
414
+ if (this._delegate.dragSessionCanTransferItems?.(this) ?? YES) {
415
+ for (const [view, dropDelegate] of BMDragSession._dropTargets.entries()) {
416
+ // Skip over the source view if it is also a drop target
417
+ if (view == this._sourceView) {
418
+ continue;
419
+ }
420
+
421
+ const dropSession = BMDropSession._sessionForDragSession(this, {delegate: dropDelegate});
422
+ if (dropDelegate.dropSessionCanBegin(dropSession)) {
423
+ this._dropSessions.set(view, dropSession);
424
+ }
425
+ }
426
+ }
427
+
428
+
429
+ this._initDropTargetHandlers();
430
+
431
+ // Determine the offset from the center, if the initial item provides a preview source,
432
+ // otherwise default to centering the drag preview on the pointer
433
+ const firstItemPreview = this._delegate.dragSessionPreviewForItem(this, this._items[0]);
434
+ let pointerOffset = BMPointMake();
435
+ if (firstItemPreview._sourceNode) {
436
+ const sourceNodeFrame = BMRectMakeWithNodeFrame(firstItemPreview._sourceNode);
437
+ const center = sourceNodeFrame.center;
438
+ pointerOffset = BMPointMake(center.x - this._position.x, center.y - this._position.y);
439
+ }
440
+
441
+ // Obtain the drag previews for the remaining items
442
+ const previews = [firstItemPreview];
443
+ for (let i = 1; i < this._items.length && previews.length <= BMDragSession._maximumAdditionalDragPreviews; i++) {
444
+ previews.push(this._delegate.dragSessionPreviewForItem(this, this._items[i]));
445
+ }
446
+
447
+ // Create the preview set that will manage the drag previews and play the lifting animation
448
+ const previewSet = new _BMDragPreviewSet().initWithPreviews(previews, {pointerOffset});
449
+ previewSet.beginLiftAtPosition(this._position);
450
+ this._previewSet = previewSet;
451
+ this._dragIndicator = previewSet._dragIndicator;
452
+ this._dragIndicator.setAcceptableItemCount(this._items.length);
453
+
454
+ // Assume that the drag starts out within the source view and notify the delegate accordingly
455
+ this._dropTarget = this._sourceView;
456
+ this._delegate.dragSessionDidEnter?.(this);
457
+ const action = this._delegate.dragSessionDidUpdate?.(this);
458
+ if (action) {
459
+ this._sourceDropAction = action;
460
+ this._updateDropAction();
461
+ }
462
+
463
+ // Register the event that will track the movement of the pointer for this drag session
464
+ window.addEventListener(this._touchIdentifier !== undefined ? 'touchmove' : 'mousemove', this, {capture: YES, passive: NO});
465
+
466
+ // Register the event that will track the end of the gesture
467
+ window.addEventListener(this._touchIdentifier !== undefined ? 'touchend' : 'mouseup', this, {capture: YES, passive: NO});
468
+ if (this._touchIdentifier !== undefined) {
469
+ window.addEventListener('touchcancel', this, {capture: YES, passive: NO});
470
+ }
471
+ },
472
+
473
+ handleEvent(/** @type {MouseEvent | TouchEvent} */ event) {
474
+ if (event.type == 'mousemove' || event.type == 'touchmove') {
475
+ this._dragDidMoveWithEvent(event);
476
+ }
477
+ else {
478
+ this._dragDidFinishWithEvent(event);
479
+ }
480
+ },
481
+
482
+ /**
483
+ * Invoked whenever the pointer moves while this drag session is in progress.
484
+ * @param event <MouseEvent or TouchEvent> The event.
485
+ */
486
+ _dragDidMoveWithEvent(event) {
487
+ event.preventDefault();
488
+
489
+ let position;
490
+ const touchIdentifier = this._touchIdentifier;
491
+ if (touchIdentifier !== undefined) {
492
+ // Verify, when handling touch events, that the pointer assigned to the drag event did change
493
+ event.preventDefault();
494
+ for (var i = 0; i < event.changedTouches.length; i++) {
495
+ if (event.changedTouches[i].identifier == touchIdentifier) {
496
+ position = BMPointMake(
497
+ event.changedTouches[i].clientX,
498
+ event.changedTouches[i].clientY,
499
+ );
500
+ break;
501
+ }
502
+ }
503
+
504
+ // If the tracked touch did not change, ignore this touchmove event
505
+ if (i == event.changedTouches.length) {
506
+ return;
507
+ }
508
+ }
509
+ else {
510
+ position = BMPointMake(event.clientX, event.clientY);
511
+ }
512
+
513
+ this._position = position;
514
+
515
+ // Update the positions of the dragging preview
516
+ this._previewSet.setPosition(position);
517
+
518
+ // Dispatch an update to the source view, and to the current drop target, if any
519
+ // then update the drop action accordingly
520
+ const sourceAction = this._delegate.dragSessionDidUpdate?.(this);
521
+ let targetAction;
522
+
523
+ if (this._dropTarget && this._dropTarget != this._sourceView) {
524
+ const session = this._dropSessions.get(this._dropTarget);
525
+ targetAction = session._delegate.dropSessionDidUpdate?.(session);
526
+ if (targetAction) {
527
+ // If the target action is delete, the source delegate must be able to specify
528
+ // a transfer of kind move
529
+ if (targetAction._action == BMDropSessionActionKind.Delete) {
530
+ const transferKind = this._delegate.dragSessionTransferKind(this);
531
+ if (transferKind != BMDragTransferKind.Move) {
532
+ targetAction = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
533
+ }
534
+ }
535
+
536
+ session._dropAction = targetAction;
537
+ }
538
+ }
539
+
540
+ if (sourceAction) {
541
+ this._sourceDropAction = sourceAction;
542
+ }
543
+ if (targetAction) {
544
+ this._targetDropAction = targetAction;
545
+ }
546
+
547
+ if (sourceAction || targetAction) {
548
+ this._updateDropAction();
549
+ }
550
+ },
551
+
552
+ /**
553
+ * Invoked when the pointer is released or cancelled while this drag session is in progress.
554
+ * @param event <MouseEvent or TouchEvent> The event.
555
+ * @return <Promise<void>> A promise that resolves when all associated animations finish.
556
+ */
557
+ async _dragDidFinishWithEvent(event) {
558
+ event.preventDefault();
559
+
560
+ this._action = this._dropAction;
561
+
562
+ // Instruct the delegates that the session is about to end
563
+ this._delegate.dragSessionWillFinish?.(this);
564
+
565
+ for (const [view, session] of this._dropSessions.entries()) {
566
+ if (view == this._sourceView) {
567
+ continue;
568
+ }
569
+ session._delegate.dropSessionWillFinish?.(this);
570
+ }
571
+
572
+ // Clear out the event listeners
573
+ window.removeEventListener(this._touchIdentifier !== undefined ? 'touchmove' : 'mousemove', this, {capture: YES, passive: NO});
574
+ window.removeEventListener(this._touchIdentifier !== undefined ? 'touchend' : 'mouseup', this, {capture: YES, passive: NO});
575
+ if (this._touchIdentifier !== undefined) {
576
+ window.removeEventListener('touchcancel', this, {capture: YES, passive: NO});
577
+ }
578
+ this._releaseDropTargetHandlers();
579
+
580
+ // If there is a drop target and its action is not ignore, instruct the delegate to perform the action
581
+ if (this._dropTarget && this._dropTarget != this._sourceView) {
582
+ const session = this._dropSessions.get(this._dropTarget);
583
+ if ((session._dropAction?._action ?? BMDropSessionActionKind.Ignore) != BMDropSessionActionKind.Ignore) {
584
+ // Determine whether the delegate wants to handle the drop animation for the acceptable items
585
+ let acceptableItems = this._items;
586
+ if (session._dropAction._action == BMDropSessionActionKind.AcceptPartially) {
587
+ acceptableItems = session._dropAction._acceptableItems ?? acceptableItems;
588
+ }
589
+ else if (session._dropAction._action == BMDropSessionActionKind.Reject) {
590
+ acceptableItems = [];
591
+ }
592
+
593
+ // A transfer only occurs if the action is to accept or accept partially
594
+ const isTransfer = [BMDropSessionActionKind.Accept, BMDropSessionActionKind.AcceptPartially].includes(session._dropAction?._action);
595
+
596
+ // Request the transfer kind from both delegates and set it to move if both of the specify a transfer kind of move,
597
+ // defaulting to copy if either of them provides a different response
598
+ let transferKind = BMDragTransferKind.Copy;
599
+ if (isTransfer) {
600
+ const sourceTransferKind = this._delegate.dragSessionTransferKind?.(this) ?? BMDragTransferKind.Copy;
601
+ const targetTransferKind = session._delegate.dropSessionTransferKind?.(session) ?? BMDragTransferKind.Copy;
602
+
603
+ if (sourceTransferKind == BMDragTransferKind.Move && targetTransferKind == BMDragTransferKind.Move) {
604
+ transferKind = BMDragTransferKind.Move;
605
+ }
606
+ }
607
+
608
+ this._transferKind = transferKind;
609
+ session._transferKind = transferKind;
610
+
611
+ const transferItems = acceptableItems;
612
+
613
+ acceptableItems = acceptableItems.filter(i => this._previewSet._dragPreviews.get(i));
614
+ let requiresTargetDropPreviews = NO;
615
+
616
+ // Custom animations are only supported for accepted items
617
+ if (isTransfer) {
618
+ requiresTargetDropPreviews = session._delegate.dropSessionRequiresCustomDropAnimationForItems?.(session, acceptableItems.slice());
619
+ }
620
+
621
+ let requiresSourceDropPreviews = NO;
622
+ const unacceptableItems = [...this._previewSet._dragPreviews.keys()].filter(i => !acceptableItems.includes(i));
623
+ // If there are still items that are not accepted, ask the drag delegate if it performs a custom animation
624
+ if (transferItems.length != this._items.length && unacceptableItems.length) {
625
+ requiresSourceDropPreviews = this._delegate.dragSessionRequiresCustomDropAnimationForItems?.(this, unacceptableItems.slice());
626
+ }
627
+
628
+ session._dropItems = transferItems;
629
+
630
+ // If a custom animation is required, create the drop previews
631
+ if (requiresTargetDropPreviews) {
632
+ session._dropPreviews = acceptableItems.map(i => {
633
+ const dragPreview = this._previewSet._dragPreviews.get(i);
634
+ dragPreview._dropHandled = YES;
635
+ return new BMDropPreview()._initWithDragPreview(dragPreview, {forItem: i});
636
+ });
637
+ }
638
+
639
+ // If the source requires a custom animation, create the drop previews
640
+ if (requiresSourceDropPreviews) {
641
+ this._dropPreviews = unacceptableItems.map(i => {
642
+ const dragPreview = this._previewSet._dragPreviews.get(i);
643
+ dragPreview._dropHandled = YES;
644
+ return new BMDropPreview()._initWithDragPreview(dragPreview, {forItem: i});
645
+ });
646
+
647
+ this._delegate.dragSessionAnimateDropWithPreviews?.(this, this._dropPreviews);
648
+ }
649
+
650
+ // If both delegates request a move, instruct the source delegate to remove the items that will be transferred
651
+ if (transferKind == BMDragTransferKind.Move && transferItems.length) {
652
+ this._delegate.dragSessionPerformMoveForItems(this, transferItems);
653
+ }
654
+
655
+ session._delegate.dropSessionPerformDrop(session);
656
+ }
657
+ }
658
+ else {
659
+ if (this._dropAction._action == BMDragSessionActionKind.Delete) {
660
+ // If the source view has specified an action to delete, instruct it to delete the items now
661
+ this._delegate.dragSessionPerformDelete(this);
662
+ }
663
+ else {
664
+ // Otherwise ask the delegate if it wants to handle the drop animation
665
+ const items = [...this._previewSet._dragPreviews.keys()];
666
+ const requiresDropPreviews = this._delegate.dragSessionRequiresCustomDropAnimationForItems?.(this, items.slice());
667
+
668
+ if (requiresDropPreviews) {
669
+ this._delegate.dragSessionAnimateDropWithPreviews?.(this, this._dropPreviews);
670
+ }
671
+ }
672
+ }
673
+
674
+ // Perform the drop animation and detach the previews
675
+ this._previewSet.performDrop().then(() => {
676
+ // At the end of the associated animations, let the delegates know that the session
677
+ // has completely finished
678
+ this._delegate.dragSessionDidFinish?.(this);
679
+
680
+ for (const [view, session] of this._dropSessions.entries()) {
681
+ if (view == this._sourceView) {
682
+ continue;
683
+ }
684
+ session._delegate.dropSessionDidFinish?.(this);
685
+ }
686
+ });
687
+ },
688
+
689
+ /**
690
+ * Sets up the event handlers that are used to determine when the drag moves over one
691
+ * of the valid drop targets.
692
+ */
693
+ _initDropTargetHandlers() {
694
+ if (this._touchIdentifier) {
695
+ // For touch drag session, mouseover and mouseout cannot be used as there are no mouse events or equivalents for touch events
696
+ // Instead, throughout the drag operation, the pointer's position is converted into the topmost node and if that node
697
+ // is a child of any collection view, that collection view becomes the drop target
698
+ // This handler is installed globally and will affect all possible drop targets
699
+ this._touchDropTargetHandler = event => {
700
+ let clientX, clientY;
701
+ for (var i = 0; i < event.changedTouches.length; i++) {
702
+ if (event.changedTouches[i].identifier == this._touchIdentifier) {
703
+ clientX = event.changedTouches[i].clientX;
704
+ clientY = event.changedTouches[i].clientY;
705
+ break;
706
+ }
707
+ }
708
+
709
+ // If the tracked touch did not change, ignore this touchmove event
710
+ if (i == event.changedTouches.length) return NO;
711
+
712
+ // Get the node corresponding to the current point
713
+ let node = document.elementFromPoint(clientX, clientY);
714
+
715
+ // Check if that node belongs to one of the drop targets
716
+ let target;
717
+ if (node) {
718
+ for (const view of this._dropSessions.keys()) {
719
+ if (view.node.contains(node)) {
720
+ target = view;
721
+ break;
722
+ }
723
+ }
724
+ }
725
+
726
+ // Update the drop target based on what was discovered
727
+ if (target != this._dropTarget) {
728
+ this._setDropTarget(target);
729
+ }
730
+ };
731
+
732
+ window.addEventListener('touchmove', this._touchDropTargetHandler, {capture: YES, passive: NO});
733
+ }
734
+ else {
735
+ // For mouse drag sessions, use "mouseenter" and "mouseleave" to determine when the drop target changes
736
+ for (const [view, session] of this._dropSessions.entries()) {
737
+ session._eventHandlers = {
738
+ mouseenter: event => {
739
+ this._setDropTarget(view);
740
+
741
+ event.preventDefault();
742
+ event.stopPropagation();
743
+ return NO;
744
+ },
745
+
746
+ mouseleave: event => {
747
+ this._setDropTarget(undefined);
748
+
749
+ event.preventDefault();
750
+ event.stopPropagation();
751
+ return NO;
752
+ }
753
+ };
754
+
755
+ for (const key in session._eventHandlers) {
756
+ view.node.addEventListener(key, session._eventHandlers[key]);
757
+ }
758
+ }
759
+ }
760
+ },
761
+
762
+ /**
763
+ * Removes the event handlers used to determine the drop target that have been set up for this drag session.
764
+ */
765
+ _releaseDropTargetHandlers() {
766
+ if (this._touchIdentifier) {
767
+ window.removeEventListener('touchmove', this._touchDropTargetHandler, {capture: YES, passive: NO});
768
+ }
769
+ else {
770
+ for (const [view, session] of this._dropSessions.entries()) {
771
+ for (const key in session._eventHandlers) {
772
+ view.node.removeEventListener(key, session._eventHandlers[key]);
773
+ }
774
+ }
775
+ }
776
+ },
777
+
778
+ /**
779
+ * An identifier for the timeout registered to update the item previews after the
780
+ * drag gesture moves over a new drop target.
781
+ */
782
+ _itemPreviewsUpdateIdentifier: undefined, // <Number, nullable>
783
+
784
+ /**
785
+ * Updates the drop target, invoking the appropriate method on the delegate objects.
786
+ * @param target <BMView, nullable> The current drop target, or `undefined` if the drag gesture
787
+ * is not currently over any drop target.
788
+ */
789
+ _setDropTarget(target) {
790
+ if (target == this._dropTarget) {
791
+ // Don't take any action if the target doesn't actually change
792
+ return;
793
+ }
794
+
795
+ // Let the delegate of the current drop target know that the drag exited its bounds
796
+ if (this._dropTarget) {
797
+ if (this._dropTarget == this._sourceView) {
798
+ this._delegate.dragSessionDidExit?.(this);
799
+ this._targetDropAction = undefined;
800
+ }
801
+ else {
802
+ const session = this._dropSessions.get(this._dropTarget);
803
+ session._delegate.dropSessionDidExit?.(session);
804
+ }
805
+ }
806
+
807
+ // Let the delegate of the new drop target know that the drag entered its bounds
808
+ // Also dispatch a movement update to the delegate and obtain the drop outcome
809
+ this._dropTarget = target;
810
+ if (target) {
811
+ if (target == this._sourceView) {
812
+ this._delegate.dragSessionDidEnter?.(this);
813
+
814
+ const action = this._delegate.dragSessionDidUpdate?.(this);
815
+ if (action) {
816
+ this._sourceDropAction = action;
817
+ }
818
+ }
819
+ else {
820
+ const session = this._dropSessions.get(target);
821
+ session._delegate.dropSessionDidEnter?.(session);
822
+
823
+ let action = session._delegate.dropSessionDidUpdate?.(this) ?? BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
824
+
825
+ // If the target action is delete, the source delegate must be able to specify
826
+ // a transfer of kind move
827
+ if (action._action == BMDropSessionActionKind.Delete) {
828
+ const transferKind = this._delegate.dragSessionTransferKind(this);
829
+ if (transferKind != BMDragTransferKind.Move) {
830
+ action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
831
+ }
832
+ }
833
+
834
+ this._targetDropAction = action;
835
+ session._dropAction = action;
836
+
837
+ const sourceAction = this._delegate.dragSessionDidUpdate?.(this);
838
+ if (action) {
839
+ this._sourceDropAction = sourceAction;
840
+ }
841
+
842
+ this._updateDropAction();
843
+ }
844
+ }
845
+ else {
846
+ this._targetDropAction = undefined;
847
+ this._updateDropAction();
848
+ }
849
+
850
+ // If an item preview update was already registered, don't take any further action
851
+ if (this._itemPreviewsUpdateIdentifier) {
852
+ return;
853
+ }
854
+
855
+ // Else register an item preview update after a short delay to account for situations where
856
+ // the gesture immediately exits and enters a new drop target
857
+ this._itemPreviewsUpdateIdentifier = setTimeout(() => {
858
+ this._itemPreviewsUpdateIdentifier = undefined;
859
+
860
+ const newPreviews = new Map();
861
+ if (this._dropTarget && this._dropTarget != this._sourceView) {
862
+ // If there is a valid drop target, request item previews and apply them
863
+ const session = this._dropSessions.get(this._dropTarget);
864
+ for (const item of this._previewSet._baseDragPreviews.keys()) {
865
+ const preview = session._delegate.dropSessionPreviewForItem?.(session, item);
866
+ newPreviews.set(item, preview);
867
+ }
868
+ }
869
+ else {
870
+ // Otherwise reset the previews
871
+ for (const item of this._previewSet._baseDragPreviews.keys()) {
872
+ newPreviews.set(item, undefined);
873
+ }
874
+ }
875
+
876
+ this._previewSet.updatePreviewsWithMap(newPreviews);
877
+ }, 16);
878
+ },
879
+
880
+ /**
881
+ * Updates the drop action displayed by this drag session, based on the current
882
+ * source and target drop actions.
883
+ */
884
+ _updateDropAction() {
885
+ let action;
886
+ // If the target specifies a drop action that is not ignore, display it
887
+ if (this._targetDropAction && this._targetDropAction._action != BMDropSessionActionKind.Ignore) {
888
+ action = this._targetDropAction;
889
+ }
890
+ else {
891
+ action = this._sourceDropAction ?? BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Ignore);
892
+ }
893
+
894
+ if (action == this._dropAction) {
895
+ return;
896
+ }
897
+
898
+ this._dropAction = action;
899
+
900
+ if (action._action == BMDropSessionActionKind.AcceptPartially && action._acceptableItems) {
901
+ if (action._acceptableItems.length == 0) {
902
+ // If the action is specified as accept partially but all items are rejected, interpret
903
+ // the action as rejecting the drop
904
+ action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Reject, {
905
+ message: action._message,
906
+ messageHTML: action._messageHTML,
907
+ });
908
+ this._dropAction = action;
909
+
910
+ this._dragIndicator.setAcceptableItemCount(this._items.length);
911
+ this._previewSet.setRejectedItems(undefined);
912
+ }
913
+ else if (action._acceptableItems.length == this._items.length) {
914
+ // If the action is specified as accept partially but all items are accepted, interpret
915
+ // the action as accepting the drop
916
+ action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Accept, {
917
+ message: action._message,
918
+ messageHTML: action._messageHTML,
919
+ });
920
+ this._dropAction = action;
921
+
922
+ this._dragIndicator.setAcceptableItemCount(this._items.length);
923
+ this._previewSet.setRejectedItems(undefined);
924
+ }
925
+ else {
926
+ this._dragIndicator.setAcceptableItemCount(action._acceptableItems.length);
927
+
928
+ // If all items with previews are rejected, request and display an additional preview for
929
+ // the first acceptable item
930
+ let requiresAdditionalPreview = YES;
931
+ const rejectedItems = this._items.filter((item, index) => {
932
+ const rejected = !action._acceptableItems.includes(item);
933
+
934
+ if (!rejected && index < BMDragSession._maximumAdditionalDragPreviews + 1) {
935
+ requiresAdditionalPreview = NO;
936
+ }
937
+
938
+ return rejected;
939
+ });
940
+
941
+ // If an additional preview is required, obtain and display it, otherwise clear any current additional preview
942
+ if (requiresAdditionalPreview) {
943
+ const item = action._acceptableItems[0];
944
+
945
+ if (this._previewSet._additionalPreview?._dragItem != item) {
946
+ // If the preview set is not already displaying an additional preview for this item, update it
947
+ if (!this._dropTarget || this._dropTarget == this._sourceView) {
948
+ const preview = this._delegate.dragSessionPreviewForItem(this, item);
949
+ this._previewSet.setAdditionalPreview(preview);
950
+ }
951
+ else {
952
+ const session = this._dropSessions.get(this._dropTarget);
953
+ let preview = session._delegate.dropSessionPreviewForItem?.(session, item);
954
+ preview ??= this._delegate.dragSessionPreviewForItem(this, item);
955
+ this._previewSet.setAdditionalPreview(preview);
956
+ }
957
+ }
958
+ }
959
+ else {
960
+ this._previewSet.setAdditionalPreview(undefined);
961
+ }
962
+
963
+ this._previewSet.setRejectedItems(rejectedItems);
964
+ }
965
+ }
966
+ else {
967
+ this._dragIndicator.setAcceptableItemCount(this._items.length);
968
+ this._previewSet.setRejectedItems(undefined);
969
+ this._previewSet.setAdditionalPreview(undefined);
970
+ }
971
+
972
+ this._dragIndicator.setAction(action);
973
+ },
974
+
975
+ }
976
+
977
+ /**
978
+ * Starts a drag and drop session from the specified mouse or touch event.
979
+ * using a delegate to supply information about the items that are being transferred.
980
+ * @param event <MouseEvent or TouchEvent> The event starting the drag.
981
+ * {
982
+ * @param dragDelegate <BMDragDelegate> The delegate object providing information about the
983
+ * items being dragged.
984
+ * @param view <BMView> The view initiating this drag session.
985
+ * @param touchIdentifier <Number, nullable> If the event is a touch event, the identifier of the
986
+ * touch that should be tracked for the drag session.
987
+ * If not specified, the first touch will be used instead.
988
+ * }
989
+ */
990
+ BMDragSession._beginDragWithEvent = function (event, args) {
991
+ // Create and initialize a drag session for the specified event
992
+ // The drag session object will handle the events after creation
993
+ const session = new BMDragSession()._initWithEvent(event, args);
994
+ session._beginDrag();
995
+ };
996
+
997
+ // The maximum number of additional drag previews for multi-item drag sessions that can be displayed
998
+ // in addition to the drag preview of the first item.
999
+ BMDragSession._maximumAdditionalDragPreviews = 10; // <Number>
1000
+
1001
+ /**
1002
+ * Sets the maximum number of additional drag previews for multi-item drag sessions that can be displayed
1003
+ * in addition to the drag preview of the first item. Updating this value only affects drag sessions
1004
+ * started after this method returns.
1005
+ * @param max <Number> The new maximum number of previews to display.
1006
+ */
1007
+ BMDragSession.setMaximumAdditionalDragPreviews = function (max) {
1008
+ BMDragSession._maximumAdditionalDragPreviews = max;
1009
+ };
1010
+
1011
+ /**
1012
+ * A map containing views that are drop targets as keys and their associated drop delegate objects as values.
1013
+ */
1014
+ BMDragSession._dropTargets = new Map();
1015
+
1016
+ /**
1017
+ * Registers a view as a potential drop target for future drag sessions, using the specified delegate
1018
+ * to handle updates to the associated drop sessions and the drop actions.
1019
+ *
1020
+ * If the view is already registered as a drop target, this will replace the delegate handling future
1021
+ * drop sessions for the view with the specified object.
1022
+ * @param target <BMView> The view that will act as a drop target.
1023
+ * {
1024
+ * @param delegate <BMDropDelegate> The delegate that will handle actions and events from the drop
1025
+ * session on behalf of the target view.
1026
+ * }
1027
+ */
1028
+ BMDragSession.registerDropTarget = function (target, args) {
1029
+ this._dropTargets.set(target, args.delegate);
1030
+ };
1031
+
1032
+ /**
1033
+ * Unregisters a view as a potential drop target for future drag sessions. This has no effect on any
1034
+ * in-progress drop sessions which will continue using the previously registered delegate to handle
1035
+ * events and actions.
1036
+ *
1037
+ * This method has no effect if the view is not registered as a drop target.
1038
+ * @param target <BMView> The view that was previously registered as a drop target.
1039
+ */
1040
+ BMDragSession.unregisterDropTarget = function (target) {
1041
+ this._dropTargets.delete(target);
1042
+ };
1043
+
1044
+ // @endtype
1045
+
1046
+ // @type BMDropSession
1047
+
1048
+ function BMDropSession() {} // <constructor>
1049
+
1050
+ BMDropSession.prototype = {
1051
+
1052
+ /**
1053
+ * The drag session managing the drag gesture for which this drop session was created.
1054
+ */
1055
+ _dragSession: undefined, // <BMDragSession>
1056
+
1057
+ /**
1058
+ * The drop delegate specifying the behaviour of the drop over this drop target.
1059
+ */
1060
+ _delegate: undefined, // <BMDropDelegate>
1061
+
1062
+ /**
1063
+ * A dictionary of event handlers that have been attached to the drop target view for which this
1064
+ * drop session was created.
1065
+ */
1066
+ _eventHandlers: undefined, // <Dictionary<unknown>>
1067
+
1068
+ /**
1069
+ * The current drop action.
1070
+ */
1071
+ _dropAction: BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore), // <BMDropSessionAction>
1072
+
1073
+ get dropAction() {
1074
+ return this._dropAction;
1075
+ },
1076
+
1077
+ /**
1078
+ * An array of drop previews that can be used to customize the drop animation. This property is only
1079
+ * initialized at the end of accepted drop sessions if the delegate object implemented the
1080
+ * `dropSessionRequiresCustomDropAnimationForItems` method and returned `YES` from it.
1081
+ */
1082
+ _dropPreviews: undefined, // <[BMDropPreview], nullable>
1083
+
1084
+ get dropPreviews() {
1085
+ return this._dropPreviews;
1086
+ },
1087
+
1088
+ /**
1089
+ * The kind of transfer that will be performed. Only set before the `dropSessionPerformDrop` delegate
1090
+ * method is invoked if the outcome of this drop session is a transfer of items into the target view.
1091
+ */
1092
+ _transferKind: undefined, // <BMDragTransferKind, nullable>
1093
+
1094
+ get transferKind() {
1095
+ return this._transferKind;
1096
+ },
1097
+
1098
+ /**
1099
+ * Initializes this drop session with the specified drag session and the drop delegate.
1100
+ * @param session <BMDragSession> The drag session associated with this drop session
1101
+ * {
1102
+ * @param delegate <BMDropDelegate> The drop delegate used to determine the outcome of the drop.
1103
+ * }
1104
+ * @returns <BMDropSession> This drop session.
1105
+ */
1106
+ _initWithDragSession(session, {delegate}) {
1107
+ this._dragSession = session;
1108
+ this._delegate = delegate;
1109
+ return this;
1110
+ },
1111
+
1112
+ /**
1113
+ * The current position of the drag and drop gesture.
1114
+ */
1115
+ get position() { // <BMPoint>
1116
+ return this._dragSession.position;
1117
+ },
1118
+
1119
+ /**
1120
+ * The items being transferred in the drag and drop gesture.
1121
+ */
1122
+ get items() { // <[BMDragItem]>
1123
+ return this._dragSession.items;
1124
+ },
1125
+
1126
+ /**
1127
+ * The items that have been accepted through this drop session. This property is only initialized
1128
+ * before the `dropSessionPerformDrop` delegate method is invoked.
1129
+ */
1130
+ _dropItems: undefined, // <[BMDragItem], nullable>
1131
+
1132
+ get dropItems() {
1133
+ return this._dropItems;
1134
+ },
1135
+
1136
+ };
1137
+
1138
+ /**
1139
+ * Creates and returns a drop session initialized with the specified drag session and the drop delegate.
1140
+ * @param session <BMDragSession> The drag session associated with the drop session
1141
+ * {
1142
+ * @param delegate <BMDropDelegate> The drop delegate used to determine the outcome of the drop.
1143
+ * }
1144
+ * @returns <BMDropSession> A drop session.
1145
+ */
1146
+ BMDropSession._sessionForDragSession = function (session, args) {
1147
+ return new BMDropSession()._initWithDragSession(session, args);
1148
+ };
1149
+
1150
+ // @endtype