@breakside/jskit 2022.22.3 → 2022.25.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 (98) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/JS/XMLSerializer.js +9 -4
  3. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  4. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  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 +279 -6
  8. package/Frameworks/Foundation.jsframework/JS/JSFileManager+Node.js +2 -2
  9. package/Frameworks/Foundation.jsframework/JS/JSFont.js +39 -15
  10. package/Frameworks/Foundation.jsframework/JS/JSFontDescriptor+HTML.js +8 -0
  11. package/Frameworks/Foundation.jsframework/JS/JSFontDescriptor.js +105 -27
  12. package/Frameworks/Foundation.jsframework/JS/JSParagraphStyle.js +71 -0
  13. package/Frameworks/Foundation.jsframework/JS/JSTextAttachment.js +1 -1
  14. package/Frameworks/Foundation.jsframework/JS/JSTextContainer.js +56 -29
  15. package/Frameworks/Foundation.jsframework/JS/JSTextFrame.js +2 -32
  16. package/Frameworks/Foundation.jsframework/JS/JSTextFramesetter.js +125 -22
  17. package/Frameworks/Foundation.jsframework/JS/JSTextLayoutManager.js +11 -3
  18. package/Frameworks/Foundation.jsframework/JS/JSTextLine.js +4 -3
  19. package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +5 -3
  20. package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +41 -25
  21. package/Frameworks/Foundation.jsframework/JS/JSZip.js +97 -76
  22. package/Frameworks/Foundation.jsframework/JS/String+JS.js +69 -0
  23. package/Frameworks/Foundation.jsframework/JS/UnicodeChar.js +8 -0
  24. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  25. package/Frameworks/Foundation.jsframework/sources.json +26 -22
  26. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  27. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  28. package/Info.json +2 -2
  29. package/Node/io.breakside.jskit-bundle.js +2 -2
  30. package/Node/jskit +8 -7
  31. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  32. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  33. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  34. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  35. package/Root/Frameworks/ChartKit/CHAxis.js +5 -5
  36. package/Root/Frameworks/ChartKit/CHPieChart.js +1 -1
  37. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  38. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  39. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  40. package/Root/Frameworks/DOM/Info.yaml +1 -1
  41. package/Root/Frameworks/DOM/XMLSerializer.js +9 -4
  42. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  43. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  44. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  45. package/Root/Frameworks/Foundation/JSAttributedString.js +279 -6
  46. package/Root/Frameworks/Foundation/JSFileManager+Node.js +2 -2
  47. package/Root/Frameworks/Foundation/JSFont.js +39 -15
  48. package/Root/Frameworks/Foundation/JSFontDescriptor+HTML.js +8 -0
  49. package/Root/Frameworks/Foundation/JSFontDescriptor.js +105 -27
  50. package/Root/Frameworks/Foundation/JSParagraphStyle.js +71 -0
  51. package/Root/Frameworks/Foundation/JSTextAttachment.js +1 -1
  52. package/Root/Frameworks/Foundation/JSTextContainer.js +56 -29
  53. package/Root/Frameworks/Foundation/JSTextFrame.js +2 -32
  54. package/Root/Frameworks/Foundation/JSTextFramesetter.js +125 -22
  55. package/Root/Frameworks/Foundation/JSTextLayoutManager.js +11 -3
  56. package/Root/Frameworks/Foundation/JSTextLine.js +4 -3
  57. package/Root/Frameworks/Foundation/JSTextRun.js +5 -3
  58. package/Root/Frameworks/Foundation/JSTextTypesetter.js +41 -25
  59. package/Root/Frameworks/Foundation/JSZip.js +97 -76
  60. package/Root/Frameworks/Foundation/String+JS.js +69 -0
  61. package/Root/Frameworks/Foundation/UnicodeChar.js +8 -0
  62. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  63. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  64. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  65. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  66. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  67. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  68. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  69. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  70. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  71. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  72. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  73. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  74. package/Root/Frameworks/UIKit/UIBasicAnimation.js +1 -1
  75. package/Root/Frameworks/UIKit/UIHTMLApplication.js +1 -1
  76. package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +36 -0
  77. package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +1 -1
  78. package/Root/Frameworks/UIKit/UIHTMLTextField.js +1 -1
  79. package/Root/Frameworks/UIKit/UIHTMLTextFrame.js +19 -91
  80. package/Root/Frameworks/UIKit/UIHTMLTextFramesetter.js +52 -9
  81. package/Root/Frameworks/UIKit/UIHTMLTextLine.js +95 -30
  82. package/Root/Frameworks/UIKit/UIHTMLTextRun.js +22 -9
  83. package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +334 -459
  84. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +16 -0
  85. package/Root/Frameworks/UIKit/UIImageLayer.js +14 -7
  86. package/Root/Frameworks/UIKit/UIScrollView.js +12 -0
  87. package/Root/Frameworks/UIKit/UISegmentedControl.js +6 -6
  88. package/Root/Frameworks/UIKit/UITextAttachmentView.js +1 -1
  89. package/Root/Frameworks/UIKit/UITextEditor.js +210 -25
  90. package/Root/Frameworks/UIKit/UITextField.js +58 -31
  91. package/Root/Frameworks/UIKit/UITextInput.js +3 -1
  92. package/Root/Frameworks/UIKit/UITextInputManager.js +2 -2
  93. package/Root/Frameworks/UIKit/UITextLayer.js +39 -26
  94. package/Root/Frameworks/UIKit/UITokenField.js +4 -4
  95. package/Root/Frameworks/UIKit/UIView.js +3 -1
  96. package/Root/Frameworks/UIKit/UIWindow.js +16 -3
  97. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  98. package/package.json +1 -1
@@ -498,6 +498,14 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
498
498
  preventDefault = true;
499
499
  }
500
500
  break;
501
+ case 66: // b
502
+ case 73: // i
503
+ case 85: // u
504
+ // For common formatting commands, we should prevent
505
+ // the browser's default behavior even if the text input
506
+ // system hinted that there was input.
507
+ preventDefault = true;
508
+ break;
501
509
  }
502
510
  }
503
511
  if (preventDefault){
@@ -1146,10 +1154,18 @@ JSClass("UIHTMLDataTransferPasteboard", UIPasteboard, {
1146
1154
  },
1147
1155
 
1148
1156
  withDataTransfer: function(dataTransfer, action, target){
1157
+ var locallySetTypes = JSCopy(this._locallySetTypes);
1149
1158
  try{
1150
1159
  this.dataTransfer = dataTransfer;
1160
+ this._updateDataTransferTypeSet();
1161
+ for (var type in this._dataTransferTypeSet){
1162
+ if (type in this._locallySetTypes){
1163
+ delete this._locallySetTypes[type];
1164
+ }
1165
+ }
1151
1166
  action.call(target);
1152
1167
  }finally{
1168
+ this._locallySetTypes = locallySetTypes;
1153
1169
  this.dataTransfer = null;
1154
1170
  }
1155
1171
  },
@@ -19,13 +19,12 @@
19
19
  JSClass("UIImageLayer", UILayer, {
20
20
 
21
21
  image: JSDynamicProperty('_image', null),
22
- imageFrame: JSDynamicProperty('_imageFrame', null),
22
+ imageFrame: UILayerAnimatedProperty(),
23
23
  templateColor: UILayerAnimatedProperty(),
24
24
  automaticRenderMode: JSDynamicProperty('_automaticRenderMode', JSImage.RenderMode.original),
25
25
 
26
26
  init: function(){
27
27
  UIImageLayer.$super.init.call(this);
28
- this._imageFrame = JSRect.Zero;
29
28
  this.model.templateColor = JSColor.black;
30
29
  },
31
30
 
@@ -42,17 +41,20 @@ JSClass("UIImageLayer", UILayer, {
42
41
  },
43
42
 
44
43
  setImageFrame: function(imageFrame){
45
- this._imageFrame = imageFrame;
46
- this.setNeedsDisplay();
44
+ if (!imageFrame.isEqual(this.model.imageFrame)){
45
+ this._addImplicitAnimationForKey('imageFrame');
46
+ this.model.imageFrame = JSRect(imageFrame);
47
+ this.didChangeProperty('imageFrame');
48
+ }
47
49
  },
48
50
 
49
51
  drawInContext: function(context){
50
- if (this._image !== null && this._imageFrame.size.width > 0 && this._imageFrame.size.height > 0){
52
+ if (this._image !== null && this.presentation.imageFrame.size.width > 0 && this.presentation.imageFrame.size.height > 0){
51
53
  if (this._image.renderMode === JSImage.RenderMode.template || (this._image.renderMode === JSImage.RenderMode.automatic && this._automaticRenderMode === JSImage.RenderMode.template)){
52
54
  context.setFillColor(this.presentation.templateColor);
53
- context.fillMaskedRect(this._imageFrame, this._image);
55
+ context.fillMaskedRect(this.presentation.imageFrame, this._image);
54
56
  }else{
55
- context.drawImage(this._image, this._imageFrame);
57
+ context.drawImage(this._image, this.presentation.imageFrame);
56
58
  }
57
59
  }
58
60
  },
@@ -75,6 +77,11 @@ JSClass("UIImageLayer", UILayer, {
75
77
 
76
78
  UIImageLayer.Properties = Object.create(UILayer.Properties, {
77
79
 
80
+ imageFrame: {
81
+ writable: true,
82
+ value: JSRect.Zero
83
+ },
84
+
78
85
  templateColor: {
79
86
  writable: true,
80
87
  value: null
@@ -476,6 +476,18 @@ JSClass('UIScrollView', UIView, {
476
476
  },
477
477
 
478
478
  _didScroll: function(){
479
+ if (this.window !== null){
480
+ var responder = this.window.firstResponder;
481
+ if (responder && responder.isKindOfClass(UIView) && responder.focusRingPath !== null){
482
+ var superview = responder.superview;
483
+ while (superview !== null && superview !== this){
484
+ superview = superview.superview;
485
+ }
486
+ if (superview === this){
487
+ this.window.invalidateFocusRing(false);
488
+ }
489
+ }
490
+ }
479
491
  if (this.delegate && this.delegate.scrollViewDidScroll){
480
492
  this.delegate.scrollViewDidScroll(this);
481
493
  }
@@ -26,13 +26,13 @@ JSClass("UISegmentedControl", UIControl, {
26
26
  // MARK: - Creating a segment control
27
27
 
28
28
  initWithSpec: function(spec){
29
+ UISegmentedControl.$super.initWithSpec.call(this, spec);
29
30
  if (spec.containsKey('font')){
30
31
  this._font = spec.valueForKey("font", JSFont);
31
32
  }
32
33
  if (spec.containsKey('titleInsets')){
33
34
  this._titleInsets = spec.valueForKey("titleInsets", JSInsets);
34
35
  }
35
- UISegmentedControl.$super.initWithSpec.call(this, spec);
36
36
  var i, l;
37
37
  if (spec.containsKey('items')){
38
38
  var items = spec.valueForKey('items');
@@ -771,15 +771,15 @@ JSClass("UISegmentedControlDefaultStyler", UISegmentedControlStyler, {
771
771
 
772
772
  sizeControlToFitSize: function(control, maxSize){
773
773
  var intrinsicSize = this.intrinsicSizeOfControl(control);
774
- var size = JSSize(control.bounds.size);
774
+ var size = JSSize(intrinsicSize);
775
775
  if (maxSize.width < Number.MAX_VALUE){
776
776
  size.width = maxSize.width;
777
777
  }
778
- if (intrinsicSize.width < maxSize.width){
779
- size.width = intrinsicSize.width;
778
+ if (maxSize.width < size.width){
779
+ size.width = maxSize.width;
780
780
  }
781
- if (intrinsicSize.height < maxSize.height){
782
- size.height = intrinsicSize.height;
781
+ if (maxSize.height < size.height){
782
+ size.height = maxSize.height;
783
783
  }
784
784
  control.bounds = JSRect(JSPoint.Zero, size);
785
785
  },
@@ -31,7 +31,7 @@ JSClass("UITextAttachmentView", JSTextAttachment, {
31
31
 
32
32
  layout: function(font, lineWidth){
33
33
  this.view.sizeToFitSize(JSSize(lineWidth, Number.MAX_VALUE));
34
- this.baselineAdjustment = -this.view.lastBaselineOffsetFromBottom;
34
+ this.baselineAdjustment = this.view.lastBaselineOffsetFromBottom;
35
35
  },
36
36
 
37
37
  drawInContextAtPoint: function(context, point){
@@ -31,7 +31,6 @@ JSClass("UITextEditor", JSObject, {
31
31
  delegate: null,
32
32
  cursorColor: JSDynamicProperty('_cursorColor', null),
33
33
  cursorWidth: 2.0,
34
- insertAttributes: null,
35
34
  _isFirstResponder: false,
36
35
  _isWindowKey: false,
37
36
  _cursorBlinkRate: 0.5,
@@ -207,6 +206,10 @@ JSClass("UITextEditor", JSObject, {
207
206
  this._setSingleSelectionAtLocation(location);
208
207
  },
209
208
 
209
+ handleTouchesEndedAtLocation: function(location, touches, event){
210
+ this._setSingleSelectionAtLocation(location);
211
+ },
212
+
210
213
  handleTouchesEnded: function(touches, event){
211
214
  },
212
215
 
@@ -416,6 +419,147 @@ JSClass("UITextEditor", JSObject, {
416
419
  // -------------------------------------------------------------------------
417
420
  // MARK: - Common editing operations
418
421
 
422
+ bold: function(sender){
423
+ this._toggleBooleanAttribute("bold", "Bold");
424
+ },
425
+
426
+ italic: function(sender){
427
+ this._toggleBooleanAttribute("italic", "Italic");
428
+ },
429
+
430
+ underline: function(sender){
431
+ this._toggleBooleanAttribute("underline", "Underline");
432
+ },
433
+
434
+ removeParagraphAttribute: function(name, undoName){
435
+ this.setParagraphAttribute(name, undefined, undoName);
436
+ },
437
+
438
+ setParagraphAttributes: function(attributes, undoName){
439
+ this.undoManager.beginUndoGrouping();
440
+ var name, value;
441
+ var i, l;
442
+ var selection;
443
+ var selections = this._selectionsCopy();
444
+ for (name in attributes){
445
+ value = attributes[name];
446
+ for (i = 0, l = selections.length; i < l; ++i){
447
+ selection = selections[i];
448
+ this._setParagraphAttributeForSelection(name, value, selection);
449
+ }
450
+ }
451
+ this._setSelectionsAllowingUndo(selections);
452
+ this.undoManager.endUndoGrouping();
453
+ this.undoManager.setActionName(undoName);
454
+ },
455
+
456
+ setParagraphAttribute: function(name, value, undoName){
457
+ this.undoManager.beginUndoGrouping();
458
+ var i, l;
459
+ var selection;
460
+ var selections = this._selectionsCopy();
461
+ for (i = 0, l = selections.length; i < l; ++i){
462
+ selection = selections[i];
463
+ this._setParagraphAttributeForSelection(name, value, selection);
464
+ }
465
+ this._setSelectionsAllowingUndo(selections);
466
+ this.undoManager.endUndoGrouping();
467
+ this.undoManager.setActionName(undoName);
468
+ },
469
+
470
+ _setParagraphAttributeForSelection: function(name, value, selection){
471
+ var textStorage = this.textLayoutManager.textStorage;
472
+ var replacementString;
473
+ var replacementRange;
474
+ var paragraphRange;
475
+ paragraphRange = textStorage.string.rangeForParagraphAtIndex(selection.range.location);
476
+ replacementRange = JSRange(paragraphRange);
477
+ while (paragraphRange.length > 0 && paragraphRange.end < selection.range.end){
478
+ paragraphRange = textStorage.string.rangeForParagraphAtIndex(replacementRange.end);
479
+ replacementRange.length += paragraphRange.length;
480
+ }
481
+ if (replacementRange.length > 0){
482
+ replacementString = textStorage.attributedSubstringInRange(replacementRange);
483
+ if (value !== undefined){
484
+ replacementString.addAttributeInRange(name, value, JSRange(0, replacementString.string.length));
485
+ }else{
486
+ replacementString.removeAttributeInRange(name, JSRange(0, replacementString.string.length));
487
+ }
488
+ this._replaceTextStorageRangeAllowingUndo(textStorage, replacementRange, replacementString);
489
+ }else{
490
+ selection.attributes = this._insertAttributesForSelection(selection);
491
+ this.undoManager.registerUndo(this, this._setParagraphAttributeForSelection, name, selection.attributes[name], selection);
492
+ if (value !== undefined){
493
+ selection.attributes[name] = value;
494
+ }else{
495
+ delete selection.attributes[name];
496
+ }
497
+ }
498
+ },
499
+
500
+ _toggleBooleanAttribute: function(name, undoName){
501
+ var selection;
502
+ var textStorage = this.textLayoutManager.textStorage;
503
+ var locationAdjustment = 0;
504
+ var attributes;
505
+ var i, l;
506
+ var currentValue = true;
507
+ var runIterator;
508
+ for (i = 0, l = this.selections.length; i < l && currentValue === true; ++i){
509
+ selection = this.selections[i];
510
+ if (selection.range.length > 0){
511
+ runIterator = textStorage.runIterator(selection.range.location);
512
+ var j = 0;
513
+ while (runIterator.range.location < selection.range.end && currentValue === true && j < 10000){
514
+ if (!runIterator.attributes[name]){
515
+ currentValue = false;
516
+ }
517
+ runIterator.increment();
518
+ }
519
+ if (j === 10000){
520
+ throw new Error("infinite loop");
521
+ }
522
+ }else{
523
+ attributes = this._insertAttributesForSelection(selection);
524
+ if (!attributes[name]){
525
+ currentValue = false;
526
+ }
527
+ }
528
+ }
529
+ this.undoManager.beginUndoGrouping();
530
+ var selections = this._selectionsCopy();
531
+ for (i = 0, l = selections.length; i < l; ++i){
532
+ selection = selections[i];
533
+ this._setBooleanAttributeForSelection(name, !currentValue, selection);
534
+ }
535
+ this._setSelectionsAllowingUndo(selections);
536
+ this.undoManager.endUndoGrouping();
537
+ this.undoManager.setActionName(undoName);
538
+ this._resetSelectionAffinity();
539
+ },
540
+
541
+ _setBooleanAttributeForSelection: function(name, value, selection){
542
+ var textStorage = this.textLayoutManager.textStorage;
543
+ var replacementString;
544
+ if (selection.range.length > 0){
545
+ replacementString = textStorage.attributedSubstringInRange(selection.range);
546
+ if (value){
547
+ replacementString.addAttributeInRange(name, true, JSRange(0, replacementString.string.length));
548
+ }else{
549
+ replacementString.removeAttributeInRange(name, JSRange(0, replacementString.string.length));
550
+ }
551
+ this._replaceTextStorageRangeAllowingUndo(textStorage, selection.range, replacementString);
552
+ }else{
553
+ selection.attributes = this._insertAttributesForSelection(selection);
554
+ if (value){
555
+ selection.attributes[name] = true;
556
+ }else{
557
+ delete selection.attributes[name];
558
+ }
559
+ this.undoManager.registerUndo(this, this._setBooleanAttributeForSelection, name, !value, selection);
560
+ }
561
+ },
562
+
419
563
  _createSelectionAtLocation: function(location){
420
564
  var index = this.textLayoutManager.characterIndexAtPoint(location);
421
565
  var rect = this.textLayoutManager.rectForCharacterAtIndex(index);
@@ -462,6 +606,20 @@ JSClass("UITextEditor", JSObject, {
462
606
  this.layout();
463
607
  },
464
608
 
609
+ _selectionsCopy: function(){
610
+ var selections = [];
611
+ var i, l;
612
+ for (i = 0, l = this.selections.length; i < l; ++i){
613
+ selections.push(UITextInputSelection(this.selections[i]));
614
+ }
615
+ return selections;
616
+ },
617
+
618
+ _setSelectionsAllowingUndo: function(selections){
619
+ this.undoManager.registerUndo(this, this._setSelectionsAllowingUndo, this._selectionsCopy());
620
+ this.setSelections(selections);
621
+ },
622
+
465
623
  _insertSelection: function(selection){
466
624
  var searcher = JSBinarySearcher(this.selections, function(a, b){
467
625
  return a.range.location - b.range.location;
@@ -489,8 +647,8 @@ JSClass("UITextEditor", JSObject, {
489
647
  var adjustedRange;
490
648
  var locationAdjustment = 0;
491
649
  var textStorage = this.textLayoutManager.textStorage;
492
- this._isHandlingSelectionAdjustments = true;
493
650
  this.undoManager.beginUndoGrouping();
651
+ var selections = this._selectionsCopy();
494
652
  for (i = 0, l = ranges.length; i < l; ++i){
495
653
  range = ranges[i];
496
654
  adjustedRange = JSRange(range.location + locationAdjustment, range.length);
@@ -498,19 +656,20 @@ JSClass("UITextEditor", JSObject, {
498
656
  this._replaceTextStorageRangeAllowingUndo(textStorage, adjustedRange, null);
499
657
  }
500
658
  locationAdjustment -= range.length;
501
- if (i == this.selections.length){
502
- this.selections.push(this._createSelection(JSRange(0, 0)));
659
+ if (i == selections.length){
660
+ selections.push(this._createSelection(JSRange(0, 0)));
503
661
  }
504
- this.selections[i].range = JSRange(adjustedRange.location, 0);
662
+ selections[i].range = JSRange(adjustedRange.location, 0);
505
663
  }
506
- this._isHandlingSelectionAdjustments = false;
507
- for (var j = this.selections.length - 1; j >= i; --j){
508
- this.selections.splice(j, 1);
664
+ for (var j = selections.length - 1; j >= i; --j){
665
+ selections.splice(j, 1);
509
666
  }
667
+ for (i = 0, l = selections.length; i < l; ++i){
668
+ selections[i].affinity = UITextInput.SelectionAffinity.beforeCurrentCharacter;
669
+ }
670
+ this._setSelectionsAllowingUndo(selections);
510
671
  this.undoManager.endUndoGrouping();
511
672
  this.undoManager.setActionName("Typing");
512
- this._collapseOverlappingSelections();
513
- this._resetSelectionAffinity();
514
673
  },
515
674
 
516
675
  _replaceTextStorageRangeAllowingUndo: function(textStorage, range, attributedString){
@@ -523,10 +682,12 @@ JSClass("UITextEditor", JSObject, {
523
682
  insertedLength = attributedString.string.length;
524
683
  }
525
684
  this.undoManager.registerUndo(this, this._replaceTextStorageRangeAllowingUndo, textStorage, JSRange(range.location, insertedLength), replacedString);
685
+ this._isHandlingSelectionAdjustments = true;
526
686
  textStorage.replaceCharactersInRangeWithAttributedString(range, attributedString);
687
+ this._isHandlingSelectionAdjustments = false;
527
688
  },
528
689
 
529
- _collapseOverlappingSelections: function(){
690
+ _collapseOverlappingSelections: function(sections){
530
691
  var removed = [];
531
692
  for (var i = this.selections.length - 1; i > 0; --i){
532
693
  if (this.selections[i - 1].range.end >= this.selections[i].range.location){
@@ -545,10 +706,20 @@ JSClass("UITextEditor", JSObject, {
545
706
  },
546
707
 
547
708
  _insertAttributesForSelection: function(selection){
548
- var index = selection.range.location > 0 ? selection.range.location - 1 : 0;
549
- var attributes = this.textLayoutManager.textStorage.attributesAtIndex(index);
709
+ if (selection.attributes){
710
+ return selection.attributes;
711
+ }
712
+ var index = selection.range.location;
713
+ var iterator;
714
+ if (index > 0){
715
+ iterator = this.textLayoutManager.textStorage.string.userPerceivedCharacterIterator(index);
716
+ iterator.decrement();
717
+ if (!iterator.isParagraphBreak){
718
+ index = iterator.range.location;
719
+ }
720
+ }
721
+ var attributes = JSCopy(this.textLayoutManager.textStorage.attributesAtIndex(index));
550
722
  if (JSAttributedString.Attribute.attachment in attributes){
551
- attributes = JSCopy(attributes);
552
723
  delete attributes[JSAttributedString.Attribute.attachment];
553
724
  }
554
725
  return attributes;
@@ -587,23 +758,21 @@ JSClass("UITextEditor", JSObject, {
587
758
  var locationAdjustment = 0;
588
759
  var adjustedRange;
589
760
  var insertAttributes;
590
- this._isHandlingSelectionAdjustments = true;
761
+ var i, l;
762
+ var selections = this._selectionsCopy();
591
763
  this.undoManager.beginUndoGrouping();
592
- for (var i = 0, l = this.selections.length; i < l; ++i){
593
- selection = this.selections[i];
594
- insertAttributes = this.insertAttributes;
595
- if (insertAttributes === null){
596
- insertAttributes = this._insertAttributesForSelection(selection);
597
- }
764
+ for (i = 0, l = selections.length; i < l; ++i){
765
+ selection = selections[i];
766
+ insertAttributes = this._insertAttributesForSelection(selection);
598
767
  adjustedRange = JSRange(selection.range.location + locationAdjustment, selection.range.length);
599
768
  this._replaceTextStorageRangeAllowingUndo(textStorage, adjustedRange, JSAttributedString.initWithString(text, insertAttributes));
600
769
  selection.range = JSRange(adjustedRange.location + textLength, 0);
770
+ selection.affinity = UITextInput.SelectionAffinity.beforeCurrentCharacter;
601
771
  locationAdjustment += textLength - adjustedRange.length;
602
772
  }
773
+ this._setSelectionsAllowingUndo(selections);
603
774
  this.undoManager.endUndoGrouping();
604
775
  this.undoManager.setActionName("Typing");
605
- this._isHandlingSelectionAdjustments = false;
606
- this._resetSelectionAffinity();
607
776
  },
608
777
 
609
778
  setMarkedText: function(text){
@@ -631,7 +800,7 @@ JSClass("UITextEditor", JSObject, {
631
800
  },
632
801
 
633
802
  insertLineBreak: function(){
634
- this.insertText("\n");
803
+ this.insertText("\u2028");
635
804
  },
636
805
 
637
806
  insertTab: function(){
@@ -812,6 +981,7 @@ JSClass("UITextEditor", JSObject, {
812
981
  var perceivedCharacterRange;
813
982
  for (var i = 0, l = this.selections.length; i < l; ++i){
814
983
  selection = this.selections[i];
984
+ selection.attributes = null;
815
985
  if (selection.range.length === 0){
816
986
  if (selection.range.location > 0){
817
987
  perceivedCharacterRange = textStorage.string.rangeForUserPerceivedCharacterAtIndex(selection.range.location - 1);
@@ -834,6 +1004,7 @@ JSClass("UITextEditor", JSObject, {
834
1004
  var indexOfPreviousWord;
835
1005
  for (var i = 0, l = this.selections.length; i < l; ++i){
836
1006
  selection = this.selections[i];
1007
+ selection.attributes = null;
837
1008
  indexOfPreviousWord = textStorage.string.indexOfWordStartBeforeIndex(selection.range.location);
838
1009
  selection.range = JSRange(indexOfPreviousWord, 0);
839
1010
  }
@@ -849,6 +1020,7 @@ JSClass("UITextEditor", JSObject, {
849
1020
  var line;
850
1021
  for (var i = 0, l = this.selections.length; i < l; ++i){
851
1022
  selection = this.selections[i];
1023
+ selection.attributes = null;
852
1024
  line = this._lineForSelectionStart(selection);
853
1025
  if (line !== null){
854
1026
  selection.range = JSRange(line.range.location, 0);
@@ -872,6 +1044,7 @@ JSClass("UITextEditor", JSObject, {
872
1044
  var cursorX;
873
1045
  for (var i = 0, l = this.selections.length; i < l; ++i){
874
1046
  selection = this.selections[i];
1047
+ selection.attributes = null;
875
1048
  selection.insertionPoint = UITextInput.SelectionInsertionPoint.start;
876
1049
  cursorRect = this._cursorRectForSelection(selection);
877
1050
  cursorX = cursorRect.origin.x + Math.floor(cursorRect.size.width / 2.0);
@@ -915,6 +1088,7 @@ JSClass("UITextEditor", JSObject, {
915
1088
  var perceivedCharacterRange;
916
1089
  for (var i = 0, l = this.selections.length; i < l; ++i){
917
1090
  selection = this.selections[i];
1091
+ selection.attributes = null;
918
1092
  if (selection.range.length === 0){
919
1093
  if (selection.range.location < textStorage.string.length){
920
1094
  perceivedCharacterRange = textStorage.string.rangeForUserPerceivedCharacterAtIndex(selection.range.location);
@@ -937,6 +1111,7 @@ JSClass("UITextEditor", JSObject, {
937
1111
  var nextWordIndex;
938
1112
  for (var i = 0, l = this.selections.length; i < l; ++i){
939
1113
  selection = this.selections[i];
1114
+ selection.attributes = null;
940
1115
  nextWordIndex = textStorage.string.indexOfWordEndAfterIndex(selection.range.end);
941
1116
  selection.range = JSRange(nextWordIndex, 0);
942
1117
  }
@@ -952,6 +1127,7 @@ JSClass("UITextEditor", JSObject, {
952
1127
  var line;
953
1128
  for (var i = 0, l = this.selections.length; i < l; ++i){
954
1129
  selection = this.selections[i];
1130
+ selection.attributes = null;
955
1131
  line = this._lineForSelectionEnd(selection);
956
1132
  if (line !== null && line.range.end > selection.range.location){
957
1133
  var iterator = textStorage.string.userPerceivedCharacterIterator(line.range.end - 1);
@@ -981,6 +1157,7 @@ JSClass("UITextEditor", JSObject, {
981
1157
  var lineEnumerator;
982
1158
  for (var i = 0, l = this.selections.length; i < l; ++i){
983
1159
  selection = this.selections[i];
1160
+ selection.attributes = null;
984
1161
  selection.insertionPoint = UITextInput.SelectionInsertionPoint.end;
985
1162
  cursorRect = this._cursorRectForSelection(selection);
986
1163
  cursorX = cursorRect.origin.x + Math.floor(cursorRect.size.width / 2.0);
@@ -1025,6 +1202,7 @@ JSClass("UITextEditor", JSObject, {
1025
1202
  var perceivedCharacterRange;
1026
1203
  for (var i = 0, l = this.selections.length; i < l; ++i){
1027
1204
  selection = this.selections[i];
1205
+ selection.attributes = null;
1028
1206
  if (selection.range.length > 0 && selection.insertionPoint == UITextInput.SelectionInsertionPoint.end){
1029
1207
  perceivedCharacterRange = textStorage.string.rangeForUserPerceivedCharacterAtIndex(selection.range.end - 1);
1030
1208
  selection.range.length -= perceivedCharacterRange.length;
@@ -1050,6 +1228,7 @@ JSClass("UITextEditor", JSObject, {
1050
1228
  var end;
1051
1229
  for (var i = 0, l = this.selections.length; i < l; ++i){
1052
1230
  selection = this.selections[i];
1231
+ selection.attributes = null;
1053
1232
  if (selection.insertionPoint == UITextInput.SelectionInsertionPoint.start){
1054
1233
  end = selection.range.end;
1055
1234
  }else{
@@ -1072,6 +1251,7 @@ JSClass("UITextEditor", JSObject, {
1072
1251
  var end;
1073
1252
  for (var i = 0, l = this.selections.length; i < l; ++i){
1074
1253
  selection = this.selections[i];
1254
+ selection.attributes = null;
1075
1255
  line = this._lineForSelectionStart(selection);
1076
1256
  if (line !== null){
1077
1257
  if (selection.insertionPoint == UITextInput.SelectionInsertionPoint.start){
@@ -1101,6 +1281,7 @@ JSClass("UITextEditor", JSObject, {
1101
1281
  var lineEnumerator;
1102
1282
  for (var i = 0, l = this.selections.length; i < l; ++i){
1103
1283
  selection = this.selections[i];
1284
+ selection.attributes = null;
1104
1285
  cursorRect = this._cursorRectForSelection(selection);
1105
1286
  cursorX = cursorRect.origin.x + Math.floor(cursorRect.size.width / 2.0);
1106
1287
  lineEnumerator = this.textLayoutManager.lineEnumerator((selection.insertionPoint == UITextInput.SelectionInsertionPoint.start ? selection.range.location : selection.range.end) - (selection.affinity === UITextInput.SelectionAffinity.afterPreviousCharacter ? 1 : 0));
@@ -1166,6 +1347,7 @@ JSClass("UITextEditor", JSObject, {
1166
1347
  var perceivedCharacterRange;
1167
1348
  for (var i = 0, l = this.selections.length; i < l; ++i){
1168
1349
  selection = this.selections[i];
1350
+ selection.attributes = null;
1169
1351
  if (selection.range.length > 0 && selection.insertionPoint == UITextInput.SelectionInsertionPoint.start){
1170
1352
  perceivedCharacterRange = textStorage.string.rangeForUserPerceivedCharacterAtIndex(selection.range.location);
1171
1353
  selection.range.advance(perceivedCharacterRange.length);
@@ -1191,6 +1373,7 @@ JSClass("UITextEditor", JSObject, {
1191
1373
  var start;
1192
1374
  for (var i = 0, l = this.selections.length; i < l; ++i){
1193
1375
  selection = this.selections[i];
1376
+ selection.attributes = null;
1194
1377
  if (selection.insertionPoint == UITextInput.SelectionInsertionPoint.start){
1195
1378
  start = selection.range.end;
1196
1379
  }else{
@@ -1213,6 +1396,7 @@ JSClass("UITextEditor", JSObject, {
1213
1396
  var start;
1214
1397
  for (var i = 0, l = this.selections.length; i < l; ++i){
1215
1398
  selection = this.selections[i];
1399
+ selection.attributes = null;
1216
1400
  line = this._lineForSelectionEnd(selection);
1217
1401
  if (selection.insertionPoint == UITextInput.SelectionInsertionPoint.start){
1218
1402
  start = selection.range.end;
@@ -1248,6 +1432,7 @@ JSClass("UITextEditor", JSObject, {
1248
1432
  var cursorX;
1249
1433
  for (var i = 0, l = this.selections.length; i < l; ++i){
1250
1434
  selection = this.selections[i];
1435
+ selection.attributes = null;
1251
1436
  cursorRect = this._cursorRectForSelection(selection);
1252
1437
  cursorX = cursorRect.origin.x + Math.floor(cursorRect.size.width / 2.0);
1253
1438
  lineEnumerator = this.textLayoutManager.lineEnumerator((selection.insertionPoint == UITextInput.SelectionInsertionPoint.start ? selection.range.location : selection.range.end) - (selection.affinity === UITextInput.SelectionAffinity.afterPreviousCharacter ? 1 : 0));
@@ -1334,7 +1519,7 @@ JSClass("UITextEditor", JSObject, {
1334
1519
  return this.textLayoutManager.lineForCharacterAtIndex(selection.range.end - 1);
1335
1520
  }
1336
1521
  return this.textLayoutManager.lineForCharacterAtIndex(selection.range.end);
1337
- }
1522
+ },
1338
1523
 
1339
1524
  });
1340
1525