@breakside/jskit 2021.44.0 → 2022.3.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 (86) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/Deflate.js +2 -3
  7. package/Frameworks/Foundation.jsframework/JS/Error+JS.js +10 -0
  8. package/Frameworks/Foundation.jsframework/JS/JSColorSpace.js +106 -20
  9. package/Frameworks/Foundation.jsframework/JS/JSDateFormatter.js +2 -2
  10. package/Frameworks/Foundation.jsframework/JS/JSTimer.js +6 -0
  11. package/Frameworks/Foundation.jsframework/JS/JSURLResponse.js +60 -1
  12. package/Frameworks/Foundation.jsframework/JS/Zlib.js +2 -3
  13. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  14. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  15. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  16. package/Info.json +2 -2
  17. package/Node/JavascriptCompilation.js +1 -1
  18. package/Node/io.breakside.jskit-bundle.js +2 -2
  19. package/Root/Frameworks/APIKit/APIError.js +2 -3
  20. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  21. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  22. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  23. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  24. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  25. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  26. package/Root/Frameworks/DBKit/DBObjectDatabase.js +6 -5
  27. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  28. package/Root/Frameworks/DOM/Info.yaml +1 -1
  29. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  30. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  31. package/Root/Frameworks/Foundation/Deflate.js +2 -3
  32. package/Root/Frameworks/Foundation/Error+JS.js +10 -0
  33. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  34. package/Root/Frameworks/Foundation/JSColorSpace.js +106 -20
  35. package/Root/Frameworks/Foundation/JSDateFormatter.js +2 -2
  36. package/Root/Frameworks/Foundation/JSTimer.js +6 -0
  37. package/Root/Frameworks/Foundation/JSURLResponse.js +60 -1
  38. package/Root/Frameworks/Foundation/Zlib.js +2 -3
  39. package/Root/Frameworks/ImageKit/IKColorProfile.js +979 -0
  40. package/Root/Frameworks/ImageKit/IKColorSpace.js +49 -198
  41. package/Root/Frameworks/ImageKit/IKDecoderPNG.js +1 -3
  42. package/Root/Frameworks/ImageKit/IKMatrix.js +124 -0
  43. package/Root/Frameworks/ImageKit/ImageKit.js +2 -0
  44. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  45. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  46. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  47. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  48. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  49. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  50. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  51. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  52. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  53. package/Root/Frameworks/ServerKit/SKFileJobQueue.js +3 -1
  54. package/Root/Frameworks/ServerKit/SKHTTPError.js +2 -3
  55. package/Root/Frameworks/ServerKit/SKHTTPRequest.js +7 -0
  56. package/Root/Frameworks/ServerKit/SKHTTPResponder.js +9 -2
  57. package/Root/Frameworks/ServerKit/SKUserAgent.js +130 -0
  58. package/Root/Frameworks/ServerKit/SKValidatingObject.js +1 -3
  59. package/Root/Frameworks/ServerKit/ServerKit.js +1 -0
  60. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  61. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  62. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  63. package/Root/Frameworks/UIKit/UICollectionReusableView.js +91 -0
  64. package/Root/Frameworks/UIKit/UICollectionView.js +1254 -0
  65. package/Root/Frameworks/UIKit/UICollectionViewCell.js +121 -0
  66. package/Root/Frameworks/UIKit/UICollectionViewGridLayout.js +425 -0
  67. package/Root/Frameworks/UIKit/UICollectionViewLayout.js +82 -0
  68. package/Root/Frameworks/UIKit/UIColorWell.js +12 -1
  69. package/Root/Frameworks/UIKit/UIDisplayServer.js +5 -1
  70. package/Root/Frameworks/UIKit/UIKit.js +7 -0
  71. package/Root/Frameworks/UIKit/UILayer.js +3 -0
  72. package/Root/Frameworks/UIKit/UIListView.js +39 -15
  73. package/Root/Frameworks/UIKit/UIMenu.js +1 -1
  74. package/Root/Frameworks/UIKit/UIMenuView.js +5 -1
  75. package/Root/Frameworks/UIKit/UINavigationBar.js +2 -2
  76. package/Root/Frameworks/UIKit/UIPopupButton.js +31 -3
  77. package/Root/Frameworks/UIKit/UIScrollView.js +2 -0
  78. package/Root/Frameworks/UIKit/UISplitViewController.js +52 -96
  79. package/Root/Frameworks/UIKit/UITabView.js +2 -2
  80. package/Root/Frameworks/UIKit/UITabViewController.js +6 -5
  81. package/Root/Frameworks/UIKit/UITextField.js +3 -0
  82. package/Root/Frameworks/UIKit/UIView.js +18 -0
  83. package/Root/Frameworks/UIKit/UIViewController.js +11 -0
  84. package/Root/Frameworks/UIKit/UIWindowServer.js +2 -2
  85. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  86. package/package.json +1 -1
@@ -0,0 +1,1254 @@
1
+ // Copyright 2022 Breakside Inc.
2
+ //
3
+ // Licensed under the Breakside Public License, Version 1.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // If a copy of the License was not distributed with this file, you may
6
+ // obtain a copy at
7
+ //
8
+ // http://breakside.io/licenses/LICENSE-1.0.txt
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ // #import Foundation
17
+ // #import "UIScrollView.js"
18
+ // #import "UIPlatform.js"
19
+ // #import "UICollectionViewLayout.js"
20
+ "use strict";
21
+
22
+ JSProtocol("UICollectionViewDelegate", JSProtocol, {
23
+
24
+ // Selection
25
+ collectionViewShouldSelectCellAtIndexPath: function(collectionView, indexPath){},
26
+ collectionViewDidSelectCellAtIndexPath: function(collectionView, indexPath){},
27
+ collectionViewDidFinishSelectingCellAtIndexPath: function(collectionView, indexPath){},
28
+ collectionViewDidOpenCellAtIndexPath: function(collectionView, indexPath){},
29
+ collectionViewSelectionDidChange: function(collectionView, selectedIndexPaths){},
30
+
31
+ // Context menu
32
+ menuForCollectionViewCellAtIndexPath: function(collectionView, indexPath, selectedIndexPaths){},
33
+
34
+ // Dragging cells
35
+ collectionViewShouldDragCellAtIndexPath: function(collectionView, indexPath){},
36
+ pasteboardItemsForCollectionViewAtIndexPath: function(collectionView, indexPath){},
37
+ collectionViewWillBeginDraggingSession: function(collectionView, session){}
38
+
39
+ });
40
+
41
+ JSProtocol("UICollectionViewDataSource", JSProtocol, {
42
+
43
+ numberOfSectionsInCollectionView: function(collectionView){},
44
+ numberOfCellsInCollectionViewSection: function(collectionView, sectionIndex){},
45
+ cellForCollectionViewAtIndexPath: function(collectionView, indexPath){},
46
+ supplimentaryViewForCollectionViewAtIndexPath: function(collectionView, indexPath, kind){}
47
+
48
+ });
49
+
50
+ JSClass("UICollectionView", UIScrollView, {
51
+
52
+ // --------------------------------------------------------------------
53
+ // MARK: - Creating a Collection View
54
+
55
+ initWithLayout: function(layout, styler){
56
+ this.collectionViewLayout = layout;
57
+ if (styler !== undefined){
58
+ this._styler = styler;
59
+ }
60
+ this.init();
61
+ },
62
+
63
+ initWithFrame: function(frame){
64
+ UICollectionView.$super.initWithFrame.call(this, frame);
65
+ if (this._collectionViewLayout === null){
66
+ throw new Error("Use initWithLayout() to create a UICollectionView");
67
+ }
68
+ this._commonListInit();
69
+ },
70
+
71
+ initWithSpec: function(spec){
72
+ UICollectionView.$super.initWithSpec.call(this, spec);
73
+ if (spec.containsKey('styler')){
74
+ this._styler = spec.valueForKey("styler", UICollectionView.Styler);
75
+ }
76
+ if (spec.containsKey('layout')){
77
+ this.collectionViewLayout = spec.valueForKey("layout", UICollectionView.Styler);
78
+ }else{
79
+ throw new Error("layout is a required property in a UICollectionView spec file");
80
+ }
81
+ this._commonListInit();
82
+ if (spec.containsKey('delegate')){
83
+ this.delegate = spec.valueForKey("delegate");
84
+ }
85
+ if (spec.containsKey('dataSource')){
86
+ this.dataSource = spec.valueForKey("dataSource");
87
+ }
88
+ var reusable;
89
+ var reuse;
90
+ var styler;
91
+ var identifiers;
92
+ var identifier;
93
+ var i, l;
94
+ if (spec.containsKey('reusableCellClasses')){
95
+ reusable = spec.valueForKey('reusableCellClasses');
96
+ identifiers = reusable.keys();
97
+ for (i = 0, l = identifiers.length; i < l; ++i){
98
+ identifier = identifiers[i];
99
+ reuse = reusable.valueForKey(identifier);
100
+ if (typeof(reuse) === 'string'){
101
+ this.registerCellClassForReuseIdentifier(JSClass.FromName(reuse), identifier);
102
+ }else{
103
+ this.registerCellClassForReuseIdentifier(JSClass.FromName(reuse.valueForKey('className')), identifier, reuse.valueForKey('styler'));
104
+ }
105
+
106
+ }
107
+ }
108
+ if (spec.containsKey('reusableViewClasses')){
109
+ reusable = spec.valueForKey('reusableViewClasses');
110
+ identifiers = reusable.keys();
111
+ for (i = 0, l = identifiers.length; i < l; ++i){
112
+ identifier = identifiers[i];
113
+ reuse = reusable.valueForKey(identifier);
114
+ if (typeof(reuse) === 'string'){
115
+ this.registerViewClassForReuseIdentifier(JSClass.FromName(reuse), identifier);
116
+ }else{
117
+ this.registerViewClassForReuseIdentifier(JSClass.FromName(reuse.valueForKey('className')), identifier, reuse.valueForKey('styler'));
118
+ }
119
+ }
120
+ }
121
+ if (spec.containsKey('allowsMultipleSelection')){
122
+ this.allowsMultipleSelection = spec.valueForKey("allowsMultipleSelection");
123
+ }
124
+ if (spec.containsKey('allowsEmptySelection')){
125
+ this.allowsEmptySelection = spec.valueForKey("allowsEmptySelection");
126
+ }
127
+ if (spec.containsKey("showsFocusRing")){
128
+ this.showsFocusRing = spec.valueForKey("showsFocusRing");
129
+ }
130
+ },
131
+
132
+ _commonListInit: function(){
133
+ this.stylerProperties = {};
134
+ this._visibleElements = [];
135
+ this._reusableCellsByIdentifier = {};
136
+ this._cellClassesByIdentifier = {};
137
+ this._reusableViewsByIdentifier = {};
138
+ this._viewClassesByIdentifier = {};
139
+ this._elementsContainerView = UIView.init();
140
+ this._selectedIndexPaths = [];
141
+ this._contextSelectedIndexPaths = [];
142
+ this.contentView.addSubview(this._elementsContainerView);
143
+ if (this._styler === null){
144
+ this._styler = this.$class.Styler.default;
145
+ }
146
+ this._styler.initializeCollectionView(this);
147
+ },
148
+
149
+ // -------------------------------------------------------------------------
150
+ // MARK: - Data Source & Delegate
151
+
152
+ dataSource: null,
153
+ delegate: null,
154
+
155
+ // -------------------------------------------------------------------------
156
+ // MARK: - Styling
157
+
158
+ sylerProperties: null,
159
+ styler: JSReadOnlyProperty("_styler", null),
160
+
161
+ // --------------------------------------------------------------------
162
+ // MARK: - Cell Reuse
163
+
164
+ registerCellClassForReuseIdentifier: function(cellClass, identifier, styler){
165
+ this._cellClassesByIdentifier[identifier] = {cellClass: cellClass, styler: styler || null};
166
+ },
167
+
168
+ dequeueReusableCellWithIdentifier: function(identifier, indexPath){
169
+ var cell = null;
170
+ var queue = this._reusableCellsByIdentifier[identifier];
171
+ if (queue && queue.length > 0){
172
+ cell = queue.pop();
173
+ }else{
174
+ var info = this._cellClassesByIdentifier[identifier];
175
+ if (info){
176
+ cell = info.cellClass.initWithReuseIdentifier(identifier, info.styler);
177
+ var styler = info.styler || this._styler;
178
+ styler.initializeCell(cell, indexPath);
179
+ }
180
+ }
181
+ return cell;
182
+ },
183
+
184
+ _reusableCellsByIdentifier: null,
185
+ _cellClassesByIdentifier: null,
186
+
187
+ _enqueueReusableCell: function(cell){
188
+ cell.indexPath = null;
189
+ cell.collectionView = null;
190
+ var identifier = cell.reuseIdentifier;
191
+ if (!(identifier in this._reusableCellsByIdentifier)){
192
+ this._reusableCellsByIdentifier[identifier] = [];
193
+ }
194
+ var queue = this._reusableCellsByIdentifier[identifier];
195
+ queue.push(cell);
196
+ },
197
+
198
+ _enqueueVisibleElement: function(element){
199
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.cell){
200
+ this._enqueueReusableCell(element.view);
201
+ }else{
202
+ this._enqueueReusableView(element.view);
203
+ }
204
+ },
205
+
206
+ _removeQueuedCells: function(){
207
+ var queue;
208
+ var cell;
209
+ for (var id in this._reusableCellsByIdentifier){
210
+ queue = this._reusableCellsByIdentifier[id];
211
+ for (var i = 0, l = queue.length; i < l; ++i){
212
+ cell = queue[i];
213
+ if (cell.superview !== null){
214
+ cell.removeFromSuperview();
215
+ }
216
+ }
217
+ }
218
+ },
219
+
220
+ // --------------------------------------------------------------------
221
+ // MARK: - Supplimentary View Reuse
222
+
223
+ registerViewClassForReuseIdentifier: function(viewClass, identifier, styler){
224
+ this._viewClassesByIdentifier[identifier] = {viewClass: viewClass, styler: styler || null};
225
+ },
226
+
227
+ dequeueReusableViewWithIdentifier: function(identifier, indexPath){
228
+ var view = null;
229
+ var queue = this._reusableViewsByIdentifier[identifier];
230
+ if (queue && queue.length > 0){
231
+ view = queue.pop();
232
+ }else{
233
+ var info = this._viewClassesByIdentifier[identifier];
234
+ if (info){
235
+ view = info.viewClass.initWithReuseIdentifier(identifier, info.styler);
236
+ var styler = info.styler || this._styler;
237
+ styler.initializeReusableView(view, indexPath);
238
+ }
239
+ }
240
+ return view;
241
+ },
242
+
243
+ _reusableViewsByIdentifier: null,
244
+ _viewClassesByIdentifier: null,
245
+
246
+ _enqueueReusableView: function(view){
247
+ view.indexPath = null;
248
+ view.collectionView = null;
249
+ var identifier = view.reuseIdentifier;
250
+ if (!(identifier in this._reusableViewsByIdentifier)){
251
+ this._reusableViewsByIdentifier[identifier] = [];
252
+ }
253
+ var queue = this._reusableViewsByIdentifier[identifier];
254
+ queue.push(view);
255
+ },
256
+
257
+ _removeQueuedViews: function(){
258
+ var queue;
259
+ var view;
260
+ for (var id in this._reusableViewsByIdentifier){
261
+ queue = this._reusableViewsByIdentifier[id];
262
+ for (var i = 0, l = queue.length; i < l; ++i){
263
+ view = queue[i];
264
+ if (view.superview !== null){
265
+ view.removeFromSuperview();
266
+ }
267
+ }
268
+ }
269
+ },
270
+
271
+ // --------------------------------------------------------------------
272
+ // MARK: - Reloading Collection Data
273
+
274
+ _needsReload: false,
275
+
276
+ reloadData: function(){
277
+ if (!this.dataSource){
278
+ return;
279
+ }
280
+ this._needsReload = true;
281
+ this.setNeedsLayout();
282
+ },
283
+
284
+ reloadCellAtIndexPath: function(indexPath, animator){
285
+ this.reloadCellsAtIndexPaths([indexPath], animator);
286
+ },
287
+
288
+ reloadCellsAtIndexPaths: function(indexPaths, animator){
289
+ if (this._visibleElements.length === 0){
290
+ return;
291
+ }
292
+ var firstVisibleElement = this._visibleElements[0];
293
+ var lastVisibleElement = this._visibleElements[this._visibleElements.length - 1];
294
+ var searcher = JSBinarySearcher(this._visibleElements, VisibleElement.indexPathCompare);
295
+
296
+ indexPaths = JSCopy(indexPaths);
297
+ indexPaths.sort(function(a, b){
298
+ return a.compare(b);
299
+ });
300
+
301
+ var i, l;
302
+ var indexPath;
303
+ var comparison;
304
+ var elementIndex;
305
+ var cell;
306
+ var element;
307
+
308
+ for (i = 0, l = indexPaths.length; i < l; ++i){
309
+ indexPath = indexPaths[i];
310
+ comparison = VisibleElement.indexPathCompare(indexPath, lastVisibleElement);
311
+ if (comparison <= 0){
312
+ comparison = VisibleElement.indexPathCompare(indexPath, firstVisibleElement);
313
+ if (comparison >= 0){
314
+ elementIndex = searcher.indexMatchingValue(indexPath);
315
+ if (elementIndex !== null){
316
+ element = this._visibleElements[elementIndex];
317
+ cell = element.view;
318
+ this._enqueueReusableCell(cell);
319
+ cell = this._createCellWithAttributes(element.attributes);
320
+ if (cell !== element.view){
321
+ this._elementsContainerView.insertSubviewBelowSibling(cell, element.view);
322
+ element.view.removeFromSuperview();
323
+ element.view = cell;
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+ },
330
+
331
+ // --------------------------------------------------------------------
332
+ // MARK: - Inserting and Deleting Rows
333
+
334
+ insertCellAtIndexPath: function(indexPath, animation){
335
+ this.insertCellsAtIndexPaths([indexPath], animation);
336
+ },
337
+
338
+ insertCellsAtIndexPaths: function(indexPaths, animation){
339
+ // TODO:
340
+ this.reloadData();
341
+ },
342
+
343
+ deleteCellAtIndexPath: function(indexPath, animation){
344
+ this.deleteCellsAtIndexPaths([indexPath], animation);
345
+ },
346
+
347
+ deleteCellsAtIndexPaths: function(indexPaths, animation){
348
+ // TODO:
349
+ this.reloadData();
350
+ },
351
+
352
+ insertSection: function(section, animation){
353
+ this.insertSections([section], animation);
354
+ },
355
+
356
+ insertSections: function(sections, animation){
357
+ // TODO:
358
+ this.reloadData();
359
+ },
360
+
361
+ deleteSection: function(section, animation){
362
+ this.deleteSections([section], animation);
363
+ },
364
+
365
+ deleteSections: function(sections, animation){
366
+ // TODO:
367
+ this.reloadData();
368
+ },
369
+
370
+ // -------------------------------------------------------------------------
371
+ // MARK: - Layout
372
+
373
+ _visibleElements: null,
374
+ _elementsContainerView: null,
375
+ _hasPreparedLayout: false,
376
+ collectionViewLayout: JSDynamicProperty("_collectionViewLayout", null),
377
+
378
+ setCollectionViewLayout: function(collectionViewLayout){
379
+ if (this._collectionViewLayout !== null){
380
+ this._collectionViewLayout.collectionView = null;
381
+ }
382
+ this._collectionViewLayout = collectionViewLayout;
383
+ if (this._collectionViewLayout !== null){
384
+ this._collectionViewLayout.collectionView = this;
385
+ }
386
+ },
387
+
388
+ layoutSubviews: function(){
389
+ UICollectionView.$super.layoutSubviews.call(this);
390
+ var needsPrepare = this._needsReload || this._hasPreparedLayout;
391
+ var i, l;
392
+ if (this._needsReload){
393
+ for (i = 0, l = this._visibleElements.length; i < l; ++i){
394
+ this._enqueueVisibleElement(this._visibleElements[i]);
395
+ }
396
+ this._visibleElements = [];
397
+ this._needsReload = false;
398
+ }
399
+ if (needsPrepare){
400
+ this.collectionViewLayout.prepare();
401
+ this.contentSize = this.collectionViewLayout.collectionViewContentSize;
402
+ this._hasPreparedLayout = true;
403
+ }
404
+ this._elementsContainerView.untransformedFrame = JSRect(JSPoint.Zero, this.contentSize);
405
+ this._updateVisibleElements();
406
+ },
407
+
408
+ _didScroll: function(){
409
+ if (!this._needsReload && this._elementsContainerView !== null){
410
+ this._updateVisibleElements();
411
+ }
412
+ UICollectionView.$super._didScroll.call(this);
413
+ },
414
+
415
+ _updateVisibleElements: function(){
416
+ if (!this._hasPreparedLayout){
417
+ return;
418
+ }
419
+ var i, l;
420
+ var visibleRect = JSRect(this.contentView.bounds);
421
+ var visibleElementMap = {};
422
+
423
+ // Get the attributes for the elements that should be visible
424
+ var attributes = this.collectionViewLayout.layoutAttributesForElementsInRect(visibleRect);
425
+ var attrs;
426
+ for (i = 0, l = attributes.length; i < l; ++i){
427
+ attrs = attributes[i];
428
+ visibleElementMap[attrs.elementIdentifier] = null;
429
+ }
430
+
431
+ // Enqueue any elements that are no longer visible
432
+ var element;
433
+ for (i = this._visibleElements.length - 1; i >= 0; --i){
434
+ element = this._visibleElements[i];
435
+ if (element.attributes.elementIdentifier in visibleElementMap){
436
+ visibleElementMap[element.attributes.elementIdentifier] = element;
437
+ }else{
438
+ this._enqueueVisibleElement(element);
439
+ this._visibleElements.splice(i, 1);
440
+ }
441
+ }
442
+
443
+ // 2. Add any elements that are newly visible
444
+ var elementIndex = 0;
445
+ for (i = 0, l = attributes.length; i < l; ++i){
446
+ attrs = attributes[i];
447
+ element = visibleElementMap[attrs.elementIdentifier];
448
+ if (element === null){
449
+ element = VisibleElement(attrs);
450
+ this._createViewForVisibleElement(element);
451
+ this._visibleElements.splice(elementIndex, 0, element);
452
+ if (elementIndex < this._visibleElements.length - 1){
453
+ this._elementsContainerView.insertSubviewBelowSibling(element.view, this._visibleElements[elementIndex + 1].view);
454
+ }else{
455
+ this._elementsContainerView.addSubview(element.view);
456
+ }
457
+ }else{
458
+ element.attributes = attrs;
459
+ element.view.applyAttributes(attrs);
460
+ }
461
+ ++elementIndex;
462
+ }
463
+
464
+ // 4. Remove views that were not reused
465
+ this._removeQueuedCells();
466
+ this._removeQueuedViews();
467
+ },
468
+
469
+ _createViewForVisibleElement: function(element){
470
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.cell){
471
+ element.view = this._createCellWithAttributes(element.attributes);
472
+ }else if (element.attributes.elementCategory === UICollectionView.ElementCategory.supplimentary){
473
+ element.view = this._createSupplimentaryViewWithAttributes(element.attributes);
474
+ }
475
+ },
476
+
477
+ _createCellWithAttributes: function(attributes){
478
+ if (!this.dataSource.cellForCollectionViewAtIndexPath){
479
+ throw new Error("%s must implement cellForCollectionViewAtIndexPath()".sprintf(this.dataSource.$class.className));
480
+ }
481
+ var cell = this.dataSource.cellForCollectionViewAtIndexPath(this, JSIndexPath(attributes.indexPath));
482
+ if (cell === null || cell === undefined){
483
+ throw new Error("%s.cellForCollectionViewAtIndexPath() returned null/undefined cell for indexPath: %s".sprintf(this.dataSource.$class.className, attributes.indexPath));
484
+ }
485
+ this._adoptCell(cell, attributes);
486
+ cell.applyAttributes(attributes);
487
+ cell.active = false;
488
+ this._updateCellState(cell);
489
+ cell.update();
490
+ cell.setNeedsLayout();
491
+ return cell;
492
+ },
493
+
494
+ _adoptCell: function(cell, attributes){
495
+ cell.collectionView = this;
496
+ cell.indexPath = JSIndexPath(attributes.indexPath);
497
+ cell.accessibilityRowIndex = attributes.rowIndex;
498
+ cell.accessibilityColumnIndex = attributes.columnIndex;
499
+ cell.postAccessibilityElementChangedNotification();
500
+ },
501
+
502
+ _createSupplimentaryViewWithAttributes: function(attributes){
503
+ if (!this.dataSource.supplimentaryViewForCollectionViewAtIndexPath){
504
+ throw new Error("%s must implement supplimentaryViewForCollectionViewAtIndexPath()".sprintf(this.dataSource.$class.className));
505
+ }
506
+ var view = this.dataSource.supplimentaryViewForCollectionViewAtIndexPath(this, JSIndexPath(attributes.indexPath), attributes.kind);
507
+ if (view === null || view === undefined){
508
+ throw new Error("%s.supplimentaryViewForCollectionViewAtIndexPath() returned null/undefined cell for indexPath: %s".sprintf(this.dataSource.$class.className, attributes.indexPath));
509
+ }
510
+ view.collectionView = this;
511
+ view.applyAttributes(attributes);
512
+ view.setNeedsLayout();
513
+ return view;
514
+ },
515
+
516
+ sizeToFitSize: function(maxSize){
517
+ var bounds = JSRect(this.bounds.origin, this.contentSize);
518
+ if (maxSize.width < Number.MAX_VALUE){
519
+ bounds.size.width = maxSize.width;
520
+ }
521
+ if (bounds.size.height > maxSize.height){
522
+ bounds.size.height = maxSize.height;
523
+ }
524
+ this.bounds = bounds;
525
+ },
526
+
527
+ // --------------------------------------------------------------------
528
+ // MARK: - Responder
529
+
530
+ canBecomeFirstResponder: function(){
531
+ return true;
532
+ },
533
+
534
+ becomeFirstResponder: function(){
535
+ this._updateVisibleCellStyles();
536
+ },
537
+
538
+ resignFirstResponder: function(){
539
+ this._updateVisibleCellStyles();
540
+ },
541
+
542
+ windowDidChangeKeyStatus: function(){
543
+ this._updateVisibleCellStyles();
544
+ },
545
+
546
+ keyDown: function(event){
547
+ // TODO:
548
+ var hasSelection = this._selectedIndexPaths.length > 0;
549
+ var extend;
550
+ if (hasSelection){
551
+ if (event.key == UIEvent.Key.up){
552
+ extend = (this.allowsMultipleSelection && this._selectionAnchorIndexPath && event.hasModifier(UIEvent.Modifier.shift));
553
+ this._selectPreviousRow(extend, {notifyDelegate: true});
554
+ }else if (event.key == UIEvent.Key.down){
555
+ extend = (this.allowsMultipleSelection && this._selectionAnchorIndexPath && event.hasModifier(UIEvent.Modifier.shift));
556
+ this._selectNextRow(extend, {notifyDelegate: true});
557
+ }else if (event.key == UIEvent.Key.enter){
558
+ if (this.delegate && this.delegate.collectionViewDidOpenCellAtIndexPath){
559
+ var indexPath = this.selectedIndexPath;
560
+ if (indexPath !== null){
561
+ this.delegate.collectionViewDidOpenCellAtIndexPath(this, indexPath);
562
+ }
563
+ }
564
+ }else{
565
+ UICollectionView.$super.keyDown.call(this, event);
566
+ }
567
+ }else{
568
+ UICollectionView.$super.keyDown.call(this, event);
569
+ }
570
+ },
571
+
572
+ keyUp: function(event){
573
+ UICollectionView.$super.keyUp.call(this, event);
574
+ },
575
+
576
+ canPerformAction: function(action, sender){
577
+ if (action == 'selectAll'){
578
+ return this.allowsMultipleSelection;
579
+ }
580
+ return UICollectionView.$super.canPerformAction.call(this, action, sender);
581
+ },
582
+
583
+ // --------------------------------------------------------------------
584
+ // MARK: - Selecting cells
585
+
586
+ allowsMultipleSelection: false,
587
+ allowsEmptySelection: true,
588
+ selectedIndexPaths: JSDynamicProperty('_selectedIndexPaths', null),
589
+ selectedIndexPath: JSDynamicProperty(),
590
+ contextSelectedIndexPaths: JSReadOnlyProperty('_contextSelectedIndexPaths', null),
591
+
592
+ setSelectedIndexPaths: function(selectedIndexPaths){
593
+ this._setSelectedIndexPaths(selectedIndexPaths, {notifyDelegate: false});
594
+ },
595
+
596
+ _setSelectedIndexPaths: function(selectedIndexPaths, options){
597
+ var indexPaths = JSCopy(selectedIndexPaths);
598
+ indexPaths.sort(JSIndexPath.compare);
599
+ if (indexPaths.length === this._selectedIndexPaths.length){
600
+ for (var i = 0, l = this._selectedIndexPaths.length; i < l; ++i){
601
+ if (!indexPaths[i].isEqual(this._selectedIndexPaths[i])){
602
+ break;
603
+ }
604
+ }
605
+ if (i === l){
606
+ return;
607
+ }
608
+ }
609
+ if (!this.allowsEmptySelection && indexPaths.length === 0){
610
+ return;
611
+ }
612
+ this._selectedIndexPaths = indexPaths;
613
+ // TODO: editing
614
+ // if (this._edit !== null){
615
+ // this._edit.selectionChanged = true;
616
+ // }
617
+ this._updateSelectedIndexPaths(options);
618
+ },
619
+
620
+ _updateSelectedIndexPaths: function(options){
621
+ var singleIndexPath = this.selectedIndexPath;
622
+ if (singleIndexPath !== null){
623
+ this._selectionAnchorIndexPath = singleIndexPath;
624
+ }
625
+ if (options.animated){
626
+ var animator = UIViewPropertyAnimator.initWithDuration(0.3);
627
+ animator.addAnimations(function(){
628
+ this._updateVisibleCellStates();
629
+ }, this);
630
+ animator.start();
631
+ }else{
632
+ this._updateVisibleCellStates();
633
+ }
634
+ if (options.notifyDelegate){
635
+ if (singleIndexPath !== null){
636
+ if (singleIndexPath !== null && this.delegate && this.delegate.collectionViewDidSelectCellAtIndexPath){
637
+ this.delegate.collectionViewDidSelectCellAtIndexPath(this, singleIndexPath);
638
+ }
639
+ }
640
+ if (this.delegate && this.delegate.collectionViewSelectionDidChange){
641
+ this.delegate.collectionViewSelectionDidChange(this, this._selectedIndexPaths);
642
+ }
643
+ }
644
+ this.postAccessibilityNotification(UIAccessibility.Notification.selectedChildrenChanged);
645
+ },
646
+
647
+ getSelectedIndexPath: function(){
648
+ if (this._selectedIndexPaths.length === 1){
649
+ return this._selectedIndexPaths[0];
650
+ }
651
+ return null;
652
+ },
653
+
654
+ setSelectedIndexPath: function(indexPath){
655
+ var indexPaths;
656
+ if (indexPath === null){
657
+ indexPaths = [];
658
+ }else{
659
+ indexPaths = [indexPath];
660
+ }
661
+ this._setSelectedIndexPaths(indexPaths, {notifyDelegate: false});
662
+ },
663
+
664
+ setSelectedIndexPathAnimated: function(indexPath){
665
+ var indexPaths;
666
+ if (indexPath === null){
667
+ indexPaths = [];
668
+ }else{
669
+ indexPaths = [indexPath];
670
+ }
671
+ this._setSelectedIndexPaths(indexPaths, {notifyDelegate: false, animated: true});
672
+ },
673
+
674
+ addIndexPathToSelection: function(indexPath){
675
+ this._addIndexPathToSelection(indexPath, {notifyDelegate: false});
676
+ },
677
+
678
+ _addIndexPathToSelection: function(indexPath, options){
679
+ var searcher = JSBinarySearcher(this._selectedIndexPaths, JSIndexPath.compare);
680
+ var index = searcher.insertionIndexForValue(indexPath);
681
+ if (index < this._selectedIndexPaths.length && this._selectedIndexPaths[index].isEqual(indexPath)){
682
+ return;
683
+ }
684
+ this._selectedIndexPaths.splice(index, 0, JSIndexPath(indexPath));
685
+ this._updateSelectedIndexPaths(options);
686
+ },
687
+
688
+ removeIndexPathFromSelection: function(indexPath){
689
+ this._removeIndexPathFromSelection(indexPath, {notifyDelegate: false});
690
+ },
691
+
692
+ _removeIndexPathFromSelection: function(indexPath, options){
693
+ var searcher = JSBinarySearcher(this._selectedIndexPaths, JSIndexPath.compare);
694
+ var index = searcher.indexMatchingValue(indexPath);
695
+ if (index === null){
696
+ return;
697
+ }
698
+ if (!this.allowsEmptySelection && this._selectedIndexPaths.length === 1){
699
+ return;
700
+ }
701
+ this._selectedIndexPaths.splice(index, 1);
702
+ this._updateSelectedIndexPaths(options);
703
+ },
704
+
705
+ selectAll: function(sender){
706
+ // TODO:
707
+ if (!this.allowsMultipleSelection){
708
+ return;
709
+ }
710
+ var indexPaths = [];
711
+ var indexPath = this._firstSelectableIndexPath();
712
+ for (; indexPath !== null; indexPath = this.selectableIndexPathAfter(indexPath)){
713
+ indexPaths.push(indexPath);
714
+ }
715
+ this._setSelectedIndexPaths(indexPaths, {notifyDelegate: sender !== undefined});
716
+ },
717
+
718
+ selectNone: function(){
719
+ this._setSelectedIndexPaths([], {notifyDelegate: false});
720
+ },
721
+
722
+ // TODO: select next/prev (whatever that means here)
723
+
724
+ _updateVisibleCellStyles: function(){
725
+ var element;
726
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
727
+ element = this._visibleElements[i];
728
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.cell){
729
+ element.view.update();
730
+ }
731
+ }
732
+ },
733
+
734
+ _updateVisibleCellStates: function(){
735
+ var item;
736
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
737
+ item = this._visibleElements[i];
738
+ if (item.attributes.elementCategory === UICollectionView.ElementCategory.cell){
739
+ this._updateCellState(item.view);
740
+ }
741
+ }
742
+ },
743
+
744
+ _updateCellState: function(cell){
745
+ cell.selected = this._selectionContainsIndexPath(cell.indexPath);
746
+ cell.contextSelected = this._contextSelectionContainsIndexPath(cell.indexPath);
747
+ },
748
+
749
+ _selectionContainsIndexPath: function(indexPath){
750
+ var searcher = JSBinarySearcher(this._selectedIndexPaths, JSIndexPath.compare);
751
+ return searcher.indexMatchingValue(indexPath) !== null;
752
+ },
753
+
754
+ _contextSelectionContainsIndexPath: function(indexPath){
755
+ var searcher = JSBinarySearcher(this._contextSelectedIndexPaths, JSIndexPath.compare);
756
+ return searcher.indexMatchingValue(indexPath) !== null;
757
+ },
758
+
759
+ // --------------------------------------------------------------------
760
+ // MARK: - Mouse Events
761
+
762
+ _activeCell: null,
763
+ _shouldDrag: false,
764
+ _didDrag: false,
765
+ _selectionAnchorIndexPath: null,
766
+ _handledSelectionOnDown: false,
767
+
768
+ mouseDown: function(event){
769
+ var location = event.locationInView(this);
770
+ var cell = this._cellHitTest(location);
771
+ this.window.firstResponder = this;
772
+ this._activeCell = null;
773
+ if (cell === null){
774
+ this._setSelectedIndexPaths([], {notifyDelegate: true});
775
+ return;
776
+ }
777
+ var shouldSelect = !this.delegate || !this.delegate.collectionViewShouldSelectCellAtIndexPath || this.delegate.collectionViewShouldSelectCellAtIndexPath(this, cell.indexPath);
778
+ if (!shouldSelect){
779
+ return;
780
+ }
781
+ cell.active = true;
782
+ this._activeCell = cell;
783
+ this._didDrag = false;
784
+ // command key takes precedence over other modifiers, like shift (observed behavior)
785
+ if (event.hasModifier(UIPlatform.shared.commandModifier)){
786
+ this._handledSelectionOnDown = true;
787
+ if (this._selectionContainsIndexPath(cell.indexPath)){
788
+ this._removeIndexPathFromSelection(cell.indexPath, {notifyDelegate: true});
789
+ // TODO: set anchor to "nearest" selected cell (could be biased in one direction, even if next selected cell is far)
790
+ this._selectionAnchorIndexPath = null;
791
+ }else if (this.allowsMultipleSelection){
792
+ this._addIndexPathToSelection(cell.indexPath, {notifyDelegate: true});
793
+ this._selectionAnchorIndexPath = cell.indexPath;
794
+ }else{
795
+ this._setSelectedIndexPaths([cell.indexPath], {notifyDelegate: true});
796
+ }
797
+ }else if (this._selectionAnchorIndexPath !== null && this.allowsMultipleSelection && event.hasModifier(UIEvent.Modifier.shift)){
798
+ // TODO:
799
+ // this._handledSelectionOnDown = true;
800
+ // this._adjustSelectionAnchorRange(this._selectionAnchorIndexPath, cell.indexPath, {notifyDelegate: true});
801
+ }else{
802
+ this._shouldDrag = this.delegate && this.delegate.collectionViewShouldDragCellAtIndexPath && this.delegate.collectionViewShouldDragCellAtIndexPath(this, cell.indexPath);
803
+ if (this._shouldDrag){
804
+ this._handledSelectionOnDown = false;
805
+ }else{
806
+ this._handledSelectionOnDown = true;
807
+ this._setSelectedIndexPaths([cell.indexPath], {notifyDelegate: true});
808
+ }
809
+ }
810
+ },
811
+
812
+ rightMouseDown: function(event){
813
+ var location = event.locationInView(this);
814
+ var cell = this._cellHitTest(location);
815
+ if (cell === null){
816
+ return;
817
+ }
818
+ if (this.delegate && this.delegate.menuForCollectionViewCellAtIndexPath){
819
+ var contextSelectedIndexPaths;
820
+ if (this._selectionContainsIndexPath(cell.indexPath)){
821
+ contextSelectedIndexPaths = JSCopy(this._selectedIndexPaths);
822
+ }else{
823
+ contextSelectedIndexPaths = [cell.indexPath];
824
+ }
825
+ var menu = this.delegate.menuForCollectionViewCellAtIndexPath(this, cell.indexPath, contextSelectedIndexPaths);
826
+ if (menu !== null){
827
+ this._contextSelectedIndexPaths = contextSelectedIndexPaths;
828
+ this._updateVisibleCellStates();
829
+ var locationInCell = this.convertPointToView(location, cell);
830
+ menu.delegate = this;
831
+ menu.openAtLocationInContextView(locationInCell, cell);
832
+ }
833
+ }
834
+ },
835
+
836
+ menuDidClose: function(menu){
837
+ this._contextSelectedIndexPaths = [];
838
+ this._updateVisibleCellStates();
839
+ },
840
+
841
+ mouseDragged: function(event){
842
+ var location = event.locationInView(this);
843
+ var cell = this._cellHitTest(location);
844
+ if (this._shouldDrag){
845
+ var dragItems = [];
846
+ if (cell !== null){
847
+ var cellItems = [];
848
+ if (this.allowsMultipleSelection && this._selectionContainsIndexPath(cell.indexPath)){
849
+ if (this.delegate && this.delegate.pasteboardItemsForCollectionViewAtIndexPath){
850
+ var indexPath;
851
+ for (var i = 0, l = this._selectedIndexPaths.length; i < l; ++i){
852
+ indexPath = this._selectedIndexPaths[i];
853
+ cellItems = this.delegate.pasteboardItemsForCollectionViewAtIndexPath(this, indexPath);
854
+ if (cellItems !== null){
855
+ dragItems = dragItems.concat(cellItems);
856
+ }
857
+ }
858
+ }
859
+ }else{
860
+ if (this.delegate && this.delegate.pasteboardItemsForCollectionViewAtIndexPath){
861
+ cellItems = this.delegate.pasteboardItemsForCollectionViewAtIndexPath(this, cell.indexPath);
862
+ if (cellItems !== null){
863
+ dragItems = cellItems;
864
+ }
865
+ }
866
+ }
867
+ }
868
+ if (dragItems.length > 0){
869
+ var session = this.beginDraggingSessionWithItems(dragItems, event);
870
+ if (this.delegate && this.delegate.collectionViewWillBeginDraggingSession){
871
+ this.delegate.collectionViewWillBeginDraggingSession(this, session);
872
+ }
873
+ }
874
+ }else{
875
+ // TODO: scrolling (see UITextEditor for similar use case)
876
+ if (cell !== this._activeCell){
877
+ var shouldSelect = !cell || !this.delegate.collectionViewShouldSelectCellAtIndexPath || this.delegate.collectionViewShouldSelectCellAtIndexPath(this, cell.indexPath);
878
+ if (shouldSelect){
879
+ if (this._activeCell !== null){
880
+ this._activeCell.active = false;
881
+ }
882
+ this._activeCell = cell;
883
+ if (this._activeCell !== null){
884
+ this._activeCell.active = true;
885
+ }
886
+ if (cell){
887
+ if (this.allowsMultipleSelection){
888
+ // TODO:
889
+ // this._adjustSelectionAnchorRange(this._selectionAnchorIndexPath, cell.indexPath, {notifyDelegate: true});
890
+ }else{
891
+ if (!cell.selected){
892
+ this._setSelectedIndexPaths([cell.indexPath], {notifyDelegate: true});
893
+ }
894
+ }
895
+ }
896
+ }
897
+ }
898
+ }
899
+ },
900
+
901
+ draggingSessionDidBecomeActive: function(session){
902
+ this._didDrag = true;
903
+ },
904
+
905
+ mouseUp: function(event){
906
+ if (this._didDrag){
907
+ this._didDrag = false;
908
+ return;
909
+ }
910
+ if (this._activeCell === null){
911
+ return;
912
+ }
913
+ var cell = this._activeCell;
914
+ this._activeCell.active = false;
915
+ this._activeCell = null;
916
+ if (event.clickCount == 2){
917
+ if (this.delegate && this.delegate.collectionViewDidOpenCellAtIndexPath){
918
+ this.delegate.collectionViewDidOpenCellAtIndexPath(this, cell.indexPath);
919
+ }
920
+ }else{
921
+ var cellFrame = this.contentView.convertRectFromView(cell.bounds, cell);
922
+ if (cellFrame.origin.y < this.contentView.bounds.origin.y){
923
+ this.scrollToCellAtIndexPath(cell.indexPath, UICollectionView.ScrollPosition.top);
924
+ }else if (cellFrame.origin.y + cellFrame.size.height > this.contentView.bounds.origin.y + this.contentView.bounds.size.height){
925
+ this.scrollToCellAtIndexPath(cell.indexPath, UICollectionView.ScrollPosition.bottom);
926
+ }
927
+ if (this._handledSelectionOnDown){
928
+ this._handledSelectionOnDown = false;
929
+ if (this.delegate && this.delegate.collectionViewDidFinishSelectingCellAtIndexPath){
930
+ this.delegate.collectionViewDidFinishSelectingCellAtIndexPath(this, cell.indexPath);
931
+ }
932
+ return;
933
+ }
934
+ var shouldSelect = !this.delegate.collectionViewShouldSelectCellAtIndexPath || this.delegate.collectionViewShouldSelectCellAtIndexPath(this, cell.indexPath);
935
+ if (shouldSelect){
936
+ this._setSelectedIndexPaths([cell.indexPath], {notifyDelegate: true});
937
+ if (this.delegate && this.delegate.collectionViewDidFinishSelectingCellAtIndexPath){
938
+ this.delegate.collectionViewDidFinishSelectingCellAtIndexPath(this, cell.indexPath);
939
+ }
940
+ }
941
+ }
942
+ },
943
+
944
+ // --------------------------------------------------------------------
945
+ // MARK: - Touch Events
946
+
947
+ _touch: null,
948
+
949
+ _cancelTouchSelection: function(){
950
+ if (this._touch.cell){
951
+ this._touch.cell.active = false;
952
+ }
953
+ if (this._touch.timer !== null){
954
+ this._touch.timer.invalidate();
955
+ }
956
+ this._touch = null;
957
+ },
958
+
959
+ _makeTouchActiveCell: function(touch){
960
+ var location = touch.locationInView(this);
961
+ var cell = this._cellHitTest(location);
962
+ if (cell === null){
963
+ return;
964
+ }
965
+ var shouldSelect = !this.delegate || !this.delegate.collectionViewShouldSelectCellAtIndexPath || this.delegate.collectionViewShouldSelectCellAtIndexPath(this, cell.indexPath);
966
+ if (!shouldSelect){
967
+ return;
968
+ }
969
+ cell.active = true;
970
+ this._touch.cell = cell;
971
+ },
972
+
973
+ touchesBegan: function(touches, event){
974
+ UICollectionView.$super.touchesBegan.call(this, touches, event);
975
+ if (touches.length > 1){
976
+ return;
977
+ }
978
+ this._touch = {
979
+ location0: touches[0].locationInView(this),
980
+ cell: null,
981
+ timer: JSTimer.scheduledTimerWithInterval(0.05, function(){
982
+ this._touch.timer = null;
983
+ this._makeTouchActiveCell(touches[0]);
984
+ }, this)
985
+ };
986
+ },
987
+
988
+ touchesMoved: function(touches, event){
989
+ UICollectionView.$super.touchesMoved.call(this, touches, event);
990
+ if (this._touch !== null){
991
+ var location = touches[0].locationInView(this);
992
+ var diff = location.subtracting(this._touch.location0);
993
+ if ((diff.x * diff.x + diff.y * diff.y) > 2){
994
+ this._cancelTouchSelection();
995
+ }
996
+ }
997
+ },
998
+
999
+ touchesEnded: function(touches, event){
1000
+ UICollectionView.$super.touchesEnded.call(this, touches, event);
1001
+ if (touches.length > 1){
1002
+ return;
1003
+ }
1004
+ if (this._touch !== null){
1005
+ if (this._touch.timer !== null){
1006
+ this._touch.timer.invalidate();
1007
+ this._makeTouchActiveCell(touches[0]);
1008
+ }
1009
+ if (this._touch.cell){
1010
+ this._touch.cell.active = false;
1011
+ this._setSelectedIndexPaths([this._touch.cell.indexPath], {notifyDelegate: true});
1012
+ }
1013
+ this._touch = null;
1014
+ }
1015
+ },
1016
+
1017
+ touchesCanceled: function(touches, event){
1018
+ UICollectionView.$super.touchesCanceled.call(this, touches, event);
1019
+ this._cancelTouchSelection();
1020
+ },
1021
+
1022
+ // --------------------------------------------------------------------
1023
+ // MARK: - Finding Cells by Location
1024
+
1025
+ _cellHitTest: function(location){
1026
+ var i;
1027
+ var element;
1028
+ var locationInSubview;
1029
+ for (i = this._visibleElements.length - 1; i >= 0; --i){
1030
+ element = this._visibleElements[i];
1031
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.supplimentary){
1032
+ locationInSubview = this.layer.convertPointToLayer(location, element.view.layer);
1033
+ if (element.view.containsPoint(locationInSubview)){
1034
+ return null;
1035
+ }
1036
+ }
1037
+ }
1038
+ return this.cellAtLocation(location);
1039
+ },
1040
+
1041
+ indexPathAtLocation: function(location){
1042
+ var cell = this.cellAtLocation(location);
1043
+ if (cell !== null){
1044
+ return cell.indexPath;
1045
+ }
1046
+ return null;
1047
+ },
1048
+
1049
+ indexPathOfCell: function(cell){
1050
+ return cell.indexPath;
1051
+ },
1052
+
1053
+ cellAtLocation: function(location){
1054
+ // For internal cell hit testing, see `_cellHitTest()`, which considers overlaid headers.
1055
+ if (!this.containsPoint(location)){
1056
+ return null;
1057
+ }
1058
+ // While a binary search over the visible items would be a bit faster,
1059
+ // the list may contain deleted items that should not be considered
1060
+ var locationInContainer = this.convertPointToView(location, this._elementsContainerView);
1061
+ var element;
1062
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
1063
+ element = this._visibleElements[i];
1064
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.cell && element.state !== UICollectionView.VisibleElementState.deleted && element.view.frame.containsPoint(locationInContainer)){
1065
+ return element.view;
1066
+ }
1067
+ }
1068
+ return null;
1069
+ },
1070
+
1071
+ cellAtIndexPath: function(indexPath){
1072
+ // While a binary search over the visible items would be a bit faster,
1073
+ // the list may contain deleted items that should not be considered
1074
+ var element;
1075
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
1076
+ element = this._visibleElements[i];
1077
+ if (element.attributes.elementCategory === UICollectionView.ElementCategory.cell && element.state !== UICollectionView.VisibleElementState.deleted && element.view.indexPath.isEqual(indexPath)){
1078
+ return element.view;
1079
+ }
1080
+ }
1081
+ return null;
1082
+ },
1083
+
1084
+ rectForCellAtIndexPath: function(indexPath){
1085
+ var attributes = this.collectionViewLayout.layoutAttributesForCellAtIndexPath(indexPath);
1086
+ return attributes.frame;
1087
+ },
1088
+
1089
+ visibleIndexPaths: JSReadOnlyProperty(),
1090
+
1091
+ getVisibleIndexPaths: function(){
1092
+ var indexPaths = [];
1093
+ var item;
1094
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
1095
+ item = this._visibleElements[i];
1096
+ if (item.attributes.elementCategory === UICollectionView.ElementCategory.cell){
1097
+ indexPaths.push(item.indexPath);
1098
+ }
1099
+ }
1100
+ return indexPaths;
1101
+ },
1102
+
1103
+ visibleCells: JSReadOnlyProperty(),
1104
+
1105
+ getVisibleCells: function(){
1106
+ var cells = [];
1107
+ var item;
1108
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
1109
+ item = this._visibleElements[i];
1110
+ if (item.attributes.elementCategory === UICollectionView.ElementCategory.cell){
1111
+ cells.push(item.view);
1112
+ }
1113
+ }
1114
+ return cells;
1115
+ },
1116
+
1117
+ // --------------------------------------------------------------------
1118
+ // MARK: - Scrolling
1119
+
1120
+ scrollToCellAtIndexPath: function(indexPath, position){
1121
+ if (position === undefined){
1122
+ position = UICollectionView.ScrollPosition.auto;
1123
+ }
1124
+ var rect = this.rectForCellAtIndexPath(indexPath);
1125
+ if (rect !== null){
1126
+ this.scrollToRect(rect, position);
1127
+ }
1128
+ },
1129
+
1130
+ // --------------------------------------------------------------------
1131
+ // MARK: - Acessibility
1132
+
1133
+ isAccessibilityElement: true,
1134
+ accessibilityRole: UIAccessibility.Role.grid,
1135
+
1136
+ getAccessibilityElements: function(){
1137
+ var views = [];
1138
+ for (var i = 0, l = this._visibleElements.length; i < l; ++i){
1139
+ views.push(this._visibleElements[i].view);
1140
+ }
1141
+ return views;
1142
+ },
1143
+
1144
+ showsFocusRing: false,
1145
+
1146
+ getFocusRingPath: function(){
1147
+ if (this.showsFocusRing){
1148
+ return UICollectionView.$super.getFocusRingPath.call(this);
1149
+ }
1150
+ return null;
1151
+ }
1152
+
1153
+ });
1154
+
1155
+
1156
+ UICollectionView.Styler = Object.create({}, {
1157
+ default: {
1158
+ configurable: true,
1159
+ get: function UICollectionView_getDefaultStyler(){
1160
+ var styler = UICollectionViewDefaultStyler.init();
1161
+ Object.defineProperty(this, 'default', {writable: true, value: styler});
1162
+ return styler;
1163
+ },
1164
+ set: function UICollectionView_setDefaultStyler(defaultStyler){
1165
+ Object.defineProperty(this, 'default', {writable: true, value: defaultStyler});
1166
+ }
1167
+ }
1168
+ });
1169
+
1170
+ UICollectionView.ElementCategory = {
1171
+ cell: 1,
1172
+ supplimentary: 2
1173
+ };
1174
+
1175
+ UICollectionView.VisibleElementState = {
1176
+ normal: 0,
1177
+ inserting: 1,
1178
+ deleting: 2,
1179
+ deleted: 3
1180
+ };
1181
+
1182
+ UICollectionView.ScrollPosition = UIScrollView.ScrollPosition;
1183
+
1184
+ JSClass("UICollectionViewStyler", JSObject, {
1185
+
1186
+ init: function(){
1187
+ },
1188
+
1189
+ initializeCollectionView: function(collectionView){
1190
+ },
1191
+
1192
+ initializeCell: function(cell, indexPath){
1193
+ },
1194
+
1195
+ initializeReusableView: function(view, indexPath){
1196
+ },
1197
+
1198
+ updateCell: function(cell, indexPath){
1199
+ },
1200
+
1201
+ layoutCell: function(cell){
1202
+ },
1203
+
1204
+ });
1205
+
1206
+
1207
+ JSClass("UICollectionViewDefaultStyler", UICollectionViewStyler, {
1208
+
1209
+ layoutCell: function(cell){
1210
+ cell.contentView.untransformedFrame = cell.bounds;
1211
+ }
1212
+
1213
+ });
1214
+
1215
+ var VisibleElement = function(attributes){
1216
+ if (this === undefined){
1217
+ return new VisibleElement(attributes);
1218
+ }
1219
+ this.attributes = attributes;
1220
+ this.indexPath = JSIndexPath(attributes.indexPath);
1221
+ this.state = UICollectionView.VisibleElementState.normal;
1222
+ };
1223
+
1224
+ VisibleElement.prototype = Object.create(Function.prototype, {
1225
+
1226
+ indexPath: { value: null, writable: true },
1227
+ rect: { value: null, writable: true, configurable: true },
1228
+ kind: { value: null, writable: true },
1229
+ view: {
1230
+ configurable: true,
1231
+ set: function(view){
1232
+ Object.defineProperty(this, 'view', {value: view});
1233
+ },
1234
+ get: function(){
1235
+ return null;
1236
+ }
1237
+ },
1238
+ state: { value: null, writable: true },
1239
+ animation: { value: null, writable: true },
1240
+
1241
+ updateViewIdentity: {
1242
+ value: function(){
1243
+ if (this.state === UICollectionView.VisibleElementState.deleting){
1244
+ return;
1245
+ }
1246
+ this.view.indexPath = JSIndexPath(this.indexPath);
1247
+ }
1248
+ }
1249
+
1250
+ });
1251
+
1252
+ VisibleElement.indexPathCompare = function VisibleElement_indexPathCompare(indexPath, element){
1253
+ return indexPath.compare(element.indexPath);
1254
+ };