@breakside/jskit 2026.8.0 → 2026.10.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 (72) 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/JSColor.js +5 -0
  7. package/Frameworks/Foundation.jsframework/JS/JSPath.js +482 -49
  8. package/Frameworks/Foundation.jsframework/JS/JSRect.js +12 -0
  9. package/Frameworks/Foundation.jsframework/JS/JSSpec.js +5 -1
  10. package/Frameworks/Foundation.jsframework/JS/String+JS.js +78 -2
  11. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  12. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  13. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  14. package/Info.json +2 -2
  15. package/Node/DocCommand.js +5 -2
  16. package/Node/Docs/DocClass.js +2 -2
  17. package/Node/Docs/DocComponent.js +6 -4
  18. package/Node/Docs/DocDictionaryProperty.js +2 -2
  19. package/Node/Docs/DocEnumOption.js +2 -2
  20. package/Node/Docs/DocFunction.js +2 -2
  21. package/Node/Docs/DocProperty.js +2 -2
  22. package/Node/Docs/DocProtocol.js +1 -1
  23. package/Node/Docs/DocSpec.js +1 -1
  24. package/Node/Docs/DocSpecProperty.js +2 -2
  25. package/Node/Docs/DocTopicBasedComponent.js +2 -2
  26. package/Node/Documentation.js +10 -2
  27. package/Node/HTMLBuilder.js +3 -1
  28. package/Node/HTMLProjectServer.js +39 -1
  29. package/Node/io.breakside.jskit-bundle.js +4 -4
  30. package/Resources/doc-default.css +4 -4
  31. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  32. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  33. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  34. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  35. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  36. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  37. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  38. package/Root/Frameworks/DOM/Info.yaml +1 -1
  39. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  40. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  41. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  42. package/Root/Frameworks/Foundation/JSColor.js +5 -0
  43. package/Root/Frameworks/Foundation/JSPath.js +482 -49
  44. package/Root/Frameworks/Foundation/JSRect.js +12 -0
  45. package/Root/Frameworks/Foundation/JSSpec.js +5 -1
  46. package/Root/Frameworks/Foundation/String+JS.js +78 -2
  47. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  48. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  49. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  50. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  51. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  52. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  53. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  54. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  55. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  56. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  57. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  58. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  59. package/Root/Frameworks/UIKit/UIApplication.js +5 -1
  60. package/Root/Frameworks/UIKit/UIHTMLApplication.js +24 -0
  61. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +17 -3
  62. package/Root/Frameworks/UIKit/UIKit.js +2 -0
  63. package/Root/Frameworks/UIKit/UIListView.js +11 -3
  64. package/Root/Frameworks/UIKit/UIOutlineView.js +65 -5
  65. package/Root/Frameworks/UIKit/UIPopupWindow.js +15 -5
  66. package/Root/Frameworks/UIKit/UITextAction.js +16 -0
  67. package/Root/Frameworks/UIKit/UITextActionsMenu.js +95 -0
  68. package/Root/Frameworks/UIKit/UITextEditor.js +57 -7
  69. package/Root/Frameworks/UIKit/UITextField.js +102 -0
  70. package/Root/Frameworks/UIKit/en.lproj/Localizable.strings.yaml +4 -1
  71. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  72. package/package.json +1 -1
@@ -1592,7 +1592,7 @@ String.printf_formatter = {
1592
1592
 
1593
1593
  x: function(arg, options){
1594
1594
  // TODO: obey any other options
1595
- var str = arg.toString(16);
1595
+ var str = Math.floor(arg).toString(16);
1596
1596
  if (options.width){
1597
1597
  if (options.zero){
1598
1598
  str = str.leftPaddedString('0', options.width);
@@ -1633,7 +1633,83 @@ String.printf_formatter = {
1633
1633
  if (Math.abs(arg) < 0.000001){
1634
1634
  arg = 0;
1635
1635
  }
1636
- return (arg !== null && arg !== undefined && arg.toString) ? arg.toString() : arg;
1636
+ var n = arg;
1637
+ if (n === null){
1638
+ return "null";
1639
+ }
1640
+ if (n === undefined){
1641
+ return "undefined";
1642
+ }
1643
+ if (isNaN(n)){
1644
+ return "NaN";
1645
+ }
1646
+ if (!isFinite(n)){
1647
+ if (n < 0){
1648
+ return "-Inf";
1649
+ }
1650
+ return "Inf";
1651
+ }
1652
+
1653
+ // Prefix & Suffix
1654
+ var negative = n < 0;
1655
+ var prefix = "";
1656
+ if (negative){
1657
+ n = -n;
1658
+ prefix = "-";
1659
+ }
1660
+
1661
+ // Integer
1662
+ var maximumFractionDigits = options.precision || 6;
1663
+ var minimumFractionDigits = options.precision || 0;
1664
+ var minimumIntegerDigits = options.zero ? 1 : 0;
1665
+ var integer = Math.floor(n);
1666
+ var fraction = n - integer;
1667
+ var maximumFraction = Math.pow(10, maximumFractionDigits);
1668
+ fraction = Math.round(fraction * maximumFraction);
1669
+ if (fraction >= maximumFraction){
1670
+ integer += 1;
1671
+ fraction = 0;
1672
+ }
1673
+ var str = (integer !== 0 || (minimumIntegerDigits === 0 && minimumFractionDigits === 0)) ? integer.toString() : "";
1674
+ var zeroFillCount = minimumIntegerDigits - str.length;
1675
+ var i, l;
1676
+ if (zeroFillCount > 0){
1677
+ var fill = "";
1678
+ for (i = 0; i < zeroFillCount; ++i){
1679
+ fill += "0";
1680
+ }
1681
+ str = fill + str;
1682
+ }
1683
+
1684
+ // Decimal
1685
+ if (maximumFraction > 0){
1686
+ if (minimumFractionDigits > 0 || fraction !== 0){
1687
+ if (minimumFractionDigits > 0 || fraction !== 0){
1688
+ str += ".";
1689
+ var fractionString = fraction.toString();
1690
+ while (fractionString.length < maximumFractionDigits){
1691
+ fractionString = "0" + fractionString;
1692
+ }
1693
+ while (fractionString.length > minimumFractionDigits && fractionString[fractionString.length - 1] === "0"){
1694
+ fractionString = fractionString.substr(0, fractionString.length - 1);
1695
+ }
1696
+ str += fractionString;
1697
+ }
1698
+ }
1699
+ }
1700
+ str = prefix + str;
1701
+ if (options.width){
1702
+ if (options.zero){
1703
+ str = str.leftPaddedString('0', options.width);
1704
+ }else if (options.left_justified){
1705
+ str = str.rightPaddedString(' ', options.width);
1706
+ }else{
1707
+ str = str.leftPaddedString(' ', options.width);
1708
+ }
1709
+ }else if (str === ""){
1710
+ str = "0";
1711
+ }
1712
+ return str;
1637
1713
  },
1638
1714
 
1639
1715
  b: function(arg, options){
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKitUI
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.NotificationKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2021 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.PDFKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.QRKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SearchKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SecurityKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKitTesting
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.TestKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.10.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -438,11 +438,15 @@ JSClass('UIApplication', UIResponder, {
438
438
  target = this.firstTargetForAction(action, target, sender);
439
439
  if (target !== null){
440
440
  actionLogger.debug("sendAction " + action + " to %{public}#%d from %{public}#%d", target.$class.className, target.objectID, sender.$class.className, sender.objectID);
441
- target[action](sender, event);
441
+ this._sendAction(action, target, sender, event);
442
442
  }
443
443
  }
444
444
  },
445
445
 
446
+ _sendAction: function(action, target, sender, event){
447
+ target[action](sender, event);
448
+ },
449
+
446
450
  targetForAction: function(action, sender){
447
451
  var target = UIApplication.$super.targetForAction.call(this, action, sender);
448
452
  if (target !== null){
@@ -140,6 +140,30 @@ JSClass("UIHTMLApplication", UIApplication, {
140
140
  return url;
141
141
  },
142
142
 
143
+ _sendAction: function(action, target, sender, event){
144
+ var application = this;
145
+ var window = this.domWindow;
146
+ if (action === "paste" && sender !== this.windowServer){
147
+ if (this.domWindow.navigator.clipboard){
148
+ logger.info("sending paste action with clipboard.readText");
149
+ window.navigator.clipboard.readText().then(function(text){
150
+ var strings = {};
151
+ strings[UIPasteboard.ContentType.plainText] = text;
152
+ UIPasteboard.general.withStrings(strings, function(){
153
+ UIHTMLApplication.$super._sendAction.call(application, action, target, sender, event);
154
+ });
155
+ }, function(e){
156
+ logger.warn("could not readText() from clipboard: %{error}", e);
157
+ });
158
+ return;
159
+ }
160
+ logger.info("sending paste action with document.execCommand");
161
+ this.domWindow.document.execCommand("paste");
162
+ return;
163
+ }
164
+ UIHTMLApplication.$super._sendAction.call(this, action, target, sender, event);
165
+ },
166
+
143
167
  addEventListeners: function(){
144
168
  this.domWindow.addEventListener("error", this);
145
169
  this.domWindow.addEventListener("unhandledrejection", this);
@@ -690,7 +690,7 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
690
690
  return;
691
691
  }
692
692
  UIPasteboard.general.withDataTransfer(e.clipboardData, function(){
693
- this.keyWindow.application.sendAction('cut');
693
+ this.keyWindow.application.sendAction('cut', null, this);
694
694
  }, this);
695
695
  },
696
696
 
@@ -702,7 +702,7 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
702
702
  return;
703
703
  }
704
704
  UIPasteboard.general.withDataTransfer(e.clipboardData, function(){
705
- this.keyWindow.application.sendAction('copy');
705
+ this.keyWindow.application.sendAction('copy', null, this);
706
706
  }, this);
707
707
  },
708
708
 
@@ -714,7 +714,7 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
714
714
  return;
715
715
  }
716
716
  UIPasteboard.general.withDataTransfer(e.clipboardData, function(){
717
- this.keyWindow.application.sendAction('paste');
717
+ this.keyWindow.application.sendAction('paste', null, this);
718
718
  }, this);
719
719
  },
720
720
 
@@ -1513,6 +1513,20 @@ JSClass("UIHTMLDataTransferPasteboard", UIPasteboard, {
1513
1513
  }
1514
1514
  return UIHTMLDataTransferPasteboard.$super.containsType.call(this, type);
1515
1515
  },
1516
+
1517
+ withStrings: function(strings, action, target){
1518
+ var locallySetTypes = JSCopy(this._locallySetTypes);
1519
+ try{
1520
+ for (var type in strings){
1521
+ this.setStringForType(strings[type], type);
1522
+ }
1523
+ action.call(target);
1524
+ }finally{
1525
+ this._locallySetTypes = locallySetTypes;
1526
+ this.dataTransfer = null;
1527
+ }
1528
+ },
1529
+
1516
1530
  });
1517
1531
 
1518
1532
 
@@ -79,6 +79,8 @@
79
79
  // #import "UITextAttachmentView.js"
80
80
  // #import "UITokenField.js"
81
81
  // #import "UITokenFieldDelegate.js"
82
+ // #import "UITextAction.js"
83
+ // #import "UITextActionsMenu.js"
82
84
 
83
85
  // Controls
84
86
  // #import "UIControl.js"
@@ -175,6 +175,7 @@ JSClass("UIListView", UIScrollView, {
175
175
 
176
176
  delegate: null,
177
177
  dataSource: null,
178
+ _dataSourceIsValid: false,
178
179
 
179
180
  // --------------------------------------------------------------------
180
181
  // MARK: - Styling
@@ -325,6 +326,7 @@ JSClass("UIListView", UIScrollView, {
325
326
  return;
326
327
  }
327
328
  this._needsReload = true;
329
+ this._dataSourceIsValid = false;
328
330
  this.setNeedsLayout();
329
331
  },
330
332
 
@@ -579,6 +581,7 @@ JSClass("UIListView", UIScrollView, {
579
581
  selectionChanged: false,
580
582
  };
581
583
  this._needsUpdate = true;
584
+ this._dataSourceIsValid = false;
582
585
  this.setNeedsLayout();
583
586
  }
584
587
  if (animation != UIListView.RowAnimation.none && this._edit.animator === null){
@@ -655,6 +658,7 @@ JSClass("UIListView", UIScrollView, {
655
658
 
656
659
  layoutSubviews: function(){
657
660
  UIListView.$super.layoutSubviews.call(this);
661
+ this._dataSourceIsValid = true;
658
662
  var origin = JSPoint.Zero;
659
663
  var fitSize = JSSize(this.bounds.size.width, Number.MAX_VALUE);
660
664
  // We have to size the header and footer first, so a reloadDuringLayout
@@ -1121,6 +1125,7 @@ JSClass("UIListView", UIScrollView, {
1121
1125
  this.__numberOfSections = this.__numberOfSections + edit.insertedSections.length - edit.deletedSections.length;
1122
1126
  this._updateVisibleIndexPathsForEdit(edit);
1123
1127
  this._updateSelectedIndexPathsForEdit(edit);
1128
+ this._updateOtherIndexPathsForEdit(edit);
1124
1129
  if (edit.didDeleteSelectedItem){
1125
1130
  this._updateSelectedIndexPaths({notifyDelegate: true});
1126
1131
  }
@@ -1466,6 +1471,9 @@ JSClass("UIListView", UIScrollView, {
1466
1471
  }
1467
1472
  },
1468
1473
 
1474
+ _updateOtherIndexPathsForEdit: function(edit){
1475
+ },
1476
+
1469
1477
  _createViewsForEdit: function(edit, anchorIndex, anchorY, rect){
1470
1478
  var diff = {offset: 0, height: 0};
1471
1479
  var items = this._visibleItems;
@@ -1597,7 +1605,7 @@ JSClass("UIListView", UIScrollView, {
1597
1605
  cell.position = JSPoint(rect.origin.x + cell.bounds.size.width * cell.anchorPoint.x, rect.origin.y + cell.bounds.size.height * cell.anchorPoint.y);
1598
1606
  cell.active = false;
1599
1607
  cell.over = false;
1600
- this._updateCellState(cell);
1608
+ this._updateCellState(cell, true);
1601
1609
  cell.update();
1602
1610
  cell.setNeedsLayout();
1603
1611
  return cell;
@@ -2002,12 +2010,12 @@ JSClass("UIListView", UIScrollView, {
2002
2010
  for (var i = 0, l = this._visibleItems.length; i < l; ++i){
2003
2011
  item = this._visibleItems[i];
2004
2012
  if (item.kind === VisibleItem.Kind.cell){
2005
- this._updateCellState(item.view);
2013
+ this._updateCellState(item.view, this._dataSourceIsValid && item.indexPath !== null);
2006
2014
  }
2007
2015
  }
2008
2016
  },
2009
2017
 
2010
- _updateCellState: function(cell){
2018
+ _updateCellState: function(cell, dataSourceIsValid){
2011
2019
  cell.selected = this._selectionContainsIndexPath(cell.indexPath);
2012
2020
  cell.contextSelected = this._contextSelectionContainsIndexPath(cell.indexPath);
2013
2021
  },
@@ -102,7 +102,7 @@ JSClass("UIOutlineView", UIListView, {
102
102
  this._edit.insertedIndexPaths.push({indexPath: JSIndexPath(iterator.indexPath), animation:this.expandRowAnimation});
103
103
  }
104
104
  if (visibleCell){
105
- this._updateCellState(visibleCell);
105
+ this._updateCellState(visibleCell, true);
106
106
  visibleCell.postAccessibilityNotification(UIAccessibility.Notification.rowExpanded);
107
107
  }
108
108
  this.postAccessibilityNotification(UIAccessibility.Notification.rowCountChanged);
@@ -139,7 +139,7 @@ JSClass("UIOutlineView", UIListView, {
139
139
  }
140
140
  }
141
141
  if (visibleCell){
142
- this._updateCellState(visibleCell);
142
+ this._updateCellState(visibleCell, true);
143
143
  visibleCell.postAccessibilityNotification(UIAccessibility.Notification.rowCollapsed);
144
144
  }
145
145
  this.postAccessibilityNotification(UIAccessibility.Notification.rowCountChanged);
@@ -220,9 +220,11 @@ JSClass("UIOutlineView", UIListView, {
220
220
  UIOutlineView.$super.keyUp.call(this, event);
221
221
  },
222
222
 
223
- _updateCellState: function(cell){
224
- UIOutlineView.$super._updateCellState.call(this, cell);
225
- cell.expandable = this.dataSource.outlineViewIsExandableAtIndexPath(this, cell.indexPath);
223
+ _updateCellState: function(cell, dataSourceIsValid){
224
+ UIOutlineView.$super._updateCellState.call(this, cell, dataSourceIsValid);
225
+ if (dataSourceIsValid){
226
+ cell.expandable = this.dataSource.outlineViewIsExandableAtIndexPath(this, cell.indexPath);
227
+ }
226
228
  cell.expanded = cell.expandable && this._isIndexPathExpanded(cell.indexPath);
227
229
  },
228
230
 
@@ -252,6 +254,64 @@ JSClass("UIOutlineView", UIListView, {
252
254
  return row;
253
255
  },
254
256
 
257
+ _updateOtherIndexPathsForEdit: function(edit){
258
+ UIOutlineView.$super._updateOtherIndexPathsForEdit.call(this, edit);
259
+ var expandedIndexPaths = Array.from(this._expandedIndexPaths).map(function(indexPathString){
260
+ var indexes = indexPathString.split(".").map(function(indexString){
261
+ return parseInt(indexString);
262
+ });
263
+ return JSIndexPath(indexes);
264
+ });
265
+ expandedIndexPaths.sort(JSIndexPath.compare);
266
+ var i, l;
267
+ var section, indexPath;
268
+ var index;
269
+ var searcher = JSBinarySearcher(expandedIndexPaths, JSIndexPath.compare);
270
+ var parent;
271
+ var selectedLength = expandedIndexPaths.length;
272
+ for (i = edit.deletedIndexPaths.length - 1; i >= 0; --i){
273
+ indexPath = edit.deletedIndexPaths[i].indexPath;
274
+ parent = indexPath.removingLastIndex();
275
+ index = searcher.insertionIndexForValue(indexPath);
276
+ if (index < selectedLength && expandedIndexPaths[index].isEqual(indexPath)){
277
+ expandedIndexPaths.splice(index, 1);
278
+ --selectedLength;
279
+ }
280
+ for (; index < selectedLength && expandedIndexPaths[index].startsWith(parent); ++index){
281
+ expandedIndexPaths[index][parent.length]--;
282
+ }
283
+ }
284
+ for (i = edit.deletedSections.length - 1; i >= 0; --i){
285
+ section = edit.deletedSections[i].section;
286
+ index = searcher.insertionIndexForValue(JSIndexPath(section, 0));
287
+ for (; index < selectedLength && expandedIndexPaths[index].section == section; --selectedLength){
288
+ expandedIndexPaths.splice(index, 1);
289
+ }
290
+ for (; index < selectedLength; ++index){
291
+ expandedIndexPaths[index].section--;
292
+ }
293
+ }
294
+ for (i = 0, l = edit.insertedSections.length; i < l; ++i){
295
+ section = edit.insertedSections[i].section;
296
+ index = searcher.insertionIndexForValue(JSIndexPath(section, 0));
297
+ for (; index < selectedLength && expandedIndexPaths[index].section >= section; ++index){
298
+ expandedIndexPaths[index].section++;
299
+ }
300
+ }
301
+ for (i = 0, l = edit.insertedIndexPaths.length; i < l; ++i){
302
+ indexPath = edit.insertedIndexPaths[i].indexPath;
303
+ parent = indexPath.removingLastIndex();
304
+ index = searcher.insertionIndexForValue(indexPath);
305
+ for (; index < selectedLength && expandedIndexPaths[index].startsWith(parent); ++index){
306
+ expandedIndexPaths[index][parent.length]++;
307
+ }
308
+ }
309
+ this._expandedIndexPaths = new Set();
310
+ for (i = 0, l = expandedIndexPaths.length; i < l; ++i){
311
+ this._expandedIndexPaths.add(expandedIndexPaths[i].toString());
312
+ }
313
+ },
314
+
255
315
  // --------------------------------------------------------------------
256
316
  // MARK: - Acessibility
257
317
 
@@ -116,17 +116,27 @@ JSClass("UIPopupWindow", UIWindow, {
116
116
  },
117
117
 
118
118
  openAdjacentToView: function(view, preferredPlacement, animated){
119
- if (animated === undefined){
120
- animated = true;
119
+ var rect = view.bounds;
120
+ if (view.sourceRectForPopupWindow){
121
+ rect = view.sourceRectForPopupWindow(this);
121
122
  }
123
+ this.openAdjacentToRectInView(rect, view, preferredPlacement, animated);
124
+ },
125
+
126
+ openAtLocationInView: function(location, view, preferredPlacement, animated){
127
+ var rect = JSRect(location, JSSize.Zero);
128
+ this.openAdjacentToRectInView(rect, view, preferredPlacement, animated);
129
+ },
130
+
131
+ openAdjacentToRectInView: function(rect, view, preferredPlacement, animated){
122
132
  if (view.window === null || view.window.screen === null){
123
133
  JSNotificationCenter.shared.post("UIWindowWillClose", this);
124
134
  return;
125
135
  }
126
- var sourceBounds = view.bounds;
127
- if (view.sourceRectForPopupWindow){
128
- sourceBounds = view.sourceRectForPopupWindow(this);
136
+ if (animated === undefined){
137
+ animated = true;
129
138
  }
139
+ var sourceBounds = rect;
130
140
  var sourceFrame = view.convertRectToScreen(sourceBounds).rectWithInsets(-this._sourceSpacing);
131
141
  var safeFrame = view.window.screen.availableFrame.rectWithInsets(20);
132
142
 
@@ -0,0 +1,16 @@
1
+ // #import Foundation
2
+ "use strict";
3
+
4
+ JSClass("UITextAction", JSObject, {
5
+
6
+ title: null,
7
+ action: null,
8
+ target: null,
9
+
10
+ initWithTitle: function(title, action, target){
11
+ this.title = title;
12
+ this.action = action;
13
+ this.target = target || null;
14
+ },
15
+
16
+ });
@@ -0,0 +1,95 @@
1
+ // #import "UIPopupWindow.js"
2
+ // #import "UIButton.js"
3
+ // #import "UIStackView.js"
4
+ "use strict";
5
+
6
+ JSClass("UITextActionsMenu", JSObject, {
7
+
8
+ delegate: null,
9
+
10
+ initWithActions: function(actions){
11
+ this.actions = actions;
12
+ },
13
+
14
+ openAtLocationInView: function(location, sourceView){
15
+ if (this._window !== null){
16
+ this._window.orderFront();
17
+ }else{
18
+ var window = UITextActionsMenuWindow.init(UITextActionsMenu.Styler.default);
19
+ window.contentInsets = JSInsets.Zero;
20
+ var buttons = [];
21
+ var button;
22
+ var action;
23
+ for (var i = 0, l = this.actions.length; i < l; ++i){
24
+ action = this.actions[i];
25
+ button = UIButton.initWithStyler(UITextActionsMenu.ButtonStyler.default);
26
+ button.titleLabel.text = action.title;
27
+ button.addAction(action.action, action.target);
28
+ button.addAction("close", this);
29
+ buttons.push(button);
30
+ }
31
+ var stackView = UIStackView.initWithArrangedSubviews(buttons);
32
+ stackView.cornerRadius = window.cornerRadius;
33
+ stackView.clipsToBounds = true;
34
+ stackView.backgroundColor = JSColor.separator;
35
+ stackView.viewSpacing = 1;
36
+ stackView.axis = UIStackView.Axis.horizontal;
37
+ window.contentView = stackView;
38
+ window.openAtLocationInView(location, sourceView, UIPopupWindow.Placement.above, false);
39
+ this._window = window;
40
+ }
41
+ },
42
+
43
+ close: function(){
44
+ if (this._window !== null){
45
+ this._window.close();
46
+ this._window = null;
47
+ if (this.delegate && this.delegate.textActionsMenuDidClose){
48
+ this.delegate.textActionsMenuDidClose(this);
49
+ }
50
+ }
51
+ },
52
+
53
+ _window: null,
54
+
55
+ });
56
+
57
+ UITextActionsMenu.Styler = Object.create({}, {
58
+
59
+ default: {
60
+ configurable: true,
61
+ get: function(){
62
+ var styler = UIPopupWindowStyler.init();
63
+ styler.cornerRadius = 7;
64
+ Object.defineProperty(this, "default", {value: styler, writable: true});
65
+ return styler;
66
+ },
67
+ set: function(value){
68
+ Object.defineProperty(this, "default", {value: value, writable: true});
69
+ }
70
+ }
71
+
72
+ });
73
+
74
+ UITextActionsMenu.ButtonStyler = Object.create({}, {
75
+
76
+ default: {
77
+ configurable: true,
78
+ get: function(){
79
+ var styler = UIButtonCustomStyler.initWithBackgroundColor(JSColor.background, JSColor.highlight);
80
+ styler.titleInsets = JSInsets(10, 10, 10, 10);
81
+ Object.defineProperty(this, "default", {value: styler, writable: true});
82
+ return styler;
83
+ },
84
+ set: function(value){
85
+ Object.defineProperty(this, "default", {value: value, writable: true});
86
+ }
87
+ }
88
+
89
+ });
90
+
91
+ JSClass("UITextActionsMenuWindow", UIPopupWindow, {
92
+
93
+ canBecomeKey: false,
94
+
95
+ });