@breakside/jskit 2026.6.0 → 2026.8.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 (44) 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/JSContext.js +23 -1
  7. package/Frameworks/Foundation.jsframework/JS/JSGradient.js +25 -0
  8. package/Frameworks/Foundation.jsframework/JS/JSPath.js +80 -0
  9. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  10. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  11. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  12. package/Info.json +2 -2
  13. package/Node/io.breakside.jskit-bundle.js +2 -2
  14. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  15. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  16. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  17. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  18. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  19. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  20. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  21. package/Root/Frameworks/DOM/Info.yaml +1 -1
  22. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  23. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  24. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  25. package/Root/Frameworks/Foundation/JSContext.js +23 -1
  26. package/Root/Frameworks/Foundation/JSGradient.js +25 -0
  27. package/Root/Frameworks/Foundation/JSPath.js +80 -0
  28. package/Root/Frameworks/ImageKit/IKHTMLCanvasBitmapContext.js +91 -13
  29. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  30. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  31. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  32. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  33. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  34. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  35. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  36. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  37. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  38. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  39. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  40. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  41. package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +87 -13
  42. package/Root/Frameworks/UIKit/UILayer.js +6 -0
  43. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  44. 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.6.0",
4
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
12
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
6
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
14
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
4
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
11
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
6
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
13
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
4
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
12
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
13
13
  }
@@ -209,7 +209,29 @@ JSClass("JSContext", JSObject, {
209
209
  drawLinearGradient: function(gradient, rect){
210
210
  },
211
211
 
212
- drawRadialGradient: function(gradient, rect, r0, r1){
212
+ drawRadialGradient: function(gradient, rect){
213
+ },
214
+
215
+ drawLinearGradientStroke: function(gradient, path){
216
+ // FIXME: gradient should be aligned to original path rect
217
+ var strokePath = path.pathThatFillsStroke(this.state.lineWidth, this.state.lineCap, this.state.lineJoin, this.state.miterLimit);
218
+ this.save();
219
+ var rect = strokePath.boundingRect;
220
+ this.addPath(path);
221
+ this.clip();
222
+ this.drawLinearGradient(gradient, rect);
223
+ this.restore();
224
+ },
225
+
226
+ drawRadialGradientStroke: function(gradient, path){
227
+ // FIXME: gradient should be aligned to original path rect
228
+ var strokePath = path.pathThatFillsStroke(this.state.lineWidth, this.state.lineCap, this.state.lineJoin, this.state.miterLimit);
229
+ this.save();
230
+ var rect = strokePath.boundingRect;
231
+ this.addPath(path);
232
+ this.clip();
233
+ this.drawRadialGradient(gradient, rect);
234
+ this.restore();
213
235
  },
214
236
 
215
237
  // ----------------------------------------------------------------------
@@ -86,6 +86,31 @@ JSClass('JSGradient', JSObject, {
86
86
  }
87
87
  },
88
88
 
89
+ initFromDictionary: function(dictionary){
90
+ if (dictionary === null || dictionary === undefined){
91
+ return null;
92
+ }
93
+ this.start = JSPoint(dictionary.x0, dictionary.y0);
94
+ this.end = JSPoint(dictionary.x1, dictionary.y1);
95
+ this.stops = [];
96
+ var i, l;
97
+ for (i = 0, l = dictionary.stops.length; i < l; ++i){
98
+ this.addStop(dictionary.stops[i][0], JSColor.initWithRGBAHexString(dictionary.stops[i][1]));
99
+ }
100
+ },
101
+
102
+ dictionaryRepresentation: function(){
103
+ return {
104
+ x0: this.start.x,
105
+ y0: this.start.y,
106
+ x1: this.end.x,
107
+ y1: this.end.y,
108
+ stops: this.stops.map(function(s){
109
+ return [s.position, s.color.rgbaHexStringRepresentation()];
110
+ })
111
+ };
112
+ },
113
+
89
114
  initWithStops: function(position1, color1 /* , ... */){
90
115
  this.init();
91
116
  var args = Array.prototype.slice.call(arguments, 0);
@@ -838,6 +838,83 @@ JSClass("JSPath", JSObject, {
838
838
  return JSRect(min, JSSize(max.x - min.x, max.y - min.y));
839
839
  },
840
840
 
841
+ initWithSVGPathData: function(svgPathData){
842
+ if (svgPathData === null || svgPathData === undefined){
843
+ return null;
844
+ }
845
+ var parts = svgPathData.split(" ").filter(function(p){
846
+ return p !== "";
847
+ });
848
+ this.subpaths = [];
849
+ var part;
850
+ var subpath = null;
851
+ var point = JSPoint.Zero;
852
+ var cp1;
853
+ var cp2;
854
+ var curve;
855
+ while (parts.length > 0){
856
+ part = parts.shift();
857
+ if (part === "M"){
858
+ if (parts.length < 2){
859
+ break;
860
+ }
861
+ point = JSPoint.Zero;
862
+ part = parts.shift();
863
+ point.x = parseFloat(part);
864
+ part = parts.shift();
865
+ point.y = parseFloat(part);
866
+ subpath = Subpath(point);
867
+ this.subpaths.push(subpath);
868
+ this._currentPoint = point;
869
+ }else if (part === "L"){
870
+ if (parts.length < 2){
871
+ break;
872
+ }
873
+ if (subpath === null){
874
+ subpath = Subpath(point);
875
+ this.subpaths.push(subpath);
876
+ }
877
+ point = JSPoint.Zero;
878
+ part = parts.shift();
879
+ point.x = parseFloat(part);
880
+ part = parts.shift();
881
+ point.y = parseFloat(part);
882
+ subpath.segments.push({type: JSPath.SegmentType.line, end: point});
883
+ this._currentPoint = point;
884
+ }else if (part === "C"){
885
+ if (parts.length < 6){
886
+ break;
887
+ }
888
+ if (subpath === null){
889
+ subpath = Subpath(point);
890
+ this.subpaths.push(subpath);
891
+ }
892
+ cp1 = JSPoint.Zero;
893
+ cp2 = JSPoint.Zero;
894
+ point = JSPoint.Zero;
895
+ part = parts.shift();
896
+ cp1.x = parseFloat(part);
897
+ part = parts.shift();
898
+ cp1.y = parseFloat(part);
899
+ part = parts.shift();
900
+ cp2.x = parseFloat(part);
901
+ part = parts.shift();
902
+ cp2.y = parseFloat(part);
903
+ part = parts.shift();
904
+ point.x = parseFloat(part);
905
+ part = parts.shift();
906
+ point.y = parseFloat(part);
907
+ subpath.segments.push({type: JSPath.SegmentType.curve, curve: JSCubicBezier(this._currentPoint, cp1, cp2, point)});
908
+ this._currentPoint = point;
909
+ }else if (part === "Z"){
910
+ if (subpath !== null){
911
+ subpath.closed = true;
912
+ subpath = null;
913
+ }
914
+ }
915
+ }
916
+ },
917
+
841
918
  svgPathData: function(){
842
919
  var subpath;
843
920
  var segment;
@@ -868,6 +945,9 @@ JSClass("JSPath", JSObject, {
868
945
  data += "Z ";
869
946
  }
870
947
  }
948
+ if (data.length > 0 && data[data.length - 1] == " "){
949
+ data = data.substr(0, data.length - 1);
950
+ }
871
951
  return data;
872
952
  },
873
953
 
@@ -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.6.0",
6
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
14
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
4
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
12
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
6
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
14
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
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.6.0",
4
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
11
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
12
12
  }
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.jskit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "node",
5
5
  "JSBundleIdentifier": "io.breakside.jskit",
6
- "JSBundleVersion": "2026.6.0",
6
+ "JSBundleVersion": "2026.8.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": "4d2276988e1dc6a50451389c84f740a16249d16e"
13
+ "GitRevision": "b4a1f6046edfde1343010e48660aff5aa3e8353e"
14
14
  },
15
15
  "Resources": [
16
16
  {
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKit
3
- JSBundleVersion: 2026.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -209,7 +209,29 @@ JSClass("JSContext", JSObject, {
209
209
  drawLinearGradient: function(gradient, rect){
210
210
  },
211
211
 
212
- drawRadialGradient: function(gradient, rect, r0, r1){
212
+ drawRadialGradient: function(gradient, rect){
213
+ },
214
+
215
+ drawLinearGradientStroke: function(gradient, path){
216
+ // FIXME: gradient should be aligned to original path rect
217
+ var strokePath = path.pathThatFillsStroke(this.state.lineWidth, this.state.lineCap, this.state.lineJoin, this.state.miterLimit);
218
+ this.save();
219
+ var rect = strokePath.boundingRect;
220
+ this.addPath(path);
221
+ this.clip();
222
+ this.drawLinearGradient(gradient, rect);
223
+ this.restore();
224
+ },
225
+
226
+ drawRadialGradientStroke: function(gradient, path){
227
+ // FIXME: gradient should be aligned to original path rect
228
+ var strokePath = path.pathThatFillsStroke(this.state.lineWidth, this.state.lineCap, this.state.lineJoin, this.state.miterLimit);
229
+ this.save();
230
+ var rect = strokePath.boundingRect;
231
+ this.addPath(path);
232
+ this.clip();
233
+ this.drawRadialGradient(gradient, rect);
234
+ this.restore();
213
235
  },
214
236
 
215
237
  // ----------------------------------------------------------------------
@@ -86,6 +86,31 @@ JSClass('JSGradient', JSObject, {
86
86
  }
87
87
  },
88
88
 
89
+ initFromDictionary: function(dictionary){
90
+ if (dictionary === null || dictionary === undefined){
91
+ return null;
92
+ }
93
+ this.start = JSPoint(dictionary.x0, dictionary.y0);
94
+ this.end = JSPoint(dictionary.x1, dictionary.y1);
95
+ this.stops = [];
96
+ var i, l;
97
+ for (i = 0, l = dictionary.stops.length; i < l; ++i){
98
+ this.addStop(dictionary.stops[i][0], JSColor.initWithRGBAHexString(dictionary.stops[i][1]));
99
+ }
100
+ },
101
+
102
+ dictionaryRepresentation: function(){
103
+ return {
104
+ x0: this.start.x,
105
+ y0: this.start.y,
106
+ x1: this.end.x,
107
+ y1: this.end.y,
108
+ stops: this.stops.map(function(s){
109
+ return [s.position, s.color.rgbaHexStringRepresentation()];
110
+ })
111
+ };
112
+ },
113
+
89
114
  initWithStops: function(position1, color1 /* , ... */){
90
115
  this.init();
91
116
  var args = Array.prototype.slice.call(arguments, 0);
@@ -838,6 +838,83 @@ JSClass("JSPath", JSObject, {
838
838
  return JSRect(min, JSSize(max.x - min.x, max.y - min.y));
839
839
  },
840
840
 
841
+ initWithSVGPathData: function(svgPathData){
842
+ if (svgPathData === null || svgPathData === undefined){
843
+ return null;
844
+ }
845
+ var parts = svgPathData.split(" ").filter(function(p){
846
+ return p !== "";
847
+ });
848
+ this.subpaths = [];
849
+ var part;
850
+ var subpath = null;
851
+ var point = JSPoint.Zero;
852
+ var cp1;
853
+ var cp2;
854
+ var curve;
855
+ while (parts.length > 0){
856
+ part = parts.shift();
857
+ if (part === "M"){
858
+ if (parts.length < 2){
859
+ break;
860
+ }
861
+ point = JSPoint.Zero;
862
+ part = parts.shift();
863
+ point.x = parseFloat(part);
864
+ part = parts.shift();
865
+ point.y = parseFloat(part);
866
+ subpath = Subpath(point);
867
+ this.subpaths.push(subpath);
868
+ this._currentPoint = point;
869
+ }else if (part === "L"){
870
+ if (parts.length < 2){
871
+ break;
872
+ }
873
+ if (subpath === null){
874
+ subpath = Subpath(point);
875
+ this.subpaths.push(subpath);
876
+ }
877
+ point = JSPoint.Zero;
878
+ part = parts.shift();
879
+ point.x = parseFloat(part);
880
+ part = parts.shift();
881
+ point.y = parseFloat(part);
882
+ subpath.segments.push({type: JSPath.SegmentType.line, end: point});
883
+ this._currentPoint = point;
884
+ }else if (part === "C"){
885
+ if (parts.length < 6){
886
+ break;
887
+ }
888
+ if (subpath === null){
889
+ subpath = Subpath(point);
890
+ this.subpaths.push(subpath);
891
+ }
892
+ cp1 = JSPoint.Zero;
893
+ cp2 = JSPoint.Zero;
894
+ point = JSPoint.Zero;
895
+ part = parts.shift();
896
+ cp1.x = parseFloat(part);
897
+ part = parts.shift();
898
+ cp1.y = parseFloat(part);
899
+ part = parts.shift();
900
+ cp2.x = parseFloat(part);
901
+ part = parts.shift();
902
+ cp2.y = parseFloat(part);
903
+ part = parts.shift();
904
+ point.x = parseFloat(part);
905
+ part = parts.shift();
906
+ point.y = parseFloat(part);
907
+ subpath.segments.push({type: JSPath.SegmentType.curve, curve: JSCubicBezier(this._currentPoint, cp1, cp2, point)});
908
+ this._currentPoint = point;
909
+ }else if (part === "Z"){
910
+ if (subpath !== null){
911
+ subpath.closed = true;
912
+ subpath = null;
913
+ }
914
+ }
915
+ }
916
+ },
917
+
841
918
  svgPathData: function(){
842
919
  var subpath;
843
920
  var segment;
@@ -868,6 +945,9 @@ JSClass("JSPath", JSObject, {
868
945
  data += "Z ";
869
946
  }
870
947
  }
948
+ if (data.length > 0 && data[data.length - 1] == " "){
949
+ data = data.substr(0, data.length - 1);
950
+ }
871
951
  return data;
872
952
  },
873
953
 
@@ -248,38 +248,116 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
248
248
 
249
249
  drawLinearGradient: function(gradient, rect){
250
250
  this.enqueueOperation(function(state, completion, target){
251
- // since all the gradient coordinates are in the unit rectangle,
252
- // we'll align our transform so the unit rectangle matches rect.
253
- this.canvasContext.translate(rect.origin.x, rect.origin.y);
254
- this.canvasContext.scale(rect.size.width, rect.size.height);
255
- var canvasGradient = this.canvasContext.createLinearGradient(gradient.start.x, gradient.start.y, gradient.end.x, gradient.end.y);
251
+ this.canvasContext.save();
252
+ var canvasGradient = this.canvasContext.createLinearGradient(
253
+ rect.origin.x + gradient.start.x * rect.size.width,
254
+ rect.origin.y + gradient.start.y * rect.size.height,
255
+ rect.origin.x + gradient.end.x * rect.size.width,
256
+ rect.origin.y + gradient.end.y * rect.size.height
257
+ );
256
258
  var stop;
257
259
  for (var i = 0, l = gradient.stops.length; i < l; ++i){
258
260
  stop = gradient.stops[i];
259
261
  canvasGradient.addColorStop(stop.position, stop.color.cssString());
260
262
  }
261
263
  this.canvasContext.fillStyle = canvasGradient;
262
- this.canvasContext.fillRect(0, 0, 1, 1);
264
+ this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
265
+ this.canvasContext.restore();
263
266
  completion.call(target);
264
267
  });
265
268
  },
266
269
 
267
- drawRadialGradient: function(gradient, rect, r0, r1){
270
+ drawRadialGradient: function(gradient, rect){
268
271
  this.enqueueOperation(function(state, completion, target){
269
- // since all the gradient coordinates are in the unit rectangle,
270
- // we'll align our transform so the unit rectangle matches rect.
271
- this.canvasContext.translate(rect.origin.x, rect.origin.y);
272
- this.canvasContext.scale(rect.size.width, rect.size.height);
273
- var canvasGradient = this.canvasContext.createRadialGradient(gradient.start.x, gradient.start.y, r0, gradient.end.x, gradient.end.y, r1);
272
+ var r0 = 0;
273
+ var start = JSPoint(
274
+ rect.origin.x + gradient.start.x * rect.size.width,
275
+ rect.origin.y + gradient.start.y * rect.size.height
276
+ );
277
+ var end = JSPoint(
278
+ rect.origin.x + gradient.end.x * rect.size.width,
279
+ rect.origin.y + gradient.end.y * rect.size.height
280
+ );
281
+ var r1 = start.distanceToPoint(end);
282
+ this.canvasContext.save();
283
+ var canvasGradient = this.canvasContext.createRadialGradient(
284
+ start.x,
285
+ start.y,
286
+ r0,
287
+ start.x,
288
+ start.y,
289
+ r1
290
+ );
274
291
  var stop;
275
292
  for (var i = 0, l = gradient.stops.length; i < l; ++i){
276
293
  stop = gradient.stops[i];
277
294
  canvasGradient.addColorStop(stop.position, stop.color.cssString());
278
295
  }
279
296
  this.canvasContext.fillStyle = canvasGradient;
280
- this.canvasContext.fillRect(0, 0, 1, 1);
297
+ this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
298
+ this.canvasContext.restore();
299
+ completion.call(target);
300
+ });
301
+ },
302
+
303
+ drawLinearGradientStroke: function(gradient, path){
304
+ this.enqueueOperation(function(state, completion, target){
305
+ var rect = path.boundingRect;
306
+ this.canvasContext.save();
307
+ this.addPath(path);
308
+ var canvasGradient = this.canvasContext.createLinearGradient(
309
+ rect.origin.x + gradient.start.x * rect.size.width,
310
+ rect.origin.y + gradient.start.y * rect.size.height,
311
+ rect.origin.x + gradient.end.x * rect.size.width,
312
+ rect.origin.y + gradient.end.y * rect.size.height
313
+ );
314
+ var stop;
315
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
316
+ stop = gradient.stops[i];
317
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
318
+ }
319
+ this.canvasContext.strokeStyle = canvasGradient;
320
+ this.canvasContext.stroke();
321
+ this.canvasContext.restore();
281
322
  completion.call(target);
282
323
  });
324
+ this.beginPath();
325
+ },
326
+
327
+ drawRadialGradientStroke: function(gradient, path){
328
+ this.enqueueOperation(function(state, completion, target){
329
+ var rect = path.boundingRect;
330
+ var r0 = 0;
331
+ var start = JSPoint(
332
+ rect.origin.x + gradient.start.x * rect.size.width,
333
+ rect.origin.y + gradient.start.y * rect.size.height
334
+ );
335
+ var end = JSPoint(
336
+ rect.origin.x + gradient.end.x * rect.size.width,
337
+ rect.origin.y + gradient.end.y * rect.size.height
338
+ );
339
+ var r1 = start.distanceToPoint(end);
340
+ this.canvasContext.save();
341
+ this.addPath(path);
342
+ var canvasGradient = this.canvasContext.createRadialGradient(
343
+ start.x,
344
+ start.y,
345
+ r0,
346
+ start.x,
347
+ start.y,
348
+ r1
349
+ );
350
+ var stop;
351
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
352
+ stop = gradient.stops[i];
353
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
354
+ }
355
+ this.canvasContext.strokeStyle = canvasGradient;
356
+ this.canvasContext.stroke();
357
+ this.canvasContext.restore();
358
+ completion.call(target);
359
+ });
360
+ this.beginPath();
283
361
  },
284
362
 
285
363
  // ----------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2026.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.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.6.0
3
+ JSBundleVersion: 2026.8.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -169,6 +169,7 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
169
169
  this[methodName](layer);
170
170
  }
171
171
  this.propertiesNeedingUpdate = {};
172
+ this.renderScale = layer._renderScale;
172
173
 
173
174
  if (this.needsCustomDisplay){
174
175
  layer._drawInContext(this);
@@ -412,13 +413,14 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
412
413
  return this.element.ownerDocument.defaultView.devicePixelRatio || 1;
413
414
  },
414
415
 
416
+ renderScale: 1,
417
+
415
418
  getCanvasContext: function(){
416
419
  if (!this._canvasContext){
417
- var scale = this.deviceScale;
418
- // FIXME: scale should account for any transform ancestor layers
420
+ var scale = this.deviceScale * this.renderScale;
419
421
  var canvas = this._dequeueReusableCanvasElement();
420
- canvas.width = this.bounds.size.width * scale;
421
- canvas.height = this.bounds.size.height * scale;
422
+ canvas.width = Math.ceil(this.bounds.size.width * scale);
423
+ canvas.height = Math.ceil(this.bounds.size.height * scale);
422
424
  this._insertChildElement(canvas);
423
425
  this._canvasContext = canvas.getContext('2d');
424
426
  this._canvasContext.save();
@@ -685,32 +687,104 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
685
687
 
686
688
  drawLinearGradient: function(gradient, rect){
687
689
  this.canvasContext.save();
688
- this.canvasContext.translate(rect.origin.x, rect.origin.y);
689
- this.canvasContext.scale(rect.size.width, rect.size.height);
690
- var canvasGradient = this.canvasContext.createLinearGradient(gradient.start.x, gradient.start.y, gradient.end.x, gradient.end.y);
690
+ var canvasGradient = this.canvasContext.createLinearGradient(
691
+ rect.origin.x + gradient.start.x * rect.size.width,
692
+ rect.origin.y + gradient.start.y * rect.size.height,
693
+ rect.origin.x + gradient.end.x * rect.size.width,
694
+ rect.origin.y + gradient.end.y * rect.size.height
695
+ );
691
696
  var stop;
692
697
  for (var i = 0, l = gradient.stops.length; i < l; ++i){
693
698
  stop = gradient.stops[i];
694
699
  canvasGradient.addColorStop(stop.position, stop.color.cssString());
695
700
  }
696
701
  this.canvasContext.fillStyle = canvasGradient;
697
- this.canvasContext.fillRect(0, 0, 1, 1);
702
+ this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
698
703
  this.canvasContext.restore();
699
704
  },
700
705
 
701
- drawRadialGradient: function(gradient, rect, r0, r1){
706
+ drawRadialGradient: function(gradient, rect){
707
+ var r0 = 0;
708
+ var start = JSPoint(
709
+ rect.origin.x + gradient.start.x * rect.size.width,
710
+ rect.origin.y + gradient.start.y * rect.size.height
711
+ );
712
+ var end = JSPoint(
713
+ rect.origin.x + gradient.end.x * rect.size.width,
714
+ rect.origin.y + gradient.end.y * rect.size.height
715
+ );
716
+ var r1 = start.distanceToPoint(end);
702
717
  this.canvasContext.save();
703
- this.canvasContext.translate(rect.origin.x, rect.origin.y);
704
- this.canvasContext.scale(rect.size.width, rect.size.height);
705
- var canvasGradient = this.canvasContext.createRadialGradient(gradient.start.x, gradient.start.y, r0, gradient.end.x, gradient.end.y, r1);
718
+ var canvasGradient = this.canvasContext.createRadialGradient(
719
+ start.x,
720
+ start.y,
721
+ r0,
722
+ start.x,
723
+ start.y,
724
+ r1
725
+ );
706
726
  var stop;
707
727
  for (var i = 0, l = gradient.stops.length; i < l; ++i){
708
728
  stop = gradient.stops[i];
709
729
  canvasGradient.addColorStop(stop.position, stop.color.cssString());
710
730
  }
711
731
  this.canvasContext.fillStyle = canvasGradient;
712
- this.canvasContext.fillRect(0, 0, 1, 1);
732
+ this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
733
+ this.canvasContext.restore();
734
+ },
735
+
736
+ drawLinearGradientStroke: function(gradient, path){
737
+ var rect = path.boundingRect;
738
+ this.canvasContext.save();
739
+ this.addPath(path);
740
+ var canvasGradient = this.canvasContext.createLinearGradient(
741
+ rect.origin.x + gradient.start.x * rect.size.width,
742
+ rect.origin.y + gradient.start.y * rect.size.height,
743
+ rect.origin.x + gradient.end.x * rect.size.width,
744
+ rect.origin.y + gradient.end.y * rect.size.height
745
+ );
746
+ var stop;
747
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
748
+ stop = gradient.stops[i];
749
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
750
+ }
751
+ this.canvasContext.strokeStyle = canvasGradient;
752
+ this.canvasContext.stroke();
713
753
  this.canvasContext.restore();
754
+ this.beginPath();
755
+ },
756
+
757
+ drawRadialGradientStroke: function(gradient, path){
758
+ var rect = path.boundingRect;
759
+ var r0 = 0;
760
+ var start = JSPoint(
761
+ rect.origin.x + gradient.start.x * rect.size.width,
762
+ rect.origin.y + gradient.start.y * rect.size.height
763
+ );
764
+ var end = JSPoint(
765
+ rect.origin.x + gradient.end.x * rect.size.width,
766
+ rect.origin.y + gradient.end.y * rect.size.height
767
+ );
768
+ var r1 = start.distanceToPoint(end);
769
+ this.canvasContext.save();
770
+ this.addPath(path);
771
+ var canvasGradient = this.canvasContext.createRadialGradient(
772
+ start.x,
773
+ start.y,
774
+ r0,
775
+ start.x,
776
+ start.y,
777
+ r1
778
+ );
779
+ var stop;
780
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
781
+ stop = gradient.stops[i];
782
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
783
+ }
784
+ this.canvasContext.strokeStyle = canvasGradient;
785
+ this.canvasContext.stroke();
786
+ this.canvasContext.restore();
787
+ this.beginPath();
714
788
  },
715
789
 
716
790
  // ----------------------------------------------------------------------
@@ -769,6 +769,12 @@ JSClass("UILayer", JSObject, {
769
769
  // MARK: - Display
770
770
 
771
771
  _needsDisplay: false,
772
+ renderScale: JSDynamicProperty("_renderScale", 1),
773
+
774
+ setRenderScale: function(renderScale){
775
+ this._renderScale = renderScale;
776
+ this.setNeedsDisplay();
777
+ },
772
778
 
773
779
  setNeedsDisplay: function(){
774
780
  if (this._displayServer !== null){
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKitTesting
3
- JSBundleVersion: 2026.6.0
3
+ JSBundleVersion: 2026.8.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.6.0",
15
+ "version": "2026.8.0",
16
16
  "license": "SEE LICENSE IN LICENSE.txt",
17
17
  "files": [
18
18
  "*"