@breakside/jskit 2026.9.0 → 2026.10.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 (60) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/JSColor.js +5 -0
  7. package/Frameworks/Foundation.jsframework/JS/JSPath.js +482 -49
  8. package/Frameworks/Foundation.jsframework/JS/JSSpec.js +5 -1
  9. package/Frameworks/Foundation.jsframework/JS/String+JS.js +78 -2
  10. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  11. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  12. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  13. package/Info.json +2 -2
  14. package/Node/DocCommand.js +5 -2
  15. package/Node/Docs/DocClass.js +2 -2
  16. package/Node/Docs/DocComponent.js +6 -4
  17. package/Node/Docs/DocDictionaryProperty.js +2 -2
  18. package/Node/Docs/DocEnumOption.js +2 -2
  19. package/Node/Docs/DocFunction.js +2 -2
  20. package/Node/Docs/DocProperty.js +2 -2
  21. package/Node/Docs/DocProtocol.js +1 -1
  22. package/Node/Docs/DocSpec.js +1 -1
  23. package/Node/Docs/DocSpecProperty.js +2 -2
  24. package/Node/Docs/DocTopicBasedComponent.js +2 -2
  25. package/Node/Documentation.js +10 -2
  26. package/Node/HTMLBuilder.js +3 -1
  27. package/Node/HTMLProjectServer.js +39 -1
  28. package/Node/io.breakside.jskit-bundle.js +4 -4
  29. package/Resources/doc-default.css +1 -9
  30. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  31. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  32. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  33. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  34. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  35. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  36. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  37. package/Root/Frameworks/DOM/Info.yaml +1 -1
  38. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  39. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  40. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  41. package/Root/Frameworks/Foundation/JSColor.js +5 -0
  42. package/Root/Frameworks/Foundation/JSPath.js +482 -49
  43. package/Root/Frameworks/Foundation/JSSpec.js +5 -1
  44. package/Root/Frameworks/Foundation/String+JS.js +78 -2
  45. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  46. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  47. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  48. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  49. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  50. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  51. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  52. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  53. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  54. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  55. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  56. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  57. package/Root/Frameworks/UIKit/UIListView.js +11 -3
  58. package/Root/Frameworks/UIKit/UIOutlineView.js +65 -5
  59. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  60. package/package.json +1 -1
@@ -838,81 +838,514 @@ JSClass("JSPath", JSObject, {
838
838
  return JSRect(min, JSSize(max.x - min.x, max.y - min.y));
839
839
  },
840
840
 
841
+ intersectsPath: function(other, fillRule){
842
+ var boundingRect = this.boundingRect;
843
+ // no need to check if bounding rect don't intersect
844
+ if (!boundingRect.intersectsRect(other.boundingRect)){
845
+ return false;
846
+ }
847
+ // are any of our points inside other?
848
+ var subpath;
849
+ var segment;
850
+ var i, l;
851
+ var j, k;
852
+ var point;
853
+ for (i = 0, l = this.subpaths.length; i < l; ++i){
854
+ subpath = this.subpaths[i];
855
+ if (subpath.segments.length > 0){
856
+ if (other.containsPoint(subpath.firstPoint, fillRule)){
857
+ return true;
858
+ }
859
+ for (j = 0, k = subpath.segments.length; j < k; ++j){
860
+ segment = subpath.segments[j];
861
+ if (segment.type == JSPath.SegmentType.line){
862
+ if (other.containsPoint(segment.end, fillRule)){
863
+ return true;
864
+ }
865
+ }else if (segment.type == JSPath.SegmentType.curve){
866
+ if (other.containsPoint(segment.curve.p2, fillRule)){
867
+ return true;
868
+ }
869
+ }
870
+ }
871
+ }
872
+ }
873
+ // are any of other's points iside us?
874
+ for (i = 0, l = other.subpaths.length; i < l; ++i){
875
+ subpath = other.subpaths[i];
876
+ if (subpath.segments.length > 0){
877
+ if (this.containsPoint(subpath.firstPoint, fillRule)){
878
+ return true;
879
+ }
880
+ for (j = 0, k = subpath.segments.length; j < k; ++j){
881
+ segment = subpath.segments[j];
882
+ if (segment.type == JSPath.SegmentType.line){
883
+ if (this.containsPoint(segment.end, fillRule)){
884
+ return true;
885
+ }
886
+ }else if (segment.type == JSPath.SegmentType.curve){
887
+ if (this.containsPoint(segment.curve.p2, fillRule)){
888
+ return true;
889
+ }
890
+ }
891
+ }
892
+ }
893
+ }
894
+ // Could still have situations where curves intersect without
895
+ // points contained within each other, but not significant enough
896
+ // to worry about for most shapes. Will refined if needed.
897
+ return false;
898
+ },
899
+
900
+ pathConvertedToTransform: function(transform){
901
+ var path = JSPath.init();
902
+ var subpath0;
903
+ var segment0;
904
+ var subpath1;
905
+ var segment1;
906
+ var point0;
907
+ var point1;
908
+ var i, l;
909
+ var j, k;
910
+ for (i = 0, l = this.subpaths.length; i < l; ++i){
911
+ subpath0 = this.subpaths[i];
912
+ if (subpath0.segments.length > 0){
913
+ point0 = subpath0.firstPoint;
914
+ point1 = transform.convertPointToTransform(point0);
915
+ subpath1 = Subpath(point1);
916
+ path.subpaths.push(subpath1);
917
+ for (j = 0, k = subpath0.segments.length; j < k; ++j){
918
+ segment0 = subpath0.segments[j];
919
+ if (segment0.type == JSPath.SegmentType.line){
920
+ segment1 = {
921
+ type: JSPath.SegmentType.line,
922
+ end: transform.convertPointToTransform(segment0.end)
923
+ };
924
+ subpath1.segments.push(segment1);
925
+ point1 = segment1.end;
926
+ }else if (segment0.type == JSPath.SegmentType.curve){
927
+ segment1 = {
928
+ type: JSPath.SegmentType.curve,
929
+ curve: JSCubicBezier(
930
+ point1,
931
+ transform.convertPointToTransform(segment0.curve.cp1),
932
+ transform.convertPointToTransform(segment0.curve.cp2),
933
+ transform.convertPointToTransform(segment0.curve.p2)
934
+ )
935
+ };
936
+ subpath1.segments.push(segment1);
937
+ point1 = segment1.curve.p2;
938
+ }
939
+ }
940
+ subpath1.closed = subpath0.closed;
941
+ }
942
+ }
943
+ return path;
944
+ },
945
+
946
+ pathConvertedFromTransform: function(transform){
947
+ var path = JSPath.init();
948
+ var subpath0;
949
+ var segment0;
950
+ var subpath1;
951
+ var segment1;
952
+ var point0;
953
+ var point1;
954
+ var i, l;
955
+ var j, k;
956
+ for (i = 0, l = this.subpaths.length; i < l; ++i){
957
+ subpath0 = this.subpaths[i];
958
+ if (subpath0.segments.length > 0){
959
+ point0 = subpath0.firstPoint;
960
+ point1 = transform.convertPointFromTransform(point0);
961
+ subpath1 = Subpath(point1);
962
+ path.subpaths.push(subpath1);
963
+ for (j = 0, k = subpath0.segments.length; j < k; ++j){
964
+ segment0 = subpath0.segments[j];
965
+ if (segment0.type == JSPath.SegmentType.line){
966
+ segment1 = {
967
+ type: JSPath.SegmentType.line,
968
+ end: transform.convertPointFromTransform(segment0.end)
969
+ };
970
+ subpath1.segments.push(segment1);
971
+ point1 = segment1.end;
972
+ }else if (segment0.type == JSPath.SegmentType.curve){
973
+ segment1 = {
974
+ type: JSPath.SegmentType.curve,
975
+ curve: JSCubicBezier(
976
+ point1,
977
+ transform.convertPointFromTransform(segment0.curve.cp1),
978
+ transform.convertPointFromTransform(segment0.curve.cp2),
979
+ transform.convertPointFromTransform(segment0.curve.p2)
980
+ )
981
+ };
982
+ subpath1.segments.push(segment1);
983
+ point1 = segment1.curve.p2;
984
+ }
985
+ }
986
+ subpath1.closed = subpath0.closed;
987
+ }
988
+ }
989
+ return path;
990
+ },
991
+
841
992
  initWithSVGPathData: function(svgPathData){
993
+ // TODO: Q, q, T, t, A, a
994
+ //
995
+ // - All instructions are expressed as one character (e.g., a moveto
996
+ // is expressed as an M).
997
+ // - Superfluous white space and separators such as commas can be
998
+ // eliminated (e.g., "M 100 100 L 200 200" contains unnecessary spaces
999
+ // and could be expressed more compactly as "M100 100L200 200").
1000
+ // - The command letter can be eliminated on subsequent commands if the
1001
+ // same command is used multiple times in a row (e.g., you can drop
1002
+ // the second "L" in "M 100 200 L 200 100 L -100 -200" and use
1003
+ // "M 100 200 L 200 100 -100 -200" instead).
1004
+ // - Relative versions of all commands are available (uppercase means
1005
+ // absolute coordinates, lowercase means relative coordinates).
1006
+ // - Alternate forms of lineto are available to optimize the special
1007
+ // cases of horizontal and vertical lines (absolute and relative).
1008
+ // - Alternate forms of curve are available to optimize the special
1009
+ // cases where some of the control points on the current segment can
1010
+ // be determined automatically from the control points on the previous
1011
+ // segment.
1012
+ // - The processing of the BNF must consume as much of a given BNF
1013
+ // production as possible, stopping at the point when a character is
1014
+ // encountered which no longer satisfies the production. Thus, in the
1015
+ // string "M 100-200", the first coordinate for the "moveto" consumes
1016
+ // the characters "100" and stops upon encountering the minus sign
1017
+ // because the minus sign cannot follow a digit in the production of
1018
+ // a "coordinate". The result is that the first coordinate will be
1019
+ // "100" and the second coordinate will be "-200".
1020
+ // - Similarly, for the string "M 0.6.5", the first coordinate of the
1021
+ // "moveto" consumes the characters "0.6" and stops upon encountering
1022
+ // the second decimal point because the production of a "coordinate"
1023
+ // only allows one decimal point. The result is that the first
1024
+ // coordinate will be "0.6" and the second coordinate will be ".5".
1025
+ // - The general rule for error handling in path data is that the SVG
1026
+ // user agent shall render a 'path' element up to (but not including)
1027
+ // the path command containing the first error...
1028
+ // - If a path data command contains an incorrect set of parameters,
1029
+ // then the given path data command is rendered up to and including
1030
+ // the last correctly defined path segment...
842
1031
  if (svgPathData === null || svgPathData === undefined){
843
1032
  return null;
844
1033
  }
845
- var parts = svgPathData.split(" ").filter(function(p){
846
- return p !== "";
847
- });
1034
+ var i = 0;
1035
+ var l = svgPathData.length;
848
1036
  this.subpaths = [];
849
- var part;
850
1037
  var subpath = null;
851
- var point = JSPoint.Zero;
852
- var cp1;
853
- var cp2;
1038
+ var segment = null;
1039
+ var point = this._currentPoint;
854
1040
  var curve;
855
- while (parts.length > 0){
856
- part = parts.shift();
857
- if (part === "M"){
858
- if (parts.length < 2){
1041
+ var previousCommand = null;
1042
+ var command = null;
1043
+ var token = null;
1044
+ var isWhitespace = function(c){
1045
+ return c === " " || c === "\t" || c === "\r" || c === "\n";
1046
+ };
1047
+ var isDigit = function(c){
1048
+ return c >= "0" && c <= "9";
1049
+ };
1050
+ var parseToken = function(){
1051
+ var c;
1052
+ var str;
1053
+ var signAllowed;
1054
+ var decimalAllowed;
1055
+ var exponentAllowed;
1056
+ if (i < l){
1057
+ c = svgPathData[i];
1058
+ while (i < l && isWhitespace(c)){
1059
+ ++i;
1060
+ if (i < l){
1061
+ c = svgPathData[i];
1062
+ }
1063
+ }
1064
+ }
1065
+ if (i < l){
1066
+ if (
1067
+ c === "M" || c === "m" ||
1068
+ c === "L" || c === "l" ||
1069
+ c === "C" || c === "c" ||
1070
+ c === "S" || c === "s" ||
1071
+ c === "Z" || c === "z" ||
1072
+ c === "Q" || c === "q" ||
1073
+ c === "T" || c === "t" ||
1074
+ c === "A" || c === "a"
1075
+ ){
1076
+ ++i;
1077
+ return c;
1078
+ }
1079
+ str = "";
1080
+ signAllowed = true;
1081
+ decimalAllowed = true;
1082
+ exponentAllowed = true;
1083
+ while (i < l){
1084
+ c = svgPathData[i];
1085
+ if ((c === "-" || c === "+") && signAllowed){
1086
+ signAllowed = false;
1087
+ }else if (c === "." && decimalAllowed){
1088
+ decimalAllowed = false;
1089
+ signAllowed = false;
1090
+ }else if ((c === "e" || c === "E") && exponentAllowed && str !== ""){
1091
+ exponentAllowed = false;
1092
+ signAllowed = true;
1093
+ decimalAllowed = true;
1094
+ }else if (isDigit(c)){
1095
+ signAllowed = false;
1096
+ }else{
1097
+ break;
1098
+ }
1099
+ str += c;
1100
+ ++i;
1101
+ }
1102
+ if (str === ""){
1103
+ ++i;
1104
+ return null;
1105
+ }
1106
+ while (i < l && (isWhitespace(c) || c === ",")){
1107
+ ++i;
1108
+ if (i < l){
1109
+ c = svgPathData[i];
1110
+ }
1111
+ }
1112
+ try{
1113
+ return parseFloat(str);
1114
+ }catch (e){
1115
+ return null;
1116
+ }
1117
+ }
1118
+ return null;
1119
+ };
1120
+ var tokenBuffer = null;
1121
+ var parseCommand = function(){
1122
+ var command = null;
1123
+ var token = tokenBuffer;
1124
+ tokenBuffer = null;
1125
+ if (token === null){
1126
+ token = parseToken();
1127
+ }
1128
+ if (token !== null){
1129
+ if (typeof(token) === "string"){
1130
+ command = {name: token, values: []};
1131
+ token = parseToken();
1132
+ while (typeof(token) === "number"){
1133
+ command.values.push(token);
1134
+ token = parseToken();
1135
+ }
1136
+ tokenBuffer = token;
1137
+ }
1138
+ }
1139
+ return command;
1140
+ };
1141
+ command = parseCommand();
1142
+ var v;
1143
+ while (command !== null){
1144
+ if (command.name === "M"){
1145
+ if (command.values.length < 2){
859
1146
  break;
860
1147
  }
861
- point = JSPoint.Zero;
862
- part = parts.shift();
863
- point.x = parseFloat(part);
864
- part = parts.shift();
865
- point.y = parseFloat(part);
1148
+ point = JSPoint(command.values[0], command.values[1]);
866
1149
  subpath = Subpath(point);
867
1150
  this.subpaths.push(subpath);
868
- this._currentPoint = point;
869
- }else if (part === "L"){
870
- if (parts.length < 2){
1151
+ segment = null;
1152
+ // If a moveto is followed by multiple pairs of coordinates,
1153
+ // the subsequent pairs are treated as implicit lineto commands.
1154
+ for (v = 2; v < command.values.length - 1; v += 2){
1155
+ point = JSPoint(command.values[v], command.values[v + 1]);
1156
+ segment = {type: JSPath.SegmentType.line, end: point};
1157
+ subpath.segments.push(segment);
1158
+ }
1159
+ }else if (command.name === "m"){
1160
+ if (command.values.length < 2){
1161
+ break;
1162
+ }
1163
+ if (point !== null){
1164
+ point = point.adding(JSPoint(command.values[0], command.values[1]))
1165
+ }else{
1166
+ point = JSPoint(command.values[0], command.values[1]);
1167
+ }
1168
+ subpath = Subpath(point);
1169
+ this.subpaths.push(subpath);
1170
+ segment = null;
1171
+ // If a moveto is followed by multiple pairs of coordinates,
1172
+ // the subsequent pairs are treated as implicit lineto commands.
1173
+ for (v = 2; v < command.values.length - 1; v += 2){
1174
+ point = point.adding(JSPoint(command.values[v], command.values[v + 1]));
1175
+ segment = {type: JSPath.SegmentType.line, end: point};
1176
+ subpath.segments.push(segment);
1177
+ }
1178
+ }else if (command.name === "L"){
1179
+ if (command.values.length < 2){
1180
+ break;
1181
+ }
1182
+ if (subpath === null){
1183
+ subpath = Subpath(point);
1184
+ this.subpaths.push(subpath);
1185
+ }
1186
+ for (v = 0; v < command.values.length - 1; v += 2){
1187
+ point = JSPoint(command.values[v], command.values[v + 1]);
1188
+ segment = {type: JSPath.SegmentType.line, end: point};
1189
+ subpath.segments.push(segment);
1190
+ }
1191
+ }else if (command.name === "l"){
1192
+ if (command.values.length < 2){
1193
+ break;
1194
+ }
1195
+ if (subpath === null){
1196
+ subpath = Subpath(point);
1197
+ this.subpaths.push(subpath);
1198
+ }
1199
+ for (v = 0; v < command.values.length - 1; v += 2){
1200
+ point = point.adding(JSPoint(command.values[v], command.values[v + 1]));
1201
+ segment = {type: JSPath.SegmentType.line, end: point};
1202
+ subpath.segments.push(segment);
1203
+ }
1204
+ }else if (command.name === "H"){
1205
+ if (command.values.length < 1){
1206
+ break;
1207
+ }
1208
+ if (subpath === null){
1209
+ subpath = Subpath(point);
1210
+ this.subpaths.push(subpath);
1211
+ }
1212
+ for (v = 0; v < command.values.length; v += 1){
1213
+ point = JSPoint(command.values[v], point.y);
1214
+ segment = {type: JSPath.SegmentType.line, end: point};
1215
+ subpath.segments.push(segment);
1216
+ }
1217
+ }else if (command.name === "h"){
1218
+ if (command.values.length < 1){
1219
+ break;
1220
+ }
1221
+ if (subpath === null){
1222
+ subpath = Subpath(point);
1223
+ this.subpaths.push(subpath);
1224
+ }
1225
+ for (v = 0; v < command.values.length; v += 1){
1226
+ point = point.adding(JSPoint(command.values[v], 0));
1227
+ segment = {type: JSPath.SegmentType.line, end: point};
1228
+ subpath.segments.push(segment);
1229
+ }
1230
+ }else if (command.name === "V"){
1231
+ if (command.values.length < 1){
1232
+ break;
1233
+ }
1234
+ if (subpath === null){
1235
+ subpath = Subpath(point);
1236
+ this.subpaths.push(subpath);
1237
+ }
1238
+ for (v = 0; v < command.values.length; v += 1){
1239
+ point = JSPoint(point.x, command.values[v]);
1240
+ segment = {type: JSPath.SegmentType.line, end: point};
1241
+ subpath.segments.push(segment);
1242
+ }
1243
+ }else if (command.name === "v"){
1244
+ if (command.values.length < 1){
1245
+ break;
1246
+ }
1247
+ if (subpath === null){
1248
+ subpath = Subpath(point);
1249
+ this.subpaths.push(subpath);
1250
+ }
1251
+ for (v = 0; v < command.values.length; v += 1){
1252
+ point = point.adding(JSPoint(0, command.values[v]));
1253
+ segment = {type: JSPath.SegmentType.line, end: point};
1254
+ subpath.segments.push(segment);
1255
+ }
1256
+ }else if (command.name === "C"){
1257
+ if (command.values.length < 6){
1258
+ break;
1259
+ }
1260
+ if (subpath === null){
1261
+ subpath = Subpath(point);
1262
+ this.subpaths.push(subpath);
1263
+ }
1264
+ for (v = 0; v < command.values.length - 5; v += 6){
1265
+ curve = JSCubicBezier(
1266
+ point,
1267
+ JSPoint(command.values[v], command.values[v + 1]),
1268
+ JSPoint(command.values[v + 2], command.values[v + 3]),
1269
+ JSPoint(command.values[v + 4], command.values[v + 5])
1270
+ );
1271
+ segment = {type: JSPath.SegmentType.curve, curve: curve};
1272
+ subpath.segments.push(segment);
1273
+ point = curve.p2;
1274
+ }
1275
+ }else if (command.name === "c"){
1276
+ if (command.values.length < 6){
1277
+ break;
1278
+ }
1279
+ if (subpath === null){
1280
+ subpath = Subpath(point);
1281
+ this.subpaths.push(subpath);
1282
+ }
1283
+ for (v = 0; v < command.values.length - 5; v += 6){
1284
+ curve = JSCubicBezier(
1285
+ point,
1286
+ point.adding(JSPoint(command.values[v], command.values[v + 1])),
1287
+ point.adding(JSPoint(command.values[v + 2], command.values[v + 3])),
1288
+ point.adding(JSPoint(command.values[v + 4], command.values[v + 5]))
1289
+ );
1290
+ segment = {type: JSPath.SegmentType.curve, curve: curve};
1291
+ subpath.segments.push(segment);
1292
+ point = curve.p2;
1293
+ }
1294
+ }else if (command.name === "S"){
1295
+ if (command.values.length < 4){
871
1296
  break;
872
1297
  }
873
1298
  if (subpath === null){
874
1299
  subpath = Subpath(point);
875
1300
  this.subpaths.push(subpath);
876
1301
  }
877
- point = JSPoint.Zero;
878
- part = parts.shift();
879
- point.x = parseFloat(part);
880
- part = parts.shift();
881
- point.y = parseFloat(part);
882
- subpath.segments.push({type: JSPath.SegmentType.line, end: point});
883
- this._currentPoint = point;
884
- }else if (part === "C"){
885
- if (parts.length < 6){
1302
+ for (v = 0; v < command.values.length - 3; v += 4){
1303
+ curve = JSCubicBezier(
1304
+ point,
1305
+ point,
1306
+ JSPoint(command.values[v + 1], command.values[v + 2]),
1307
+ JSPoint(command.values[v + 3], command.values[v + 4])
1308
+ );
1309
+ if (previousCommand !== null && (previousCommand.name === "C" || previousCommand.name === "c" || previousCommand.name === "S" || previousCommand.name === "s")){
1310
+ curve.cp1 = point.adding(segment.curve.p2.subtracting(segment.curve.cp2));
1311
+ }
1312
+ segment = {type: JSPath.SegmentType.curve, curve: curve};
1313
+ subpath.segments.push(segment);
1314
+ point = curve.p2;
1315
+ }
1316
+ }else if (command.name === "s"){
1317
+ if (command.values.length < 4){
886
1318
  break;
887
1319
  }
888
1320
  if (subpath === null){
889
1321
  subpath = Subpath(point);
890
1322
  this.subpaths.push(subpath);
891
1323
  }
892
- cp1 = JSPoint.Zero;
893
- cp2 = JSPoint.Zero;
894
- point = JSPoint.Zero;
895
- part = parts.shift();
896
- cp1.x = parseFloat(part);
897
- part = parts.shift();
898
- cp1.y = parseFloat(part);
899
- part = parts.shift();
900
- cp2.x = parseFloat(part);
901
- part = parts.shift();
902
- cp2.y = parseFloat(part);
903
- part = parts.shift();
904
- point.x = parseFloat(part);
905
- part = parts.shift();
906
- point.y = parseFloat(part);
907
- subpath.segments.push({type: JSPath.SegmentType.curve, curve: JSCubicBezier(this._currentPoint, cp1, cp2, point)});
908
- this._currentPoint = point;
909
- }else if (part === "Z"){
1324
+ for (v = 0; v < command.values.length - 3; v += 4){
1325
+ curve = JSCubicBezier(
1326
+ point,
1327
+ point,
1328
+ point.adding(JSPoint(command.values[v + 1], command.values[v + 2])),
1329
+ point.adding(JSPoint(command.values[v + 3], command.values[v + 4]))
1330
+ );
1331
+ if (previousCommand !== null && (previousCommand.name === "C" || previousCommand.name === "c" || previousCommand.name === "S" || previousCommand.name === "s")){
1332
+ curve.cp1 = point.adding(segment.curve.p2.subtracting(segment.curve.cp2));
1333
+ }
1334
+ subpath.segments.push({type: JSPath.SegmentType.curve, curve: curve});
1335
+ point = curve.p2;
1336
+ }
1337
+ }else if (command.name === "Z" || command.name === "z"){
910
1338
  if (subpath !== null){
911
1339
  subpath.closed = true;
912
1340
  subpath = null;
913
1341
  }
1342
+ }else{
1343
+ break;
914
1344
  }
1345
+ previousCommand = command;
1346
+ command = parseCommand();
915
1347
  }
1348
+ this._currentPoint = point;
916
1349
  },
917
1350
 
918
1351
  svgPathData: function(){
@@ -923,14 +1356,14 @@ JSClass("JSPath", JSObject, {
923
1356
  var data = "";
924
1357
  for (i = 0, l = this.subpaths.length; i < l; ++i){
925
1358
  subpath = this.subpaths[i];
1359
+ data += "M %0.f %0.f ".sprintf(subpath.firstPoint.x, subpath.firstPoint.y);
926
1360
  if (subpath.segments.length > 0){
927
- data += "M %f %f ".sprintf(subpath.firstPoint.x, subpath.firstPoint.y);
928
1361
  for (j = 0, k = subpath.segments.length; j < k; ++j){
929
1362
  segment = subpath.segments[j];
930
1363
  if (segment.type == JSPath.SegmentType.line){
931
- data += "L %f %f ".sprintf(segment.end.x, segment.end.y);
1364
+ data += "L %0.f %0.f ".sprintf(segment.end.x, segment.end.y);
932
1365
  }else if (segment.type == JSPath.SegmentType.curve){
933
- data += "C %f %f %f %f %f %f ".sprintf(
1366
+ data += "C %0.f %0.f %0.f %0.f %0.f %0.f ".sprintf(
934
1367
  segment.curve.cp1.x,
935
1368
  segment.curve.cp1.y,
936
1369
  segment.curve.cp2.x,
@@ -177,7 +177,11 @@ JSClass("JSSpec", JSObject, {
177
177
  return this._root.valueForKey(value.substr(1), type, allocatedObject);
178
178
  }
179
179
  if (prefix == '@'){
180
- return JSObject.initWithSpecName(value.substr(1), this._root._bundle);
180
+ value = JSSpec.initWithResource(value.substr(1), this._root._bundle);
181
+ if (value === null){
182
+ throw new Error("Spec not found: %s".sprintf(value.substr(1)));
183
+ }
184
+ return value.filesOwner;
181
185
  }
182
186
  if (type !== null){
183
187
  if (value in type){