@breakside/jskit 2026.11.0 → 2026.26.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 (85) 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/JS/FNTOpenTypeFont.js +331 -22
  5. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  6. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  7. package/Frameworks/Foundation.jsframework/JS/JSAttributedString.js +13 -1
  8. package/Frameworks/Foundation.jsframework/JS/JSContext.js +44 -17
  9. package/Frameworks/Foundation.jsframework/JS/JSCubicBezier.js +54 -1
  10. package/Frameworks/Foundation.jsframework/JS/JSFont.js +8 -9
  11. package/Frameworks/Foundation.jsframework/JS/JSGradient.js +28 -2
  12. package/Frameworks/Foundation.jsframework/JS/JSImage+HTML.js +27 -1
  13. package/Frameworks/Foundation.jsframework/JS/JSNumberFormatter.js +23 -1
  14. package/Frameworks/Foundation.jsframework/JS/JSPath.js +192 -6
  15. package/Frameworks/Foundation.jsframework/JS/JSPoint.js +56 -0
  16. package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +21 -4
  17. package/Frameworks/Foundation.jsframework/JS/JSTextStorage.js +5 -0
  18. package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +6 -1
  19. package/Frameworks/Foundation.jsframework/JS/JSXMLParser.js +1 -1
  20. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  21. package/Frameworks/Foundation.jsframework/sources.json +1 -0
  22. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  23. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  24. package/Info.json +2 -2
  25. package/Node/io.breakside.jskit-bundle.js +2 -2
  26. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  27. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  28. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  29. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  30. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  31. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  32. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  33. package/Root/Frameworks/DOM/Info.yaml +1 -1
  34. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  35. package/Root/Frameworks/FontKit/FNTOpenTypeFont.js +331 -22
  36. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  37. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  38. package/Root/Frameworks/Foundation/JSAttributedString.js +13 -1
  39. package/Root/Frameworks/Foundation/JSContext.js +44 -17
  40. package/Root/Frameworks/Foundation/JSCubicBezier.js +54 -1
  41. package/Root/Frameworks/Foundation/JSFont.js +8 -9
  42. package/Root/Frameworks/Foundation/JSGradient.js +28 -2
  43. package/Root/Frameworks/Foundation/JSImage+HTML.js +27 -1
  44. package/Root/Frameworks/Foundation/JSNumberFormatter.js +23 -1
  45. package/Root/Frameworks/Foundation/JSPath.js +192 -6
  46. package/Root/Frameworks/Foundation/JSPoint.js +56 -0
  47. package/Root/Frameworks/Foundation/JSTextRun.js +21 -4
  48. package/Root/Frameworks/Foundation/JSTextStorage.js +5 -0
  49. package/Root/Frameworks/Foundation/JSTextTypesetter.js +6 -1
  50. package/Root/Frameworks/Foundation/JSXMLParser.js +1 -1
  51. package/Root/Frameworks/ImageKit/IKHTMLCanvasBitmapContext.js +187 -101
  52. package/Root/Frameworks/ImageKit/IKNodeBitmapContext.js +4 -4
  53. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  54. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  55. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  56. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  57. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  58. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  59. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  60. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  61. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  62. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  63. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  64. package/Root/Frameworks/TestKit/TKAssert.js +4 -4
  65. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  66. package/Root/Frameworks/UIKit/UIApplication.js +9 -1
  67. package/Root/Frameworks/UIKit/UICollectionView.js +4 -0
  68. package/Root/Frameworks/UIKit/UIColorPanel.js +46 -0
  69. package/Root/Frameworks/UIKit/UIColorWell.js +8 -0
  70. package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +21 -2
  71. package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +150 -69
  72. package/Root/Frameworks/UIKit/UIHTMLTextRun.js +5 -0
  73. package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +18 -3
  74. package/Root/Frameworks/UIKit/UIListView.js +7 -5
  75. package/Root/Frameworks/UIKit/UIMenu.js +50 -32
  76. package/Root/Frameworks/UIKit/UIMenuItem.js +12 -0
  77. package/Root/Frameworks/UIKit/UIMenuView.js +27 -5
  78. package/Root/Frameworks/UIKit/UIPlatform.js +7 -0
  79. package/Root/Frameworks/UIKit/UIPopupButton.js +7 -1
  80. package/Root/Frameworks/UIKit/UIResponder.js +8 -0
  81. package/Root/Frameworks/UIKit/UITextEditor.js +14 -7
  82. package/Root/Frameworks/UIKit/UITokenField.js +65 -0
  83. package/Root/Frameworks/UIKit/UIToolbar.js +1 -1
  84. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  85. package/package.json +1 -1
@@ -72,6 +72,62 @@ JSPoint.prototype = {
72
72
 
73
73
  subtracting: function(other){
74
74
  return JSPoint(this.x - other.x, this.y - other.y);
75
+ },
76
+
77
+ interpolation: function(p1, percentage){
78
+ return JSPoint(
79
+ this.x + (p1.x - this.x) * percentage,
80
+ this.y + (p1.y - this.y) * percentage
81
+ );
82
+ },
83
+
84
+ lineToPointContainsPoint: function(other, point, tolerance){
85
+ // first check if we're within the box defined by the segment
86
+ var minX, maxX;
87
+ if (this.x <= other.x){
88
+ minX = this.x - tolerance;
89
+ maxX = other.x + tolerance;
90
+ }else{
91
+ minX = other.x - tolerance;
92
+ maxX = this.x + tolerance;
93
+ }
94
+ var minY, maxY;
95
+ if (this.y <= other.y){
96
+ minY = this.y - tolerance;
97
+ maxY = other.y + tolerance;
98
+ }else{
99
+ minY = other.y - tolerance;
100
+ maxY = this.y + tolerance;
101
+ }
102
+ if (point.x < minX){
103
+ return false;
104
+ }
105
+ if (point.x > maxX){
106
+ return false;
107
+ }
108
+ if (point.y < minY){
109
+ return false;
110
+ }
111
+ if (point.y > maxY){
112
+ return false;
113
+ }
114
+
115
+ // next check for the special cases of a vertical and horizontal line,
116
+ // where we must be near the line if we're within the box
117
+ var dx = (other.x - this.x);
118
+ var dy = (other.y - this.y);
119
+ if (Math.abs(dx) < 0.00001 || (Math.abs(dy) < 0.00001)){
120
+ return true;
121
+ }
122
+
123
+ // Finally, find the nearest point to the line and see how far away we are
124
+ var m1 = dy / dx;
125
+ var b1 = this.y - m1 * this.x;
126
+ var m2 = -1/m1;
127
+ var b2 = point.y - m2 * point.x;
128
+ var x = (b2 - b1) / (m1 - m2);
129
+ var y = m1 * x + b1;
130
+ return JSPoint(x, y).distanceToPoint(point) <= tolerance;
75
131
  }
76
132
  };
77
133
 
@@ -16,6 +16,7 @@
16
16
  // #import "JSObject.js"
17
17
  // #import "CoreTypes.js"
18
18
  // #import "JSAttributedString.js"
19
+ // #import "JSContext.js"
19
20
  'use strict';
20
21
 
21
22
  (function(){
@@ -31,8 +32,10 @@ JSClass("JSTextRun", JSObject, {
31
32
  attributes: null,
32
33
  font: null,
33
34
  attachment: null,
35
+ _useDisplayMetrics: false,
34
36
 
35
- initWithGlyphs: function(glyphs, glyphCharacterLengths, font, attributes, range){
37
+ initWithGlyphs: function(glyphs, glyphCharacterLengths, font, attributes, range, useDisplayMetrics){
38
+ this._useDisplayMetrics = useDisplayMetrics === true;
36
39
  this.glyphs = glyphs;
37
40
  this.glyphCharacterLengths = glyphCharacterLengths;
38
41
  this.attributes = attributes;
@@ -42,8 +45,13 @@ JSClass("JSTextRun", JSObject, {
42
45
  this._size = JSSize(this.attachment.size);
43
46
  }else{
44
47
  this.font = font;
45
- this.baseline = -font.descender;
46
- this._size = JSSize(0, font.lineHeight);
48
+ if (this._useDisplayMetrics){
49
+ this.baseline = -font.displayDescender;
50
+ this._size = JSSize(0, font.displayLineHeight);
51
+ }else{
52
+ this.baseline = -font.descender;
53
+ this._size = JSSize(0, font.lineHeight);
54
+ }
47
55
  for (var i = 0, l = glyphs.length; i < l; ++i){
48
56
  this._size.width += font.widthOfGlyph(glyphs[i]);
49
57
  }
@@ -67,9 +75,18 @@ JSClass("JSTextRun", JSObject, {
67
75
  context.restore();
68
76
  }else{
69
77
  context.save();
70
- context.translateBy(point.x, point.y + this.font.ascender);
78
+ if (this._useDisplayMetrics){
79
+ context.translateBy(point.x, point.y + this.font.displayAscender);
80
+ }else{
81
+ context.translateBy(point.x, point.y + this.font.ascender);
82
+ }
71
83
  context.setFont(this.font);
72
84
  context.setFillColor(this.attributes.textColor);
85
+ if (this.attributes.strokeWidth){
86
+ context.setLineWidth(this.attributes.strokeWidth);
87
+ context.setStrokeColor(this.attributes.strokeColor);
88
+ context.setTextDrawingMode(JSContext.TextDrawingMode.fillStroke);
89
+ }
73
90
  context.showGlyphs(this.glyphs);
74
91
  context.restore();
75
92
  }
@@ -30,6 +30,11 @@ JSClass("JSTextStorage", JSAttributedString, {
30
30
  this._commonTextStorageInit();
31
31
  },
32
32
 
33
+ initFromDictionary: function(dictionary){
34
+ JSTextStorage.$super.initFromDictionary.call(this, dictionary);
35
+ this._commonTextStorageInit();
36
+ },
37
+
33
38
  _commonTextStorageInit: function(){
34
39
  this._layoutManagers = [];
35
40
  },
@@ -77,9 +77,14 @@ JSClass("JSTextTypesetter", JSObject, {
77
77
  var attributes = this._attributedString.attributesAtIndex(range.location);
78
78
  attributes = this.resolveAttributes(attributes);
79
79
  var font = attributes.font;
80
+ if (this.useDisplayMetrics){
81
+ return JSTextLine.initWithHeight(font.displayLineHeight, -font.displayDescender, range.location);
82
+ }
80
83
  return JSTextLine.initWithHeight(font.lineHeight, -font.descender, range.location);
81
84
  },
82
85
 
86
+ useDisplayMetrics: false,
87
+
83
88
  _createLineFromLayout: function(layout){
84
89
  // Create runs from the adjusted run descriptors
85
90
  var runs = [];
@@ -88,7 +93,7 @@ JSClass("JSTextTypesetter", JSObject, {
88
93
  var runDescriptor;
89
94
  for (var i = 0, l = layout.runDescriptors.length; i < l; ++i){
90
95
  runDescriptor = layout.runDescriptors[i];
91
- run = JSTextRun.initWithGlyphs(runDescriptor.glyphs, runDescriptor.glyphCharacterLengths, runDescriptor.font, runDescriptor.attributes, JSRange(runDescriptor.location, runDescriptor.length));
96
+ run = JSTextRun.initWithGlyphs(runDescriptor.glyphs, runDescriptor.glyphCharacterLengths, runDescriptor.font, runDescriptor.attributes, JSRange(runDescriptor.location, runDescriptor.length), this.useDisplayMetrics);
92
97
  run.origin.x = x;
93
98
  x += run.size.width;
94
99
  runs.push(run);
@@ -475,7 +475,7 @@ JSClass("JSXMLParser", JSObject, {
475
475
  }
476
476
  obj = readObject();
477
477
  }
478
- if (!this.isStopped && this.delegate.xmlParserDidEndDocument){
478
+ if (!this.isStopped && this.delegate && this.delegate.xmlParserDidEndDocument){
479
479
  this.delegate.xmlParserDidEndDocument(this);
480
480
  }
481
481
  }catch (e){
@@ -167,9 +167,11 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
167
167
  drawPath: function(drawingMode){
168
168
  var path = this.path.copy();
169
169
  this.enqueueOperation(function(state, completion, target){
170
+ this.canvasContext.save();
170
171
  this.canvasContext.setTransform(1, 0, 0, 1, 0, 0);
171
172
  this.canvasContext.beginPath();
172
173
  path.addToIKHTMLCanvasContext(this.canvasContext);
174
+ this.canvasContext.restore();
173
175
  switch (drawingMode){
174
176
  case JSContext.DrawingMode.fill:
175
177
  this.canvasContext.fill();
@@ -213,19 +215,21 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
213
215
  return;
214
216
  }
215
217
  var scale = Math.ceil(Math.max(state.transform.a, state.transform.b));
218
+ var size = JSSize(rect.size.width * scale, rect.size.height * scale);
216
219
  var maskCanvas = this.canvasContext.canvas.ownerDocument.createElement("canvas");
217
- maskCanvas.width = maskImage.size.width * scale;
218
- maskCanvas.height = maskImage.size.height * scale;
220
+ maskCanvas.width = size.width;
221
+ maskCanvas.height = size.height;
219
222
  var maskContext = maskCanvas.getContext('2d');
220
223
  maskContext.fillStyle = state.fillColor.cssString();
221
- maskContext.fillRect(0, 0, maskCanvas.width, maskCanvas.height);
224
+ maskContext.fillRect(0, 0, size.width, size.height);
222
225
  maskContext.globalCompositeOperation = 'destination-in';
223
- maskContext.drawImage(imgElement, 0, 0, maskCanvas.width, maskCanvas.height);
226
+ maskContext.drawImage(imgElement, 0, 0, size.width, size.height);
224
227
  this.canvasContext.drawImage(maskCanvas, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
225
228
  completion.call(target);
226
229
  }, this);
227
230
  });
228
231
  },
232
+
229
233
  // ----------------------------------------------------------------------
230
234
  // MARK: - Images
231
235
 
@@ -249,17 +253,7 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
249
253
  drawLinearGradient: function(gradient, rect){
250
254
  this.enqueueOperation(function(state, completion, target){
251
255
  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
- );
258
- var stop;
259
- for (var i = 0, l = gradient.stops.length; i < l; ++i){
260
- stop = gradient.stops[i];
261
- canvasGradient.addColorStop(stop.position, stop.color.cssString());
262
- }
256
+ var canvasGradient = this._linearGradientForCanvasContext(this.canvasContext, gradient, rect);
263
257
  this.canvasContext.fillStyle = canvasGradient;
264
258
  this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
265
259
  this.canvasContext.restore();
@@ -269,30 +263,8 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
269
263
 
270
264
  drawRadialGradient: function(gradient, rect){
271
265
  this.enqueueOperation(function(state, completion, target){
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);
266
+ var canvasGradient = this._radialGradientForCanvasContext(this.canvasContext, gradient, rect);
282
267
  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
- );
291
- var stop;
292
- for (var i = 0, l = gradient.stops.length; i < l; ++i){
293
- stop = gradient.stops[i];
294
- canvasGradient.addColorStop(stop.position, stop.color.cssString());
295
- }
296
268
  this.canvasContext.fillStyle = canvasGradient;
297
269
  this.canvasContext.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
298
270
  this.canvasContext.restore();
@@ -300,22 +272,61 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
300
272
  });
301
273
  },
302
274
 
303
- drawLinearGradientStroke: function(gradient, path){
275
+ drawMaskedLinearGradient: function(gradient, rect, maskImage){
276
+ this.enqueueOperation(function(state, completion, target){
277
+ drawableElementForImage(maskImage, this.canvasContext.canvas.ownerDocument, function(imgElement){
278
+ if (imgElement === null){
279
+ completion.call(target);
280
+ return;
281
+ }
282
+ var scale = Math.ceil(Math.max(state.transform.a, state.transform.b));
283
+ var size = JSSize(rect.size.width * scale, rect.size.height * scale);
284
+ var maskCanvas = this.canvasContext.canvas.ownerDocument.createElement("canvas");
285
+ maskCanvas.width = size.width;
286
+ maskCanvas.height = size.height;
287
+ var maskContext = maskCanvas.getContext('2d');
288
+ var canvasGradient = this._linearGradientForCanvasContext(maskContext, gradient, JSRect(JSPoint.Zero, size));
289
+ maskContext.fillStyle = canvasGradient;
290
+ maskContext.fillRect(0, 0, size.width, size.height);
291
+ maskContext.globalCompositeOperation = 'destination-in';
292
+ maskContext.drawImage(imgElement, 0, 0, size.width, size.height);
293
+ this.canvasContext.drawImage(maskCanvas, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
294
+ completion.call(target);
295
+ }, this);
296
+ });
297
+ },
298
+
299
+ drawMaskedRadialGradient: function(gradient, rect, maskImage){
300
+ this.enqueueOperation(function(state, completion, target){
301
+ drawableElementForImage(maskImage, this.canvasContext.canvas.ownerDocument, function(imgElement){
302
+ if (imgElement === null){
303
+ completion.call(target);
304
+ return;
305
+ }
306
+ var scale = Math.ceil(Math.max(state.transform.a, state.transform.b));
307
+ var size = JSSize(rect.size.width * scale, rect.size.height * scale);
308
+ var maskCanvas = this.canvasContext.canvas.ownerDocument.createElement("canvas");
309
+ maskCanvas.width = size.width;
310
+ maskCanvas.height = size.height;
311
+ var maskContext = maskCanvas.getContext('2d');
312
+ var canvasGradient = this._radialGradientForCanvasContext(maskContext, gradient, JSRect(JSPoint.Zero, size));
313
+ maskContext.fillStyle = canvasGradient;
314
+ maskContext.fillRect(0, 0, size.width, size.height);
315
+ maskContext.globalCompositeOperation = 'destination-in';
316
+ maskContext.drawImage(imgElement, 0, 0, size.width, size.height);
317
+ this.canvasContext.drawImage(maskCanvas, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
318
+ completion.call(target);
319
+ }, this);
320
+ });
321
+ },
322
+
323
+ drawLinearGradientStroke: function(gradient, path, alternateRect){
304
324
  this.enqueueOperation(function(state, completion, target){
305
325
  var rect = path.boundingRect;
306
326
  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
- }
327
+ this.canvasContext.beginPath();
328
+ path.addToIKHTMLCanvasContext(this.canvasContext);
329
+ var canvasGradient = this._linearGradientForCanvasContext(this.canvasContext, gradient, alternateRect || rect);
319
330
  this.canvasContext.strokeStyle = canvasGradient;
320
331
  this.canvasContext.stroke();
321
332
  this.canvasContext.restore();
@@ -324,34 +335,13 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
324
335
  this.beginPath();
325
336
  },
326
337
 
327
- drawRadialGradientStroke: function(gradient, path){
338
+ drawRadialGradientStroke: function(gradient, path, alternateRect){
328
339
  this.enqueueOperation(function(state, completion, target){
329
340
  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
341
  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
- }
342
+ this.canvasContext.beginPath();
343
+ path.addToIKHTMLCanvasContext(this.canvasContext);
344
+ var canvasGradient = this._radialGradientForCanvasContext(this.canvasContext, gradient, alternateRect || rect);
355
345
  this.canvasContext.strokeStyle = canvasGradient;
356
346
  this.canvasContext.stroke();
357
347
  this.canvasContext.restore();
@@ -360,6 +350,105 @@ JSClass("IKHTMLCanvasBitmapContext", IKBitmapContext, {
360
350
  this.beginPath();
361
351
  },
362
352
 
353
+ _linearGradientForCanvasContext: function(canvasContext, gradient, rect){
354
+ var canvasGradient = canvasContext.createLinearGradient(
355
+ rect.origin.x + gradient.start.x * rect.size.width,
356
+ rect.origin.y + gradient.start.y * rect.size.height,
357
+ rect.origin.x + gradient.end.x * rect.size.width,
358
+ rect.origin.y + gradient.end.y * rect.size.height
359
+ );
360
+ var stop;
361
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
362
+ stop = gradient.stops[i];
363
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
364
+ }
365
+ return canvasGradient;
366
+ },
367
+
368
+ _radialGradientForCanvasContext: function(canvasContext, gradient, rect){
369
+ var r0 = 0;
370
+ var start = JSPoint(
371
+ rect.origin.x + gradient.start.x * rect.size.width,
372
+ rect.origin.y + gradient.start.y * rect.size.height
373
+ );
374
+ var end = JSPoint(
375
+ rect.origin.x + gradient.end.x * rect.size.width,
376
+ rect.origin.y + gradient.end.y * rect.size.height
377
+ );
378
+ var r1 = start.distanceToPoint(end);
379
+ var canvasGradient = canvasContext.createRadialGradient(
380
+ start.x,
381
+ start.y,
382
+ r0,
383
+ start.x,
384
+ start.y,
385
+ r1
386
+ );
387
+ var stop;
388
+ for (var i = 0, l = gradient.stops.length; i < l; ++i){
389
+ stop = gradient.stops[i];
390
+ canvasGradient.addColorStop(stop.position, stop.color.cssString());
391
+ }
392
+ return canvasGradient;
393
+ },
394
+
395
+ // ----------------------------------------------------------------------
396
+ // MARK: - Transparency Layers
397
+
398
+ _reusableTransparencyLayer: null,
399
+
400
+ createTransparencyLayer: function(){
401
+ if (this._reusableTransparencyLayer !== null){
402
+ layer = this._reusableTransparencyLayer;
403
+ this._reusableTransparencyLayer = null;
404
+ layer.context.resetTransform();
405
+ layer.context.clearRect(0, 0, layer.canvas.width, layer.canvas.height);
406
+ this._canvasContextAdoptState(layer.context, this.state);
407
+ layer.parentContext = this.canvasContext;
408
+ this.canvasContext = layer.context;
409
+ return layer;
410
+ }
411
+ var parentContext = this.canvasContext;
412
+ var canvas = this.canvasElement.ownerDocument.createElement("canvas");
413
+ canvas.width = this.canvasElement.width;
414
+ canvas.height = this.canvasElement.height;
415
+ var context = canvas.getContext("2d");
416
+ this._canvasContextAdoptState(context, this.state);
417
+ var layer = {
418
+ canvas: canvas,
419
+ context: context,
420
+ parentContext: parentContext
421
+ };
422
+ this.canvasContext = context;
423
+ return layer;
424
+ },
425
+
426
+ drawTransparencyLayer: function(transparencyLayer){
427
+ this.canvasContext = transparencyLayer.parentContext;
428
+ this.canvasContext.resetTransform();
429
+ this.canvasContext.drawImage(transparencyLayer.canvas, 0, 0);
430
+ this._reusableTransparencyLayer = transparencyLayer;
431
+ },
432
+
433
+ _canvasContextAdoptState: function(context, state){
434
+ context.globalAlpha = state.alpha;
435
+ context.fillStyle = state.fillColor ? state.fillColor.cssString() : '';
436
+ context.strokeStyle = state.strokeColor ? state.strokeColor.cssString() : '';
437
+ context.shadowOffsetX = state.shadowOffset.x;
438
+ context.shadowOffsetY = state.shadowOffset.y;
439
+ context.shadowBlur = state.shadowBlur;
440
+ context.shadowColor = state.shadowColor ? state.shadowColor.cssString() : '';
441
+ context.lineWidth = state.lineWidth;
442
+ context.lineCap = state.lineCap;
443
+ context.lineJoin = state.lineJoin;
444
+ context.miterLimit = state.miterLimit;
445
+ context.lineDashOffset = state.lineDashPhase;
446
+ context.setLineDash(state.lineDashArray);
447
+ var transform = state.transform;
448
+ context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty);
449
+ context.font = state.font ? state.font.cssString() : '';
450
+ },
451
+
363
452
  // ----------------------------------------------------------------------
364
453
  // MARK: - Text
365
454
 
@@ -502,31 +591,24 @@ JSPath.definePropertiesFromExtensions({
502
591
 
503
592
  addToIKHTMLCanvasContext: function(canvasContext){
504
593
  var i, l;
505
- var j, k;
506
- var subpath;
507
- var segment;
508
- var point;
509
- var cp1;
510
- var cp2;
511
- for (i = 0, l = this.subpaths.length; i < l; ++i){
512
- subpath = this.subpaths[i];
513
- canvasContext.moveTo(subpath.firstPoint.x, subpath.firstPoint.y);
514
- for (j = 0, k = subpath.segments.length; j < k; ++j){
515
- segment = subpath.segments[j];
516
- if (segment.type === JSPath.SegmentType.line){
517
- canvasContext.lineTo(segment.end.x, segment.end.y);
518
- }else if (segment.type === JSPath.SegmentType.curve){
519
- canvasContext.bezierCurveTo(
520
- segment.curve.cp1.x,
521
- segment.curve.cp1.y,
522
- segment.curve.cp2.x,
523
- segment.curve.cp2.y,
524
- segment.curve.p2.x,
525
- segment.curve.p2.y
526
- );
527
- }
528
- }
529
- if (subpath.closed){
594
+ var elements = this.elements;
595
+ var element;
596
+ for (i = 0, l = elements.length; i < l; ++i){
597
+ element = elements[i];
598
+ if (element.type === JSPathElement.Type.move){
599
+ canvasContext.moveTo(element.point.x, element.point.y);
600
+ }else if (element.type === JSPathElement.Type.line){
601
+ canvasContext.lineTo(element.point.x, element.point.y);
602
+ }else if (element.type === JSPathElement.Type.cubicCurve){
603
+ canvasContext.bezierCurveTo(
604
+ element.curve.cp1.x,
605
+ element.curve.cp1.y,
606
+ element.curve.cp2.x,
607
+ element.curve.cp2.y,
608
+ element.curve.p2.x,
609
+ element.curve.p2.y
610
+ );
611
+ }else if (element.type === JSPathElement.Type.close){
530
612
  canvasContext.closePath();
531
613
  }
532
614
  }
@@ -535,6 +617,10 @@ JSPath.definePropertiesFromExtensions({
535
617
  });
536
618
 
537
619
  var drawableElementForImage = function(image, domDocument, completion, target){
620
+ if (image._htmlImage !== undefined && image._htmlImage !== null){
621
+ completion.call(target, image._htmlImage);
622
+ return;
623
+ }
538
624
  var imgElement = domDocument.createElement("img");
539
625
  imgElement.setAttribute("crossorigin", "anonymous");
540
626
  imgElement.src = image.htmlURLString();
@@ -185,10 +185,10 @@ JSClass("IKNodeBitmapContext", IKBitmapContext, {
185
185
  squarePath.addLineToPoint(JSPoint(1, 1));
186
186
  squarePath.addLineToPoint(JSPoint(0, 1));
187
187
  squarePath.close();
188
- var p1 = squarePath.subpaths[0].firstPoint;
189
- var p2 = squarePath.subpaths[0].segments[0].end;
190
- var p3 = squarePath.subpaths[0].segments[1].end;
191
- var p4 = squarePath.subpaths[0].segments[2].end;
188
+ var p1 = squarePath.elements[0].point;
189
+ var p2 = squarePath.elements[1].point;
190
+ var p3 = squarePath.elements[2].point;
191
+ var p4 = squarePath.elements[3].point;
192
192
  for (var i = 0; p1.y < bitmap.size.height; ++p1.y, ++p2.y, ++p3.y, ++p4.y){
193
193
  for (p1.x = 0, p2.x = 1, p3.x = 1, p4.x = 0; p1.x < bitmap.size.width; ++p1.x, ++p2.x, ++p3.x, ++p4.x){
194
194
  r = bitmap.data[i++];
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2026.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.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.11.0
3
+ JSBundleVersion: 2026.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |