@breakside/jskit 2026.11.0 → 2026.27.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.
- 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/JS/FNTOpenTypeFont.js +331 -22
- 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 +56 -1
- package/Frameworks/Foundation.jsframework/JS/JSContext.js +44 -17
- package/Frameworks/Foundation.jsframework/JS/JSCubicBezier.js +54 -1
- package/Frameworks/Foundation.jsframework/JS/JSFont.js +8 -9
- package/Frameworks/Foundation.jsframework/JS/JSGradient.js +28 -2
- package/Frameworks/Foundation.jsframework/JS/JSImage+HTML.js +27 -1
- package/Frameworks/Foundation.jsframework/JS/JSNumberFormatter.js +23 -1
- package/Frameworks/Foundation.jsframework/JS/JSPath.js +194 -6
- package/Frameworks/Foundation.jsframework/JS/JSPoint.js +56 -0
- package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +21 -4
- package/Frameworks/Foundation.jsframework/JS/JSTextStorage.js +5 -0
- package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +6 -1
- package/Frameworks/Foundation.jsframework/JS/JSXMLParser.js +1 -1
- package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/sources.json +1 -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/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/CSSTokenizer.js +633 -470
- package/Root/Frameworks/CSSOM/Info.yaml +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/FNTOpenTypeFont.js +331 -22
- package/Root/Frameworks/FontKit/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/JSAttributedString.js +56 -1
- package/Root/Frameworks/Foundation/JSContext.js +44 -17
- package/Root/Frameworks/Foundation/JSCubicBezier.js +54 -1
- package/Root/Frameworks/Foundation/JSFont.js +8 -9
- package/Root/Frameworks/Foundation/JSGradient.js +28 -2
- package/Root/Frameworks/Foundation/JSImage+HTML.js +27 -1
- package/Root/Frameworks/Foundation/JSNumberFormatter.js +23 -1
- package/Root/Frameworks/Foundation/JSPath.js +194 -6
- package/Root/Frameworks/Foundation/JSPoint.js +56 -0
- package/Root/Frameworks/Foundation/JSTextRun.js +21 -4
- package/Root/Frameworks/Foundation/JSTextStorage.js +5 -0
- package/Root/Frameworks/Foundation/JSTextTypesetter.js +6 -1
- package/Root/Frameworks/Foundation/JSXMLParser.js +1 -1
- package/Root/Frameworks/ImageKit/IKHTMLCanvasBitmapContext.js +187 -101
- package/Root/Frameworks/ImageKit/IKNodeBitmapContext.js +4 -4
- 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/TestKit/TKAssert.js +4 -4
- package/Root/Frameworks/UIKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/UIApplication.js +9 -1
- package/Root/Frameworks/UIKit/UICollectionView.js +4 -0
- package/Root/Frameworks/UIKit/UIColorPanel.js +46 -0
- package/Root/Frameworks/UIKit/UIColorWell.js +8 -0
- package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +21 -2
- package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +150 -69
- package/Root/Frameworks/UIKit/UIHTMLTextRun.js +5 -0
- package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +18 -3
- package/Root/Frameworks/UIKit/UIListView.js +7 -5
- package/Root/Frameworks/UIKit/UIMenu.js +50 -32
- package/Root/Frameworks/UIKit/UIMenuItem.js +12 -0
- package/Root/Frameworks/UIKit/UIMenuView.js +27 -5
- package/Root/Frameworks/UIKit/UIPlatform.js +7 -0
- package/Root/Frameworks/UIKit/UIPopupButton.js +7 -1
- package/Root/Frameworks/UIKit/UIResponder.js +8 -0
- package/Root/Frameworks/UIKit/UITextEditor.js +31 -7
- package/Root/Frameworks/UIKit/UITextField.js +7 -0
- package/Root/Frameworks/UIKit/UITokenField.js +65 -0
- package/Root/Frameworks/UIKit/UIToolbar.js +1 -1
- package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
- package/package.json +1 -1
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
JSGlobalObject.JSCubicBezier = function(p1, cp1, cp2, p2){
|
|
21
21
|
if (this === undefined){
|
|
22
|
+
if (p1 === null){
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
22
25
|
return new JSCubicBezier(p1, cp1, cp2, p2);
|
|
23
26
|
}
|
|
24
27
|
if (p1 instanceof JSCubicBezier){
|
|
@@ -126,6 +129,56 @@ JSCubicBezier.prototype = {
|
|
|
126
129
|
points.push(this.pointAtInterval(t[1]));
|
|
127
130
|
}
|
|
128
131
|
return points;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
curvesBySplittingAtInterval: function(t){
|
|
135
|
+
var q0 = this.p1.interpolation(this.cp1, t);
|
|
136
|
+
var q1 = this.cp1.interpolation(this.cp2, t);
|
|
137
|
+
var q2 = this.cp2.interpolation(this.p2, t);
|
|
138
|
+
var r0 = q0.interpolation(q1, t);
|
|
139
|
+
var r1 = q1.interpolation(q2, t);
|
|
140
|
+
var p = r0.interpolation(r1, t);
|
|
141
|
+
var cp1 = this.p1.interpolation(this.cp1, t);
|
|
142
|
+
var cp2 = this.p2.interpolation(this.cp2, (1 - t));
|
|
143
|
+
return [
|
|
144
|
+
JSCubicBezier(this.p1, cp1, r0, p),
|
|
145
|
+
JSCubicBezier(p, r1, cp2, this.p2)
|
|
146
|
+
];
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
pointsWithFlatness: function(flatness){
|
|
150
|
+
var points = [JSPoint(this.p1)];
|
|
151
|
+
var dt = 0.1;
|
|
152
|
+
var t = 0;
|
|
153
|
+
var p0 = this.p1;
|
|
154
|
+
var p;
|
|
155
|
+
var q;
|
|
156
|
+
var mid;
|
|
157
|
+
var delta;
|
|
158
|
+
var iterationCount = 0;
|
|
159
|
+
var acceptable;
|
|
160
|
+
while (t < 1 && iterationCount < 1000){
|
|
161
|
+
p = this.pointAtInterval(t + dt);
|
|
162
|
+
q = this.pointAtInterval(t + dt / 2);
|
|
163
|
+
mid = p0.interpolation(p, 0.5);
|
|
164
|
+
acceptable = q.distanceToPoint(mid) <= flatness;
|
|
165
|
+
if (acceptable){
|
|
166
|
+
points.push(p);
|
|
167
|
+
t += dt;
|
|
168
|
+
dt = Math.min(dt * 2, 1 - t);
|
|
169
|
+
p0 = p;
|
|
170
|
+
}else{
|
|
171
|
+
dt /= 2;
|
|
172
|
+
}
|
|
173
|
+
++iterationCount;
|
|
174
|
+
}
|
|
175
|
+
return points;
|
|
129
176
|
}
|
|
130
177
|
|
|
131
|
-
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
JSCubicBezier.fromQuadradicBezier = function(p1, cp, p2){
|
|
181
|
+
var cp1 = p1.interpolation(cp, 2/3);
|
|
182
|
+
var cp2 = p2.interpolation(cp, 2/3);
|
|
183
|
+
return JSCubicBezier(p1, cp1, cp2, p2);
|
|
184
|
+
};
|
|
@@ -88,12 +88,6 @@ JSClass("JSFont", JSObject, {
|
|
|
88
88
|
|
|
89
89
|
initFromDictionary: function(dictionary){
|
|
90
90
|
var descriptor = JSFontDescriptor.descriptorWithFamily(dictionary.family, dictionary.weight, dictionary.style);
|
|
91
|
-
if (descriptor === null){
|
|
92
|
-
if (dictionary.url){
|
|
93
|
-
descriptor = JSURLFontDescriptor.initWithURL(JSURL.initWithString(dictionary.url), dictionary.family, dictionary.weight, dictionary.style);
|
|
94
|
-
JSFontDescriptor.registerDescriptor(descriptor);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
91
|
if (descriptor !== null){
|
|
98
92
|
return JSFont.initWithDescriptor(descriptor, dictionary.size);
|
|
99
93
|
}
|
|
@@ -102,7 +96,6 @@ JSClass("JSFont", JSObject, {
|
|
|
102
96
|
|
|
103
97
|
dictionaryRepresentation: function(){
|
|
104
98
|
return {
|
|
105
|
-
url: this.descriptor instanceof JSURLFontDescriptor ? this._descriptor.url : undefined,
|
|
106
99
|
family: this._descriptor.family,
|
|
107
100
|
weight: this._descriptor.weight,
|
|
108
101
|
style: this._descriptor.style,
|
|
@@ -164,8 +157,14 @@ JSClass("JSFont", JSObject, {
|
|
|
164
157
|
},
|
|
165
158
|
|
|
166
159
|
_calculateDisplayMetrics: function(){
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
// Math.round always rounds towards +Infinity when a given number
|
|
161
|
+
// has a fractional part exactly equal to 0.5.
|
|
162
|
+
// So, 2.5 rounds to 3 but -2.5 rounds to -2.
|
|
163
|
+
//
|
|
164
|
+
// For the purposes of this calculation, we want negative
|
|
165
|
+
// values like -2.5 to round to -3.
|
|
166
|
+
this._displayAscender = this._ascender < 0 ? -Math.round(-this._ascender) : Math.round(this._ascender);
|
|
167
|
+
this._displayDescender = this._descender < 0 ? -Math.round(-this._descender) : Math.round(this._descender);
|
|
169
168
|
this._displayLineHeight = this._displayAscender - this._displayDescender;
|
|
170
169
|
},
|
|
171
170
|
|
|
@@ -142,7 +142,17 @@ JSClass('JSGradient', JSObject, {
|
|
|
142
142
|
},
|
|
143
143
|
|
|
144
144
|
addStop: function(position, color){
|
|
145
|
-
this.stops.
|
|
145
|
+
var l = this.stops.length;
|
|
146
|
+
var stop = {position: position, color: color};
|
|
147
|
+
var i = l - 1;
|
|
148
|
+
for (; i >= 0; --i){
|
|
149
|
+
if (this.stops[i].position <= position){
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
i++;
|
|
154
|
+
this.stops.splice(i, 0, stop);
|
|
155
|
+
return i;
|
|
146
156
|
},
|
|
147
157
|
|
|
148
158
|
cssString: function(size){
|
|
@@ -167,6 +177,19 @@ JSClass('JSGradient', JSObject, {
|
|
|
167
177
|
return 'linear-gradient(%fdeg, %s)'.sprintf(angle, cssStops.join(', '));
|
|
168
178
|
},
|
|
169
179
|
|
|
180
|
+
radialCSSString: function(size){
|
|
181
|
+
var cssStops = [];
|
|
182
|
+
var stop;
|
|
183
|
+
for (var i = 0, l = this.stops.length; i < l; ++i){
|
|
184
|
+
stop = this.stops[i];
|
|
185
|
+
cssStops.push('%s %f%%'.sprintf(stop.color.cssString(), stop.position * 100));
|
|
186
|
+
}
|
|
187
|
+
var start = JSPoint(this.start.x * size.width, this.start.y * size.height);
|
|
188
|
+
var end = JSPoint(this.end.x * size.width, this.end.y * size.height);
|
|
189
|
+
var d = start.distanceToPoint(end);
|
|
190
|
+
return 'radial-gradient(%fpx at %f%% %f%%, %s)'.sprintf(d, this.start.x * 100, this.start.y * 100, cssStops.join(', '));
|
|
191
|
+
},
|
|
192
|
+
|
|
170
193
|
colorAtPosition: function(position){
|
|
171
194
|
if (position <= this.stops[0].position){
|
|
172
195
|
return this.stops[0].color;
|
|
@@ -229,7 +252,10 @@ JSClass('JSGradient', JSObject, {
|
|
|
229
252
|
var gradient = JSGradient.init();
|
|
230
253
|
gradient.start = transform.convertPointFromTransform(this.start);
|
|
231
254
|
gradient.end = transform.convertPointFromTransform(this.end);
|
|
232
|
-
|
|
255
|
+
var i, l;
|
|
256
|
+
for (i = 0, l = this.stops.length; i < l; ++i){
|
|
257
|
+
gradient.addStop(this.stops[i].position, this.stops[i].color);
|
|
258
|
+
}
|
|
233
259
|
return gradient;
|
|
234
260
|
}
|
|
235
261
|
|
|
@@ -32,4 +32,30 @@ _JSURLImage.definePropertiesFromExtensions({
|
|
|
32
32
|
htmlURLString: function(){
|
|
33
33
|
return this.url.encodedString;
|
|
34
34
|
}
|
|
35
|
-
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
JSImage.definePropertiesFromExtensions({
|
|
38
|
+
|
|
39
|
+
_htmlImage: null,
|
|
40
|
+
_htmlImagePromise: null,
|
|
41
|
+
|
|
42
|
+
preloadForHTMLContexts: function(){
|
|
43
|
+
if (this._htmlImagePromise === null){
|
|
44
|
+
var image = this;
|
|
45
|
+
this._htmlImagePromise = new Promise(function(resolve, reject){
|
|
46
|
+
var htmlImage = new JSGlobalObject.Image();
|
|
47
|
+
htmlImage.src = image.htmlURLString();
|
|
48
|
+
htmlImage.onload = function(){
|
|
49
|
+
resolve(htmlImage);
|
|
50
|
+
};
|
|
51
|
+
htmlImage.onerror = function(){
|
|
52
|
+
reject(new Error("failed to load image"));
|
|
53
|
+
};
|
|
54
|
+
}).then(function(htmlImage){
|
|
55
|
+
image._htmlImage = htmlImage;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return this._htmlImagePromise;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
});
|
|
@@ -560,6 +560,8 @@ JSClass("JSNumberFormatter", JSObject, {
|
|
|
560
560
|
var canSeePercent = false;
|
|
561
561
|
var canSeeGroup = false;
|
|
562
562
|
var seenDigit = false;
|
|
563
|
+
var prefix = "";
|
|
564
|
+
var suffix = "";
|
|
563
565
|
var stringMatches = function(substr){
|
|
564
566
|
if (substr.length === 0){
|
|
565
567
|
return false;
|
|
@@ -588,6 +590,7 @@ JSClass("JSNumberFormatter", JSObject, {
|
|
|
588
590
|
sign = -1;
|
|
589
591
|
canSeeSign = false;
|
|
590
592
|
i += this.minusSign.length;
|
|
593
|
+
prefix += this.minusSign;
|
|
591
594
|
}else if (canSeeSign && stringMatches(this.plusSign)){
|
|
592
595
|
canSeeSign = false;
|
|
593
596
|
i += this.plusSign.length;
|
|
@@ -625,8 +628,12 @@ JSClass("JSNumberFormatter", JSObject, {
|
|
|
625
628
|
i += this.groupingSeparator.length;
|
|
626
629
|
}else if (c == 0x20){
|
|
627
630
|
++i;
|
|
631
|
+
}else if (!seenDigit){
|
|
632
|
+
prefix += string[i];
|
|
633
|
+
++i;
|
|
628
634
|
}else{
|
|
629
|
-
|
|
635
|
+
suffix += string[i];
|
|
636
|
+
++i;
|
|
630
637
|
}
|
|
631
638
|
}
|
|
632
639
|
if (!seenDigit){
|
|
@@ -639,6 +646,21 @@ JSClass("JSNumberFormatter", JSObject, {
|
|
|
639
646
|
if (sign < 0){
|
|
640
647
|
n = -n;
|
|
641
648
|
}
|
|
649
|
+
if (n >= 0){
|
|
650
|
+
if (prefix !== "" && prefix !== this.positivePrefix){
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
if (suffix !== "" && suffix !== this.positiveSuffix){
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
}else{
|
|
657
|
+
if (prefix !== "" && prefix !== this.negativePrefix){
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
if (suffix !== "" && suffix !== this.negativeSuffix){
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
642
664
|
return n;
|
|
643
665
|
},
|
|
644
666
|
|
|
@@ -53,6 +53,40 @@ JSClass("JSPath", JSObject, {
|
|
|
53
53
|
return true;
|
|
54
54
|
},
|
|
55
55
|
|
|
56
|
+
elements: JSReadOnlyProperty(),
|
|
57
|
+
|
|
58
|
+
_elements: null,
|
|
59
|
+
|
|
60
|
+
_invalidateElements: function(){
|
|
61
|
+
this._elements = null;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
getElements: function(){
|
|
65
|
+
if (this._elements === null){
|
|
66
|
+
this._elements = [];
|
|
67
|
+
var i, l, j, k;
|
|
68
|
+
var subpath, segment;
|
|
69
|
+
for (i = 0, l = this.subpaths.length; i < l; ++i){
|
|
70
|
+
subpath = this.subpaths[i];
|
|
71
|
+
this._elements.push(JSPathElement(JSPathElement.Type.move, subpath.firstPoint));
|
|
72
|
+
if (subpath.segments.length > 0){
|
|
73
|
+
for (j = 0, k = subpath.segments.length; j < k; ++j){
|
|
74
|
+
segment = subpath.segments[j];
|
|
75
|
+
if (segment.type == JSPath.SegmentType.line){
|
|
76
|
+
this._elements.push(JSPathElement(JSPathElement.Type.line, segment.end));
|
|
77
|
+
}else if (segment.type == JSPath.SegmentType.curve){
|
|
78
|
+
this._elements.push(JSPathElement(JSPathElement.Type.cubicCurve, segment.curve.p2, segment.curve.p1, segment.curve.cp1, segment.curve.cp2));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (subpath.closed){
|
|
83
|
+
this._elements.push(JSPathElement(JSPathElement.Type.close));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return this._elements;
|
|
88
|
+
},
|
|
89
|
+
|
|
56
90
|
subpaths: null,
|
|
57
91
|
currentPoint: JSReadOnlyProperty('_currentPoint', null),
|
|
58
92
|
currentSubpath: null,
|
|
@@ -68,6 +102,7 @@ JSClass("JSPath", JSObject, {
|
|
|
68
102
|
this.subpaths.push(this.currentSubpath);
|
|
69
103
|
this._currentPoint = point;
|
|
70
104
|
this._invalidateBoundingRect();
|
|
105
|
+
this._invalidateElements();
|
|
71
106
|
},
|
|
72
107
|
|
|
73
108
|
addLineToPoint: function(point, transform){
|
|
@@ -80,6 +115,7 @@ JSClass("JSPath", JSObject, {
|
|
|
80
115
|
this.currentSubpath.segments.push({type: JSPath.SegmentType.line, end: point});
|
|
81
116
|
this._currentPoint = point;
|
|
82
117
|
this._invalidateBoundingRect();
|
|
118
|
+
this._invalidateElements();
|
|
83
119
|
},
|
|
84
120
|
|
|
85
121
|
addCurveToPoint: function(point, control1, control2, transform){
|
|
@@ -93,6 +129,15 @@ JSClass("JSPath", JSObject, {
|
|
|
93
129
|
this.currentSubpath.segments.push({type: JSPath.SegmentType.curve, curve: curve});
|
|
94
130
|
this._currentPoint = point;
|
|
95
131
|
this._invalidateBoundingRect();
|
|
132
|
+
this._invalidateElements();
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
addQuadraticCurveToPoint: function(point, control, transform){
|
|
136
|
+
if (transform !== undefined){
|
|
137
|
+
point = transform.convertPointFromTransform(point);
|
|
138
|
+
control = transform.convertPointFromTransform(control);
|
|
139
|
+
}
|
|
140
|
+
this.addCurveToPoint(point, this._currentPoint.interpolation(control, 2/3), point.interpolation(control, 2/3));
|
|
96
141
|
},
|
|
97
142
|
|
|
98
143
|
addRect: function(rect, transform){
|
|
@@ -328,7 +373,9 @@ JSClass("JSPath", JSObject, {
|
|
|
328
373
|
if (this.subpaths.length === 0){
|
|
329
374
|
this.moveToPoint(p1, transform);
|
|
330
375
|
}else{
|
|
331
|
-
this.
|
|
376
|
+
if (Math.abs(p1.x - this._currentPoint.x) > 0.00001 || Math.abs(p1.y - this._currentPoint.y) > 0.00001){
|
|
377
|
+
this.addLineToPoint(p1, transform);
|
|
378
|
+
}
|
|
332
379
|
}
|
|
333
380
|
|
|
334
381
|
// If there's no radius, then there's nothing left to do
|
|
@@ -512,10 +559,10 @@ JSClass("JSPath", JSObject, {
|
|
|
512
559
|
// distance is clockwise or counter clockwise.
|
|
513
560
|
// Note: we could swap the angles and always arc in a fixed direction,
|
|
514
561
|
// but I think it makes more sense to always arc from p0 to p2.
|
|
515
|
-
if (startAngle
|
|
562
|
+
if (startAngle <= 0){
|
|
516
563
|
startAngle += TWO_PI;
|
|
517
564
|
}
|
|
518
|
-
if (endAngle
|
|
565
|
+
if (endAngle <= 0){
|
|
519
566
|
endAngle += TWO_PI;
|
|
520
567
|
}
|
|
521
568
|
var clockwise;
|
|
@@ -524,7 +571,9 @@ JSClass("JSPath", JSObject, {
|
|
|
524
571
|
}else{
|
|
525
572
|
clockwise = endAngle - startAngle <= Math.PI;
|
|
526
573
|
}
|
|
527
|
-
this.
|
|
574
|
+
if (Math.abs(t1.x - this._currentPoint.x) > 0.00001 || Math.abs(t1.y - this._currentPoint.y) > 0.00001){
|
|
575
|
+
this.moveToPoint(t1);
|
|
576
|
+
}
|
|
528
577
|
this.addArc(center, radius, startAngle, endAngle, clockwise);
|
|
529
578
|
},
|
|
530
579
|
|
|
@@ -533,6 +582,25 @@ JSClass("JSPath", JSObject, {
|
|
|
533
582
|
this._currentPoint = this.currentSubpath.firstPoint;
|
|
534
583
|
this.currentSubpath.closed = true;
|
|
535
584
|
this.currentSubpath = null;
|
|
585
|
+
this._invalidateElements();
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
addPath: function(path, transform){
|
|
590
|
+
var i, l;
|
|
591
|
+
var elements = path.elements;
|
|
592
|
+
var element;
|
|
593
|
+
for (i = 0, l = elements.length; i < l; ++i){
|
|
594
|
+
element = elements[i];
|
|
595
|
+
if (element.type === JSPathElement.Type.move){
|
|
596
|
+
this.moveToPoint(element.point, transform);
|
|
597
|
+
}else if (element.type === JSPathElement.Type.line){
|
|
598
|
+
this.addLineToPoint(element.point, transform);
|
|
599
|
+
}else if (element.type === JSPathElement.Type.cubicCurve){
|
|
600
|
+
this.addCurveToPoint(element.curve.p2, element.curve.cp1, element.curve.cp2, transform);
|
|
601
|
+
}else if (element.type === JSPathElement.Type.close){
|
|
602
|
+
this.closeSubpath();
|
|
603
|
+
}
|
|
536
604
|
}
|
|
537
605
|
},
|
|
538
606
|
|
|
@@ -841,6 +909,9 @@ JSClass("JSPath", JSObject, {
|
|
|
841
909
|
intersectsPath: function(other, fillRule){
|
|
842
910
|
var boundingRect = this.boundingRect;
|
|
843
911
|
// no need to check if bounding rect don't intersect
|
|
912
|
+
if (boundingRect === null){
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
844
915
|
if (!boundingRect.intersectsRect(other.boundingRect)){
|
|
845
916
|
return false;
|
|
846
917
|
}
|
|
@@ -989,8 +1060,59 @@ JSClass("JSPath", JSObject, {
|
|
|
989
1060
|
return path;
|
|
990
1061
|
},
|
|
991
1062
|
|
|
1063
|
+
pathWithFlatness: function(flatness){
|
|
1064
|
+
var path = JSPath.init();
|
|
1065
|
+
var i, l, j, k;
|
|
1066
|
+
var element;
|
|
1067
|
+
var elements = this.elements;
|
|
1068
|
+
var points;
|
|
1069
|
+
for (i = 0, l = elements.length; i < l; ++i){
|
|
1070
|
+
element = elements[i];
|
|
1071
|
+
if (element.type === JSPathElement.Type.move){
|
|
1072
|
+
path.moveToPoint(element.point);
|
|
1073
|
+
}else if (element.type === JSPathElement.Type.line){
|
|
1074
|
+
path.addLineToPoint(element.point);
|
|
1075
|
+
}else if (element.type === JSPathElement.Type.cubicCurve){
|
|
1076
|
+
points = element.curve.pointsWithFlatness(flatness);
|
|
1077
|
+
for (j = 1, k = points.length; j < k; ++j){
|
|
1078
|
+
path.addLineToPoint(points[j]);
|
|
1079
|
+
}
|
|
1080
|
+
}else if (element.type === JSPathElement.Type.close){
|
|
1081
|
+
path.closeSubpath();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return path;
|
|
1085
|
+
},
|
|
1086
|
+
|
|
1087
|
+
outlineContainsPoint: function(point, tolerance){
|
|
1088
|
+
// Only works on an already flattened path (we don't check curves)
|
|
1089
|
+
var firstPoint = JSPoint.Zero;
|
|
1090
|
+
var currentPoint = JSPoint.firstPoint;
|
|
1091
|
+
var elements = this.elements;
|
|
1092
|
+
var element;
|
|
1093
|
+
var i, l;
|
|
1094
|
+
for (i = 0, l = elements.length; i < l; ++i){
|
|
1095
|
+
element = elements[i];
|
|
1096
|
+
if (element.type === JSPathElement.Type.move){
|
|
1097
|
+
firstPoint = element.point;
|
|
1098
|
+
currentPoint = firstPoint;
|
|
1099
|
+
}else if (element.type === JSPathElement.Type.line){
|
|
1100
|
+
if (currentPoint.lineToPointContainsPoint(element.point, point, tolerance)){
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
currentPoint = element.point;
|
|
1104
|
+
}else if (element.type === JSPathElement.Type.close){
|
|
1105
|
+
if (currentPoint.lineToPointContainsPoint(firstPoint, point, tolerance)){
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
currentPoint = firstPoint;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return false;
|
|
1112
|
+
},
|
|
1113
|
+
|
|
992
1114
|
initWithSVGPathData: function(svgPathData){
|
|
993
|
-
// TODO:
|
|
1115
|
+
// TODO: T, t, A, a
|
|
994
1116
|
//
|
|
995
1117
|
// - All instructions are expressed as one character (e.g., a moveto
|
|
996
1118
|
// is expressed as an M).
|
|
@@ -1066,6 +1188,8 @@ JSClass("JSPath", JSObject, {
|
|
|
1066
1188
|
if (
|
|
1067
1189
|
c === "M" || c === "m" ||
|
|
1068
1190
|
c === "L" || c === "l" ||
|
|
1191
|
+
c === "H" || c === "h" ||
|
|
1192
|
+
c === "V" || c === "v" ||
|
|
1069
1193
|
c === "C" || c === "c" ||
|
|
1070
1194
|
c === "S" || c === "s" ||
|
|
1071
1195
|
c === "Z" || c === "z" ||
|
|
@@ -1161,7 +1285,7 @@ JSClass("JSPath", JSObject, {
|
|
|
1161
1285
|
break;
|
|
1162
1286
|
}
|
|
1163
1287
|
if (point !== null){
|
|
1164
|
-
point = point.adding(JSPoint(command.values[0], command.values[1]))
|
|
1288
|
+
point = point.adding(JSPoint(command.values[0], command.values[1]));
|
|
1165
1289
|
}else{
|
|
1166
1290
|
point = JSPoint(command.values[0], command.values[1]);
|
|
1167
1291
|
}
|
|
@@ -1291,6 +1415,42 @@ JSClass("JSPath", JSObject, {
|
|
|
1291
1415
|
subpath.segments.push(segment);
|
|
1292
1416
|
point = curve.p2;
|
|
1293
1417
|
}
|
|
1418
|
+
}else if (command.name === "Q"){
|
|
1419
|
+
if (command.values.length < 4){
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
if (subpath === null){
|
|
1423
|
+
subpath = Subpath(point);
|
|
1424
|
+
this.subpaths.push(subpath);
|
|
1425
|
+
}
|
|
1426
|
+
for (v = 0; v < command.values.length - 3; v += 4){
|
|
1427
|
+
curve = JSCubicBezier.fromQuadradicBezier(
|
|
1428
|
+
point,
|
|
1429
|
+
JSPoint(command.values[v], command.values[v + 1]),
|
|
1430
|
+
JSPoint(command.values[v + 2], command.values[v + 3])
|
|
1431
|
+
);
|
|
1432
|
+
segment = {type: JSPath.SegmentType.curve, curve: curve};
|
|
1433
|
+
subpath.segments.push(segment);
|
|
1434
|
+
point = curve.p2;
|
|
1435
|
+
}
|
|
1436
|
+
}else if (command.name === "q"){
|
|
1437
|
+
if (command.values.length < 4){
|
|
1438
|
+
break;
|
|
1439
|
+
}
|
|
1440
|
+
if (subpath === null){
|
|
1441
|
+
subpath = Subpath(point);
|
|
1442
|
+
this.subpaths.push(subpath);
|
|
1443
|
+
}
|
|
1444
|
+
for (v = 0; v < command.values.length - 3; v += 4){
|
|
1445
|
+
curve = JSCubicBezier.fromQuadradicBezier(
|
|
1446
|
+
point,
|
|
1447
|
+
point.adding(JSPoint(command.values[v], command.values[v + 1])),
|
|
1448
|
+
point.adding(JSPoint(command.values[v + 2], command.values[v + 3]))
|
|
1449
|
+
);
|
|
1450
|
+
segment = {type: JSPath.SegmentType.curve, curve: curve};
|
|
1451
|
+
subpath.segments.push(segment);
|
|
1452
|
+
point = curve.p2;
|
|
1453
|
+
}
|
|
1294
1454
|
}else if (command.name === "S"){
|
|
1295
1455
|
if (command.values.length < 4){
|
|
1296
1456
|
break;
|
|
@@ -1502,6 +1662,34 @@ JSPath.Sides = {
|
|
|
1502
1662
|
}
|
|
1503
1663
|
};
|
|
1504
1664
|
|
|
1665
|
+
JSGlobalObject.JSPathElement = function(type, point, point1, point2, point3){
|
|
1666
|
+
if (this === undefined){
|
|
1667
|
+
if (type === null){
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
return new JSPathElement(type, point, point1, point2, point3);
|
|
1671
|
+
}
|
|
1672
|
+
if (type instanceof JSPathElement){
|
|
1673
|
+
this.type = type.type;
|
|
1674
|
+
this.point = JSPoint(type.point);
|
|
1675
|
+
this.curve = JSCubicBezier(type.curve);
|
|
1676
|
+
}else{
|
|
1677
|
+
this.type = type;
|
|
1678
|
+
this.point = JSPoint(point || null);
|
|
1679
|
+
if (type === JSPathElement.Type.cubicCurve){
|
|
1680
|
+
this.curve = JSCubicBezier(point1, point2, point3, point);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
|
|
1685
|
+
JSPathElement.Type = {
|
|
1686
|
+
move: 1,
|
|
1687
|
+
line: 2,
|
|
1688
|
+
cubicCurve: 3,
|
|
1689
|
+
close: 4
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
|
|
1505
1693
|
JSPath.Sides.minYmaxY = JSPath.Sides.minY | JSPath.Sides.maxY;
|
|
1506
1694
|
JSPath.Sides.minXmaxX = JSPath.Sides.minX | JSPath.Sides.maxX;
|
|
1507
1695
|
|
|
@@ -72,6 +72,62 @@ JSPoint.prototype = {
|
|
|
72
72
|
|
|
73
73
|
subtracting: function(other){
|
|
74
74
|
return JSPoint(this.x - other.x, this.y - other.y);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
interpolation: function(p1, percentage){
|
|
78
|
+
return JSPoint(
|
|
79
|
+
this.x + (p1.x - this.x) * percentage,
|
|
80
|
+
this.y + (p1.y - this.y) * percentage
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
lineToPointContainsPoint: function(other, point, tolerance){
|
|
85
|
+
// first check if we're within the box defined by the segment
|
|
86
|
+
var minX, maxX;
|
|
87
|
+
if (this.x <= other.x){
|
|
88
|
+
minX = this.x - tolerance;
|
|
89
|
+
maxX = other.x + tolerance;
|
|
90
|
+
}else{
|
|
91
|
+
minX = other.x - tolerance;
|
|
92
|
+
maxX = this.x + tolerance;
|
|
93
|
+
}
|
|
94
|
+
var minY, maxY;
|
|
95
|
+
if (this.y <= other.y){
|
|
96
|
+
minY = this.y - tolerance;
|
|
97
|
+
maxY = other.y + tolerance;
|
|
98
|
+
}else{
|
|
99
|
+
minY = other.y - tolerance;
|
|
100
|
+
maxY = this.y + tolerance;
|
|
101
|
+
}
|
|
102
|
+
if (point.x < minX){
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (point.x > maxX){
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (point.y < minY){
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (point.y > maxY){
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// next check for the special cases of a vertical and horizontal line,
|
|
116
|
+
// where we must be near the line if we're within the box
|
|
117
|
+
var dx = (other.x - this.x);
|
|
118
|
+
var dy = (other.y - this.y);
|
|
119
|
+
if (Math.abs(dx) < 0.00001 || (Math.abs(dy) < 0.00001)){
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Finally, find the nearest point to the line and see how far away we are
|
|
124
|
+
var m1 = dy / dx;
|
|
125
|
+
var b1 = this.y - m1 * this.x;
|
|
126
|
+
var m2 = -1/m1;
|
|
127
|
+
var b2 = point.y - m2 * point.x;
|
|
128
|
+
var x = (b2 - b1) / (m1 - m2);
|
|
129
|
+
var y = m1 * x + b1;
|
|
130
|
+
return JSPoint(x, y).distanceToPoint(point) <= tolerance;
|
|
75
131
|
}
|
|
76
132
|
};
|
|
77
133
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// #import "JSObject.js"
|
|
17
17
|
// #import "CoreTypes.js"
|
|
18
18
|
// #import "JSAttributedString.js"
|
|
19
|
+
// #import "JSContext.js"
|
|
19
20
|
'use strict';
|
|
20
21
|
|
|
21
22
|
(function(){
|
|
@@ -31,8 +32,10 @@ JSClass("JSTextRun", JSObject, {
|
|
|
31
32
|
attributes: null,
|
|
32
33
|
font: null,
|
|
33
34
|
attachment: null,
|
|
35
|
+
_useDisplayMetrics: false,
|
|
34
36
|
|
|
35
|
-
initWithGlyphs: function(glyphs, glyphCharacterLengths, font, attributes, range){
|
|
37
|
+
initWithGlyphs: function(glyphs, glyphCharacterLengths, font, attributes, range, useDisplayMetrics){
|
|
38
|
+
this._useDisplayMetrics = useDisplayMetrics === true;
|
|
36
39
|
this.glyphs = glyphs;
|
|
37
40
|
this.glyphCharacterLengths = glyphCharacterLengths;
|
|
38
41
|
this.attributes = attributes;
|
|
@@ -42,8 +45,13 @@ JSClass("JSTextRun", JSObject, {
|
|
|
42
45
|
this._size = JSSize(this.attachment.size);
|
|
43
46
|
}else{
|
|
44
47
|
this.font = font;
|
|
45
|
-
this.
|
|
46
|
-
|
|
48
|
+
if (this._useDisplayMetrics){
|
|
49
|
+
this.baseline = -font.displayDescender;
|
|
50
|
+
this._size = JSSize(0, font.displayLineHeight);
|
|
51
|
+
}else{
|
|
52
|
+
this.baseline = -font.descender;
|
|
53
|
+
this._size = JSSize(0, font.lineHeight);
|
|
54
|
+
}
|
|
47
55
|
for (var i = 0, l = glyphs.length; i < l; ++i){
|
|
48
56
|
this._size.width += font.widthOfGlyph(glyphs[i]);
|
|
49
57
|
}
|
|
@@ -67,9 +75,18 @@ JSClass("JSTextRun", JSObject, {
|
|
|
67
75
|
context.restore();
|
|
68
76
|
}else{
|
|
69
77
|
context.save();
|
|
70
|
-
|
|
78
|
+
if (this._useDisplayMetrics){
|
|
79
|
+
context.translateBy(point.x, point.y + this.font.displayAscender);
|
|
80
|
+
}else{
|
|
81
|
+
context.translateBy(point.x, point.y + this.font.ascender);
|
|
82
|
+
}
|
|
71
83
|
context.setFont(this.font);
|
|
72
84
|
context.setFillColor(this.attributes.textColor);
|
|
85
|
+
if (this.attributes.strokeWidth){
|
|
86
|
+
context.setLineWidth(this.attributes.strokeWidth);
|
|
87
|
+
context.setStrokeColor(this.attributes.strokeColor);
|
|
88
|
+
context.setTextDrawingMode(JSContext.TextDrawingMode.fillStroke);
|
|
89
|
+
}
|
|
73
90
|
context.showGlyphs(this.glyphs);
|
|
74
91
|
context.restore();
|
|
75
92
|
}
|
|
@@ -30,6 +30,11 @@ JSClass("JSTextStorage", JSAttributedString, {
|
|
|
30
30
|
this._commonTextStorageInit();
|
|
31
31
|
},
|
|
32
32
|
|
|
33
|
+
initFromDictionary: function(dictionary){
|
|
34
|
+
JSTextStorage.$super.initFromDictionary.call(this, dictionary);
|
|
35
|
+
this._commonTextStorageInit();
|
|
36
|
+
},
|
|
37
|
+
|
|
33
38
|
_commonTextStorageInit: function(){
|
|
34
39
|
this._layoutManagers = [];
|
|
35
40
|
},
|