@breakside/jskit 2026.6.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 (57) 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/JS/JSRect.js +12 -0
  10. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  11. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  12. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  13. package/Info.json +2 -2
  14. package/Node/io.breakside.jskit-bundle.js +4 -4
  15. package/Resources/doc-default.css +8 -0
  16. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  17. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  18. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  19. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  20. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  21. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  22. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  23. package/Root/Frameworks/DOM/Info.yaml +1 -1
  24. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  25. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  26. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  27. package/Root/Frameworks/Foundation/JSContext.js +23 -1
  28. package/Root/Frameworks/Foundation/JSGradient.js +25 -0
  29. package/Root/Frameworks/Foundation/JSPath.js +80 -0
  30. package/Root/Frameworks/Foundation/JSRect.js +12 -0
  31. package/Root/Frameworks/ImageKit/IKHTMLCanvasBitmapContext.js +91 -13
  32. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  33. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  34. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  35. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  36. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  37. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  38. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  39. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  40. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  41. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  42. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  43. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  44. package/Root/Frameworks/UIKit/UIApplication.js +5 -1
  45. package/Root/Frameworks/UIKit/UIHTMLApplication.js +24 -0
  46. package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +87 -13
  47. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +17 -3
  48. package/Root/Frameworks/UIKit/UIKit.js +2 -0
  49. package/Root/Frameworks/UIKit/UILayer.js +6 -0
  50. package/Root/Frameworks/UIKit/UIPopupWindow.js +15 -5
  51. package/Root/Frameworks/UIKit/UITextAction.js +16 -0
  52. package/Root/Frameworks/UIKit/UITextActionsMenu.js +95 -0
  53. package/Root/Frameworks/UIKit/UITextEditor.js +57 -7
  54. package/Root/Frameworks/UIKit/UITextField.js +102 -0
  55. package/Root/Frameworks/UIKit/en.lproj/Localizable.strings.yaml +4 -1
  56. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  57. package/package.json +1 -1
@@ -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
  },
@@ -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.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.6.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.6.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.6.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.6.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.6.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.6.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.6.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.6.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.6.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.6.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.6.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);
@@ -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
  // ----------------------------------------------------------------------
@@ -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"
@@ -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){
@@ -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
+ });