@breakside/jskit 2026.8.0 → 2026.9.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 (48) 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/JSRect.js +12 -0
  7. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  8. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  9. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  10. package/Info.json +2 -2
  11. package/Node/io.breakside.jskit-bundle.js +4 -4
  12. package/Resources/doc-default.css +8 -0
  13. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  14. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  15. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  16. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  17. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  18. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  19. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  20. package/Root/Frameworks/DOM/Info.yaml +1 -1
  21. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  22. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  23. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  24. package/Root/Frameworks/Foundation/JSRect.js +12 -0
  25. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  26. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  27. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  28. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  29. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  30. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  31. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  32. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  33. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  34. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  35. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  36. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  37. package/Root/Frameworks/UIKit/UIApplication.js +5 -1
  38. package/Root/Frameworks/UIKit/UIHTMLApplication.js +24 -0
  39. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +17 -3
  40. package/Root/Frameworks/UIKit/UIKit.js +2 -0
  41. package/Root/Frameworks/UIKit/UIPopupWindow.js +15 -5
  42. package/Root/Frameworks/UIKit/UITextAction.js +16 -0
  43. package/Root/Frameworks/UIKit/UITextActionsMenu.js +95 -0
  44. package/Root/Frameworks/UIKit/UITextEditor.js +57 -7
  45. package/Root/Frameworks/UIKit/UITextField.js +102 -0
  46. package/Root/Frameworks/UIKit/en.lproj/Localizable.strings.yaml +4 -1
  47. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  48. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
4
- "JSBundleVersion": "2026.8.0",
4
+ "JSBundleVersion": "2026.9.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "DOM+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
12
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
13
13
  }
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
6
- "JSBundleVersion": "2026.8.0",
6
+ "JSBundleVersion": "2026.9.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
11
11
  "node": "DOM+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
14
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
4
- "JSBundleVersion": "2026.8.0",
4
+ "JSBundleVersion": "2026.9.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
8
8
  "html": "FontKit+HTML.js"
9
9
  },
10
10
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
11
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
11
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
12
12
  }
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.JSKit.FontKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
6
- "JSBundleVersion": "2026.8.0",
6
+ "JSBundleVersion": "2026.9.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
10
10
  "html": "FontKit+HTML.js"
11
11
  },
12
12
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
13
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
13
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
14
14
  },
15
15
  "Resources": [],
16
16
  "ResourceLookup": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
4
- "JSBundleVersion": "2026.8.0",
4
+ "JSBundleVersion": "2026.9.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "Foundation+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
12
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
13
13
  }
@@ -77,6 +77,18 @@ JSRect.prototype = {
77
77
  return JSRect(origin, size);
78
78
  },
79
79
 
80
+ unioningRect: function(other){
81
+ var origin = JSPoint(
82
+ Math.min(this.origin.x, other.origin.x),
83
+ Math.min(this.origin.y, other.origin.y)
84
+ );
85
+ var size = JSSize(
86
+ Math.max(0, Math.max(this.origin.x + this.size.width, other.origin.x + other.size.width) - origin.x),
87
+ Math.max(0, Math.max(this.origin.y + this.size.height, other.origin.y + other.size.height) - origin.y)
88
+ );
89
+ return JSRect(origin, size);
90
+ },
91
+
80
92
  isEqual: function(other){
81
93
  return this.origin.isEqual(other.origin) && this.size.isEqual(other.size);
82
94
  },
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
6
- "JSBundleVersion": "2026.8.0",
6
+ "JSBundleVersion": "2026.9.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
11
11
  "node": "Foundation+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
14
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
15
15
  },
16
16
  "Resources": [
17
17
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
4
- "JSBundleVersion": "2026.8.0",
4
+ "JSBundleVersion": "2026.9.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "SecurityKit+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
12
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
13
13
  }
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
6
- "JSBundleVersion": "2026.8.0",
6
+ "JSBundleVersion": "2026.9.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
11
11
  "node": "SecurityKit+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
14
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
package/Info.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "node",
3
3
  "JSBundleIdentifier": "io.breakside.jskit",
4
- "JSBundleVersion": "2026.8.0",
4
+ "JSBundleVersion": "2026.9.0",
5
5
  "JSExecutableName": "jskit",
6
6
  "NPMOrganization": "breakside",
7
7
  "JSResources": [
8
8
  "Tests/HTMLTestRunner.js",
9
9
  "Tests/NodeTestRunner.js"
10
10
  ],
11
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
11
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
12
12
  }
@@ -3,22 +3,22 @@ JSBundle.bundles['io.breakside.jskit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "node",
5
5
  "JSBundleIdentifier": "io.breakside.jskit",
6
- "JSBundleVersion": "2026.8.0",
6
+ "JSBundleVersion": "2026.9.0",
7
7
  "JSExecutableName": "jskit",
8
8
  "NPMOrganization": "breakside",
9
9
  "JSResources": [
10
10
  "Tests/HTMLTestRunner.js",
11
11
  "Tests/NodeTestRunner.js"
12
12
  ],
13
- "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
13
+ "GitRevision": "7481a2557e5555dbf2a58e88ba6720b16de86277"
14
14
  },
15
15
  "Resources": [
16
16
  {
17
17
  "path": "doc-default.css",
18
18
  "ext": ".css",
19
- "byte_size": 10928,
19
+ "byte_size": 11088,
20
20
  "mimetype": "text/css",
21
- "hash": "d8198dd825ddbc89e00cbbbc1c575e08a101ecaa",
21
+ "hash": "fb20238b7c988f0d959bad2d071b06000068a39d",
22
22
  "nodeBundlePath": "Resources/doc-default.css"
23
23
  },
24
24
  {
@@ -21,6 +21,14 @@ article.doc h1,h2,h3,h4,h5,h5,b,strong{
21
21
  font-weight: 600;
22
22
  }
23
23
 
24
+ article.doc > section > header > h1, article.doc > section > h1{
25
+ font-size: 1.4em;
26
+ }
27
+
28
+ article.doc > section > section > header > h1{
29
+ font-size: 1em;
30
+ }
31
+
24
32
  article.doc a{
25
33
  text-decoration: none;
26
34
  color: rgb(90,90,200);
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSIncludeDirectories:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKitTesting
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.AuthKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.CSSOM
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.ChartKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.ConferenceKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.DBKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.DOM
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.Dispatch
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.0
4
4
  JSCopyright: Copyright © 2020 Breakside Inc.
5
5
  JSBundleEnvironments:
6
6
  html: Dispatch+HTML.js
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.FontKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.Foundation
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -77,6 +77,18 @@ JSRect.prototype = {
77
77
  return JSRect(origin, size);
78
78
  },
79
79
 
80
+ unioningRect: function(other){
81
+ var origin = JSPoint(
82
+ Math.min(this.origin.x, other.origin.x),
83
+ Math.min(this.origin.y, other.origin.y)
84
+ );
85
+ var size = JSSize(
86
+ Math.max(0, Math.max(this.origin.x + this.size.width, other.origin.x + other.size.width) - origin.x),
87
+ Math.max(0, Math.max(this.origin.y + this.size.height, other.origin.y + other.size.height) - origin.y)
88
+ );
89
+ return JSRect(origin, size);
90
+ },
91
+
80
92
  isEqual: function(other){
81
93
  return this.origin.isEqual(other.origin) && this.size.isEqual(other.size);
82
94
  },
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.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.9.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.9.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.9.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.9.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.9.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.9.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.9.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.9.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.9.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.9.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.9.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"
@@ -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
+ });
@@ -214,25 +214,69 @@ JSClass("UITextEditor", JSObject, {
214
214
  _touchBeganInfo: null,
215
215
 
216
216
  handleTouchesBeganAtLocation: function(location, touches, event){
217
- this._setSingleSelectionAtLocation(location);
217
+ var newSelection = this._createSelectionAtLocation(location);
218
+ var existingSelection = null;
219
+ for (var i = 0, l = this.selections.length; i < l; ++i){
220
+ if (this.selections[i].containsIndex(newSelection.range.location)){
221
+ existingSelection = this.selections[i];
222
+ break;
223
+ }
224
+ }
225
+ if (existingSelection === null){
226
+ this._setSingleSelectionAtLocation(location);
227
+ }
218
228
  this._touchBeganInfo = {
219
229
  timestamp: event.timestamp,
220
230
  location: location,
231
+ selection: existingSelection,
221
232
  moved: false
222
233
  };
223
234
  },
224
235
 
225
236
  handleTouchesMovedAtLocation: function(location, touches, event){
226
- this._setSingleSelectionAtLocation(location);
227
237
  if (this._touchBeganInfo !== null){
238
+ if (this._touchBeganInfo.selection === null){
239
+ this._setSingleSelectionAtLocation(location);
240
+ }
228
241
  this._touchBeganInfo.moved = this._touchBeganInfo.moved || location.distanceToPoint(this._touchBeganInfo.location) > 10;
242
+ if (this._touchBeganInfo.moved && this._touchBeganInfo.selection !== null){
243
+ var existingSelection = this._touchBeganInfo.selection;
244
+ var selection = this._createSelectionAtLocation(location);
245
+ if (selection.range.location < existingSelection.range.location){
246
+ selection.range.length = existingSelection.range.end - selection.range.location;
247
+ this._setSingleSelection(selection);
248
+ this._touchBeganInfo.selection = selection;
249
+ }else if (selection.range.location > existingSelection.range.end){
250
+ selection.range = JSRange(existingSelection.range.location, selection.range.location - existingSelection.range.location);
251
+ this._setSingleSelection(selection);
252
+ this._touchBeganInfo.selection = selection;
253
+ }else{
254
+ var dl = selection.range.location - existingSelection.range.location;
255
+ var de = existingSelection.range.end - selection.range.location;
256
+ if (dl > 0 && de > 0){
257
+ if (dl <= de){
258
+ selection.range.length = existingSelection.range.end - selection.range.location;
259
+ }else{
260
+ selection.range = JSRange(existingSelection.range.location, selection.range.location - existingSelection.range.location);
261
+ }
262
+ this._setSingleSelection(selection);
263
+ this._touchBeganInfo.selection = selection;
264
+ }
265
+ }
266
+ }
267
+ }else{
268
+ this._setSingleSelectionAtLocation(location);
229
269
  }
230
270
  },
231
271
 
232
272
  handleTouchesEndedAtLocation: function(location, touches, event){
233
- this._setSingleSelectionAtLocation(location);
234
273
  if (this._touchBeganInfo !== null){
274
+ if (this._touchBeganInfo.selection === null){
275
+ this._setSingleSelectionAtLocation(location);
276
+ }
235
277
  this._touchBeganInfo = null;
278
+ }else{
279
+ this._setSingleSelectionAtLocation(location);
236
280
  }
237
281
  },
238
282
 
@@ -241,10 +285,16 @@ JSClass("UITextEditor", JSObject, {
241
285
  if (this.selections.length === 1 && !this._touchBeganInfo.moved){
242
286
  if ((event.timestamp - this._touchBeganInfo.timestamp) > JSTimeInterval.seconds(0.5)){
243
287
  var selection = this.selections[0];
244
- var range = this.textLayoutManager.textStorage.string.rangeForWordAtIndex(this.selections[0].range.location);
245
- if (range.length > 0){
246
- var wordSelection = this._createSelection(range);
247
- this._setSingleSelection(wordSelection);
288
+ var updatedSelection;
289
+ if (this._touchBeganInfo.selection !== null){
290
+ updatedSelection = this._createSelection(JSRange(selection.range.location, 0));
291
+ this._setSingleSelection(updatedSelection);
292
+ }else{
293
+ var range = this.textLayoutManager.textStorage.string.rangeForWordAtIndex(selection.range.location);
294
+ if (range.length > 0){
295
+ updatedSelection = this._createSelection(range);
296
+ this._setSingleSelection(updatedSelection);
297
+ }
248
298
  }
249
299
  }
250
300
  }
@@ -22,6 +22,9 @@
22
22
  // #import "UIImageView.js"
23
23
  // #import "UIPasteboard.js"
24
24
  // #import "JSColor+UIKit.js"
25
+ // #import "UITextAction.js"
26
+ // #import "UIMenu.js"
27
+ // #import "UITextActionsMenu.js"
25
28
  'use strict';
26
29
 
27
30
  (function(){
@@ -794,6 +797,7 @@ JSClass("UITextField", UIControl, {
794
797
  },
795
798
 
796
799
  textEditorDidReplaceCharactersInRange: function(textEditor, range, insertedLength){
800
+ this.hideTextActionsMenu();
797
801
  if (this._isEmpty && insertedLength > 0){
798
802
  this._updateIsEmpty(false);
799
803
  }else if (!this._isEmpty && insertedLength === 0 && !this._textLayer.hasText()){
@@ -889,6 +893,7 @@ JSClass("UITextField", UIControl, {
889
893
  },
890
894
 
891
895
  resignFirstResponder: function(){
896
+ this.hideTextActionsMenu();
892
897
  this.active = false;
893
898
  this._updateAccessoryViewHidden(this._leftAccessoryView, this._leftAccessoryVisibility);
894
899
  this._updateAccessoryViewHidden(this._rightAccessoryView, this._rightAccessoryVisibility);
@@ -977,9 +982,27 @@ JSClass("UITextField", UIControl, {
977
982
  UITextField.$super.mouseUp.call(this, event);
978
983
  },
979
984
 
985
+ rightMouseDown: function(event){
986
+ var actions = this.textActions();
987
+ if (actions.length > 0){
988
+ var menu = UIMenu.init();
989
+ var action;
990
+ for (var i = 0, l = actions.length; i < l; ++i){
991
+ action = actions[i];
992
+ menu.addItemWithTitle(action.title, action.action, action.target);
993
+ }
994
+ var location = event.locationInView(this);
995
+ menu.openAtLocationInContextView(location, this);
996
+ return;
997
+ }
998
+ UITextField.$super.rightMouseDown.call(this, event);
999
+ },
1000
+
980
1001
  _touchOrigin: null,
1002
+ _touchesBeganSelections: null,
981
1003
 
982
1004
  touchesBegan: function(touches, event){
1005
+ this._touchesBeganSelections = null;
983
1006
  if (this.enabled){
984
1007
  var location = touches[0].locationInView(this);
985
1008
  this._touchOrigin = this.convertPointToView(this.bounds.origin, this.window);
@@ -989,6 +1012,7 @@ JSClass("UITextField", UIControl, {
989
1012
  // when mobile safari ignores focus() calls that aren't tied to
990
1013
  // user interaction
991
1014
  this.window.firstResponder = this;
1015
+ this._touchesBeganSelections = this.selections.map(function(s){ return UITextInputSelection(s)});
992
1016
  this._localEditor.handleTouchesBeganAtLocation(this.layer.convertPointToLayer(location, this._textLayer), touches, event);
993
1017
  return;
994
1018
  }
@@ -1012,6 +1036,26 @@ JSClass("UITextField", UIControl, {
1012
1036
  var location = touches[0].locationInView(this);
1013
1037
  if (this.isFirstResponder()){
1014
1038
  this._localEditor.handleTouchesEnded(touches, event);
1039
+ if (this.textActionsMenu !== null){
1040
+ this.hideTextActionsMenu();
1041
+ }else if (this._touchesBeganSelections !== null){
1042
+ var selections = this.selections;
1043
+ var match = true;
1044
+ if (selections.length === this._touchesBeganSelections.length){
1045
+ for (var i = 0, l = selections.length; i < l; ++i){
1046
+ if (!selections[i].range.isEqual(this._touchesBeganSelections[i].range)){
1047
+ match = false;
1048
+ break;
1049
+ }
1050
+ }
1051
+ }else{
1052
+ match = false;
1053
+ }
1054
+ if (match){
1055
+ this.showTextActionsMenu(location);
1056
+ }
1057
+ }
1058
+ this._touchesBeganSelections = null;
1015
1059
  return;
1016
1060
  }
1017
1061
  if (this._touchOrigin !== null){
@@ -1022,10 +1066,12 @@ JSClass("UITextField", UIControl, {
1022
1066
  }
1023
1067
  }
1024
1068
  }
1069
+ this._touchesBeganSelections = null;
1025
1070
  UITextField.$super.touchesEnded.call(this, touches, event);
1026
1071
  },
1027
1072
 
1028
1073
  touchesCanceled: function(touches, event){
1074
+ this._touchesBeganSelections = null;
1029
1075
  if (this.enabled){
1030
1076
  var location = touches[0].locationInView(this);
1031
1077
  this._localEditor.handleTouchesCanceled(touches, event);
@@ -1041,6 +1087,51 @@ JSClass("UITextField", UIControl, {
1041
1087
  return text;
1042
1088
  },
1043
1089
 
1090
+ // ----------------------------------------------------------------------
1091
+ // MARK: Text Actions
1092
+
1093
+ copyTextAction: JSLazyInitProperty(function(){
1094
+ return UITextAction.initWithTitle(UITextField.bundle.localizedString("textActions.copy"), "copy", this);
1095
+ }),
1096
+
1097
+ pasteTextAction: JSLazyInitProperty(function(){
1098
+ return UITextAction.initWithTitle(UITextField.bundle.localizedString("textActions.paste"), "paste", this);
1099
+ }),
1100
+
1101
+ textActions: function(){
1102
+ return [this.copyTextAction, this.pasteTextAction];
1103
+ },
1104
+
1105
+ textActionsMenu: null,
1106
+
1107
+ showTextActionsMenu: function(location){
1108
+ var responder = this;
1109
+ var actions = this.textActions().filter(function(textAction){
1110
+ var target = responder.targetForAction(textAction.action, null);
1111
+ return target !== null && target.canPerformAction(textAction.action, null);
1112
+ });
1113
+ if (actions.length === 0){
1114
+ return;
1115
+ }
1116
+ this.textActionsMenu = UITextActionsMenu.initWithActions(actions);
1117
+ this.textActionsMenu.delegate = this;
1118
+ this.textActionsMenu.openAtLocationInView(location, this);
1119
+ },
1120
+
1121
+ hideTextActionsMenu: function(){
1122
+ if (this.textActionsMenu !== null){
1123
+ this.textActionsMenu.close();
1124
+ this.textActionsMenu = null;
1125
+ }
1126
+ },
1127
+
1128
+ textActionsMenuDidClose: function(textActionsMenu){
1129
+ if (textActionsMenu === this.textActionsMenu){
1130
+ textActionsMenu.delegate = null;
1131
+ this.textActionsMenu = null;
1132
+ }
1133
+ },
1134
+
1044
1135
  // --------------------------------------------------------------------
1045
1136
  // MARK: - Accessibility
1046
1137
 
@@ -1265,6 +1356,17 @@ JSClass("UITextField", UIControl, {
1265
1356
 
1266
1357
  });
1267
1358
 
1359
+ Object.defineProperties(UITextField, {
1360
+ bundle: {
1361
+ configurable: true,
1362
+ get: function(){
1363
+ var bundle = JSBundle.initWithIdentifier("io.breakside.JSKit.UIKit");
1364
+ Object.defineProperty(UITextField, "bundle", {value: bundle});
1365
+ return bundle;
1366
+ }
1367
+ }
1368
+ });
1369
+
1268
1370
  UITextField.AccessoryVisibility = {
1269
1371
  always: 0,
1270
1372
  onlyWhenActive: 1,
@@ -17,4 +17,7 @@ en:
17
17
  value:
18
18
  "on": "On"
19
19
  "off": "Off"
20
- mixed: Mixed
20
+ mixed: Mixed
21
+ textActions:
22
+ copy: Copy
23
+ paste: Paste
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKitTesting
3
- JSBundleVersion: 2026.8.0
3
+ JSBundleVersion: 2026.9.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "node": ">=10.10.0"
13
13
  },
14
14
  "name": "@breakside/jskit",
15
- "version": "2026.8.0",
15
+ "version": "2026.9.0",
16
16
  "license": "SEE LICENSE IN LICENSE.txt",
17
17
  "files": [
18
18
  "*"