bm-core-ui 2.7.0

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 (99) hide show
  1. package/CHANGELOG.md +1624 -0
  2. package/LICENSE +21 -0
  3. package/README.md +145 -0
  4. package/build/@types/index.d.ts +14078 -0
  5. package/build/BMCodeEditor/BMCodeEditor.js +707 -0
  6. package/build/BMCollectionView/BMCollectionView.js +5861 -0
  7. package/build/BMCollectionView/BMCollectionViewCell.js +688 -0
  8. package/build/BMCollectionView/BMCollectionViewFlowLayout.js +4467 -0
  9. package/build/BMCollectionView/BMCollectionViewLayout.js +830 -0
  10. package/build/BMCollectionView/BMCollectionViewLayoutAttributes.js +673 -0
  11. package/build/BMCollectionView/BMCollectionViewMasonryLayout.js +491 -0
  12. package/build/BMCollectionView/BMCollectionViewStackLayout.js +634 -0
  13. package/build/BMCollectionView/BMCollectionViewTileLayout.js +1121 -0
  14. package/build/BMCoreUI.css +2833 -0
  15. package/build/BMView/BMAttributedLabelView.js +310 -0
  16. package/build/BMView/BMLayoutConstraint_v2.5.js +1813 -0
  17. package/build/BMView/BMLayoutGuide.js +192 -0
  18. package/build/BMView/BMLayoutSizeClass.js +466 -0
  19. package/build/BMView/BMMenu.js +574 -0
  20. package/build/BMView/BMScrollView.js +247 -0
  21. package/build/BMView/BMTextField.js +512 -0
  22. package/build/BMView/BMTextFieldDelegate.js +71 -0
  23. package/build/BMView/BMView_v2.5.js +3572 -0
  24. package/build/BMView/BMViewport.js +221 -0
  25. package/build/BMViewLayoutEditor/BMLayoutEditor.js +6117 -0
  26. package/build/BMViewLayoutEditor/BMLayoutEditorConstraintSettings.js +417 -0
  27. package/build/BMViewLayoutEditor/BMLayoutEditorDelegate.js +59 -0
  28. package/build/BMViewLayoutEditor/BMLayoutEditorSettingCells.js +1653 -0
  29. package/build/BMViewLayoutEditor/BMLayoutEditorSettings.js +1480 -0
  30. package/build/BMViewLayoutEditor/BMLayoutEditorSettingsComplexCells.js +431 -0
  31. package/build/BMViewLayoutEditor/BMLayoutEditorSettingsDelegate.js +46 -0
  32. package/build/BMViewLayoutEditor/BMLayoutEditorVariablesController.js +460 -0
  33. package/build/BMViewLayoutEditor/BMLayoutEditorViewGroupSettings.js +293 -0
  34. package/build/BMViewLayoutEditor/BMLayoutEditorViewSettings.js +382 -0
  35. package/build/BMViewLayoutEditor/BMLayoutVariableProvider.js +202 -0
  36. package/build/BMWindow/BMConfirmationPopup.js +477 -0
  37. package/build/BMWindow/BMKeyboardShortcut.js +151 -0
  38. package/build/BMWindow/BMPopover/BMPopover.js +492 -0
  39. package/build/BMWindow/BMToolWindow.js +71 -0
  40. package/build/BMWindow/BMWindow.js +2000 -0
  41. package/build/Core/BMAnimationContext.js +1181 -0
  42. package/build/Core/BMColor.js +991 -0
  43. package/build/Core/BMCoreUI.js +470 -0
  44. package/build/Core/BMFunctionCollection.js +110 -0
  45. package/build/Core/BMIndexPath.js +165 -0
  46. package/build/Core/BMInset.js +138 -0
  47. package/build/Core/BMKeyPath.js +100 -0
  48. package/build/Core/BMPoint.js +291 -0
  49. package/build/Core/BMRect.js +556 -0
  50. package/build/Core/BMSize.js +137 -0
  51. package/build/iScroll/LICENSE +22 -0
  52. package/build/iScroll/iscroll-probe.js +2154 -0
  53. package/build/images/AlignBottom.png +0 -0
  54. package/build/images/AlignCenterX.png +0 -0
  55. package/build/images/AlignCenterY.png +0 -0
  56. package/build/images/AlignLeading.png +0 -0
  57. package/build/images/AlignTop.png +0 -0
  58. package/build/images/AlignTrailing.png +0 -0
  59. package/build/images/AllConstraints.png +0 -0
  60. package/build/images/BottomConstraint.png +0 -0
  61. package/build/images/CenterXConstraint.png +0 -0
  62. package/build/images/CenterYConstraint.png +0 -0
  63. package/build/images/CoreUI2.png +0 -0
  64. package/build/images/CoreUI2@2x.png +0 -0
  65. package/build/images/Desktop.png +0 -0
  66. package/build/images/DesktopMini.png +0 -0
  67. package/build/images/EqualHeight.png +0 -0
  68. package/build/images/EqualHorizontalSpacing.png +0 -0
  69. package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
  70. package/build/images/EqualVerticalSpacing.png +0 -0
  71. package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
  72. package/build/images/EqualWidth.png +0 -0
  73. package/build/images/HeightConstraint.png +0 -0
  74. package/build/images/InactiveConstraints.png +0 -0
  75. package/build/images/Layout.png +0 -0
  76. package/build/images/LayoutVariables.png +0 -0
  77. package/build/images/LeftConstraint.png +0 -0
  78. package/build/images/OwnConstraints.png +0 -0
  79. package/build/images/Phone.png +0 -0
  80. package/build/images/PhoneLandscape.png +0 -0
  81. package/build/images/PhoneLandscapeMini.png +0 -0
  82. package/build/images/PhoneMini.png +0 -0
  83. package/build/images/PhonePortrait.png +0 -0
  84. package/build/images/PhonePortraitMini.png +0 -0
  85. package/build/images/Properties.png +0 -0
  86. package/build/images/RightConstraint.png +0 -0
  87. package/build/images/SubviewConstraints.png +0 -0
  88. package/build/images/Tablet.png +0 -0
  89. package/build/images/TabletLandscape.png +0 -0
  90. package/build/images/TabletLandscapeMini.png +0 -0
  91. package/build/images/TabletMini.png +0 -0
  92. package/build/images/TabletPortrait.png +0 -0
  93. package/build/images/TabletPortraitMini.png +0 -0
  94. package/build/images/TopConstraint.png +0 -0
  95. package/build/images/WidthConstraint.png +0 -0
  96. package/build/index.js +40 -0
  97. package/lib/@types/BMCoreUI.min.d.ts +14078 -0
  98. package/lib/BMCoreUI.min.js +1 -0
  99. package/package.json +58 -0
@@ -0,0 +1,1121 @@
1
+ // @ts-check
2
+
3
+ import {YES, NO, BMExtend, BMCopyProperties} from '../Core/BMCoreUI'
4
+ import {BMInsetMake, BMInset} from '../Core/BMInset'
5
+ import {BMPointMakeWithX} from '../Core/BMPoint'
6
+ import {BMSizeMake} from '../Core/BMSize'
7
+ import {BMRectMake, BMRectMakeWithX, BMRectMakeWithOrigin} from '../Core/BMRect'
8
+ import {BMIndexPathMakeWithIndexes} from '../Core/BMIndexPath'
9
+ import {BMCollectionViewLayoutAttributesMakeForCellAtIndexPath, BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier} from './BMCollectionViewLayoutAttributes'
10
+ import {BMCollectionViewFlowLayoutSupplementaryView, BMCollectionViewTableLayoutSupplementaryView} from './BMCollectionViewFlowLayout'
11
+ import {BMCollectionViewLayout} from './BMCollectionViewLayout'
12
+
13
+ // @type BMCollectionViewTileLayoutOrientation
14
+
15
+ /**
16
+ * An enum containing the possible orientations that the tile layout can use.
17
+ */
18
+ export var BMCollectionViewTileLayoutOrientation = Object.freeze({
19
+ /**
20
+ * Indicates that the tile layout will arrange cells primarily along the horizontal axis.
21
+ * When the orientation is set to this value the tile layout will scroll horizontally and cells
22
+ * will be primarily placed into the leftmost available positions.
23
+ */
24
+ Horizontal: {},
25
+
26
+ /**
27
+ * Indicates that the tile layout will arrange cells primarily along the vertical axis.
28
+ * When the orientation is set to this value the tile layout will scroll vertically and cells
29
+ * will be primarily placed into the topmost available positions.
30
+ */
31
+ Vertical: {}
32
+ });
33
+
34
+ // @endtype
35
+
36
+ // @type BMCollectionViewTileLayout extends BMCollectionViewLayout
37
+
38
+ /**
39
+ * TBD.
40
+ *
41
+ * Collection views using the tile layout must have a delegate object that conforms to the <code>BMCollectionViewDelegateTileLayout</code> protocol.
42
+ */
43
+ export function BMCollectionViewTileLayout() {}; // <constructor>
44
+
45
+ BMCollectionViewTileLayout.prototype = BMExtend({}, BMCollectionViewLayout.prototype, {
46
+
47
+ // @override - BMCollectionViewLayout
48
+ get supportsCopying() {
49
+ return YES;
50
+ },
51
+
52
+ /**
53
+ * An optional number which represents the grid size to which all sizes will be constrained.
54
+ * If this property is set to a positive number, all size dimensions will be constrained to the closest number
55
+ * that is a multiple of this property.
56
+ */
57
+ _gridSize: 0, // <Number>
58
+ get gridSize() {
59
+ return this._gridSize;
60
+ },
61
+ set gridSize(gridSize) {
62
+ this._gridSize = gridSize || 0;
63
+ this.invalidateLayout();
64
+ },
65
+
66
+ /**
67
+ * When set to a positive number, this represents the spacing to use between cells.
68
+ */
69
+ _spacing: 0, // <Number>
70
+ get spacing() {
71
+ return this._spacing;
72
+ },
73
+ set spacing(spacing) {
74
+ this._spacing = spacing || 0;
75
+ this.invalidateLayout();
76
+ },
77
+
78
+ /**
79
+ * Determines whether or not the tile layout should generate header supplementary views or not.
80
+ */
81
+ _showsHeaders: NO, // <Boolean>
82
+
83
+ get showsHeaders() { return this._showsHeaders; },
84
+ set showsHeaders(showsHeaders) {
85
+ this._showsHeaders = showsHeaders;
86
+ this.invalidateLayout();
87
+ },
88
+
89
+ /**
90
+ * The height to use for headers. Does not have any effect if showsHeaders is set to NO.
91
+ * This attribute is always 0 if showsHeader is set to NO.
92
+ */
93
+ _headerHeight: 22, // <Number>
94
+
95
+ get headerHeight() {
96
+ return this._showsHeaders ? this._headerHeight : 0;
97
+ },
98
+
99
+ set headerHeight(headerHeight) {
100
+ this._headerHeight = headerHeight;
101
+ if (this._showsHeader) this.invalidateLayout();
102
+ },
103
+
104
+ /**
105
+ * Determines whether or not the tile layout should generate footer supplementary views or not.
106
+ */
107
+ _showsFooters: NO, // <Boolean>
108
+
109
+ get showsFooters() { return this._showsFooters; },
110
+ set showsFooters(showsFooters) {
111
+ this._showsFooters = showsFooters;
112
+ this.invalidateLayout();
113
+ },
114
+
115
+ /**
116
+ * The height to use for footers. Does not have any effect if showsFooters is set to NO.
117
+ * This attribute is always 0 if showsFooters is set to NO.
118
+ */
119
+ _footerHeight: 22, // <Number>
120
+
121
+ get footerHeight() {
122
+ return this._showsFooters? this._footerHeight : 0;
123
+ },
124
+
125
+ set footerHeight(footerHeight) {
126
+ this._footerHeight = footerHeight;
127
+ if (this._showsFooters) this.invalidateLayout();
128
+ },
129
+
130
+ /**
131
+ * The insets to apply to each section which control their distance from the collection view edges and from other sections.
132
+ */
133
+ _sectionInsets: BMInsetMake(), // <BMInset, nullResettable>
134
+
135
+ get sectionInsets() { return this._sectionInsets; },
136
+ set sectionInsets(insets) {
137
+ this._sectionInsets = insets || BMInsetMake();
138
+ this.invalidateLayout();
139
+ },
140
+
141
+ /**
142
+ * The padding from the top edge to the first item.
143
+ */
144
+ _topPadding: 0, // <Number>
145
+ get topPadding() { return this._topPadding; },
146
+ set topPadding(padding) {
147
+ this._topPadding = padding;
148
+
149
+ this.invalidateLayout();
150
+ },
151
+
152
+ /**
153
+ * The padding from the bottom edge to the last item.
154
+ */
155
+ _bottomPadding: 0, // <Number>
156
+ get bottomPadding() { return this._bottomPadding; },
157
+ set bottomPadding(padding) {
158
+ this._bottomPadding = padding;
159
+
160
+ this.invalidateLayout();
161
+ },
162
+
163
+ /**
164
+ * When set to YES, the tile layout will stick the header supplementary views to the top edge of the collection view while scrolling.
165
+ * Otherwise, the header supplementary views will scroll with the rest of the content.
166
+ */
167
+ _pinsHeadersToContentEdge: NO, // <Boolean>
168
+
169
+ get pinsHeadersToContentEdge() { return this._pinsHeadersToContentEdge; },
170
+ set pinsHeadersToContentEdge(pins) {
171
+ this._pinsHeadersToContentEdge = pins;
172
+ this.invalidateLayout();
173
+ },
174
+
175
+ /**
176
+ * When set to YES, the tile layout will stick the footer supplementary views to the bottom edge of the collection view while scrolling.
177
+ * Otherwise, the footer supplementary views will scroll with the rest of the content.
178
+ */
179
+ _pinsFootersToContentEdge: NO, // <Boolean>
180
+
181
+ get pinsFootersToContentEdge() { return this._pinsFootersToContentEdge; },
182
+ set pinsFootersToContentEdge(pins) {
183
+ this._pinsFootersToContentEdge = pins;
184
+ this.invalidateLayout();
185
+ },
186
+
187
+ /**
188
+ * The tile layout's orientation.
189
+ * For more information about the possible values, refer to the
190
+ * documentation for the <code>BMCollectionViewTileLayoutOrientation</code> enum.
191
+ */
192
+ _orientation: BMCollectionViewTileLayoutOrientation.Vertical, // <BMCollectionViewTileLayoutOrientation>
193
+ get orientation() { return this._orientation; },
194
+ set orientation(orientation) {
195
+ this._orientation = orientation;
196
+ this.invalidateLayout();
197
+ },
198
+
199
+ // @override - BMCollectionViewLayout
200
+ copy() {
201
+ let copy = new BMCollectionViewTileLayout();
202
+
203
+ copy._gridSize = this._gridSize;
204
+ copy._spacing = this._spacing;
205
+ copy._showsHeaders = this._showsHeaders;
206
+ copy._headerHeight = this._headerHeight;
207
+ copy._showsFooters = this._showsFooters;
208
+ copy._footerHeight = this._footerHeight;
209
+ copy._sectionInsets = this._sectionInsets.copy();
210
+ copy._topPadding = this._topPadding;
211
+ copy._bottomPadding = this._bottomPadding;
212
+ copy._pinsFootersToContentEdge = this._pinsFootersToContentEdge;
213
+ copy._pinsHeadersToContentEdge = this._pinsHeadersToContentEdge;
214
+ copy._orientation = this._orientation;
215
+
216
+ return copy;
217
+ },
218
+
219
+ /**
220
+ * Set to YES while the collection view is performing a layout invalidation
221
+ * in response to a bounds change.
222
+ */
223
+ _isChangingBounds: NO, // <Boolean>
224
+
225
+ /**
226
+ * Returns a size that represents the given size, constrained to this layout's grid size and maximum available space.
227
+ * This does not modify the original size.
228
+ * @param size <BMSize> A size.
229
+ * @return <BMSize> The constrained size.
230
+ */
231
+ constrainedSizeWithSize: function (size) {
232
+ return BMSizeMake(
233
+ Math.min(this.constrainedValueWithValue(size.width), this.collectionView.frame.size.width - this._sectionInsets.left - this._sectionInsets.right),
234
+ this.constrainedValueWithValue(size.height)
235
+ );
236
+ },
237
+
238
+ /**
239
+ * Returns a value that represents the multiple of this layout's grid size that is closest to the given value.
240
+ * @param value <Number> A value.
241
+ * @return <Number> The constrained value.
242
+ */
243
+ constrainedValueWithValue: function (value) {
244
+ if (this._gridSize > 1) {
245
+ var gridSize = this._gridSize;
246
+ var spacing = this._spacing;
247
+ var multiple = (value / (gridSize + spacing)) | 0;
248
+ if (multiple == 0) multiple = 1;
249
+ var constrainedValue = multiple * gridSize + (multiple - 1) * spacing;
250
+ if (value - constrainedValue > constrainedValue + gridSize + spacing - value) {
251
+ return constrainedValue + gridSize + spacing;
252
+ }
253
+ return constrainedValue;
254
+ }
255
+ return value;
256
+ },
257
+
258
+ // @override
259
+ collectionViewWillStartUpdates: function (updates) {
260
+ this.previousLayoutCache = this.layoutCache;
261
+ this.prepareLayout();
262
+ },
263
+
264
+ // @override
265
+ collectionViewDidStartUpdates: function (updates) {
266
+ this.previousLayoutCache = undefined;
267
+ },
268
+
269
+ // @override - BMCollectionViewLayout
270
+ prepareLayout: function () {
271
+ return this._prepareLayoutWithScrollbarOffset(NO);
272
+ },
273
+
274
+ /**
275
+ * Prepares the layout, optionally taking the scrollbar size into account.
276
+ */
277
+ _prepareLayoutWithScrollbarOffset: function (useOffset) {
278
+
279
+ if (this._isChangingBounds) {
280
+ // If the layout is invalidated because of scrolling, it is not required to recalculate the caches, so this request should be ignored.
281
+ return;
282
+ }
283
+
284
+ var availableWidth = this.collectionView.frame.size.width - this._sectionInsets.left - this._sectionInsets.right;
285
+
286
+ // TODO: headers, footers
287
+ var layoutCache = {};
288
+ var sectionCount = this.collectionView.numberOfSections();
289
+
290
+ this.layoutCache = layoutCache;
291
+
292
+ layoutCache.sections = new Array(sectionCount);
293
+ layoutCache.size = BMSizeMake(this.collectionView.frame.size.width, 0);
294
+
295
+ var top = this._topPadding;
296
+
297
+ for (var i = 0; i < sectionCount; i++) {
298
+ var sectionCache = {attributes: []};
299
+ layoutCache.sections[i] = sectionCache;
300
+
301
+ top += this._sectionInsets.top;
302
+
303
+ sectionCache.outerFrame = BMRectMake(this._sectionInsets.left, top, availableWidth, 0);
304
+
305
+ if (this._showsHeaders) {
306
+ sectionCache.outerFrame.size.height += this._headerHeight + this._spacing;
307
+ top += this._headerHeight + this._spacing;
308
+ var headerAttributes = BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(BMCollectionViewFlowLayoutSupplementaryView.Header, {atIndexPath: BMIndexPathMakeWithIndexes([i])});
309
+ headerAttributes.frame = sectionCache.outerFrame.copy();
310
+ headerAttributes.frame.size.height = this._headerHeight;
311
+ sectionCache.headerAttributes = headerAttributes;
312
+ }
313
+
314
+ sectionCache.frame = BMRectMake(this._sectionInsets.left, top, availableWidth, 0);
315
+
316
+ this._prepareLayoutForSection(i, {intoCache: sectionCache});
317
+ sectionCache.outerFrame.size.height += sectionCache.frame.size.height;
318
+ top = sectionCache.outerFrame.bottom;
319
+
320
+ if (this._showsFooters) {
321
+ var footerAttributes = BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(BMCollectionViewFlowLayoutSupplementaryView.Footer, {atIndexPath: BMIndexPathMakeWithIndexes([i])});
322
+ footerAttributes.frame = sectionCache.outerFrame.copy();
323
+ footerAttributes.frame.size.height = this._footerHeight;
324
+ footerAttributes.frame.origin.y = top + this._spacing;
325
+
326
+ sectionCache.footerAttributes = footerAttributes;
327
+
328
+ sectionCache.outerFrame.size.height += this._footerHeight + this._spacing;
329
+ top += this._footerHeight + this._spacing;
330
+ }
331
+
332
+ sectionCache.outerFrame.size.height += this._sectionInsets.bottom;
333
+
334
+ top = sectionCache.outerFrame.bottom;
335
+ }
336
+
337
+ layoutCache.size.height = top + this._bottomPadding;
338
+
339
+ },
340
+
341
+ /**
342
+ * Prepares the layout for a single section, optionally taking the scrollbar size into account.
343
+ * @param section <Number> The index of the section for which to prepare the layout.
344
+ * {
345
+ * @param intoCache <Object> The layout cache into which the layout will be prepared.
346
+ * }
347
+ */
348
+ _prepareLayoutForSection: function (section, args) {
349
+ var count = this.collectionView.numberOfObjectsInSectionAtIndex(section);
350
+ var cache = args.intoCache;
351
+
352
+ // Represents the bottommost extent of this section's content
353
+ var bottom = cache.frame.origin.y;
354
+ // Represents the rightmost extent of this section's content
355
+ var right = cache.frame.origin.x;
356
+
357
+ // The extents are the zones in which content may be placed
358
+ // The extents start out with a single rect encompassing the entire available collection view area
359
+ // This rect then gets split up into several rects as content is added into the collection view.
360
+ var extents = [BMRectMakeWithX(cache.frame.origin.x, {y: cache.frame.origin.y, width: cache.frame.size.width, height: Number.MAX_SAFE_INTEGER})];
361
+
362
+ for (var i = 0; i < count; i++) {
363
+ if (this.DEBUG) debugger;
364
+ var indexPath = this.collectionView.indexPathForObjectAtRow(i, {inSectionAtIndex: section});
365
+ var size = this.collectionView.delegate.collectionViewSizeForCellAtIndexPath(this.collectionView, indexPath);
366
+ size = this.constrainedSizeWithSize(size);
367
+
368
+ // After obtaining the size for each element, enumerate the available extents, selecting the topmost one
369
+ // If there are several extents with the same y origin, the leftmost one will be used
370
+ var topLeftMostExtent = undefined;
371
+
372
+ for (var j = 0; j < extents.length; j++) {
373
+ var extent = extents[j];
374
+
375
+ // Exclude extents that cannot accomodate the size of this cell
376
+ // NOTE: because the extents have 'infinite' height/width and the sizes are always constrained
377
+ // there will always be at least one extent that will fit this size
378
+ if (extent.size.width < size.width || extent.size.height < size.height) {
379
+ continue;
380
+ }
381
+
382
+ // Because the extents are always sorted so that the top-leftmost ones are the first,
383
+ // finding the first extent is sufficient and the iteration can stop
384
+ if (!topLeftMostExtent) {
385
+ topLeftMostExtent = extent;
386
+ break;
387
+ }
388
+ }
389
+
390
+ // Once the extent has been found, create the attributes and slice up the extents intersecting the newly created frame
391
+ var frame = BMRectMakeWithOrigin(BMPointMakeWithX(topLeftMostExtent.origin.x, {y: topLeftMostExtent.origin.y}), {size: size});
392
+
393
+ var attributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
394
+ attributes.frame = frame;
395
+
396
+ cache.attributes.push(attributes);
397
+
398
+ if (this.DEBUG) {
399
+ var aDiv = document.createElement('div');
400
+ BMCopyProperties(aDiv.style, {position: 'absolute', boxSizing: 'border-box',
401
+ top: frame.origin.y + 'px',
402
+ left: frame.origin.x + 'px',
403
+ width: frame.size.width + 'px',
404
+ height: frame.size.height + 'px',
405
+ boxShadow: '0px 0px 1px 1px blue'
406
+ });
407
+ ((this.collectionView._contentWrapper && this.collectionView._contentWrapper[0]) || this.collectionView._container[0]).appendChild(aDiv);
408
+ }
409
+
410
+ // Adjust the section extents as needed
411
+ if (bottom < frame.bottom) bottom = frame.bottom;
412
+ if (right < frame.right) right = frame.right;
413
+
414
+ if (this.DEBUG) debugger;
415
+
416
+ // The trimming frame is the cell's frame, expanded in all dimensions by the spacing.
417
+ // This rect is then used to trim all available extents
418
+ var trimmingFrame = frame.rectWithInset(BMInset.insetWithEqualInsets(-this._spacing));
419
+
420
+ // Slice up the extents that intersect this frame
421
+ for (var j = 0; j < extents.length; j++) {
422
+ var extent = extents[j];
423
+
424
+ // For frames that are identical to their extents, or extents that are fully contained within the rect, remove the extents entirely
425
+ if (extent.isEqualToRect(trimmingFrame) || trimmingFrame.containsRect(extent)) {
426
+ extents.splice(j, 1);
427
+ if (extent.div) extent.div.remove();
428
+ j--;
429
+ continue;
430
+ }
431
+
432
+ // For all others, slice the extents up
433
+ if (trimmingFrame.intersectsContentOfRect(extent)) {
434
+ var extentSlices = this.rectsWithDifferenceOfExtent(extent, {fromRect: trimmingFrame, spacing: this._spacing});
435
+
436
+ // Remove the extent that was just sliced up
437
+ extents.splice(j, 1);
438
+ // In debugging-mode also remove the slice's outline
439
+ if (extent.div) extent.div.remove();
440
+
441
+ var currentExtentSliceIndex = 0;
442
+ var currentExtentSlice = extentSlices[0];
443
+ // Remove duplicate extents to prevent an exponentially growing list of possible extents
444
+ // and add the extent slices to the appropriate positions
445
+ if (currentExtentSlice) for (var k = j; k < extents.length; k++) {
446
+ var currentExtent = extents[k];
447
+
448
+ // Remove the slice if it is a duplicate of an already existing slice
449
+ if (currentExtent.isEqualToRect(currentExtentSlice)) {
450
+ currentExtentSliceIndex++;
451
+ currentExtentSlice.isInvalid = YES;
452
+ currentExtentSlice = extentSlices[currentExtentSliceIndex];
453
+ if (!currentExtentSlice) break;
454
+ }
455
+
456
+ // When the current extent in the iteration is in a lower position than the
457
+ // current sliced extent, insert the sliced extent in the place of the current iteration extent
458
+ if (currentExtent.origin.y > currentExtentSlice.origin.y) {
459
+ extents.splice(k, 0, currentExtentSlice);
460
+ k++;
461
+
462
+ currentExtentSliceIndex++;
463
+ currentExtentSlice = extentSlices[currentExtentSliceIndex];
464
+ if (!currentExtentSlice) break;
465
+ }
466
+
467
+ // If the current iteration extent is in the same top position as the current sliced extent
468
+ // but its horizontal position is more towards the right than the sliced extent, insert the
469
+ // sliced extent in place of the current iteration extent
470
+ if (currentExtent.origin.y == currentExtentSlice.origin.y && currentExtent.origin.x > currentExtentSlice.origin.x) {
471
+ extents.splice(k, 0, currentExtentSlice);
472
+ k++;
473
+
474
+ currentExtentSliceIndex++;
475
+ currentExtentSlice = extentSlices[currentExtentSliceIndex];
476
+ if (!currentExtentSlice) break;
477
+ }
478
+ }
479
+
480
+ // Add the remaining extents to the end of the extents array, if there are still any extent slices after the iteration,
481
+ // then they are lower and more to the right than any of the other extents
482
+ if (currentExtentSlice) {
483
+ for (; currentExtentSliceIndex < extentSlices.length; currentExtentSliceIndex++) {
484
+ extents.push(extentSlices[currentExtentSliceIndex]);
485
+ }
486
+ }
487
+
488
+ // Restart the iteration from the beginning
489
+ j = -1;
490
+
491
+ if (this.DEBUG) for (var k = 0; k < extentSlices.length; k++) {
492
+ var div = document.createElement('div');
493
+ var dExtent = extentSlices[k];
494
+ if (dExtent.isInvalid) continue;
495
+ BMCopyProperties(div.style, {position: 'absolute', boxSizing: 'border-box',
496
+ top: dExtent.origin.y + 'px',
497
+ left: dExtent.origin.x + 'px',
498
+ width: dExtent.size.width + 'px',
499
+ height: dExtent.size.height + 'px',
500
+ boxShadow: '0px 0px 1px 1px red'
501
+ });
502
+ dExtent.div = div;
503
+
504
+ ((this.collectionView._contentWrapper && this.collectionView._contentWrapper[0]) || this.collectionView._container[0]).appendChild(div);
505
+ }
506
+ }
507
+ }
508
+
509
+ }
510
+
511
+ bottom += this._spacing + this._sectionInsets.bottom;
512
+
513
+ var offset = (cache.frame.right - right) / 2 | 0;
514
+ if (offset) {
515
+ cache.attributes = cache.attributes.map(function (attribute) { attribute.frame.origin.x += offset; return attribute });
516
+ }
517
+
518
+ // Update the section's frame according to the extents
519
+ cache.frame.size.width = right - cache.frame.origin.x;
520
+ cache.frame.size.height = bottom - cache.frame.origin.y;
521
+
522
+ },
523
+
524
+ // @override - BMCollectionViewLayout
525
+ attributesForElementsInRect: function (rect) {
526
+
527
+ // When there are no sections in the collection view, this method will always return a single instance of attributes
528
+ // for the empty supplementary view
529
+ if (this.collectionView.numberOfSections() == 0) {
530
+ var attribute = BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
531
+ BMCollectionViewTableLayoutSupplementaryView.Empty,
532
+ {atIndexPath: BMIndexPathMakeWithIndexes([0])}
533
+ );
534
+
535
+ attribute.frame = BMRectMake(0, 0, this.collectionView.frame.size.width, this.collectionView.frame.size.height);
536
+ return [attribute];
537
+ }
538
+
539
+
540
+ var sectionCount = this.layoutCache.sections.length;
541
+
542
+ var attributes = [];
543
+
544
+ // foundSections is set to NO until the first section whose outer frame intersects the visible rect is found
545
+ var foundSections = NO;
546
+ var lastSection, lastFooterAttributes;
547
+
548
+ var pinnedHeaderAttributes, lastCreatedFooterAttributes;
549
+
550
+ for (var i = 0; i < sectionCount; i++) {
551
+ var section = this.layoutCache.sections[i];
552
+
553
+ if (rect.intersectsRect(section.outerFrame)) {
554
+
555
+ var lastSection = section;
556
+
557
+ var headerAttributes;
558
+ // If the section's header frame intersects the visible rect, add it to the attributes array and retain a reference to it
559
+ // This reference will be used later if the tile layout pins headers to content edges where its position will be adjusted to fit the content edges
560
+ if (section.headerAttributes && rect.intersectsRect(section.headerAttributes.frame)) {
561
+ attributes.push(headerAttributes = section.headerAttributes.copy());
562
+
563
+ // If an incoming header intersects the pinned header attributes, displace pinned header so it doesn't cover the incoming header
564
+ if (pinnedHeaderAttributes && headerAttributes.frame.intersectsRect(pinnedHeaderAttributes.frame)) {
565
+ if (this.orientation == BMCollectionViewTileLayoutOrientation.Horizontal) {
566
+ pinnedHeaderAttributes.frame.origin.x = headerAttributes.frame.origin.x - pinnedHeaderAttributes.frame.size.width;
567
+ }
568
+ if (this.orientation == BMCollectionViewTileLayoutOrientation.Vertical) {
569
+ pinnedHeaderAttributes.frame.origin.y = headerAttributes.frame.origin.y - pinnedHeaderAttributes.frame.size.height;
570
+ }
571
+ }
572
+ }
573
+
574
+ if (!foundSections) {
575
+ foundSections = YES;
576
+ if (this.pinsHeadersToContentEdge && section.headerAttributes) {
577
+ // If the header attributes did not intersect the visible rect, add them to the attributes array now
578
+ if (!headerAttributes) {
579
+ attributes.push(headerAttributes = section.headerAttributes.copy());
580
+ }
581
+
582
+ pinnedHeaderAttributes = headerAttributes;
583
+ pinnedHeaderAttributes.style.zIndex = 1;
584
+
585
+ // Adjust the position so it appears at the edge of the collection view
586
+ headerAttributes.frame.origin.y = Math.max(this.collectionView.scrollOffset.y, headerAttributes.frame.origin.y);
587
+ headerAttributes.frame.origin.x = Math.max(this.collectionView.scrollOffset.x, headerAttributes.frame.origin.x);
588
+ }
589
+ }
590
+
591
+ var attributesLength = section.attributes.length;
592
+ for (var j = 0; j < attributesLength; j++) {
593
+ if (rect.intersectsRect(section.attributes[j].frame)) {
594
+ attributes.push(section.attributes[j]);
595
+ }
596
+ }
597
+
598
+ if (section.footerAttributes && rect.intersectsRect(section.footerAttributes.frame)) {
599
+ attributes.push(lastFooterAttributes = section.footerAttributes.copy());
600
+ lastCreatedFooterAttributes = lastFooterAttributes;
601
+ }
602
+ else {
603
+ // If this section's footer attributes do not intersect the visible rect, reset the
604
+ // lastFooterAttributes reference so that tile layout will know to add the footer attributes for this section if it is the last one
605
+ lastFooterAttributes = undefined;
606
+ }
607
+
608
+ }
609
+ else if (foundSections) {
610
+ // Break after sections no longer intersect the visible rect because sections are ordered by position in the cache
611
+ break;
612
+ }
613
+ }
614
+
615
+ if (lastSection && this.pinsFootersToContentEdge && lastSection.footerAttributes) {
616
+ // If the footer attributes did not intersect the visible rect, add them to the attributes array now
617
+ if (!lastFooterAttributes) {
618
+ attributes.push(lastFooterAttributes = lastSection.footerAttributes.copy());
619
+ }
620
+
621
+ lastFooterAttributes.style.zIndex = 1;
622
+
623
+ // Adjust the position so it appears at the edge of the collection view
624
+ lastFooterAttributes.frame.origin.y = Math.min(this.collectionView.scrollOffset.y + this.collectionView.frame.size.height - lastFooterAttributes.frame.size.height, lastFooterAttributes.frame.origin.y);
625
+ lastFooterAttributes.frame.origin.x = Math.min(this.collectionView.scrollOffset.x + this.collectionView.frame.size.width - lastFooterAttributes.frame.size.width, lastFooterAttributes.frame.origin.x);
626
+
627
+ // If an outgoing footer intersects the pinned footer's frame, adjust the pinned footer's position so it appears below the outgoing footer
628
+ if (lastCreatedFooterAttributes && lastFooterAttributes.frame.intersectsRect(lastCreatedFooterAttributes.frame)) {
629
+ if (this.orientation == BMCollectionViewTileLayoutOrientation.Horizontal) {
630
+ lastFooterAttributes.frame.origin.x = lastCreatedFooterAttributes.frame.origin.x - lastFooterAttributes.frame.size.width;
631
+ }
632
+ if (this.orientation == BMCollectionViewTileLayoutOrientation.Vertical) {
633
+ lastFooterAttributes.frame.origin.y = lastCreatedFooterAttributes.frame.origin.y - lastFooterAttributes.frame.size.height;
634
+ }
635
+ }
636
+ }
637
+
638
+ return attributes;
639
+ },
640
+
641
+ // @override - BMCollectionViewLayout
642
+ shouldInvalidateLayoutForBoundsChange: function () {
643
+ var shouldInvalidate = this._pinsHeadersToContentEdge || this._pinsFootersToContentEdge;
644
+ if (shouldInvalidate) {
645
+ this._isChangingBounds = YES;
646
+ }
647
+ return shouldInvalidate;
648
+ },
649
+
650
+ // @override - BMCollectionViewLayout
651
+ didInvalidateLayoutForBoundsChange: function () {
652
+ this._isChangingBounds = NO;
653
+ },
654
+
655
+ // @override - BMCollectionViewLayout
656
+ attributesForCellAtIndexPath: function (indexPath) {
657
+ return this.cachedAttributesForCellAtIndexPath(indexPath, {withCache: this.layoutCache});
658
+ },
659
+
660
+ cachedAttributesForCellAtIndexPath: function (indexPath, args) {
661
+ return args.withCache.sections[indexPath.section].attributes[indexPath.row];
662
+ },
663
+
664
+ initialAttributesForMovingCellFromIndexPath: function (indexPath) {
665
+ return this.cachedAttributesForCellAtIndexPath(indexPath, {withCache: this.previousLayoutCache});
666
+ },
667
+
668
+ // @override - BMCollectionViewLayout
669
+ attributesForSupplementaryViewWithIdentifier: function (identifier, args) {
670
+
671
+ // The empty supplementary view will always ever have one instance with a preset index path
672
+ if (identifier === BMCollectionViewTableLayoutSupplementaryView.Empty) {
673
+ var attribute = BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
674
+ BMCollectionViewTableLayoutSupplementaryView.Empty,
675
+ {atIndexPath: BMIndexPathMakeWithIndexes([0])}
676
+ );
677
+
678
+ attribute.frame = BMRectMake(0, 0, this.collectionView.frame.size.width, this.collectionView.frame.size.height);
679
+ return attribute;
680
+ }
681
+
682
+ var indexPath = args.atIndexPath;
683
+ if (identifier == BMCollectionViewFlowLayoutSupplementaryView.Footer) {
684
+ return this.layoutCache.sections[indexPath.section].footerAttributes;
685
+ }
686
+ else if (identifier == BMCollectionViewFlowLayoutSupplementaryView.Header) {
687
+ return this.layoutCache.sections[indexPath.section].headerAttributes;
688
+ }
689
+ },
690
+
691
+ // @override - BMCollectionViewLayout
692
+ contentSize: function () {
693
+ let size = this.layoutCache.size;
694
+
695
+ size.height = Math.max(size.height, this.collectionView.frame.size.height);
696
+
697
+ return size;
698
+ },
699
+
700
+ initialAttributesForPresentedCellAtIndexPath: function (indexPath, args) {
701
+ var attributes = args.withTargetAttributes.copy();
702
+
703
+ var frame = attributes.frame;
704
+
705
+ var slope = this.collectionView.bounds.center.slopeAngleToPoint(frame.center);
706
+ var distance = this.collectionView.bounds.center.distanceToPoint(frame.center);
707
+
708
+ frame.offset(distance * 4 * Math.cos(slope), distance * 4 * Math.sin(slope));
709
+ attributes.style.scaleX = 2;
710
+ attributes.style.scaleY = 2;
711
+ attributes.style.opacity = 0;
712
+
713
+ return attributes;
714
+ },
715
+
716
+ // @override - BMCollectionViewLayout
717
+ supplementaryViewsToInsert: function () {
718
+ var count = this.collectionView.numberOfSections();
719
+ var oldCount;
720
+ var self = this;
721
+
722
+ this.collectionView.usingOldDataSet(function () {
723
+ oldCount = self.collectionView.numberOfSections();
724
+ });
725
+
726
+ var views = [];
727
+
728
+ for (var i = oldCount; i < count; i++) {
729
+ if (this.showsHeaders) {
730
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
731
+ BMCollectionViewFlowLayoutSupplementaryView.Header,
732
+ {atIndexPath: BMIndexPathMakeWithIndexes([i])}
733
+ ));
734
+ }
735
+
736
+ if (this.showsFooters) {
737
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
738
+ BMCollectionViewFlowLayoutSupplementaryView.Footer,
739
+ {atIndexPath: BMIndexPathMakeWithIndexes([i])}
740
+ ));
741
+ }
742
+ }
743
+
744
+ if (oldCount > 0 && count == 0) {
745
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
746
+ BMCollectionViewTableLayoutSupplementaryView.Empty,
747
+ {atIndexPath: BMIndexPathMakeWithIndexes([0])}
748
+ ));
749
+ }
750
+
751
+ return views;
752
+ },
753
+
754
+ // @override - BMCollectionViewLayout
755
+ supplementaryViewsToDelete: function () {
756
+ var count = this.collectionView.numberOfSections();
757
+ var oldCount;
758
+ var self = this;
759
+
760
+ this.collectionView.usingOldDataSet(function () {
761
+ oldCount = self.collectionView.numberOfSections();
762
+ });
763
+
764
+ var views = [];
765
+
766
+ for (var i = count; i < oldCount; i++) {
767
+ if (this.showsHeaders) {
768
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
769
+ BMCollectionViewFlowLayoutSupplementaryView.Header,
770
+ {atIndexPath: BMIndexPathMakeWithIndexes([i])}
771
+ ));
772
+ }
773
+
774
+ if (this.showsFooters) {
775
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
776
+ BMCollectionViewFlowLayoutSupplementaryView.Footer,
777
+ {atIndexPath: BMIndexPathMakeWithIndexes([i])}
778
+ ));
779
+ }
780
+ }
781
+
782
+ if (oldCount == 0 && count > 0) {
783
+ views.push(BMCollectionViewLayoutAttributesMakeForSupplementaryViewWithIdentifier(
784
+ BMCollectionViewTableLayoutSupplementaryView.Empty,
785
+ {atIndexPath: BMIndexPathMakeWithIndexes([0])}
786
+ ));
787
+ }
788
+
789
+ return views;
790
+ },
791
+
792
+ /**
793
+ * Returns the extent resulting by trimming the entire left side of the given extent
794
+ * by the given rect.
795
+ * The given rect must intersect the extent from the top, left or bottom, otherwise the resulting
796
+ * extent will not be valid.
797
+ * @param extent <BMRect> The extent to trim.
798
+ * {
799
+ * @param withIntersectingRect <BMRect> The rect intersecting the extent.
800
+ * }
801
+ * @return <BMRect> The resulting extent.
802
+ */
803
+ rightExtentByTrimmingLeftSideOfExtent: function (extent, args) {
804
+ var rect = args.withIntersectingRect;
805
+
806
+ // The right trim of the extent will start at the rect's right side,
807
+ // run up to the extent's right edge and retain its vertical size and position
808
+ return BMRectMakeWithX(rect.right, {y: extent.origin.y, width: extent.right - rect.right, height: extent.height});
809
+ },
810
+
811
+ /**
812
+ * Returns the extent resulting by trimming the entire right side of the given extent
813
+ * by the given rect.
814
+ * The given rect must intersect the extent from the top, right or bottom, otherwise the resulting
815
+ * extent will not be valid.
816
+ * @param extent <BMRect> The extent to trim.
817
+ * {
818
+ * @param withIntersectingRect <BMRect> The rect intersecting the extent.
819
+ * }
820
+ * @return <BMRect> The resulting extent.
821
+ */
822
+ leftExtentByTrimmingRightSideOfExtent: function (extent, args) {
823
+ var rect = args.withIntersectingRect;
824
+
825
+ // The left trim of the extent will start at the extent's left side,
826
+ // run up to the extent's left edge and retain its vertical size and position
827
+ return BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: rect.origin.x - extent.origin.x, height: extent.height});
828
+ },
829
+
830
+ /**
831
+ * Returns the extent resulting by trimming the entire bottom side of the given extent
832
+ * by the given rect.
833
+ * The given rect must intersect the extent from the top, right or left, otherwise the resulting
834
+ * extent will not be valid.
835
+ * @param extent <BMRect> The extent to trim.
836
+ * {
837
+ * @param withIntersectingRect <BMRect> The rect intersecting the extent.
838
+ * }
839
+ * @return <BMRect> The resulting extent.
840
+ */
841
+ topExtentByTrimmingBottomSideOfExtent: function (extent, args) {
842
+ var rect = args.withIntersectingRect;
843
+
844
+ // The top trim of the extent will start at the extent's top edge,
845
+ // run up to the rect's top edge and retain its horizontal size and position
846
+ return BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: extent.size.width, height: rect.origin.y - extent.origin.y});
847
+ },
848
+
849
+ /**
850
+ * Returns the extent resulting by trimming the entire top side of the given extent
851
+ * by the given rect.
852
+ * The given rect must intersect the extent from the bottom, right or left, otherwise the resulting
853
+ * extent will not be valid.
854
+ * @param extent <BMRect> The extent to trim.
855
+ * {
856
+ * @param withIntersectingRect <BMRect> The rect intersecting the extent.
857
+ * }
858
+ * @return <BMRect> The resulting extent.
859
+ */
860
+ bottomExtentByTrimmingTopSideOfExtent: function (extent, args) {
861
+ var rect = args.withIntersectingRect;
862
+
863
+ // The top trim of the extent will start at the extent's top edge,
864
+ // run up to the rect's top edge and retain its horizontal size and position
865
+ return BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.size.width, height: extent.bottom - rect.bottom});
866
+ },
867
+
868
+ /**
869
+ * Returns an array of extent rects that represent the possible extents created after the given rect is placed within an extent.
870
+ * This will slice up the given extent and return the remaining extents. These extents will contain intersecting regions.
871
+ * The given rect must intersect the extent from the top, otherwise this function's result will not be well-defined.
872
+ * @param extent <BMRect> The extent intersected by the rect.
873
+ * {
874
+ * @param fromRect <BMRect> The rect interescting the extent.
875
+ * @param spacing <Number, nullable> Defaults to 0. Additional spacing to maintain between the rect and the extents.
876
+ * }
877
+ * @return <[BMRect]> The resulting extents.
878
+ */
879
+ rectsWithDifferenceOfExtent: function (extent, args) {
880
+ var spacing = args.spacing || 0;
881
+ var gridSize = this._gridSize;
882
+
883
+ var rect = args.fromRect;
884
+
885
+ // The are several cases handled by this method
886
+ // In all cases, it is assumed that the rect lies partially on the left, top, bottom or right edge of the extent
887
+ // or any combination of at most three edges
888
+
889
+ // Up to three extents will be generated in each case by trimming the base extent with the given rect
890
+ // If the rect intersects more than one edge of the extent, some of the resulting trimmed
891
+ // extents will be invalid, in which case they will not be included in the result
892
+
893
+ // Additionally, the resulting extents will be ordered so that the topmost and leftmost extents appear before other extents
894
+
895
+ // The case where the rect intersects the top edge of the extent
896
+ if (rect.bottom >= extent.origin.y && rect.top <= extent.origin.y) {
897
+ var resultingExtents = [];
898
+
899
+ var leftExtent = this.leftExtentByTrimmingRightSideOfExtent(extent, {withIntersectingRect: rect});
900
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
901
+
902
+ var rightExtent = this.rightExtentByTrimmingLeftSideOfExtent(extent, {withIntersectingRect: rect});
903
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
904
+
905
+ var bottomExtent = this.bottomExtentByTrimmingTopSideOfExtent(extent, {withIntersectingRect: rect});
906
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
907
+
908
+ return resultingExtents;
909
+ }
910
+
911
+ // The final case where the rect intersects the left edge of the extent
912
+ if (rect.right >= extent.origin.x && rect.origin.x <= extent.origin.x) {
913
+ var resultingExtents = [];
914
+
915
+ var topExtent = this.topExtentByTrimmingBottomSideOfExtent(extent, {withIntersectingRect: rect});
916
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
917
+
918
+ var rightExtent = this.rightExtentByTrimmingLeftSideOfExtent(extent, {withIntersectingRect: rect});
919
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
920
+
921
+ var bottomExtent = this.bottomExtentByTrimmingTopSideOfExtent(extent, {withIntersectingRect: rect});
922
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
923
+
924
+ return resultingExtents;
925
+ }
926
+
927
+ // The final case where the rect intersects the right edge of the extent
928
+ if (rect.origin.x <= extent.right && rect.right >= extent.right) {
929
+ var resultingExtents = [];
930
+
931
+ var topExtent = this.topExtentByTrimmingBottomSideOfExtent(extent, {withIntersectingRect: rect});
932
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
933
+
934
+ var leftExtent = this.leftExtentByTrimmingRightSideOfExtent(extent, {withIntersectingRect: rect});
935
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
936
+
937
+ var bottomExtent = this.bottomExtentByTrimmingTopSideOfExtent(extent, {withIntersectingRect: rect});
938
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
939
+
940
+ return resultingExtents;
941
+ }
942
+
943
+ // The case where the rect intersects the bottom edge of the extent
944
+ if (rect.origin.y <= extent.bottom && rect.bottom >= extent.bottom) {
945
+ var resultingExtents = [];
946
+
947
+ var leftExtent = this.leftExtentByTrimmingRightSideOfExtent(extent, {withIntersectingRect: rect});
948
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
949
+
950
+ var topExtent = this.topExtentByTrimmingBottomSideOfExtent(extent, {withIntersectingRect: rect});
951
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
952
+
953
+ var rightExtent = this.rightExtentByTrimmingLeftSideOfExtent(extent, {withIntersectingRect: rect});
954
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
955
+
956
+ return resultingExtents;
957
+ }
958
+
959
+ // Code below this point is deprecated
960
+ // Control shouldn't reach this point
961
+ debugger;
962
+
963
+ // First case is when the rect intersects the top-left corner of the extent
964
+ if (rect.intersectsPoint(extent.origin)) {
965
+ var resultingExtents = [];
966
+
967
+ // In this case there are two extents
968
+ // The first one runs alongside the rect's right edge
969
+ var rightExtent = BMRectMakeWithX(rect.right, {y: extent.origin.y, width: extent.right - rect.right, height: extent.height});
970
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
971
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
972
+
973
+ // The second one runs below the rect's bottom edge
974
+ var bottomExtent = BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.width, height: extent.bottom - rect.bottom});
975
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
976
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
977
+
978
+ return resultingExtents;
979
+ }
980
+ else {
981
+ // The other cases handle intersection with a single edge and none of the corners
982
+ // Rarely, however, in certain configurations it is possible that the rect will intersect the top-right edge of the extent
983
+ if (rect.intersectsPoint({x: extent.right, y: extent.origin.y})) {
984
+ // This case is similar to the first one, except that the first extent will run alongside the rect's left edge instead of the right
985
+ var resultingExtents = [];
986
+
987
+ // In this case there are two extents
988
+ // The first one runs alongside the rect's left edge
989
+ var leftExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: rect.origin.x - extent.origin.x, height: extent.height});
990
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
991
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
992
+
993
+ // The second one runs below the rect's bottom edge
994
+ var bottomExtent = BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.width, height: extent.bottom - rect.bottom});
995
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
996
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
997
+
998
+ return resultingExtents;
999
+ }
1000
+
1001
+ if (rect.intersectsPoint({x: extent.left, y: extent.bottom})) {
1002
+ // This case is similar to the first one, except that the first extent will run alongside the rect's left edge instead of the right
1003
+ var resultingExtents = [];
1004
+
1005
+ // The first one will run above the rect's top edge
1006
+ var topExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: extent.size.width, height: rect.origin.y - extent.origin.y});
1007
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1008
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
1009
+
1010
+ // In this case there are two extents
1011
+ // The first one runs alongside the rect's right edge
1012
+ var rightExtent = BMRectMakeWithX(rect.right, {y: extent.origin.y, width: extent.right - rect.right, height: extent.height});
1013
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1014
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
1015
+
1016
+ return resultingExtents;
1017
+ }
1018
+
1019
+ if (rect.intersectsPoint({x: extent.right, y: extent.bottom})) {
1020
+ // This case is similar to the first one, except that the first extent will run alongside the rect's left edge instead of the right
1021
+ var resultingExtents = [];
1022
+
1023
+ // The first one will run above the rect's top edge
1024
+ var topExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: extent.size.width, height: rect.origin.y - extent.origin.y});
1025
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1026
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
1027
+
1028
+ // In this case there are two extents
1029
+ // The first one runs alongside the rect's left edge
1030
+ var leftExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: rect.origin.x - extent.origin.x, height: extent.height});
1031
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1032
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
1033
+
1034
+ return resultingExtents;
1035
+ }
1036
+
1037
+ // The case where the rect intersects the top edge of the extent
1038
+ if (rect.bottom >= extent.origin.y && rect.top <= extent.origin.y) {
1039
+ // Three extents will be generated in this case
1040
+ var resultingExtents = [];
1041
+
1042
+ // The first one will run up to the rect's left edge
1043
+ var leftExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: rect.origin.x - extent.origin.x, height: extent.height});
1044
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1045
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
1046
+
1047
+ // The second one will start at the rect's right edge
1048
+ var rightExtent = BMRectMakeWithX(rect.right, {y: extent.origin.y, width: extent.right - rect.right, height: extent.height});
1049
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1050
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
1051
+
1052
+ // The last one runs below the rect's bottom edge
1053
+ var bottomExtent = BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.width, height: extent.bottom - rect.bottom});
1054
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1055
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
1056
+
1057
+ return resultingExtents;
1058
+ }
1059
+
1060
+ // The final case where the rect intersects the left edge of the extent
1061
+ if (rect.right >= extent.origin.x && rect.origin.x <= extent.origin.x) {
1062
+ // Three extents will be generated in this case
1063
+ var resultingExtents = [];
1064
+
1065
+ // The first one will run above the rect's top edge
1066
+ var topExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: extent.size.width, height: rect.origin.y - extent.origin.y});
1067
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1068
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
1069
+
1070
+ // The second one will start at the rect's right edge
1071
+ var rightExtent = BMRectMakeWithX(rect.right, {y: extent.origin.y, width: extent.right - rect.right, height: extent.height});
1072
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1073
+ (rightExtent.width >= gridSize) && resultingExtents.push(rightExtent);
1074
+
1075
+ // The last one runs below the rect's bottom edge
1076
+ var bottomExtent = BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.width, height: extent.bottom - rect.bottom});
1077
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1078
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
1079
+
1080
+ return resultingExtents;
1081
+ }
1082
+
1083
+ // The final case where the rect intersects the right edge of the extent
1084
+ if (rect.origin.x <= extent.right && rect.right >= extent.right) {
1085
+ // Three extents will be generated in this case
1086
+ var resultingExtents = [];
1087
+
1088
+ // The first one will run above the rect's top edge
1089
+ var topExtent = BMRectMakeWithX(extent.origin.x, {y: extent.origin.y, width: extent.size.width, height: rect.origin.y - extent.origin.y});
1090
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1091
+ (topExtent.height >= gridSize) && resultingExtents.push(topExtent);
1092
+
1093
+ // The second one will start at the rect's left edge
1094
+ var leftExtent = BMRectMakeWithX(extent.right, {y: extent.origin.y, width: rect.right - extent.right, height: extent.height});
1095
+ // This extent will have negative or zero width if the rect is as wide or wider than the extent, in that case it will be discarded
1096
+ (leftExtent.width >= gridSize) && resultingExtents.push(leftExtent);
1097
+
1098
+ // The last one runs below the rect's bottom edge
1099
+ var bottomExtent = BMRectMakeWithX(extent.origin.x, {y: rect.bottom, width: extent.width, height: extent.bottom - rect.bottom});
1100
+ // This extent will have negative or zero height if the rect is as tall or taller than the extent, in that case it will be discarded
1101
+ (bottomExtent.height >= gridSize) && resultingExtents.push(bottomExtent);
1102
+
1103
+ return resultingExtents;
1104
+ }
1105
+ }
1106
+
1107
+ // Control shouldn't reach this point
1108
+ debugger;
1109
+ }
1110
+
1111
+ });
1112
+
1113
+ /**
1114
+ * Constructs and returns a new tile layout.
1115
+ * @return <BMCollectionViewTileLayout> A tile layout.
1116
+ */
1117
+ BMCollectionViewTileLayout.tileLayout = function () {
1118
+ return new BMCollectionViewTileLayout();
1119
+ };
1120
+
1121
+ // @endtype