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,634 @@
1
+ // @ts-check
2
+
3
+ import {YES, NO, BMExtend, BMNumberByInterpolatingNumbersWithFraction, BMScrollingDirectionVertical} from '../Core/BMCoreUI'
4
+ import {BMInsetMake} from '../Core/BMInset'
5
+ import {BMPointMake} from '../Core/BMPoint'
6
+ import {BMSizeMake} from '../Core/BMSize'
7
+ import {BMRectMakeWithX} from '../Core/BMRect'
8
+ import {BMCollectionViewLayoutAttributesMakeForCellAtIndexPath} from './BMCollectionViewLayoutAttributes'
9
+ import {BMCollectionViewLayout} from './BMCollectionViewLayout'
10
+
11
+ // @type BMCollectionViewStackLayout extends BMCollectionViewLayout
12
+
13
+ /**
14
+ * The stack layout is a vertically scrolling layout that presents cells as a stack, where the current cell appears above the other cells.
15
+ * In the stack layout, previous cells appear behind the current cell, while upcoming cells are hidden.
16
+ *
17
+ * When scrolling in the stack layout, the scroll position will always snap back to fully show a single cell.
18
+ *
19
+ * The stack layout does not support sections or supplementary views.
20
+ */
21
+ export function BMCollectionViewStackLayout() {}; // <constructor>
22
+
23
+ BMCollectionViewStackLayout.prototype = BMExtend({}, BMCollectionViewLayout.prototype, {
24
+
25
+ // @override - BMCollectionViewLayout
26
+ get supportsCopying() {
27
+ return YES;
28
+ },
29
+
30
+ /**
31
+ * Controls how the stack layout will appear when there is a single cell in the data set.
32
+ * When this property is set to YES, insets are ignored when there is a single cell and that cell will occupy
33
+ * the collection view's entire area.
34
+ * If it is set to NO, the single cell will have the usual insets applied to it.
35
+ */
36
+ _showsSingleCellFullScreen: NO, // <Boolean>
37
+
38
+ get showsSingleCellFullScreen() { return this._showsSingleCellFullScreen; },
39
+ set showsSingleCellFullScreen(shows) {
40
+ this._showsSingleCellFullScreen = shows;
41
+ this.invalidateLayout();
42
+ },
43
+
44
+ /**
45
+ * When enabled, stack layout will only show the first cell in the data set, without any padding.
46
+ */
47
+ _showsSingleCell: NO, // <Boolean>
48
+
49
+ get showsSingleCell() { return this._showsSingleCell; },
50
+ set showsSingleCell(shows) {
51
+ this._showsSingleCell = shows;
52
+ this.invalidateLayout();
53
+ },
54
+
55
+ /**
56
+ * Controls the edges between the content and the collection view.
57
+ */
58
+ _insets: BMInsetMake(88, 88, 88, 88), // <BMInset>
59
+
60
+ get insets() { return this._insets.copy(); },
61
+ set insets(insets) {
62
+ this._insets = insets.copy();
63
+ this.invalidateLayout();
64
+ },
65
+
66
+ /**
67
+ * Controls the spread between background cells.
68
+ */
69
+ _spread: 22, // <Number>
70
+
71
+ get spread() { return this._spread; },
72
+ set spread(spread) {
73
+ this._spread = spread;
74
+ this.invalidateLayout();
75
+ },
76
+
77
+ /**
78
+ * Controls how many background cells will be shown.
79
+ */
80
+ _numberOfBackgroundCells: 3, // <Number>
81
+
82
+ get numberOfBackgroundCells() { return this._numberOfBackgroundCells; },
83
+ set numberOfBackgroundCells(number) {
84
+ this._numberOfBackgroundCells = number;
85
+ this.invalidateLayout();
86
+ },
87
+
88
+ /**
89
+ * Controls the minimum scale for background cells.
90
+ */
91
+ _minimumScale: .9, // <Number>
92
+
93
+ get minimumScale() { return this._minimumScale; },
94
+ set minimumScale(scale) {
95
+ this._minimumScale = scale;
96
+ this.invalidateLayout();
97
+ },
98
+
99
+ /**
100
+ * Controls whether cells behind the topmost cell are blurred.
101
+ */
102
+ _blursBackgroundCells: NO, // <Boolean>
103
+
104
+ get blursBackgroundCells() { return this._blursBackgroundCells; },
105
+ set blursBackgroundCells(blurs) {
106
+ this._blursBackgroundCells = blurs;
107
+ this.invalidateLayout();
108
+ },
109
+
110
+ /**
111
+ * Controls the maximum blur of background cells.
112
+ */
113
+ _maximumBlur: 15, // <Number>
114
+
115
+ get maximumBlur() { return this._maximumBlur; },
116
+ set maximumBlur(blur) {
117
+ this._maximumBlur = blur;
118
+ this.invalidateLayout();
119
+ },
120
+
121
+ /**
122
+ * Controls whether new cells scroll in with a rotation effect.
123
+ */
124
+ _rotatesCells: NO, // <Boolean>
125
+
126
+ get rotatesCells() { return this._rotatesCells; },
127
+ set rotatesCells(rotates) {
128
+ this._rotatesCells = rotates;
129
+ this.invalidateLayout();
130
+ },
131
+
132
+ /**
133
+ * Controls the maximum rotation of cells, in degrees.
134
+ */
135
+ _rotation: -90, // <Number>
136
+
137
+ get rotation() { return this._rotation; },
138
+ set rotation(rotation) {
139
+ this._rotation = rotation;
140
+ this.invalidateLayout();
141
+ },
142
+
143
+ /**
144
+ * Cached number of items in the current data set.
145
+ */
146
+ _numberOfObjects: 0, // <Number>
147
+
148
+ // @override - BMCollectionViewLayout
149
+ shouldInvalidateLayoutForFrameChange: function () {
150
+ return YES;
151
+ },
152
+
153
+ // @override - BMCollectionViewLayout
154
+ shouldInvalidateLayoutForBoundsChange: function () {
155
+ return YES;
156
+ },
157
+
158
+ // @override - BMCollectionViewLayout
159
+ collectionViewWillStartUpdates: function () {
160
+ this.prepareLayout();
161
+ },
162
+
163
+ prepareLayout: function () {
164
+ var numberOfObjects = 0;
165
+ var numberOfSections = this.collectionView.numberOfSections();
166
+
167
+ for (var i = 0; i < numberOfSections; i++) {
168
+ numberOfObjects += this.collectionView.numberOfObjectsInSectionAtIndex(i);
169
+ }
170
+
171
+ this._numberOfObjects = numberOfObjects;
172
+
173
+ },
174
+
175
+ contentSize: function () {
176
+ if (this._showsSingleCell) {
177
+ return this.collectionView.frame.size.copy();
178
+ }
179
+ return BMSizeMake(this.collectionView.frame.size.width, this.collectionView.frame.size.height * this._numberOfObjects);
180
+ },
181
+
182
+ /**
183
+ * Returns the index path that appears before the given index path in the data set.
184
+ * If the given index path is the first index path in the data set, the result will be undefined.
185
+ * @param indexPath <BMIndexPath> The current index path.
186
+ * @return <BMIndexPath> The previous index path if it could be found, undefiend otherwise.
187
+ */
188
+ previousIndexPathWithIndexPath: function (indexPath) {
189
+ if (indexPath.row) {
190
+ return this.collectionView.indexPathForObjectAtRow(indexPath.row - 1, {inSectionAtIndex: indexPath.section});
191
+ }
192
+
193
+ var section = indexPath.section - 1;
194
+ while (section >= 0) {
195
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(section);
196
+
197
+ if (numberOfObjects) {
198
+ return this.collectionView.indexPathForObjectAtRow(numberOfObjects - 1, {inSectionAtIndex: section});
199
+ }
200
+
201
+ section--;
202
+ }
203
+ },
204
+
205
+ /**
206
+ * Returns the index path that appears after the given index path in the data set.
207
+ * If the given index path is the last index path in the data set, the result will be undefined.
208
+ * @param indexPath <BMIndexPath> The current index path.
209
+ * @return <BMIndexPath> The next index path if it could be found, undefiend otherwise.
210
+ */
211
+ nextIndexPathWithIndexPath: function (indexPath) {
212
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(indexPath.section);
213
+ if (indexPath.row < numberOfObjects - 1) {
214
+ return this.collectionView.indexPathForObjectAtRow(indexPath.row + 1, {inSectionAtIndex: indexPath.section});
215
+ }
216
+
217
+ var numberOfSections = this.collectionView.numberOfSections();
218
+ var section = indexPath.section + 1;
219
+ while (section < numberOfSections) {
220
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(section);
221
+
222
+ if (numberOfObjects) {
223
+ return this.collectionView.indexPathForObjectAtRow(0, {inSectionAtIndex: section});
224
+ }
225
+
226
+ section++;
227
+ }
228
+ },
229
+
230
+ // @override - BMCollectionViewLayout
231
+ attributesForElementsInRect: function (rect) {
232
+ if (this._showsSingleCell) {
233
+ var attributes = [];
234
+
235
+ if (this.collectionView.numberOfSections() && this.collectionView.numberOfObjectsInSectionAtIndex(0)) {
236
+ var attribute = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(
237
+ this.collectionView.indexPathForObjectAtRow(0, {inSectionAtIndex: 0})
238
+ );
239
+
240
+ attribute.frame = this.collectionView.frame.copy();
241
+ attribute.frame.origin = BMPointMake();
242
+
243
+ attributes.push(attribute);
244
+ }
245
+
246
+ return attributes;
247
+ }
248
+
249
+ var scrollOffset = this.collectionView.scrollOffset;
250
+ var collectionViewHeight = this.collectionView.frame.size.height;
251
+ var collectionViewWidth = this.collectionView.frame.size.width;
252
+
253
+ var topVisibleMargin = scrollOffset.y;
254
+ var bottomVisibleMargin = scrollOffset.y + collectionViewHeight;
255
+
256
+ var cellWidth = collectionViewWidth - this.insets.left - this.insets.right;
257
+ var cellHeight = collectionViewHeight - this.insets.top - this.insets.bottom;
258
+
259
+ var attributes = [];
260
+
261
+ var currentOffset = 0;
262
+
263
+ // Find the current topmost cell. This will be the first cell whose bottom edge is within the visible area
264
+ var indexPath;
265
+ var numberOfSections = this.collectionView.numberOfSections();
266
+ for (var i = 0; i < numberOfSections; i++) {
267
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(i);
268
+
269
+ for (var j = 0; j < numberOfObjects; j++) {
270
+ currentOffset += collectionViewHeight;
271
+
272
+ if (currentOffset >= topVisibleMargin && currentOffset <= bottomVisibleMargin) {
273
+ indexPath = this.collectionView.indexPathForObjectAtRow(j, {inSectionAtIndex: i});
274
+ break;
275
+ }
276
+ }
277
+
278
+ // If the index path was found, stop looking
279
+ if (indexPath) break;
280
+
281
+ }
282
+
283
+ // If no index path was found, return an empty array.
284
+ if (!indexPath) return attributes;
285
+
286
+ var spread = this._spread;
287
+
288
+ // The displacement percentage represents how far away the topmost cell is from the scroll offset.
289
+ var displacementPercentage = (collectionViewHeight - currentOffset + topVisibleMargin) / collectionViewHeight;
290
+
291
+ var displacementPixels = (this._spread * displacementPercentage) | 0;
292
+
293
+ var scalePerCell = (1 - this._minimumScale) / (this._numberOfBackgroundCells + 1);
294
+ var opacityPerCell = 1 / (this._numberOfBackgroundCells + 1);
295
+ var blurPerCell = this._maximumBlur / (this._numberOfBackgroundCells + 1);
296
+
297
+ // Create the attributes for the topmost cell.
298
+ var currentCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
299
+ currentCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
300
+ // The topmost cell's position is adjusted so it appears in the center of the view
301
+ y: topVisibleMargin + this.insets.top - displacementPixels,
302
+ width: cellWidth,
303
+ height: cellHeight
304
+ });
305
+
306
+ currentCellAttributes.style.zIndex = 2 * this._numberOfBackgroundCells + 4;
307
+ currentCellAttributes.style.scaleX = 1 - scalePerCell * displacementPercentage;
308
+ currentCellAttributes.style.scaleY = 1 - scalePerCell * displacementPercentage;
309
+ currentCellAttributes.style.opacity = 1 - opacityPerCell * displacementPercentage;
310
+ if (this._blursBackgroundCells) currentCellAttributes.style.blur = (displacementPercentage * blurPerCell) + 'px';
311
+
312
+ if (this._rotatesCells) {
313
+ currentCellAttributes.style.rotateX = (this.numberOfBackgroundCells - 1) / 10000 + 'deg';
314
+ currentCellAttributes.style.transformOriginX = '50%';
315
+ currentCellAttributes.style.transformOriginY = '100%';
316
+ currentCellAttributes.style.translateZ = 2 * this._numberOfBackgroundCells + 4 + 'px';
317
+ }
318
+
319
+ attributes.push(currentCellAttributes);
320
+
321
+ // Create the attributes for the next cell
322
+ var nextIndexPath = this.nextIndexPathWithIndexPath(indexPath);
323
+ if (nextIndexPath) {
324
+ let baseY = currentOffset - this.insets.bottom + this.insets.top / 3;
325
+ let expectedY = currentOffset + this.insets.top;
326
+
327
+ // Adjust the Y so the next cell is visible when the current cell is on screen
328
+ var nextCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(nextIndexPath);
329
+ nextCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
330
+ y: BMNumberByInterpolatingNumbersWithFraction(baseY, expectedY, displacementPercentage),
331
+ width: cellWidth,
332
+ height: cellHeight
333
+ });
334
+
335
+ nextCellAttributes.style.zIndex = this._numberOfBackgroundCells * 2 + 6;
336
+
337
+ if (this._rotatesCells) {
338
+ nextCellAttributes.style.rotateX = BMNumberByInterpolatingNumbersWithFraction(this._rotation, 0, displacementPercentage) + 'deg';
339
+ nextCellAttributes.style.transformOriginX = '50%';
340
+ nextCellAttributes.style.transformOriginY = '100%';
341
+ nextCellAttributes.style.translateZ = this._numberOfBackgroundCells * 2 + 6 + 'px';
342
+ }
343
+
344
+ attributes.push(nextCellAttributes);
345
+
346
+
347
+ // Create the attributes for the upcoming cell
348
+ let upcomingIndexPath = this.nextIndexPathWithIndexPath(nextIndexPath);
349
+ if (upcomingIndexPath) {
350
+ //let upcomingDisplacement = displacementPercentage >= 1 ? displacementPercentage - 1 : displacementPercentage;
351
+ let upcomingOffset = currentOffset + this.collectionView.frame.size.height;
352
+
353
+ let baseY = upcomingOffset - this.insets.bottom + this.insets.top / 3;
354
+ let expectedY = upcomingOffset + this.insets.top;
355
+
356
+ // Adjust the Y so the next cell is visible when the current cell is on screen
357
+ var nextCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(upcomingIndexPath);
358
+ nextCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
359
+ y: baseY, //BMNumberByInterpolatingNumbersWithFraction(baseY, expectedY, 1 - displacementPercentage),
360
+ width: cellWidth,
361
+ height: cellHeight
362
+ });
363
+
364
+ nextCellAttributes.style.zIndex = this._numberOfBackgroundCells * 2 + 8;
365
+
366
+ if (this._rotatesCells) {
367
+ nextCellAttributes.style.rotateX = this._rotation + 'deg';
368
+ nextCellAttributes.style.transformOriginX = '50%';
369
+ nextCellAttributes.style.transformOriginY = '100%';
370
+ nextCellAttributes.style.translateZ = this._numberOfBackgroundCells * 2 + 8 + 'px';
371
+ }
372
+
373
+ attributes.push(nextCellAttributes);
374
+ }
375
+ }
376
+
377
+ // Create the attributes for the previous cells
378
+ for (var i = 0; i < this._numberOfBackgroundCells; i++) {
379
+ var previousIndexPath = this.previousIndexPathWithIndexPath(indexPath);
380
+
381
+ if (!previousIndexPath) return attributes;
382
+
383
+ var previousCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(previousIndexPath);
384
+ previousCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
385
+ y: topVisibleMargin + this.insets.top - displacementPixels - (i + 1) * this._spread,
386
+ width: cellWidth,
387
+ height: cellHeight
388
+ });
389
+
390
+ previousCellAttributes.style.zIndex = 2 * (this._numberOfBackgroundCells + 1 - i);
391
+ previousCellAttributes.style.scaleX = 1 - scalePerCell * displacementPercentage - scalePerCell * (i + 1);
392
+ previousCellAttributes.style.scaleY = 1 - scalePerCell * displacementPercentage - scalePerCell * (i + 1);
393
+ previousCellAttributes.style.opacity = 1 - opacityPerCell * displacementPercentage - opacityPerCell * (i + 1);
394
+ if (this._blursBackgroundCells) previousCellAttributes.style.blur = ((displacementPercentage + i + 1) * blurPerCell) + 'px';
395
+
396
+ if (this._rotatesCells) {
397
+ previousCellAttributes.style.rotateX = (this.numberOfBackgroundCells + i) / 10000 + 'deg';
398
+ previousCellAttributes.style.transformOriginX = '50%';
399
+ previousCellAttributes.style.transformOriginY = '100%';
400
+ previousCellAttributes.style.translateZ = 2 * (this._numberOfBackgroundCells + 1 - i) + 'px';
401
+ }
402
+
403
+ attributes.push(previousCellAttributes);
404
+
405
+ indexPath = previousIndexPath;
406
+ }
407
+
408
+ return attributes;
409
+ },
410
+
411
+ // @override - BMCollectionViewLayout
412
+ attributesForCellAtIndexPath: function (indexPath) {
413
+ if (this._showsSingleCell) {
414
+ var attribute = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
415
+
416
+ attribute.frame = this.collectionView.frame.copy();
417
+ attribute.frame.origin = BMPointMake();
418
+
419
+ if (indexPath.row != 0 || indexPath.section != 0) {
420
+ attribute.isHidden = YES;
421
+ attribute.style.opacity = 0;
422
+ }
423
+
424
+ return attribute;
425
+ }
426
+
427
+ var scrollOffset = this.collectionView.scrollOffset;
428
+ var collectionViewHeight = this.collectionView.frame.size.height;
429
+ var collectionViewWidth = this.collectionView.frame.size.width;
430
+
431
+ var topVisibleMargin = scrollOffset.y;
432
+ var bottomVisibleMargin = scrollOffset.y + collectionViewHeight;
433
+
434
+ var cellWidth = collectionViewWidth - this.insets.left - this.insets.right;
435
+ var cellHeight = collectionViewHeight - this.insets.top - this.insets.bottom;
436
+
437
+ var attributes = [];
438
+
439
+ var currentOffset = 0;
440
+
441
+ // Find the current topmost cell. This will be the first cell whose bottom edge is within the visible area
442
+ var topmostIndexPath;
443
+ var numberOfSections = this.collectionView.numberOfSections();
444
+ for (var i = 0; i < numberOfSections; i++) {
445
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(i);
446
+
447
+ for (var j = 0; j < numberOfObjects; j++) {
448
+ currentOffset += collectionViewHeight;
449
+
450
+ if (currentOffset >= topVisibleMargin && currentOffset <= bottomVisibleMargin) {
451
+ topmostIndexPath = this.collectionView.indexPathForObjectAtRow(j, {inSectionAtIndex: i});
452
+ break;
453
+ }
454
+ }
455
+
456
+ // If the index path was found, stop looking
457
+ if (topmostIndexPath) break;
458
+
459
+ }
460
+
461
+ var spread = this._spread;
462
+
463
+ // The displacement percentage represents how far away the topmost cell is from the scroll offset.
464
+ var displacementPercentage = (collectionViewHeight - currentOffset + topVisibleMargin) / collectionViewHeight;
465
+
466
+ var displacementPixels = (this._spread * displacementPercentage) | 0;
467
+
468
+ var scalePerCell = (1 - this._minimumScale) / (this._numberOfBackgroundCells + 1);
469
+ var opacityPerCell = 1 / (this._numberOfBackgroundCells + 1);
470
+ var blurPerCell = this._maximumBlur / (this._numberOfBackgroundCells + 1);
471
+
472
+ if (indexPath.isEqualToIndexPath(topmostIndexPath, {usingComparator: this.collectionView.identityComparator})) {
473
+
474
+ var currentCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
475
+ currentCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
476
+ // The topmost cell's position is adjusted so it appears in the center of the view
477
+ y: topVisibleMargin + this.insets.top - displacementPixels,
478
+ width: cellWidth,
479
+ height: cellHeight
480
+ });
481
+
482
+ currentCellAttributes.style.zIndex = this._numberOfBackgroundCells + 2;
483
+ currentCellAttributes.style.scaleX = 1 - scalePerCell * displacementPercentage;
484
+ currentCellAttributes.style.scaleY = 1 - scalePerCell * displacementPercentage;
485
+ currentCellAttributes.style.opacity = 1 - opacityPerCell * displacementPercentage;
486
+ if (this._blursBackgroundCells) currentCellAttributes.style.blur = (displacementPercentage * blurPerCell) + 'px';
487
+
488
+ return currentCellAttributes;
489
+ }
490
+
491
+ // Create the attributes for the next cell
492
+ var nextIndexPath = this.nextIndexPathWithIndexPath(topmostIndexPath);
493
+ if (nextIndexPath && indexPath.isEqualToIndexPath(nextIndexPath, {usingComparator: this.collectionView.identityComparator})) {
494
+ var nextCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(nextIndexPath);
495
+ nextCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
496
+ y: currentOffset + this.insets.top,
497
+ width: cellWidth,
498
+ height: cellHeight
499
+ });
500
+
501
+ nextCellAttributes.style.zIndex = this._numberOfBackgroundCells + 3;
502
+
503
+ return nextCellAttributes;
504
+ }
505
+
506
+ if (indexPath.section > nextIndexPath.section || (indexPath.section == nextIndexPath.section && indexPath.row > nextIndexPath.row)) {
507
+ var y = 0;
508
+ for (var i = 0; i < indexPath.section; i++) {
509
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(i);
510
+
511
+ y += collectionViewHeight * numberOfObjects;
512
+
513
+ }
514
+
515
+ y += collectionViewHeight * indexPath.row;
516
+
517
+ var attributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
518
+ attributes.frame = BMRectMakeWithX(this.insets.left, {
519
+ y: y,
520
+ width: cellWidth,
521
+ height: cellHeight
522
+ });
523
+ attributes.isHidden = YES;
524
+ attributes.opacity = 0;
525
+ return attributes;
526
+ }
527
+
528
+ // Create the attributes for the previous cells
529
+ for (var i = 0; i < this._numberOfBackgroundCells; i++) {
530
+ var previousIndexPath = this.previousIndexPathWithIndexPath(topmostIndexPath);
531
+
532
+ if (!previousIndexPath) break;
533
+
534
+ if (!indexPath.isEqualToIndexPath(previousIndexPath, {usingComparator: this.collectionView.identityComparator})) {
535
+
536
+ topmostIndexPath = previousIndexPath;
537
+ continue;
538
+ }
539
+
540
+ var previousCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(previousIndexPath);
541
+ previousCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
542
+ y: topVisibleMargin + this.insets.top - displacementPixels - (i + 1) * this._spread,
543
+ width: cellWidth,
544
+ height: cellHeight
545
+ });
546
+
547
+ previousCellAttributes.style.zIndex = this._numberOfBackgroundCells + 1 - i;
548
+ previousCellAttributes.style.scaleX = 1 - scalePerCell * displacementPercentage - scalePerCell * (i + 1);
549
+ previousCellAttributes.style.scaleY = 1 - scalePerCell * displacementPercentage - scalePerCell * (i + 1);
550
+ previousCellAttributes.style.opacity = 1 - opacityPerCell * displacementPercentage - opacityPerCell * (i + 1);
551
+ if (this._blursBackgroundCells) previousCellAttributes.style.blur = ((displacementPercentage + i + 1) * blurPerCell) + 'px';
552
+
553
+ return previousCellAttributes;
554
+ }
555
+
556
+ var previousCellAttributes = BMCollectionViewLayoutAttributesMakeForCellAtIndexPath(indexPath);
557
+ previousCellAttributes.frame = BMRectMakeWithX(this.insets.left, {
558
+ y: topVisibleMargin + this.insets.top - (this._numberOfBackgroundCells + 1) * this._spread,
559
+ width: cellWidth,
560
+ height: cellHeight
561
+ });
562
+
563
+ previousCellAttributes.style.zIndex = 0;
564
+ previousCellAttributes.style.scaleX = this._minimumScale;
565
+ previousCellAttributes.style.scaleY = this._minimumScale;
566
+ previousCellAttributes.style.opacity = 0;
567
+ if (this._blursBackgroundCells) previousCellAttributes.style.blur = (this._maximumBlur) + 'px';
568
+
569
+ return previousCellAttributes;
570
+
571
+ },
572
+
573
+ // @override - BMCollectionViewLayout
574
+ rectWithScrollingPositionOfCellAtIndexPath: function (indexPath) {
575
+ var collectionViewHeight = this.collectionView.frame.size.height;
576
+
577
+ var y = 0;
578
+ for (var i = 0; i < indexPath.section; i++) {
579
+ var numberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(i);
580
+
581
+ y += collectionViewHeight * numberOfObjects;
582
+
583
+ }
584
+
585
+ y += collectionViewHeight * indexPath.row;
586
+
587
+ return BMRectMakeWithX(0, {y: y, width: this.collectionView.frame.size.width, height: this.collectionView.frame.size.height});
588
+ },
589
+
590
+ // @override - BMCollectionViewLayout
591
+ snapsScrollPosition() {
592
+ return YES;
593
+ },
594
+
595
+ // @override - BMCollectionViewLayout
596
+ snappingScrollOffsetForScrollOffset(offset, args) {
597
+ var offset = offset.copy();
598
+
599
+ var additionalOffset = (args.withVerticalDirection == BMScrollingDirectionVertical.Bottom ? this.collectionView.frame.size.height : 0);
600
+
601
+ offset.y = ((offset.y / this.collectionView.frame.size.height) | 0) * this.collectionView.frame.size.height + additionalOffset;
602
+
603
+ offset.y = Math.max(0, Math.min(this.collectionView.size.height - this.collectionView.frame.size.height, offset.y));
604
+
605
+ return offset;
606
+ },
607
+
608
+ // @override - BMCollectionViewLayout
609
+ copy() {
610
+ var layout = new BMCollectionViewStackLayout();
611
+
612
+ layout._showsSingleCellFullScreen = this._showsSingleCellFullScreen;
613
+ layout._showsSingleCell = this._showsSingleCell;
614
+ layout._insets = this._insets.copy();
615
+ layout._spread = this._spread;
616
+ layout._numberOfBackgroundCells = this._numberOfBackgroundCells;
617
+ layout._minimumScale = this._minimumScale;
618
+ layout._blursBackgroundCells = this._blursBackgroundCells;
619
+ layout._maximumBlur = this._maximumBlur;
620
+
621
+ return layout;
622
+ }
623
+
624
+ });
625
+
626
+ /**
627
+ * Constructs and returns a new stack layout.
628
+ * @returns <BMCollectionViewStackLayout> A stack layout.
629
+ */
630
+ BMCollectionViewStackLayout.stackLayout = function () {
631
+ return new BMCollectionViewStackLayout();
632
+ }
633
+
634
+ // @endtype