@breakside/jskit 2022.23.0 → 2022.25.1
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.
- package/Frameworks/DOM.jsframework/Info.json +2 -2
- package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
- package/Frameworks/FontKit.jsframework/Info.json +2 -2
- package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/Info.json +2 -2
- package/Frameworks/Foundation.jsframework/JS/JSAttributedString.js +139 -10
- package/Frameworks/Foundation.jsframework/JS/JSFileManager+Node.js +2 -2
- package/Frameworks/Foundation.jsframework/JS/JSParagraphStyle.js +71 -0
- package/Frameworks/Foundation.jsframework/JS/JSTextAttachment.js +1 -1
- package/Frameworks/Foundation.jsframework/JS/JSTextContainer.js +56 -29
- package/Frameworks/Foundation.jsframework/JS/JSTextFrame.js +2 -32
- package/Frameworks/Foundation.jsframework/JS/JSTextFramesetter.js +125 -22
- package/Frameworks/Foundation.jsframework/JS/JSTextLayoutManager.js +11 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextLine.js +4 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +5 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +41 -25
- package/Frameworks/Foundation.jsframework/JS/JSURL.js +3 -0
- package/Frameworks/Foundation.jsframework/JS/JSZip.js +97 -76
- package/Frameworks/Foundation.jsframework/JS/String+JS.js +35 -0
- package/Frameworks/Foundation.jsframework/JS/UnicodeChar.js +8 -0
- package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/sources.json +3 -0
- package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
- package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
- package/Info.json +2 -2
- package/Node/io.breakside.jskit-bundle.js +2 -2
- package/Node/jskit +1 -0
- package/Root/Frameworks/APIKit/Info.yaml +1 -1
- package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/AuthKit/Info.yaml +1 -1
- package/Root/Frameworks/CSSOM/Info.yaml +1 -1
- package/Root/Frameworks/ChartKit/CHAxis.js +5 -5
- package/Root/Frameworks/ChartKit/CHPieChart.js +1 -1
- package/Root/Frameworks/ChartKit/Info.yaml +1 -1
- package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
- package/Root/Frameworks/DBKit/Info.yaml +1 -1
- package/Root/Frameworks/DOM/Info.yaml +1 -1
- package/Root/Frameworks/Dispatch/Info.yaml +1 -1
- package/Root/Frameworks/FontKit/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/JSAttributedString.js +139 -10
- package/Root/Frameworks/Foundation/JSFileManager+Node.js +2 -2
- package/Root/Frameworks/Foundation/JSParagraphStyle.js +71 -0
- package/Root/Frameworks/Foundation/JSTextAttachment.js +1 -1
- package/Root/Frameworks/Foundation/JSTextContainer.js +56 -29
- package/Root/Frameworks/Foundation/JSTextFrame.js +2 -32
- package/Root/Frameworks/Foundation/JSTextFramesetter.js +125 -22
- package/Root/Frameworks/Foundation/JSTextLayoutManager.js +11 -3
- package/Root/Frameworks/Foundation/JSTextLine.js +4 -3
- package/Root/Frameworks/Foundation/JSTextRun.js +5 -3
- package/Root/Frameworks/Foundation/JSTextTypesetter.js +41 -25
- package/Root/Frameworks/Foundation/JSURL.js +3 -0
- package/Root/Frameworks/Foundation/JSZip.js +97 -76
- package/Root/Frameworks/Foundation/String+JS.js +35 -0
- package/Root/Frameworks/Foundation/UnicodeChar.js +8 -0
- package/Root/Frameworks/ImageKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
- package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
- package/Root/Frameworks/PDFKit/Info.yaml +1 -1
- package/Root/Frameworks/QRKit/Info.yaml +1 -1
- package/Root/Frameworks/SearchKit/Info.yaml +1 -1
- package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/TestKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/UIBasicAnimation.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLApplication.js +53 -15
- package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +36 -0
- package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLTextField.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLTextFrame.js +19 -91
- package/Root/Frameworks/UIKit/UIHTMLTextFramesetter.js +52 -9
- package/Root/Frameworks/UIKit/UIHTMLTextLine.js +95 -30
- package/Root/Frameworks/UIKit/UIHTMLTextRun.js +22 -9
- package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +334 -459
- package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +16 -0
- package/Root/Frameworks/UIKit/UIImageLayer.js +14 -7
- package/Root/Frameworks/UIKit/UILabel.js +2 -0
- package/Root/Frameworks/UIKit/UIScrollView.js +12 -0
- package/Root/Frameworks/UIKit/UISegmentedControl.js +6 -6
- package/Root/Frameworks/UIKit/UIState.js +8 -1
- package/Root/Frameworks/UIKit/UITextAttachmentView.js +1 -1
- package/Root/Frameworks/UIKit/UITextEditor.js +210 -25
- package/Root/Frameworks/UIKit/UITextField.js +58 -31
- package/Root/Frameworks/UIKit/UITextInput.js +3 -1
- package/Root/Frameworks/UIKit/UITextInputManager.js +2 -2
- package/Root/Frameworks/UIKit/UITextLayer.js +39 -26
- package/Root/Frameworks/UIKit/UITokenField.js +4 -4
- package/Root/Frameworks/UIKit/UIView.js +3 -1
- package/Root/Frameworks/UIKit/UIWindow.js +16 -3
- package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
- package/package.json +1 -1
|
@@ -15,37 +15,34 @@
|
|
|
15
15
|
|
|
16
16
|
// #import Foundation
|
|
17
17
|
// #import "UIHTMLTextLine.js"
|
|
18
|
-
// #import "UIHTMLTextRun.js"
|
|
19
|
-
// #import "UITextAttachmentView.js"
|
|
20
|
-
// jshint browser: true
|
|
21
18
|
'use strict';
|
|
22
19
|
|
|
23
20
|
(function(){
|
|
24
21
|
|
|
25
22
|
var logger = JSLog("uikit", "text");
|
|
26
|
-
|
|
27
|
-
var sharedDomRange = null;
|
|
28
|
-
var layoutElementStack = [];
|
|
29
|
-
var layoutElementIndex = 0;
|
|
23
|
+
var sharedCanvas = null;
|
|
30
24
|
|
|
31
25
|
JSClass("UIHTMLTextTypesetter", JSTextTypesetter, {
|
|
32
26
|
|
|
33
27
|
domDocument: JSReadOnlyProperty('_domDocument'),
|
|
34
|
-
_cachedLayout: null,
|
|
35
|
-
_suggestionCache: null,
|
|
36
|
-
_htmlDisplayServer: null,
|
|
37
28
|
|
|
38
|
-
initWithDocument: function(domDocument
|
|
29
|
+
initWithDocument: function(domDocument){
|
|
39
30
|
UIHTMLTextTypesetter.$super.init.call(this);
|
|
40
31
|
this._domDocument = domDocument;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
sharedDomRange = this._domDocument.createRange();
|
|
32
|
+
if (sharedCanvas === null){
|
|
33
|
+
sharedCanvas = this._domDocument.createElement("canvas");
|
|
44
34
|
}
|
|
45
|
-
this.
|
|
35
|
+
this.canvasContext = sharedCanvas.getContext("2d");
|
|
46
36
|
this._runElementQueue = [];
|
|
37
|
+
this._lineElementQueue = [];
|
|
47
38
|
},
|
|
48
39
|
|
|
40
|
+
_suggestedHTMLLineLayout: null,
|
|
41
|
+
|
|
42
|
+
canvasContext: null,
|
|
43
|
+
|
|
44
|
+
// MARK: - Element queue
|
|
45
|
+
|
|
49
46
|
_lineElementQueue: null,
|
|
50
47
|
_runElementQueue: null,
|
|
51
48
|
|
|
@@ -71,208 +68,6 @@ JSClass("UIHTMLTextTypesetter", JSTextTypesetter, {
|
|
|
71
68
|
return null;
|
|
72
69
|
},
|
|
73
70
|
|
|
74
|
-
layoutRange: function(range, size, lineBreakMode){
|
|
75
|
-
if (size.width <= 0 || size.height <= 0){
|
|
76
|
-
size = JSSize(size.width <= 0 ? Number.MAX_VALUE : size.width, size.height <= 0 ? Number.MAX_VALUE : size.height);
|
|
77
|
-
}
|
|
78
|
-
// Because Javascript only has a single thread, two typesetter instances
|
|
79
|
-
// generally won't overlap, and we can save some energy by reusing the same
|
|
80
|
-
// dom element to do the layout for every typesetter.
|
|
81
|
-
// However, there is a case where layout happens recursively:
|
|
82
|
-
// when an attachment view in one typesetter needs to typeset a string of its own.
|
|
83
|
-
// (attachment views need to layout immediately so their size is known).
|
|
84
|
-
// Therefore, we need a stack of layout elements rather than a single shared element.
|
|
85
|
-
// FIXME: Allow removal of element after it is no longer needed
|
|
86
|
-
var layoutElement;
|
|
87
|
-
if (layoutElementIndex < layoutElementStack.length){
|
|
88
|
-
layoutElement = layoutElementStack[layoutElementIndex];
|
|
89
|
-
}else{
|
|
90
|
-
layoutElement = this._domDocument.createElement('div');
|
|
91
|
-
layoutElement.setAttribute("role", "none presentation");
|
|
92
|
-
layoutElement.setAttribute("aria-hidden", "true");
|
|
93
|
-
layoutElement.style.position = 'absolute';
|
|
94
|
-
layoutElement.style.right = '0';
|
|
95
|
-
layoutElement.style.right = '0';
|
|
96
|
-
layoutElement.style.backgroundColor = 'rgb(255,240,240)';
|
|
97
|
-
layoutElement.style.lineHeight = '0';
|
|
98
|
-
// Browsers are a bit particular here:
|
|
99
|
-
// - We want to use caretRangeFromPoint if available, because it's
|
|
100
|
-
// super fast at finding line breaks
|
|
101
|
-
// - Most browsers can't use caretRangeFromPoint for elements offscreen,
|
|
102
|
-
// so we need an invisible element onscreen
|
|
103
|
-
// - display=none is out because browsers don't even bother rendering those elements
|
|
104
|
-
// - visibility=hidden works for some, but Chrome will return a caretRangeFromPoint of 0,0
|
|
105
|
-
// - opacity=0 seems to work best across all browsers
|
|
106
|
-
// layoutElement.style.visibility = 'hidden';
|
|
107
|
-
layoutElement.style.opacity = '0';
|
|
108
|
-
this._domDocument.body.appendChild(layoutElement);
|
|
109
|
-
layoutElementStack.push(layoutElement);
|
|
110
|
-
}
|
|
111
|
-
++layoutElementIndex;
|
|
112
|
-
this._styleElement(layoutElement, size, lineBreakMode);
|
|
113
|
-
this._updateElement(layoutElement, size, range);
|
|
114
|
-
this._cachedLayout.lineBreakMode = lineBreakMode;
|
|
115
|
-
--layoutElementIndex;
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
_styleElement: function(element, size, lineBreakMode){
|
|
119
|
-
if (size.width < Number.MAX_VALUE){
|
|
120
|
-
element.style.width = '%dpx'.sprintf(size.width);
|
|
121
|
-
element.style.whiteSpace = 'pre-wrap';
|
|
122
|
-
}else{
|
|
123
|
-
element.style.width = '';
|
|
124
|
-
element.style.whiteSpace = 'pre';
|
|
125
|
-
}
|
|
126
|
-
if (lineBreakMode === null){
|
|
127
|
-
element.style.whiteSpace = 'nowrap';
|
|
128
|
-
element.style.wordBreak = '';
|
|
129
|
-
element.style.overflowWrap = '';
|
|
130
|
-
}else{
|
|
131
|
-
switch (lineBreakMode){
|
|
132
|
-
case JSLineBreakMode.truncateTail:
|
|
133
|
-
case JSLineBreakMode.characterWrap:
|
|
134
|
-
element.style.wordBreak = 'break-all';
|
|
135
|
-
element.style.overflowWrap = '';
|
|
136
|
-
break;
|
|
137
|
-
case JSLineBreakMode.wordWrap:
|
|
138
|
-
element.style.wordBreak = '';
|
|
139
|
-
element.style.overflowWrap = 'break-word';
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
_updateElement: function(element, size, range){
|
|
146
|
-
var remainingRange = JSRange(range);
|
|
147
|
-
var runIterator = this.attributedString.runIterator(range.location);
|
|
148
|
-
var i = 0;
|
|
149
|
-
var span;
|
|
150
|
-
var runDescriptors = [];
|
|
151
|
-
var runDescriptor;
|
|
152
|
-
var attachment;
|
|
153
|
-
var attachments = [];
|
|
154
|
-
var previousAttachmentsByID = {};
|
|
155
|
-
var superview;
|
|
156
|
-
if (this._cachedLayout !== null){
|
|
157
|
-
for (i = 0; i < this._cachedLayout.attachments.length; ++i){
|
|
158
|
-
previousAttachmentsByID[this._cachedLayout.attachments[i].objectID] = this._cachedLayout.attachments[i];
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
i = 0;
|
|
162
|
-
while (remainingRange.length > 0){
|
|
163
|
-
var utf16 = this._attributedString.string.substringInRange(runIterator.range.intersection(remainingRange));
|
|
164
|
-
if (runIterator.attributes[JSAttributedString.Attribute.maskCharacter]){
|
|
165
|
-
utf16 = utf16.stringByMaskingWithCharacter(runIterator.attributes[JSAttributedString.Attribute.maskCharacter]);
|
|
166
|
-
}
|
|
167
|
-
if (runIterator.range.length === 1 && utf16 == JSAttributedString.SpecialCharacter.attachmentUTF16){
|
|
168
|
-
attachment = runIterator.attributes[JSAttributedString.Attribute.attachment];
|
|
169
|
-
utf16 = '';
|
|
170
|
-
}else{
|
|
171
|
-
attachment = null;
|
|
172
|
-
}
|
|
173
|
-
if (i < element.childNodes.length){
|
|
174
|
-
span = element.childNodes[i];
|
|
175
|
-
span.firstChild.nodeValue = utf16;
|
|
176
|
-
}else{
|
|
177
|
-
span = element.appendChild(element.ownerDocument.createElement('span'));
|
|
178
|
-
span.setAttribute("role", "none presentation");
|
|
179
|
-
span.appendChild(span.ownerDocument.createTextNode(utf16));
|
|
180
|
-
}
|
|
181
|
-
if (attachment !== null){
|
|
182
|
-
this._htmlDisplayServer.attachmentInserted(attachment);
|
|
183
|
-
if (attachment.objectID in previousAttachmentsByID){
|
|
184
|
-
delete previousAttachmentsByID[attachment.objectID];
|
|
185
|
-
}
|
|
186
|
-
attachments.push(attachment);
|
|
187
|
-
attachment.layout(JSTextTypesetter.FontFromAttributes(runIterator.attributes), size.width);
|
|
188
|
-
}
|
|
189
|
-
runDescriptor = UIHTMLTextTypesetterRunDescriptor(span, runIterator.attributes, runIterator.range.intersection(remainingRange), attachment);
|
|
190
|
-
runDescriptors.push(runDescriptor);
|
|
191
|
-
remainingRange.advance(runIterator.range.length);
|
|
192
|
-
runIterator.increment();
|
|
193
|
-
++i;
|
|
194
|
-
}
|
|
195
|
-
for (var j = element.childNodes.length - 1; j >= i; --j){
|
|
196
|
-
element.removeChild(element.childNodes[j]);
|
|
197
|
-
}
|
|
198
|
-
for (var id in previousAttachmentsByID){
|
|
199
|
-
this._htmlDisplayServer.attachmentRemoved(previousAttachmentsByID[id]);
|
|
200
|
-
}
|
|
201
|
-
this._cachedLayout = {
|
|
202
|
-
runDescriptors: runDescriptors,
|
|
203
|
-
range: JSRange(range),
|
|
204
|
-
attachments: attachments
|
|
205
|
-
};
|
|
206
|
-
this._suggestionCache = null;
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
createLine: function(range){
|
|
210
|
-
if (range.length === 0){
|
|
211
|
-
return this._createEmptyLine(range);
|
|
212
|
-
}
|
|
213
|
-
var fragments = null;
|
|
214
|
-
var i, l;
|
|
215
|
-
if (this._suggestionCache !== null){
|
|
216
|
-
fragments = this._suggestionCache.fragments;
|
|
217
|
-
}
|
|
218
|
-
if (fragments === null || fragments[0].range.location != range.location || fragments[fragments.length - 1].range.end != range.end){
|
|
219
|
-
logger.warn("Calling createLine without matching suggestion, re-calculating layout");
|
|
220
|
-
this.layoutRange(range, JSSize.Zero, JSLineBreakMode.wordWrap);
|
|
221
|
-
var runDescriptors = this._cachedLayout.runDescriptors;
|
|
222
|
-
fragments = [];
|
|
223
|
-
for (i = 0, l = runDescriptors.length; i < l; ++i){
|
|
224
|
-
fragments.push(UIHTMLTextTypesetterRunDescriptorFragment(runDescriptors[i], runDescriptors[i].range));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
var element = this._createLineElement();
|
|
228
|
-
var fragment;
|
|
229
|
-
var runs = [];
|
|
230
|
-
var attachments = [];
|
|
231
|
-
var run;
|
|
232
|
-
var overflowed = false;
|
|
233
|
-
for (i = 0, l = fragments.length; i < l; ++i){
|
|
234
|
-
fragment = fragments[i];
|
|
235
|
-
var span = this._createRunElement();
|
|
236
|
-
span.setAttribute("style", fragment.runDescriptor.span.getAttribute("style"));
|
|
237
|
-
// var span = fragment.runDescriptor.span.cloneNode(false);
|
|
238
|
-
if (!fragment.runDescriptor.attachment){
|
|
239
|
-
var utf16 = this.attributedString.string.substringInRange(fragment.range);
|
|
240
|
-
if (fragment.runDescriptor.attributes[JSAttributedString.Attribute.maskCharacter]){
|
|
241
|
-
utf16 = utf16.stringByMaskingWithCharacter(fragment.runDescriptor.attributes[JSAttributedString.Attribute.maskCharacter]);
|
|
242
|
-
}
|
|
243
|
-
if (span.childNodes.length > 0){
|
|
244
|
-
span.childNodes[0].nodeValue = utf16;
|
|
245
|
-
}else{
|
|
246
|
-
span.appendChild(span.ownerDocument.createTextNode(utf16));
|
|
247
|
-
}
|
|
248
|
-
}else{
|
|
249
|
-
if (span.childNodes.length > 0){
|
|
250
|
-
span.removeChild(span.childNodes[0]);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
run = UIHTMLTextRun.initWithElement(span, fragment.runDescriptor.font, fragment.runDescriptor.attributes, fragment.range);
|
|
254
|
-
runs.push(run);
|
|
255
|
-
if (fragment.runDescriptor.attachment){
|
|
256
|
-
// FIXME:
|
|
257
|
-
// 1. Want context for attachment to be resuable to minimize dom manipulations, but
|
|
258
|
-
// also need a distinct context per text frame that uses this string+attachment
|
|
259
|
-
attachments.push({
|
|
260
|
-
context: this._htmlDisplayServer.contextForAttachment(fragment.runDescriptor.attachment),
|
|
261
|
-
attachment: fragment.runDescriptor.attachment,
|
|
262
|
-
run: run
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
overflowed = fragment.overflowed;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// NOTE: Deferring calculation of trailingWhitespaceWidth until all the lines are done and added to
|
|
269
|
-
// a frame, otherwise unecessary and expensive browser layout operations will be triggered.
|
|
270
|
-
|
|
271
|
-
var line = UIHTMLTextLine.initWithElement(element, runs, 0, attachments);
|
|
272
|
-
line.overflowed = overflowed;
|
|
273
|
-
return line;
|
|
274
|
-
},
|
|
275
|
-
|
|
276
71
|
_runID: 0,
|
|
277
72
|
|
|
278
73
|
_createRunElement: function(){
|
|
@@ -283,7 +78,7 @@ JSClass("UIHTMLTextTypesetter", JSTextTypesetter, {
|
|
|
283
78
|
span.dataset.typesetterId = ++this._runID;
|
|
284
79
|
return span;
|
|
285
80
|
},
|
|
286
|
-
|
|
81
|
+
|
|
287
82
|
_lineID: 0,
|
|
288
83
|
|
|
289
84
|
_createLineElement: function(){
|
|
@@ -300,294 +95,374 @@ JSClass("UIHTMLTextTypesetter", JSTextTypesetter, {
|
|
|
300
95
|
return element;
|
|
301
96
|
},
|
|
302
97
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
98
|
+
// MARK: - Creating a Line
|
|
99
|
+
|
|
100
|
+
createLine: function(range){
|
|
101
|
+
if (range.length === 0){
|
|
102
|
+
return this._createEmptyHTMLLine(range);
|
|
103
|
+
}
|
|
104
|
+
var layout = this._suggestedHTMLLineLayout;
|
|
105
|
+
if (layout === null || !range.isEqual(layout.range)){
|
|
106
|
+
layout = this._layoutHTMLLine(Number.MAX_VALUE, range, JSLineBreakMode.characterWrap);
|
|
107
|
+
}
|
|
108
|
+
return this._createLineFromHTMLLayout(layout);
|
|
308
109
|
},
|
|
309
110
|
|
|
310
111
|
suggestLineBreak: function(width, range, lineBreakMode){
|
|
311
112
|
if (range.length === 0){
|
|
312
113
|
return JSRange(range);
|
|
313
114
|
}
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
this.layoutRange(range, JSSize(width, 0), lineBreakMode);
|
|
317
|
-
}else if (this._cachedLayout.lineBreakMode != lineBreakMode){
|
|
318
|
-
// When truncating a multi-line string, we have to do two layouts:
|
|
319
|
-
// 1. layout first with word-wrap so we know where the line breaks are
|
|
320
|
-
// 2. layout the final line again with truncate tail
|
|
321
|
-
// The result is the same as the condition above, a new layoutRange call, but
|
|
322
|
-
// in this case we don't want to bother logging a warning since it's an expected
|
|
323
|
-
// use case
|
|
324
|
-
this.layoutRange(range, JSSize(width, 0), lineBreakMode);
|
|
115
|
+
if (width <= 0){
|
|
116
|
+
width = Number.MAX_VALUE;
|
|
325
117
|
}
|
|
118
|
+
this._suggestedHTMLLineLayout = this._layoutHTMLLine(width, range, lineBreakMode);
|
|
119
|
+
return this._suggestedHTMLLineLayout.range;
|
|
120
|
+
},
|
|
326
121
|
|
|
327
|
-
|
|
328
|
-
var runIndex = 0;
|
|
329
|
-
var runLineIndex = 0;
|
|
330
|
-
if (this._suggestionCache !== null){
|
|
331
|
-
expectedLocation = this._suggestionCache.fragments[this._suggestionCache.fragments.length - 1].range.end;
|
|
332
|
-
runIndex = this._suggestionCache.runIndex;
|
|
333
|
-
runLineIndex = this._suggestionCache.runLineIndex;
|
|
334
|
-
}
|
|
122
|
+
// MARK: - Private Helpers for Line Creation
|
|
335
123
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
124
|
+
_createEmptyHTMLLine: function(range){
|
|
125
|
+
var attributes = this._attributedString.attributesAtIndex(range.location);
|
|
126
|
+
attributes = this.resolveAttributes(attributes);
|
|
127
|
+
var font = attributes.font || null;
|
|
128
|
+
var element = this._createLineElement();
|
|
129
|
+
return UIHTMLTextLine.initWithElementAndFont(element, font, font.displayLineHeight, range.location, this.canvasContext);
|
|
130
|
+
},
|
|
342
131
|
|
|
343
|
-
|
|
132
|
+
_createLineFromHTMLLayout: function(layout){
|
|
133
|
+
var runs = [];
|
|
134
|
+
var run;
|
|
135
|
+
var x = 0;
|
|
344
136
|
var runDescriptor;
|
|
345
|
-
var
|
|
346
|
-
var
|
|
347
|
-
var
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
// Since we've found our break, just stop here
|
|
362
|
-
stop = true;
|
|
363
|
-
x = -Number.MAX_VALUE;
|
|
364
|
-
}else if (runLineIndex < runDescriptor.numberOfLines - 1){
|
|
365
|
-
// This run has at least one more wrap/break point, so just add up to the next line
|
|
366
|
-
lineRange = runDescriptor.rangeOfLine(runLineIndex, remainingRange.location);
|
|
367
|
-
fragment = UIHTMLTextTypesetterRunDescriptorFragment(runDescriptor, remainingRange.intersection(lineRange));
|
|
368
|
-
runLineIndex++;
|
|
369
|
-
stop = true;
|
|
370
|
-
x = -Number.MAX_VALUE;
|
|
371
|
-
remainingRange.advance(fragment.range.length);
|
|
372
|
-
fragments.push(fragment);
|
|
137
|
+
var span;
|
|
138
|
+
var attachmentRuns = [];
|
|
139
|
+
for (var i = 0, l = layout.runDescriptors.length; i < l; ++i){
|
|
140
|
+
runDescriptor = layout.runDescriptors[i];
|
|
141
|
+
span = this._createRunElement();
|
|
142
|
+
if (!runDescriptor.attachment){
|
|
143
|
+
this._styleTextElementWithAttributes(span, runDescriptor.attributes, runDescriptor.font);
|
|
144
|
+
var utf16 = this.attributedString.string.substringInRange(JSRange(runDescriptor.location, runDescriptor.length));
|
|
145
|
+
if (runDescriptor.attributes[JSAttributedString.Attribute.maskCharacter] !== undefined){
|
|
146
|
+
utf16 = utf16.stringByMaskingWithCharacter(runDescriptor.attributes[JSAttributedString.Attribute.maskCharacter]);
|
|
147
|
+
}
|
|
148
|
+
if (span.childNodes.length > 0){
|
|
149
|
+
span.childNodes[0].nodeValue = utf16;
|
|
150
|
+
}else{
|
|
151
|
+
span.appendChild(span.ownerDocument.createTextNode(utf16));
|
|
152
|
+
}
|
|
373
153
|
}else{
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
remainingRange.advance(fragment.range.length);
|
|
387
|
-
fragments.push(fragment);
|
|
154
|
+
this._styleAttachmentElementWithAttributes(span, runDescriptor.attributes, runDescriptor.attachment);
|
|
155
|
+
if (span.childNodes.length > 0){
|
|
156
|
+
span.removeChild(span.childNodes[0]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
run = UIHTMLTextRun.initWithElement(span, runDescriptor.font, runDescriptor.attributes, JSRange(runDescriptor.location, runDescriptor.length));
|
|
160
|
+
run.origin.x = x;
|
|
161
|
+
run.size.width = runDescriptor.width;
|
|
162
|
+
x += run.size.width;
|
|
163
|
+
runs.push(run);
|
|
164
|
+
if (runDescriptor.attachment){
|
|
165
|
+
attachmentRuns.push(run);
|
|
388
166
|
}
|
|
389
|
-
|
|
390
|
-
}while (runIndex < runDescriptors.length && !stop && remainingRange.length > 0);
|
|
391
|
-
|
|
392
|
-
if (lineBreakMode == JSLineBreakMode.truncateTail && remainingRange.length > 0){
|
|
393
|
-
fragment.overflowed = true;
|
|
394
167
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
fragments: fragments,
|
|
398
|
-
runIndex: runIndex,
|
|
399
|
-
runLineIndex: runLineIndex
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
if (fragments.length > 0){
|
|
403
|
-
return JSRange(fragments[0].range.location, fragments[fragments.length - 1].range.end - fragments[0].range.location);
|
|
168
|
+
if (runs.length === 0){
|
|
169
|
+
return this._createEmptyHTMLLine(layout.range);
|
|
404
170
|
}
|
|
171
|
+
var div = this._createLineElement();
|
|
172
|
+
return UIHTMLTextLine.initWithElement(div, runs, layout.trailingWhitespaceWidth, attachmentRuns, this.canvasContext);
|
|
173
|
+
},
|
|
405
174
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
175
|
+
_styleTextElementWithAttributes: function(span, attributes, font){
|
|
176
|
+
span.style.display = '';
|
|
177
|
+
span.style.width = '';
|
|
178
|
+
span.style.height = '';
|
|
179
|
+
span.style.position = '';
|
|
180
|
+
span.style.verticalAlign = '';
|
|
181
|
+
span.style.borderBottomWidth = '100%';
|
|
182
|
+
// Font
|
|
183
|
+
span.style.font = font.cssString();
|
|
184
|
+
|
|
185
|
+
// Decorations (underline, strike)
|
|
186
|
+
var decorations = [];
|
|
187
|
+
if (attributes[JSAttributedString.Attribute.underline]){
|
|
188
|
+
decorations.push('underline');
|
|
189
|
+
}
|
|
190
|
+
if (attributes[JSAttributedString.Attribute.strike]){
|
|
191
|
+
decorations.push('line-through');
|
|
192
|
+
}
|
|
193
|
+
span.style.textDecoration = decorations.join(' ');
|
|
194
|
+
|
|
195
|
+
// Colors
|
|
196
|
+
var textColor = attributes[JSAttributedString.Attribute.textColor];
|
|
197
|
+
var backgroundColor = attributes[JSAttributedString.Attribute.backgroundColor];
|
|
198
|
+
span.style.color = textColor ? textColor.cssString() : 'black';
|
|
199
|
+
span.style.backgroundColor = backgroundColor ? backgroundColor.cssString() : '';
|
|
200
|
+
|
|
201
|
+
// Cursor
|
|
202
|
+
var cursor = attributes[JSAttributedString.Attribute.cursor];
|
|
203
|
+
if (cursor){
|
|
204
|
+
var cssCursorStrings = cursor.cssStrings();
|
|
205
|
+
for (var i = 0, l = cssCursorStrings.length; i < l; ++i){
|
|
206
|
+
span.style.cursor = cssCursorStrings[i];
|
|
207
|
+
if (span.style.cursor !== ''){
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
span.style.pointerEvents = 'all';
|
|
212
|
+
}else{
|
|
213
|
+
span.style.cursor = '';
|
|
214
|
+
span.style.pointerEvents = 'none';
|
|
215
|
+
}
|
|
216
|
+
},
|
|
422
217
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
var cssCursorStrings = cursor.cssStrings();
|
|
442
|
-
for (var i = 0, l = cssCursorStrings.length; i < l; ++i){
|
|
443
|
-
this.span.style.cursor = cssCursorStrings[i];
|
|
444
|
-
if (this.span.style.cursor !== ''){
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
218
|
+
_styleAttachmentElementWithAttributes: function(span, attributes, attachment){
|
|
219
|
+
span.style.display = 'inline-block';
|
|
220
|
+
span.style.position = 'relative';
|
|
221
|
+
span.style.width = '%dpx'.sprintf(attachment.size.width);
|
|
222
|
+
span.style.height = '%dpx'.sprintf(attachment.size.height);
|
|
223
|
+
span.style.verticalAlign = '%dpx'.sprintf(-attachment.baselineAdjustment);
|
|
224
|
+
span.style.font = '';
|
|
225
|
+
span.style.textDecoration = '';
|
|
226
|
+
span.style.color = '';
|
|
227
|
+
span.style.backgroundColor = '';
|
|
228
|
+
span.style.pointerEvents = 'all';
|
|
229
|
+
var cursor = attributes[JSAttributedString.Attribute.cursor];
|
|
230
|
+
if (cursor){
|
|
231
|
+
var cssCursorStrings = cursor.cssStrings();
|
|
232
|
+
for (var i = 0, l = cssCursorStrings.length; i < l; ++i){
|
|
233
|
+
span.style.cursor = cssCursorStrings[i];
|
|
234
|
+
if (span.style.cursor !== ''){
|
|
235
|
+
break;
|
|
447
236
|
}
|
|
448
|
-
}else{
|
|
449
|
-
this.span.style.cursor = '';
|
|
450
237
|
}
|
|
451
238
|
}else{
|
|
452
|
-
|
|
453
|
-
this.span.style.width = '';
|
|
454
|
-
this.span.style.height = '';
|
|
455
|
-
this.span.style.position = '';
|
|
456
|
-
this.span.style.verticalAlign = '';
|
|
457
|
-
this.span.style.borderBottomWidth = '100%';
|
|
458
|
-
// Font
|
|
459
|
-
var font = JSTextTypesetter.FontFromAttributes(this.attributes);
|
|
460
|
-
this.span.style.font = font.cssString();
|
|
461
|
-
|
|
462
|
-
// Decorations (underline, strike)
|
|
463
|
-
var decorations = [];
|
|
464
|
-
if (this.attributes[JSAttributedString.Attribute.underline]){
|
|
465
|
-
decorations.push('underline');
|
|
466
|
-
}
|
|
467
|
-
if (this.attributes[JSAttributedString.Attribute.strike]){
|
|
468
|
-
decorations.push('line-through');
|
|
469
|
-
}
|
|
470
|
-
this.span.style.textDecoration = decorations.join(' ');
|
|
471
|
-
|
|
472
|
-
// Colors
|
|
473
|
-
var textColor = this.attributes[JSAttributedString.Attribute.textColor];
|
|
474
|
-
var backgroundColor = this.attributes[JSAttributedString.Attribute.backgroundColor];
|
|
475
|
-
this.span.style.color = textColor ? textColor.cssString() : 'black';
|
|
476
|
-
this.span.style.backgroundColor = backgroundColor ? backgroundColor.cssString() : '';
|
|
477
|
-
|
|
478
|
-
// Cursor
|
|
479
|
-
var cursor = this.attributes[JSAttributedString.Attribute.cursor];
|
|
480
|
-
if (cursor){
|
|
481
|
-
var cssCursorStrings = cursor.cssStrings();
|
|
482
|
-
for (var i = 0, l = cssCursorStrings.length; i < l; ++i){
|
|
483
|
-
this.span.style.cursor = cssCursorStrings[i];
|
|
484
|
-
if (this.span.style.cursor !== ''){
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
this.span.style.pointerEvents = 'all';
|
|
489
|
-
}else{
|
|
490
|
-
this.span.style.cursor = '';
|
|
491
|
-
this.span.style.pointerEvents = 'none';
|
|
492
|
-
}
|
|
239
|
+
span.style.cursor = '';
|
|
493
240
|
}
|
|
494
241
|
},
|
|
495
242
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
var
|
|
500
|
-
var
|
|
501
|
-
var
|
|
502
|
-
var
|
|
503
|
-
var
|
|
504
|
-
var
|
|
505
|
-
var
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
243
|
+
// MARK: - Private Helpers for Line Break Suggestion
|
|
244
|
+
|
|
245
|
+
_layoutHTMLLine: function(width, range, lineBreakMode){
|
|
246
|
+
var remainingRange = JSRange(range);
|
|
247
|
+
var runDescriptors = [];
|
|
248
|
+
var i, l;
|
|
249
|
+
var usedWidth = 0;
|
|
250
|
+
var newline = false;
|
|
251
|
+
var runDescriptor = null;
|
|
252
|
+
var printableRange = UIHTMLTextTypesetterPrintableRange(range.location, 0, 0);
|
|
253
|
+
var printableRanges = [printableRange];
|
|
254
|
+
var printable;
|
|
255
|
+
var preferredFont = null;
|
|
256
|
+
|
|
257
|
+
var iterator = this._attributedString.string.userPerceivedCharacterIterator(range.location);
|
|
258
|
+
var runIterator = this._attributedString.runIterator(range.location);
|
|
259
|
+
var attachment;
|
|
260
|
+
var attributes;
|
|
261
|
+
var metrics;
|
|
262
|
+
// Create run descriptors that at fill the line, maybe going a bit over
|
|
263
|
+
do {
|
|
264
|
+
attributes = this.resolveAttributes(runIterator.attributes);
|
|
265
|
+
if (runDescriptor === null){
|
|
266
|
+
runDescriptor = UIHTMLTextTypesetterRunDescriptor(remainingRange.location, attributes);
|
|
267
|
+
runDescriptors.push(runDescriptor);
|
|
268
|
+
preferredFont = runDescriptor.font;
|
|
269
|
+
if (preferredFont !== null){
|
|
270
|
+
this.canvasContext.font = preferredFont.cssString();
|
|
271
|
+
}
|
|
512
272
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
273
|
+
newline = iterator.isMandatoryLineBreak;
|
|
274
|
+
printable = !newline && !iterator.isWhiteSpace;
|
|
275
|
+
attachment = runIterator.attachment;
|
|
276
|
+
if (attachment !== null){
|
|
277
|
+
attachment.layout(preferredFont, width);
|
|
278
|
+
usedWidth += attachment.size.width;
|
|
279
|
+
runDescriptor.userPerceivedCharacterWidths.push(attachment.size.width);
|
|
280
|
+
runDescriptor.userPerceivedCharacterLengths.push(1);
|
|
281
|
+
runDescriptor.attachment = attachment;
|
|
282
|
+
}else{
|
|
283
|
+
if (!newline){
|
|
284
|
+
if (attributes[JSAttributedString.Attribute.maskCharacter] !== undefined){
|
|
285
|
+
metrics = this.canvasContext.measureText(attributes[JSAttributedString.Attribute.maskCharacter]);
|
|
286
|
+
}else{
|
|
287
|
+
metrics = this.canvasContext.measureText(iterator.utf16);
|
|
288
|
+
}
|
|
289
|
+
usedWidth += metrics.width;
|
|
290
|
+
runDescriptor.userPerceivedCharacterWidths.push(metrics.width);
|
|
291
|
+
runDescriptor.userPerceivedCharacterLengths.push(iterator.range.length);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
runDescriptor.length += iterator.range.length;
|
|
295
|
+
remainingRange.advance(iterator.range.length);
|
|
296
|
+
if (printable){
|
|
297
|
+
if (printableRange === null){
|
|
298
|
+
printableRange = UIHTMLTextTypesetterPrintableRange(iterator.range.location, iterator.range.length, usedWidth);
|
|
299
|
+
printableRanges.push(printableRange);
|
|
522
300
|
}else{
|
|
523
|
-
|
|
301
|
+
printableRange.length += iterator.range.length;
|
|
302
|
+
printableRange.width = usedWidth;
|
|
524
303
|
}
|
|
525
304
|
}else{
|
|
526
|
-
|
|
527
|
-
|
|
305
|
+
printableRange = null;
|
|
306
|
+
}
|
|
307
|
+
iterator.increment();
|
|
308
|
+
if (iterator.range.location >= runIterator.range.end){
|
|
309
|
+
// NOTE: We expect iterator.location to fall on a run boundary, but
|
|
310
|
+
// if it doesn't, then the run boundary splits a user-perceived character, and we'll
|
|
311
|
+
// paint every byte of the character with the attributes that begin the character.
|
|
312
|
+
// It's also technically possible for N runs to be in a N-length user-perceived
|
|
313
|
+
// character, so we can't just assume that a single runIterator.increment() will get
|
|
314
|
+
// us to the run that applies to the next user-perceived charater; therefore, we'll loop.
|
|
315
|
+
// It is up to applications to ensure that run boundaries fall on user-perceived
|
|
316
|
+
// character boundaries, but this code must assume that applications will do the
|
|
317
|
+
// wrong thing, and handle those situations without crashing/looping forever/etc.
|
|
318
|
+
do {
|
|
319
|
+
runIterator.increment();
|
|
320
|
+
}while (runIterator.range.end < iterator.range.location);
|
|
321
|
+
runDescriptor = null;
|
|
322
|
+
}
|
|
323
|
+
} while (remainingRange.length > 0 && printableRanges[printableRanges.length - 1].width <= width && !newline);
|
|
324
|
+
|
|
325
|
+
// Remove any characters causing overage
|
|
326
|
+
// But do nothing in truncation mode, just let the final character overflow so it can
|
|
327
|
+
// be appropriately truncated by the caller
|
|
328
|
+
if (printableRanges[printableRanges.length - 1].width > width && lineBreakMode != JSLineBreakMode.truncateTail){
|
|
329
|
+
|
|
330
|
+
if (lineBreakMode == JSLineBreakMode.wordWrap){
|
|
331
|
+
// word wrapping
|
|
332
|
+
iterator.decrement();
|
|
333
|
+
var characterBreakLocation = iterator.range.location;
|
|
334
|
+
// back up to a word boundary
|
|
335
|
+
while (iterator.index > range.location && !iterator.isWordBoundary){
|
|
336
|
+
iterator.decrement();
|
|
337
|
+
}
|
|
338
|
+
if (iterator.range.location <= range.location){
|
|
339
|
+
// we couldn't fit a word in, so break by character
|
|
340
|
+
usedWidth -= this._truncateHTMLRunDescriptorsToLocation(runDescriptors, characterBreakLocation);
|
|
341
|
+
this._truncateHTMLRangesToLocation(printableRanges, characterBreakLocation);
|
|
528
342
|
}else{
|
|
529
|
-
|
|
343
|
+
usedWidth -= this._truncateHTMLRunDescriptorsToLocation(runDescriptors, iterator.range.location);
|
|
344
|
+
this._truncateHTMLRangesToLocation(printableRanges, iterator.range.location);
|
|
530
345
|
}
|
|
346
|
+
}else{
|
|
347
|
+
// character wrapping (or unknown/invalid line break mode value)
|
|
348
|
+
iterator.decrement();
|
|
349
|
+
usedWidth -= this._truncateHTMLRunDescriptorsToLocation(runDescriptors, iterator.range.location);
|
|
350
|
+
this._truncateHTMLRangesToLocation(printableRanges, iterator.range.location);
|
|
531
351
|
}
|
|
532
|
-
}
|
|
533
|
-
return JSRange(startingLocation, this.range.location + min - startingLocation);
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
352
|
|
|
537
|
-
|
|
353
|
+
printableRanges[printableRanges.length - 1].width = Math.min(usedWidth, printableRanges[printableRanges.length - 1].width);
|
|
354
|
+
}
|
|
538
355
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if (rects[i].y === rects[i - 1].y){
|
|
547
|
-
rects[i - 1] = {
|
|
548
|
-
x: rects[i - 1].x,
|
|
549
|
-
y: rects[i - 1].y,
|
|
550
|
-
width: rects[i - 1].width + rects[i].width,
|
|
551
|
-
height: rects[i - 1].height,
|
|
552
|
-
};
|
|
553
|
-
rects.splice(i, 1);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
Object.defineProperty(this, 'clientRects', {
|
|
557
|
-
value: rects
|
|
558
|
-
});
|
|
559
|
-
return rects;
|
|
356
|
+
if (runDescriptors.length > 0){
|
|
357
|
+
var lastDescriptor = runDescriptors[runDescriptors.length - 1];
|
|
358
|
+
return {
|
|
359
|
+
runDescriptors: runDescriptors,
|
|
360
|
+
range: JSRange(range.location, lastDescriptor.location + lastDescriptor.length - range.location),
|
|
361
|
+
trailingWhitespaceWidth: usedWidth - printableRanges[printableRanges.length - 1].width
|
|
362
|
+
};
|
|
560
363
|
}
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
runDescriptors: [],
|
|
367
|
+
range: JSRange(range.location, 0),
|
|
368
|
+
trailingWhitespaceWidth: 0
|
|
369
|
+
};
|
|
561
370
|
},
|
|
562
371
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
372
|
+
_truncateHTMLRunDescriptorsToLocation: function(runDescriptors, location){
|
|
373
|
+
var runDescriptor;
|
|
374
|
+
var end = location + 1;
|
|
375
|
+
var removedWidth = 0;
|
|
376
|
+
var characterWidth;
|
|
377
|
+
var characterLength;
|
|
378
|
+
var i, l;
|
|
379
|
+
while (runDescriptors.length > 0 && end > location){
|
|
380
|
+
runDescriptor = runDescriptors[runDescriptors.length - 1];
|
|
381
|
+
end = runDescriptor.location + runDescriptor.length;
|
|
382
|
+
while (end > location && runDescriptor.userPerceivedCharacterWidths.length > 0){
|
|
383
|
+
characterWidth = runDescriptor.userPerceivedCharacterWidths.pop();
|
|
384
|
+
removedWidth += characterWidth;
|
|
385
|
+
characterLength = runDescriptor.userPerceivedCharacterLengths.pop();
|
|
386
|
+
runDescriptor.length -= characterLength;
|
|
387
|
+
end -= characterLength;
|
|
388
|
+
}
|
|
389
|
+
if (runDescriptor.userPerceivedCharacterWidths.length === 0){
|
|
390
|
+
runDescriptors.pop();
|
|
391
|
+
}
|
|
567
392
|
}
|
|
393
|
+
return removedWidth;
|
|
568
394
|
},
|
|
569
395
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
396
|
+
_truncateHTMLRangesToLocation: function(ranges, location){
|
|
397
|
+
var end = location + 1;
|
|
398
|
+
var range;
|
|
399
|
+
while (ranges.length > 0 && end > location){
|
|
400
|
+
range = ranges[ranges.length - 1];
|
|
401
|
+
end = range.location;
|
|
402
|
+
if (end >= location){
|
|
403
|
+
ranges.pop();
|
|
404
|
+
}else{
|
|
405
|
+
range.length = location - range.location;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (ranges.length === 0){
|
|
409
|
+
ranges.push(UIHTMLTextTypesetterPrintableRange(location, 0, 0));
|
|
574
410
|
}
|
|
575
411
|
},
|
|
576
412
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
var UIHTMLTextTypesetterRunDescriptor = function(location, attributes){
|
|
416
|
+
if (this === undefined){
|
|
417
|
+
return new UIHTMLTextTypesetterRunDescriptor(location, attributes);
|
|
418
|
+
}
|
|
419
|
+
if (location instanceof UIHTMLTextTypesetterRunDescriptor){
|
|
420
|
+
this.location = location.location;
|
|
421
|
+
this.attributes = location.attributes;
|
|
422
|
+
this.length = location.length;
|
|
423
|
+
this.userPerceivedCharacterWidths = JSCopy(location.userPerceivedCharacterWidths);
|
|
424
|
+
this.userPerceivedCharacterLengths = JSCopy(location.userPerceivedCharacterLengths);
|
|
425
|
+
this.font = location.font;
|
|
426
|
+
this.attachment = location.attachment;
|
|
427
|
+
}else{
|
|
428
|
+
this.location = location;
|
|
429
|
+
this.attributes = attributes;
|
|
430
|
+
this.length = 0;
|
|
431
|
+
this.userPerceivedCharacterWidths = [];
|
|
432
|
+
this.userPerceivedCharacterLengths = [];
|
|
433
|
+
this.font = attributes.font || null;
|
|
434
|
+
this.attachment = null;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
Object.defineProperties(UIHTMLTextTypesetterRunDescriptor.prototype, {
|
|
439
|
+
|
|
440
|
+
width: {
|
|
441
|
+
get: function(){
|
|
442
|
+
var width = 0;
|
|
443
|
+
var i, l;
|
|
444
|
+
for (i = 0, l = this.userPerceivedCharacterWidths.length; i < l; ++i){
|
|
445
|
+
width += this.userPerceivedCharacterWidths[i];
|
|
446
|
+
}
|
|
447
|
+
return width;
|
|
580
448
|
}
|
|
581
449
|
}
|
|
450
|
+
|
|
582
451
|
});
|
|
583
452
|
|
|
584
|
-
var
|
|
453
|
+
var UIHTMLTextTypesetterPrintableRange = function(location, length, width){
|
|
585
454
|
if (this === undefined){
|
|
586
|
-
return new
|
|
455
|
+
return new UIHTMLTextTypesetterPrintableRange(location, length, width);
|
|
456
|
+
}
|
|
457
|
+
if (location instanceof UIHTMLTextTypesetterPrintableRange){
|
|
458
|
+
this.location = location.location;
|
|
459
|
+
this.length = location.length;
|
|
460
|
+
this.width = location.width;
|
|
461
|
+
}else{
|
|
462
|
+
this.location = location;
|
|
463
|
+
this.length = length;
|
|
464
|
+
this.width = width;
|
|
587
465
|
}
|
|
588
|
-
this.runDescriptor = runDescriptor;
|
|
589
|
-
this.range = range;
|
|
590
|
-
this.overflowed = false;
|
|
591
466
|
};
|
|
592
467
|
|
|
593
468
|
})();
|