paper 0.12.20 → 0.14.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 (54) hide show
  1. package/CHANGELOG.md +318 -0
  2. package/README.md +45 -26
  3. package/dist/docs/assets/js/paper.js +1210 -481
  4. package/dist/docs/classes/ChangeFlag.html +384 -0
  5. package/dist/docs/classes/Color.html +63 -47
  6. package/dist/docs/classes/CompoundPath.html +219 -62
  7. package/dist/docs/classes/Curve.html +9 -7
  8. package/dist/docs/classes/CurveLocation.html +1 -1
  9. package/dist/docs/classes/Event.html +1 -1
  10. package/dist/docs/classes/Gradient.html +1 -1
  11. package/dist/docs/classes/GradientStop.html +1 -1
  12. package/dist/docs/classes/Group.html +151 -27
  13. package/dist/docs/classes/HitResult.html +3 -2
  14. package/dist/docs/classes/HostItem.html +6721 -0
  15. package/dist/docs/classes/Item.html +120 -27
  16. package/dist/docs/classes/Key.html +1 -1
  17. package/dist/docs/classes/KeyEvent.html +1 -1
  18. package/dist/docs/classes/Layer.html +151 -27
  19. package/dist/docs/classes/Matrix.html +1 -1
  20. package/dist/docs/classes/MouseEvent.html +1 -1
  21. package/dist/docs/classes/PaperScope.html +88 -1
  22. package/dist/docs/classes/PaperScript.html +2 -1
  23. package/dist/docs/classes/Path.html +232 -72
  24. package/dist/docs/classes/PathItem.html +337 -180
  25. package/dist/docs/classes/Point.html +143 -21
  26. package/dist/docs/classes/PointText.html +151 -27
  27. package/dist/docs/classes/Project.html +201 -8
  28. package/dist/docs/classes/Raster.html +150 -38
  29. package/dist/docs/classes/Rectangle.html +12 -11
  30. package/dist/docs/classes/Segment.html +1 -1
  31. package/dist/docs/classes/Shape.html +120 -27
  32. package/dist/docs/classes/Size.html +81 -21
  33. package/dist/docs/classes/Style.html +35 -3
  34. package/dist/docs/classes/SymbolDefinition.html +1 -1
  35. package/dist/docs/classes/SymbolItem.html +121 -28
  36. package/dist/docs/classes/TextItem.html +151 -27
  37. package/dist/docs/classes/Tool.html +127 -1
  38. package/dist/docs/classes/ToolEvent.html +27 -1
  39. package/dist/docs/classes/Tween.html +1 -1
  40. package/dist/docs/classes/View.html +345 -10
  41. package/dist/docs/classes/global.html +1 -1
  42. package/dist/docs/classes/index.html +2 -2
  43. package/dist/docs/index.html +1 -1
  44. package/dist/node/canvas.js +1 -1
  45. package/dist/node/self.js +4 -3
  46. package/dist/paper-core.js +1182 -465
  47. package/dist/paper-core.min.js +3 -3
  48. package/dist/paper-full.js +1210 -481
  49. package/dist/paper-full.min.js +3 -3
  50. package/dist/paper-sourced.js +18631 -0
  51. package/dist/paper.d.ts +606 -78
  52. package/examples/Rasters/Smoothing.html +1 -1
  53. package/package.json +6 -17
  54. package/dist/node/xml.js +0 -40
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Paper.js v0.12.20 - The Swiss Army Knife of Vector Graphics Scripting.
2
+ * Paper.js v0.14.0 - The Swiss Army Knife of Vector Graphics Scripting.
3
3
  * http://paperjs.org/
4
4
  *
5
5
  * Copyright (c) 2011 - 2020, Jürg Lehni & Jonathan Puckey
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * All rights reserved.
11
11
  *
12
- * Date: Sun Oct 18 17:04:58 2026 +0200
12
+ * Date: Tue Oct 20 01:20:31 2026 +0200
13
13
  *
14
14
  ***
15
15
  *
@@ -36,6 +36,9 @@ self = self || require('./node/self.js');
36
36
  var window = self.window,
37
37
  document = self.document;
38
38
 
39
+ if (window && !document.createElement('canvas').getContext('2d'))
40
+ window = null;
41
+
39
42
  var Base = new function() {
40
43
  var hidden = /^(statics|enumerable|beans|preserve)$/,
41
44
  array = [],
@@ -777,7 +780,8 @@ var PaperScope = Base.extend({
777
780
  applyMatrix: true,
778
781
  insertItems: true,
779
782
  handleSize: 4,
780
- hitTolerance: 0
783
+ hitTolerance: 0,
784
+ scaleStrokes: false
781
785
  });
782
786
  this.project = null;
783
787
  this.projects = [];
@@ -821,7 +825,7 @@ var PaperScope = Base.extend({
821
825
  }
822
826
  },
823
827
 
824
- version: "0.12.20",
828
+ version: "0.14.0",
825
829
 
826
830
  getView: function() {
827
831
  var project = this.project;
@@ -834,7 +838,8 @@ var PaperScope = Base.extend({
834
838
 
835
839
  execute: function(code, options) {
836
840
  var exports = paper.PaperScript.execute(code, this, options);
837
- View.updateFocus();
841
+ if (window)
842
+ View.updateFocus();
838
843
  return exports;
839
844
  },
840
845
 
@@ -848,9 +853,10 @@ var PaperScope = Base.extend({
848
853
  }
849
854
  });
850
855
  });
851
- for (var key in this)
852
- if (!/^_/.test(key) && this[key])
856
+ for (var key in this) {
857
+ if (!/^_/.test(key) && this[key] && !(key in scope))
853
858
  scope[key] = this[key];
859
+ }
854
860
  },
855
861
 
856
862
  setup: function(element) {
@@ -927,6 +933,10 @@ var PaperScopeItem = Base.extend(Emitter, {
927
933
  return this._scope[this._reference] === this;
928
934
  },
929
935
 
936
+ getScope: function() {
937
+ return this._scope;
938
+ },
939
+
930
940
  remove: function() {
931
941
  if (this._index == null)
932
942
  return false;
@@ -1649,6 +1659,16 @@ var Point = Base.extend({
1649
1659
  );
1650
1660
  },
1651
1661
 
1662
+ clamp: function() {
1663
+ var args = arguments,
1664
+ min = Point.read(args),
1665
+ max = Point.read(args);
1666
+ return new Point(
1667
+ Numerical.clamp(this.x, min.x, max.x),
1668
+ Numerical.clamp(this.y, min.y, max.y)
1669
+ );
1670
+ },
1671
+
1652
1672
  statics: {
1653
1673
  min: function() {
1654
1674
  var args = arguments,
@@ -1930,10 +1950,11 @@ var Rectangle = Base.extend({
1930
1950
  arg0.width || 0, arg0.height || 0);
1931
1951
  read = 1;
1932
1952
  } else if (arg0.from === undefined && arg0.to === undefined) {
1933
- this._set(0, 0, 0, 0);
1934
- if (Base.readSupported(args, this)) {
1953
+ var builder = new RectangleBuilder();
1954
+ if (Base.readSupported(args, builder)) {
1935
1955
  read = 1;
1936
1956
  }
1957
+ this._set(builder.x, builder.y, builder.width, builder.height);
1937
1958
  }
1938
1959
  }
1939
1960
  if (read === undefined) {
@@ -2028,24 +2049,10 @@ var Rectangle = Base.extend({
2028
2049
  return new ctor(this.width, this.height, this, 'setSize');
2029
2050
  },
2030
2051
 
2031
- _fw: 1,
2032
- _fh: 1,
2033
-
2034
2052
  setSize: function() {
2035
- var size = Size.read(arguments),
2036
- sx = this._sx,
2037
- sy = this._sy,
2038
- w = size.width,
2039
- h = size.height;
2040
- if (sx) {
2041
- this.x += (this.width - w) * sx;
2042
- }
2043
- if (sy) {
2044
- this.y += (this.height - h) * sy;
2045
- }
2046
- this.width = w;
2047
- this.height = h;
2048
- this._fw = this._fh = 1;
2053
+ var size = Size.read(arguments);
2054
+ this.width = size.width;
2055
+ this.height = size.height;
2049
2056
  },
2050
2057
 
2051
2058
  getLeft: function() {
@@ -2053,12 +2060,8 @@ var Rectangle = Base.extend({
2053
2060
  },
2054
2061
 
2055
2062
  setLeft: function(left) {
2056
- if (!this._fw) {
2057
- var amount = left - this.x;
2058
- this.width -= this._sx === 0.5 ? amount * 2 : amount;
2059
- }
2063
+ this.width -= left - this.x;
2060
2064
  this.x = left;
2061
- this._sx = this._fw = 0;
2062
2065
  },
2063
2066
 
2064
2067
  getTop: function() {
@@ -2066,12 +2069,8 @@ var Rectangle = Base.extend({
2066
2069
  },
2067
2070
 
2068
2071
  setTop: function(top) {
2069
- if (!this._fh) {
2070
- var amount = top - this.y;
2071
- this.height -= this._sy === 0.5 ? amount * 2 : amount;
2072
- }
2072
+ this.height -= top - this.y;
2073
2073
  this.y = top;
2074
- this._sy = this._fh = 0;
2075
2074
  },
2076
2075
 
2077
2076
  getRight: function() {
@@ -2079,13 +2078,7 @@ var Rectangle = Base.extend({
2079
2078
  },
2080
2079
 
2081
2080
  setRight: function(right) {
2082
- if (!this._fw) {
2083
- var amount = right - this.x;
2084
- this.width = this._sx === 0.5 ? amount * 2 : amount;
2085
- }
2086
- this.x = right - this.width;
2087
- this._sx = 1;
2088
- this._fw = 0;
2081
+ this.width = right - this.x;
2089
2082
  },
2090
2083
 
2091
2084
  getBottom: function() {
@@ -2093,13 +2086,7 @@ var Rectangle = Base.extend({
2093
2086
  },
2094
2087
 
2095
2088
  setBottom: function(bottom) {
2096
- if (!this._fh) {
2097
- var amount = bottom - this.y;
2098
- this.height = this._sy === 0.5 ? amount * 2 : amount;
2099
- }
2100
- this.y = bottom - this.height;
2101
- this._sy = 1;
2102
- this._fh = 0;
2089
+ this.height = bottom - this.y;
2103
2090
  },
2104
2091
 
2105
2092
  getCenterX: function() {
@@ -2107,16 +2094,7 @@ var Rectangle = Base.extend({
2107
2094
  },
2108
2095
 
2109
2096
  setCenterX: function(x) {
2110
- if (this._fw || this._sx === 0.5) {
2111
- this.x = x - this.width / 2;
2112
- } else {
2113
- if (this._sx) {
2114
- this.x += (x - this.x) * 2 * this._sx;
2115
- }
2116
- this.width = (x - this.x) * 2;
2117
- }
2118
- this._sx = 0.5;
2119
- this._fw = 0;
2097
+ this.x = x - this.width / 2;
2120
2098
  },
2121
2099
 
2122
2100
  getCenterY: function() {
@@ -2124,16 +2102,7 @@ var Rectangle = Base.extend({
2124
2102
  },
2125
2103
 
2126
2104
  setCenterY: function(y) {
2127
- if (this._fh || this._sy === 0.5) {
2128
- this.y = y - this.height / 2;
2129
- } else {
2130
- if (this._sy) {
2131
- this.y += (y - this.y) * 2 * this._sy;
2132
- }
2133
- this.height = (y - this.y) * 2;
2134
- }
2135
- this._sy = 0.5;
2136
- this._fh = 0;
2105
+ this.y = y - this.height / 2;
2137
2106
  },
2138
2107
 
2139
2108
  getCenter: function(_dontLink) {
@@ -2319,6 +2288,94 @@ new function() {
2319
2288
  );
2320
2289
  });
2321
2290
 
2291
+ var RectangleBuilder = Rectangle.extend({
2292
+ beans: true,
2293
+
2294
+ _fw: 1,
2295
+ _fh: 1,
2296
+
2297
+ setSize: function() {
2298
+ var size = Size.read(arguments),
2299
+ sx = this._sx,
2300
+ sy = this._sy,
2301
+ w = size.width,
2302
+ h = size.height;
2303
+ if (sx) {
2304
+ this.x += (this.width - w) * sx;
2305
+ }
2306
+ if (sy) {
2307
+ this.y += (this.height - h) * sy;
2308
+ }
2309
+ this.width = w;
2310
+ this.height = h;
2311
+ this._fw = this._fh = 1;
2312
+ },
2313
+
2314
+ setLeft: function(left) {
2315
+ if (!this._fw) {
2316
+ var amount = left - this.x;
2317
+ this.width -= this._sx === 0.5 ? amount * 2 : amount;
2318
+ }
2319
+ this.x = left;
2320
+ this._sx = this._fw = 0;
2321
+ },
2322
+
2323
+ setTop: function(top) {
2324
+ if (!this._fh) {
2325
+ var amount = top - this.y;
2326
+ this.height -= this._sy === 0.5 ? amount * 2 : amount;
2327
+ }
2328
+ this.y = top;
2329
+ this._sy = this._fh = 0;
2330
+ },
2331
+
2332
+ setRight: function(right) {
2333
+ if (!this._fw) {
2334
+ var amount = right - this.x;
2335
+ this.width = this._sx === 0.5 ? amount * 2 : amount;
2336
+ }
2337
+ this.x = right - this.width;
2338
+ this._sx = 1;
2339
+ this._fw = 0;
2340
+ },
2341
+
2342
+ setBottom: function(bottom) {
2343
+ if (!this._fh) {
2344
+ var amount = bottom - this.y;
2345
+ this.height = this._sy === 0.5 ? amount * 2 : amount;
2346
+ }
2347
+ this.y = bottom - this.height;
2348
+ this._sy = 1;
2349
+ this._fh = 0;
2350
+ },
2351
+
2352
+ setCenterX: function(x) {
2353
+ if (this._fw || this._sx === 0.5) {
2354
+ this.x = x - this.width / 2;
2355
+ } else {
2356
+ if (this._sx) {
2357
+ this.x += (x - this.x) * 2 * this._sx;
2358
+ }
2359
+ this.width = (x - this.x) * 2;
2360
+ }
2361
+ this._sx = 0.5;
2362
+ this._fw = 0;
2363
+ },
2364
+
2365
+ setCenterY: function(y) {
2366
+ if (this._fh || this._sy === 0.5) {
2367
+ this.y = y - this.height / 2;
2368
+ } else {
2369
+ if (this._sy) {
2370
+ this.y += (y - this.y) * 2 * this._sy;
2371
+ }
2372
+ this.height = (y - this.y) * 2;
2373
+ }
2374
+ this._sy = 0.5;
2375
+ this._fh = 0;
2376
+ }
2377
+ });
2378
+
2322
2379
  var Matrix = Base.extend({
2323
2380
  _class: 'Matrix',
2324
2381
 
@@ -2596,6 +2653,10 @@ var Matrix = Base.extend({
2596
2653
  return det && !isNaN(det) && isFinite(this._tx) && isFinite(this._ty);
2597
2654
  },
2598
2655
 
2656
+ _getMeanScale: function() {
2657
+ return Math.sqrt(Math.abs(this._a * this._d - this._b * this._c));
2658
+ },
2659
+
2599
2660
  isSingular: function() {
2600
2661
  return !this.isInvertible();
2601
2662
  },
@@ -2870,8 +2931,10 @@ var Line = Base.extend({
2870
2931
  : vy === 0 ? (vx < 0 ? y - py : py - y)
2871
2932
  : ((x - px) * vy - (y - py) * vx) / (
2872
2933
  vy > vx
2873
- ? vy * Math.sqrt(1 + (vx * vx) / (vy * vy))
2874
- : vx * Math.sqrt(1 + (vy * vy) / (vx * vx))
2934
+ ? Math.abs(vy)
2935
+ * Math.sqrt(1 + (vx * vx) / (vy * vy))
2936
+ : Math.abs(vx)
2937
+ * Math.sqrt(1 + (vy * vy) / (vx * vx))
2875
2938
  );
2876
2939
  },
2877
2940
 
@@ -2882,6 +2945,48 @@ var Line = Base.extend({
2882
2945
  }
2883
2946
  });
2884
2947
 
2948
+ var ChangeFlag = {
2949
+ APPEARANCE: 0x1,
2950
+
2951
+ CHILDREN: 0x2,
2952
+
2953
+ INSERTION: 0x4,
2954
+
2955
+ GEOMETRY: 0x8,
2956
+
2957
+ MATRIX: 0x10,
2958
+
2959
+ SEGMENTS: 0x20,
2960
+
2961
+ STROKE: 0x40,
2962
+
2963
+ STYLE: 0x80,
2964
+
2965
+ ATTRIBUTE: 0x100,
2966
+
2967
+ CONTENT: 0x200,
2968
+
2969
+ PIXELS: 0x400,
2970
+
2971
+ CLIPPING: 0x800,
2972
+
2973
+ VIEW: 0x1000
2974
+ };
2975
+
2976
+ var Change = {
2977
+ CHILDREN: ChangeFlag.CHILDREN | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2978
+ INSERTION: ChangeFlag.INSERTION | ChangeFlag.APPEARANCE,
2979
+ GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2980
+ MATRIX: ChangeFlag.MATRIX | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2981
+ SEGMENTS: ChangeFlag.SEGMENTS | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2982
+ STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE,
2983
+ STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE,
2984
+ ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE,
2985
+ CONTENT: ChangeFlag.CONTENT | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2986
+ PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE,
2987
+ VIEW: ChangeFlag.VIEW | ChangeFlag.APPEARANCE
2988
+ };
2989
+
2885
2990
  var Project = PaperScopeItem.extend({
2886
2991
  _class: 'Project',
2887
2992
  _list: 'projects',
@@ -2915,7 +3020,7 @@ var Project = PaperScopeItem.extend({
2915
3020
  }
2916
3021
  }
2917
3022
  var changes = this._changes;
2918
- if (changes && item) {
3023
+ if (changes && item && item.isInserted()) {
2919
3024
  var changesById = this._changesById,
2920
3025
  id = item._id,
2921
3026
  entry = changesById[id];
@@ -2927,6 +3032,26 @@ var Project = PaperScopeItem.extend({
2927
3032
  }
2928
3033
  },
2929
3034
 
3035
+ getTrackChanges: function() {
3036
+ return !!this._changes;
3037
+ },
3038
+
3039
+ setTrackChanges: function(trackChanges) {
3040
+ if (!this._changes ^ !trackChanges) {
3041
+ this._changes = trackChanges ? [] : null;
3042
+ this._changesById = trackChanges ? {} : null;
3043
+ }
3044
+ },
3045
+
3046
+ takeChanges: function() {
3047
+ var changes = this._changes;
3048
+ if (changes) {
3049
+ this._changes = [];
3050
+ this._changesById = {};
3051
+ }
3052
+ return changes || null;
3053
+ },
3054
+
2930
3055
  clear: function() {
2931
3056
  var children = this._children;
2932
3057
  for (var i = children.length - 1; i >= 0; i--)
@@ -3008,10 +3133,10 @@ var Project = PaperScopeItem.extend({
3008
3133
  return items;
3009
3134
  },
3010
3135
 
3011
- _updateSelection: function(item) {
3136
+ _updateSelection: function(item, remove) {
3012
3137
  var id = item._id,
3013
3138
  selectionItems = this._selectionItems;
3014
- if (item._selection) {
3139
+ if (item._selection && !remove && item.isInserted()) {
3015
3140
  if (selectionItems[id] !== item) {
3016
3141
  this._selectionCount++;
3017
3142
  selectionItems[id] = item;
@@ -3120,7 +3245,7 @@ var Project = PaperScopeItem.extend({
3120
3245
 
3121
3246
  if (this._selectionCount > 0) {
3122
3247
  ctx.save();
3123
- ctx.strokeWidth = 1;
3248
+ ctx.lineWidth = 1;
3124
3249
  var items = this._selectionItems,
3125
3250
  size = this._scope.settings.handleSize,
3126
3251
  version = this._updateVersion;
@@ -3183,11 +3308,13 @@ new function() {
3183
3308
  function(name) {
3184
3309
  this._events[name] = {
3185
3310
  install: function(type) {
3186
- this.getView()._countItemEvent(type, 1);
3311
+ if (this.isInserted())
3312
+ this.getView()._countItemEvent(type, 1);
3187
3313
  },
3188
3314
 
3189
3315
  uninstall: function(type) {
3190
- this.getView()._countItemEvent(type, -1);
3316
+ if (this.isInserted())
3317
+ this.getView()._countItemEvent(type, -1);
3191
3318
  }
3192
3319
  };
3193
3320
  }, {
@@ -3220,6 +3347,10 @@ new function() {
3220
3347
  matrix = this._matrix = new Matrix(),
3221
3348
  project = hasProps && props.project || paper.project,
3222
3349
  settings = paper.settings;
3350
+ if (!project) {
3351
+ throw new Error('Unable to create an item without a project. '
3352
+ + 'Call paper.setup() or create a Project first.');
3353
+ }
3223
3354
  this._id = internal ? null : UID.get();
3224
3355
  this._parent = this._index = null;
3225
3356
  this._applyMatrix = this._canApplyMatrix && settings.applyMatrix;
@@ -3496,8 +3627,8 @@ new function() {
3496
3627
  bounds = this.getBounds();
3497
3628
  }
3498
3629
  matrix.scale(
3499
- bounds.width !== 0 ? rect.width / bounds.width : 0,
3500
- bounds.height !== 0 ? rect.height / bounds.height : 0);
3630
+ bounds.width !== 0 ? rect.width / bounds.width : 1,
3631
+ bounds.height !== 0 ? rect.height / bounds.height : 1);
3501
3632
  }
3502
3633
  center = bounds.getCenter();
3503
3634
  matrix.translate(-center.x, -center.y);
@@ -3521,14 +3652,18 @@ new function() {
3521
3652
  },
3522
3653
 
3523
3654
  _getCachedBounds: function(matrix, options, noInternal) {
3524
- matrix = matrix && matrix._orNullIfIdentity();
3525
3655
  var internal = options.internal && !noInternal,
3526
3656
  cacheItem = options.cacheItem,
3527
- _matrix = internal ? null : this._matrix._orNullIfIdentity(),
3528
- cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
3657
+ _matrix = internal ? null : this._matrix._orNullIfIdentity();
3658
+ matrix = matrix ? matrix._orNullIfIdentity() : _matrix;
3659
+ var cacheKey = cacheItem && (matrix ? matrix.equals(_matrix) : !_matrix)
3529
3660
  && this._getBoundsCacheKey(options, internal),
3661
+ isIntermediate = cacheKey && cacheItem !== this
3662
+ && (this._children || this._definition),
3530
3663
  bounds = this._bounds;
3531
3664
  Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
3665
+ if (isIntermediate)
3666
+ Item._updateBoundsCache(this, cacheItem);
3532
3667
  if (cacheKey && bounds && cacheKey in bounds) {
3533
3668
  var cached = bounds[cacheKey];
3534
3669
  return {
@@ -3536,7 +3671,9 @@ new function() {
3536
3671
  nonscaling: cached.nonscaling
3537
3672
  };
3538
3673
  }
3539
- var res = this._getBounds(matrix || _matrix, options),
3674
+ var res = this._getBounds(matrix, isIntermediate
3675
+ ? Base.set({}, options, { cacheItem: this })
3676
+ : options),
3540
3677
  rect = res.rect || res,
3541
3678
  style = this._style,
3542
3679
  nonscaling = res.nonscaling || style.hasStroke()
@@ -3700,27 +3837,14 @@ new function() {
3700
3837
  },
3701
3838
 
3702
3839
  getGlobalMatrix: function(_dontClone) {
3703
- var matrix = this._globalMatrix;
3704
- if (matrix) {
3705
- var parent = this._parent;
3706
- var parents = [];
3707
- while (parent) {
3708
- if (!parent._globalMatrix) {
3709
- matrix = null;
3710
- for (var i = 0, l = parents.length; i < l; i++) {
3711
- parents[i]._globalMatrix = null;
3712
- }
3713
- break;
3714
- }
3715
- parents.push(parent);
3716
- parent = parent._parent;
3717
- }
3718
- }
3719
- if (!matrix) {
3840
+ var parent = this._parent,
3841
+ parentMatrix = parent && parent.getGlobalMatrix(true),
3842
+ matrix = this._globalMatrix;
3843
+ if (!matrix || this._parentGlobalMatrix !== parentMatrix) {
3720
3844
  matrix = this._globalMatrix = this._matrix.clone();
3721
- var parent = this._parent;
3722
- if (parent)
3723
- matrix.prepend(parent.getGlobalMatrix(true));
3845
+ if (parentMatrix)
3846
+ matrix.prepend(parentMatrix);
3847
+ this._parentGlobalMatrix = parentMatrix;
3724
3848
  }
3725
3849
  return _dontClone ? matrix : matrix.clone();
3726
3850
  },
@@ -3747,13 +3871,10 @@ new function() {
3747
3871
 
3748
3872
  _setProject: function(project, installEvents) {
3749
3873
  if (this._project !== project) {
3750
- if (this._project)
3751
- this._installEvents(false);
3752
3874
  this._project = project;
3753
3875
  var children = this._children;
3754
3876
  for (var i = 0, l = children && children.length; i < l; i++)
3755
3877
  children[i]._setProject(project);
3756
- installEvents = true;
3757
3878
  }
3758
3879
  if (installEvents)
3759
3880
  this._installEvents(true);
@@ -3763,8 +3884,14 @@ new function() {
3763
3884
  return this._project._view;
3764
3885
  },
3765
3886
 
3887
+ getScope: function() {
3888
+ return this._project._scope;
3889
+ },
3890
+
3766
3891
  _installEvents: function _installEvents(install) {
3767
3892
  _installEvents.base.call(this, install);
3893
+ if (this._selection)
3894
+ this._project._updateSelection(this, !install);
3768
3895
  var children = this._children;
3769
3896
  for (var i = 0, l = children && children.length; i < l; i++)
3770
3897
  children[i]._installEvents(install);
@@ -3852,7 +3979,8 @@ new function() {
3852
3979
  var copy = new this.constructor(Item.NO_INSERT),
3853
3980
  children = this._children,
3854
3981
  insert = Base.pick(options ? options.insert : undefined,
3855
- options === undefined || options === true),
3982
+ typeof options === 'boolean' ? options
3983
+ : paper.settings.insertItems),
3856
3984
  deep = Base.pick(options ? options.deep : undefined, true);
3857
3985
  if (children)
3858
3986
  copy.copyAttributes(this);
@@ -3940,7 +4068,7 @@ new function() {
3940
4068
  .translate(topLeft.add(boundsSize.divide(2)))
3941
4069
  .scale(1 / scale)
3942
4070
  );
3943
- if (insert === undefined || insert) {
4071
+ if (Base.pick(insert, paper.settings.insertItems)) {
3944
4072
  raster.insertAbove(this);
3945
4073
  }
3946
4074
  return raster;
@@ -3973,6 +4101,7 @@ new function() {
3973
4101
  _asPathItem: function() {
3974
4102
  return new Path.Rectangle({
3975
4103
  rectangle: this.getInternalBounds(),
4104
+ applyMatrix: false,
3976
4105
  matrix: this._matrix,
3977
4106
  insert: false,
3978
4107
  });
@@ -4031,20 +4160,22 @@ new function() {
4031
4160
 
4032
4161
  _hitTest: function(point, options, parentViewMatrix) {
4033
4162
  if (this._locked || !this._visible || this._guide && !options.guides
4034
- || this.isEmpty()) {
4163
+ || !this._shell && this.isEmpty()) {
4035
4164
  return null;
4036
4165
  }
4037
4166
 
4038
4167
  var matrix = this._matrix,
4168
+ pointMatrix = parentViewMatrix ? matrix : this.getGlobalMatrix(),
4039
4169
  viewMatrix = parentViewMatrix
4040
4170
  ? parentViewMatrix.appended(matrix)
4041
- : this.getGlobalMatrix().prepend(this.getView()._matrix),
4171
+ : pointMatrix.prepended(this.getView()._matrix),
4042
4172
  tolerance = Math.max(options.tolerance, 1e-12),
4043
4173
  tolerancePadding = options._tolerancePadding = new Size(
4044
- Path._getStrokePadding(tolerance,
4045
- matrix._shiftless().invert()));
4046
- point = matrix._inverseTransform(point);
4047
- if (!point || !this._children &&
4174
+ Path._getStrokePadding(tolerance, viewMatrix
4175
+ .prepended(this.getView()._matrix.inverted())
4176
+ ._shiftless().invert()));
4177
+ point = pointMatrix._inverseTransform(point);
4178
+ if (!point || (this._shell || !this._children) &&
4048
4179
  !this.getBounds({ internal: true, stroke: true, handle: true })
4049
4180
  .expand(tolerancePadding.multiply(2))._containsPoint(point)) {
4050
4181
  return null;
@@ -4060,10 +4191,21 @@ new function() {
4060
4191
  res;
4061
4192
 
4062
4193
  function filter(hit) {
4194
+ if (Array.isArray(hit)) {
4195
+ var first = null;
4196
+ for (var i = 0, l = hit.length; i < l; i++) {
4197
+ var res = filter(hit[i]);
4198
+ first = first || res;
4199
+ }
4200
+ return first;
4201
+ }
4063
4202
  if (hit && match && !match(hit))
4064
4203
  hit = null;
4065
- if (hit && options.all)
4204
+ if (hit && options.all) {
4205
+ if (hit.point)
4206
+ hit.point = that.getGlobalMatrix().transform(hit.point);
4066
4207
  options.all.push(hit);
4208
+ }
4067
4209
  return hit;
4068
4210
  }
4069
4211
 
@@ -4107,14 +4249,14 @@ new function() {
4107
4249
  : viewMatrix._shiftless().invert()))
4108
4250
  || null;
4109
4251
  }
4110
- if (res && res.point) {
4111
- res.point = matrix.transform(res.point);
4252
+ if (res && res.point && !options.all) {
4253
+ res.point = pointMatrix.transform(res.point);
4112
4254
  }
4113
4255
  return res;
4114
4256
  },
4115
4257
 
4116
4258
  _hitTestSelf: function(point, options) {
4117
- if (options.fill && this.hasFill() && this._contains(point))
4259
+ if (options.fill && this.getFillColor() && this._contains(point))
4118
4260
  return new HitResult('fill', this);
4119
4261
  },
4120
4262
 
@@ -4188,7 +4330,7 @@ new function() {
4188
4330
  rect = bounds && Rectangle.read([bounds]);
4189
4331
  param = {
4190
4332
  items: [],
4191
- recursive: obj && obj.recursive !== false,
4333
+ recursive: !obj || obj.recursive !== false,
4192
4334
  inside: !!inside,
4193
4335
  overlapping: !!overlapping,
4194
4336
  rect: rect,
@@ -4237,6 +4379,19 @@ new function() {
4237
4379
  }, {
4238
4380
 
4239
4381
  importJSON: function(json) {
4382
+ json = typeof json === 'string' ? JSON.parse(json) : json;
4383
+ var props = json && json[0] === this._class && json[1];
4384
+ if (Base.isPlainObject(props)) {
4385
+ var item = new this.constructor(Item.NO_INSERT),
4386
+ fields = Base.set({}, this._serializeFields,
4387
+ this instanceof Group ? null : this._style._defaults),
4388
+ defaults = {};
4389
+ for (var key in fields) {
4390
+ if (!(key in props) && !Base.equals(this[key], item[key]))
4391
+ defaults[key] = item[key];
4392
+ }
4393
+ this.set(defaults);
4394
+ }
4240
4395
  var res = Base.importJSON(json, this);
4241
4396
  return res !== this ? this.addChild(res) : res;
4242
4397
  },
@@ -4382,15 +4537,15 @@ new function() {
4382
4537
  if (owner) {
4383
4538
  if (this._name)
4384
4539
  this._removeNamed();
4540
+ this._installEvents(false);
4541
+ if (notifySelf && project._changes)
4542
+ this._changed(5);
4385
4543
  if (index != null) {
4386
4544
  if (project._activeLayer === this)
4387
4545
  project._activeLayer = this.getNextSibling()
4388
4546
  || this.getPreviousSibling();
4389
4547
  Base.splice(owner._children, null, index, 1);
4390
4548
  }
4391
- this._installEvents(false);
4392
- if (notifySelf && project._changes)
4393
- this._changed(5);
4394
4549
  if (notifyParent)
4395
4550
  owner._changed(11, this);
4396
4551
  this._parent = null;
@@ -4647,30 +4802,46 @@ new function() {
4647
4802
  return this._matrix._transformPoint(Point.read(arguments));
4648
4803
  },
4649
4804
 
4650
- fitBounds: function(rectangle, fill) {
4805
+ fitBounds: function(rectangle, fit) {
4651
4806
  rectangle = Rectangle.read(arguments);
4652
- fill = Base.read(arguments);
4807
+ fit = Base.read(arguments);
4808
+ if (fit === true)
4809
+ fit = 'cover';
4653
4810
  var bounds = this.getBounds(),
4654
- itemRatio = bounds.height / bounds.width,
4655
- rectRatio = rectangle.height / rectangle.width,
4656
- scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
4657
- ? rectangle.width / bounds.width
4658
- : rectangle.height / bounds.height,
4659
- newBounds = new Rectangle(new Point(),
4660
- new Size(bounds.width * scale, bounds.height * scale));
4661
- newBounds.setCenter(rectangle.getCenter());
4662
- this.setBounds(newBounds);
4811
+ hor = rectangle.width / bounds.width,
4812
+ ver = rectangle.height / bounds.height,
4813
+ size;
4814
+
4815
+ function isScaling(ratio) {
4816
+ return ratio > 0 && isFinite(ratio);
4817
+ }
4818
+
4819
+ if (fit === 'fill') {
4820
+ size = new Size(
4821
+ isScaling(hor) ? rectangle.width : bounds.width,
4822
+ isScaling(ver) ? rectangle.height : bounds.height);
4823
+ } else {
4824
+ var ratios = [hor, ver].filter(isScaling),
4825
+ scale = !ratios.length ? 1
4826
+ : fit === 'cover' ? Math.max.apply(Math, ratios)
4827
+ : Math.min.apply(Math, ratios);
4828
+ size = bounds.getSize().multiply(scale);
4829
+ }
4830
+ this.setBounds(new Rectangle(new Point(), size)
4831
+ .setCenter(rectangle.getCenter()));
4663
4832
  }
4664
4833
  }), {
4665
4834
 
4666
- _setStyles: function(ctx, param, viewMatrix) {
4835
+ _setStyles: function(ctx, param, viewMatrix, strokeMatrix) {
4667
4836
  var style = this._style,
4668
4837
  matrix = this._matrix;
4669
4838
  if (style.hasFill()) {
4670
- ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
4839
+ ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix,
4840
+ strokeMatrix);
4671
4841
  }
4672
4842
  if (style.hasStroke()) {
4673
- ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix);
4843
+ ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx,
4844
+ matrix, strokeMatrix);
4674
4845
  ctx.lineWidth = style.getStrokeWidth();
4675
4846
  var strokeJoin = style.getStrokeJoin(),
4676
4847
  strokeCap = style.getStrokeCap(),
@@ -4715,7 +4886,8 @@ new function() {
4715
4886
  var matrices = param.matrices,
4716
4887
  viewMatrix = param.viewMatrix,
4717
4888
  matrix = this._matrix,
4718
- globalMatrix = matrices[matrices.length - 1].appended(matrix);
4889
+ parentMatrix = matrices[matrices.length - 1],
4890
+ globalMatrix = parentMatrix.appended(matrix);
4719
4891
  if (!globalMatrix.isInvertible())
4720
4892
  return;
4721
4893
 
@@ -4725,13 +4897,14 @@ new function() {
4725
4897
  matrices.push(globalMatrix);
4726
4898
  if (param.updateMatrix) {
4727
4899
  this._globalMatrix = globalMatrix;
4900
+ this._parentGlobalMatrix = this._parent && parentMatrix;
4728
4901
  }
4729
4902
 
4730
4903
  var blendMode = this._blendMode,
4731
4904
  opacity = Numerical.clamp(this._opacity, 0, 1),
4732
4905
  normalBlend = blendMode === 'normal',
4733
4906
  nativeBlend = BlendMode.nativeModes[blendMode],
4734
- direct = normalBlend && opacity === 1
4907
+ direct = normalBlend && opacity === 1 && !this._isolated
4735
4908
  || param.dontStart
4736
4909
  || param.clip
4737
4910
  || (nativeBlend || normalBlend && opacity < 1)
@@ -4739,8 +4912,23 @@ new function() {
4739
4912
  pixelRatio = param.pixelRatio || 1,
4740
4913
  mainCtx, itemOffset, prevOffset;
4741
4914
  if (!direct) {
4742
- var bounds = this.getStrokeBounds(viewMatrix);
4743
- if (!bounds.width || !bounds.height) {
4915
+ var bounds = this.getStrokeBounds(viewMatrix),
4916
+ style = this._style;
4917
+ if (style.getShadowColor(true)) {
4918
+ var mx = viewMatrix._shiftless(),
4919
+ blur = mx.transform(
4920
+ new Point(style.getShadowBlur(true), 0)),
4921
+ offset = mx.transform(style.getShadowOffset(true));
4922
+ bounds = bounds.unite(
4923
+ new Rectangle(bounds.getPoint().add(offset),
4924
+ bounds.getSize()).expand(blur.getLength() * 3));
4925
+ }
4926
+ var canvas = ctx.canvas;
4927
+ bounds = bounds.intersect(
4928
+ new Rectangle(new Point(param.offset),
4929
+ new Size(canvas.width, canvas.height)
4930
+ .divide(pixelRatio)));
4931
+ if (bounds.width <= 0 || bounds.height <= 0) {
4744
4932
  matrices.pop();
4745
4933
  return;
4746
4934
  }
@@ -4766,11 +4954,16 @@ new function() {
4766
4954
  } else if (transform) {
4767
4955
  ctx.translate(-itemOffset.x, -itemOffset.y);
4768
4956
  }
4769
- if (transform) {
4770
- (direct ? matrix : viewMatrix).applyToContext(ctx);
4771
- }
4772
4957
  if (clip) {
4773
- param.clipItem.draw(ctx, param.extend({ clip: true }));
4958
+ (param.viewMatrix ? param.viewMatrix.appended(parentMatrix)
4959
+ : parentMatrix).applyToContext(ctx);
4960
+ param.clipItem.draw(ctx, param.extend({
4961
+ clip: true,
4962
+ matrices: matrices.slice(0, -1)
4963
+ }));
4964
+ matrix.applyToContext(ctx);
4965
+ } else if (transform) {
4966
+ (direct ? matrix : viewMatrix).applyToContext(ctx);
4774
4967
  }
4775
4968
  if (strokeMatrix) {
4776
4969
  ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -4814,7 +5007,7 @@ new function() {
4814
5007
  if (!this._drawSelected)
4815
5008
  itemSelected = false;
4816
5009
  if ((itemSelected || boundsSelected || positionSelected)
4817
- && this._isUpdated(updateVersion)) {
5010
+ && this._isUpdated(updateVersion) && this.isInserted()) {
4818
5011
  var layer,
4819
5012
  color = this.getSelectedColor(true) || (layer = this.getLayer())
4820
5013
  && layer.getSelectedColor(true),
@@ -4927,8 +5120,10 @@ var Group = Item.extend({
4927
5120
  _selectBounds: false,
4928
5121
  _selectChildren: true,
4929
5122
  _serializeFields: {
5123
+ isolated: false,
4930
5124
  children: []
4931
5125
  },
5126
+ _isolated: false,
4932
5127
 
4933
5128
  initialize: function Group(arg) {
4934
5129
  this._children = [];
@@ -4970,6 +5165,15 @@ var Group = Item.extend({
4970
5165
  child.setClipMask(clipped);
4971
5166
  },
4972
5167
 
5168
+ isIsolated: function() {
5169
+ return this._isolated;
5170
+ },
5171
+
5172
+ setIsolated: function(isolated) {
5173
+ this._isolated = !!isolated;
5174
+ this._changed(257);
5175
+ },
5176
+
4973
5177
  _getBounds: function _getBounds(matrix, options) {
4974
5178
  var clipItem = this._getClipItem();
4975
5179
  return clipItem
@@ -4985,10 +5189,21 @@ var Group = Item.extend({
4985
5189
  clipItem);
4986
5190
  },
4987
5191
 
5192
+ _hitTestSelf: function(point, options, viewMatrix) {
5193
+ if (options.match || options.class || options.type || options.all) {
5194
+ var res = this._hitTestChildren(point, options.extend({
5195
+ all: null, match: null, class: null, type: null
5196
+ }), viewMatrix);
5197
+ if (res)
5198
+ res.item = this;
5199
+ return res;
5200
+ }
5201
+ },
5202
+
4988
5203
  _draw: function(ctx, param) {
4989
5204
  var clip = param.clip,
4990
- clipItem = !clip && this._getClipItem();
4991
- param = param.extend({ clipItem: clipItem, clip: false });
5205
+ clipItem = this._getClipItem();
5206
+ param = param.extend({ clipItem: !clip && clipItem, clip: false });
4992
5207
  if (clip) {
4993
5208
  ctx.beginPath();
4994
5209
  param.dontStart = param.dontFinish = true;
@@ -5037,6 +5252,7 @@ var Shape = Item.extend({
5037
5252
  size: null,
5038
5253
  radius: null
5039
5254
  },
5255
+ _prioritize: ['type'],
5040
5256
 
5041
5257
  initialize: function Shape(props, point) {
5042
5258
  this._initialize(props, point);
@@ -5134,16 +5350,10 @@ var Shape = Item.extend({
5134
5350
  },
5135
5351
 
5136
5352
  toPath: function(insert) {
5137
- var path = new Path[Base.capitalize(this._type)]({
5138
- center: new Point(),
5139
- size: this._size,
5140
- radius: this._radius,
5141
- insert: false
5142
- });
5143
- path.copyAttributes(this);
5353
+ var path = this._asPathItem();
5144
5354
  if (paper.settings.applyMatrix)
5145
5355
  path.setApplyMatrix(true);
5146
- if (insert === undefined || insert)
5356
+ if (Base.pick(insert, paper.settings.insertItems))
5147
5357
  path.insertAbove(this);
5148
5358
  return path;
5149
5359
  },
@@ -5151,7 +5361,14 @@ var Shape = Item.extend({
5151
5361
  toShape: '#clone',
5152
5362
 
5153
5363
  _asPathItem: function() {
5154
- return this.toPath(false);
5364
+ var path = new Path[Base.capitalize(this._type)]({
5365
+ center: new Point(),
5366
+ size: this._size,
5367
+ radius: this._radius,
5368
+ insert: false
5369
+ });
5370
+ path.copyAttributes(this);
5371
+ return path;
5155
5372
  },
5156
5373
 
5157
5374
  _draw: function(ctx, param, viewMatrix, strokeMatrix) {
@@ -5218,7 +5435,7 @@ var Shape = Item.extend({
5218
5435
  ctx.closePath();
5219
5436
  }
5220
5437
  if (!dontPaint && (hasFill || hasStroke)) {
5221
- this._setStyles(ctx, param, viewMatrix);
5438
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
5222
5439
  if (hasFill) {
5223
5440
  ctx.fill(style.getFillRule());
5224
5441
  ctx.shadowColor = 'rgba(0,0,0,0)';
@@ -5233,12 +5450,25 @@ var Shape = Item.extend({
5233
5450
  },
5234
5451
 
5235
5452
  _getBounds: function(matrix, options) {
5236
- var rect = new Rectangle(this._size).setCenter(0, 0),
5453
+ var size = this._size,
5454
+ rect = new Rectangle(size).setCenter(0, 0),
5237
5455
  style = this._style,
5238
5456
  strokeWidth = options.stroke && style.hasStroke()
5239
5457
  && style.getStrokeWidth();
5240
- if (matrix)
5241
- rect = matrix._transformBounds(rect);
5458
+ if (matrix) {
5459
+ if (this._type === 'rectangle') {
5460
+ rect = matrix._transformBounds(rect);
5461
+ } else {
5462
+ var rx = size.width / 2,
5463
+ ry = size.height / 2,
5464
+ dx = Math.sqrt(Math.pow(matrix._a * rx, 2)
5465
+ + Math.pow(matrix._c * ry, 2)),
5466
+ dy = Math.sqrt(Math.pow(matrix._b * rx, 2)
5467
+ + Math.pow(matrix._d * ry, 2));
5468
+ rect = new Rectangle(matrix._tx - dx, matrix._ty - dy,
5469
+ 2 * dx, 2 * dy);
5470
+ }
5471
+ }
5242
5472
  return strokeWidth
5243
5473
  ? rect.expand(Path._getStrokePadding(strokeWidth,
5244
5474
  this._getStrokeMatrix(matrix, options)))
@@ -5288,7 +5518,7 @@ new function() {
5288
5518
  var hit = false,
5289
5519
  style = this._style,
5290
5520
  hitStroke = options.stroke && style.hasStroke(),
5291
- hitFill = options.fill && style.hasFill();
5521
+ hitFill = options.fill && !!style.getFillColor();
5292
5522
  if (hitStroke || hitFill) {
5293
5523
  var type = this._type,
5294
5524
  radius = this._radius,
@@ -5378,6 +5608,7 @@ var Raster = Item.extend({
5378
5608
  _boundsOptions: { stroke: false, handle: false },
5379
5609
  _serializeFields: {
5380
5610
  crossOrigin: null,
5611
+ size: new Size(),
5381
5612
  source: null
5382
5613
  },
5383
5614
  _prioritize: ['crossOrigin'],
@@ -5527,6 +5758,14 @@ var Raster = Item.extend({
5527
5758
  }
5528
5759
  },
5529
5760
 
5761
+ set: function set(props, exclude) {
5762
+ if (props && props.source && props.size) {
5763
+ this._size = new Size(props.size);
5764
+ exclude = Base.set({ size: true }, exclude);
5765
+ }
5766
+ return set.base.call(this, props, exclude);
5767
+ },
5768
+
5530
5769
  _setImage: function(image) {
5531
5770
  if (this._canvas)
5532
5771
  CanvasProvider.release(this._canvas);
@@ -5539,9 +5778,11 @@ var Raster = Item.extend({
5539
5778
  this._canvas = null;
5540
5779
  this._loaded = !!(image && image.src && image.complete);
5541
5780
  }
5542
- this._size = new Size(
5543
- image ? image.naturalWidth || image.width : 0,
5544
- image ? image.naturalHeight || image.height : 0);
5781
+ this._size = image && !this._loaded
5782
+ ? this._size || new Size()
5783
+ : new Size(
5784
+ image ? image.naturalWidth || image.width : 0,
5785
+ image ? image.naturalHeight || image.height : 0);
5545
5786
  this._context = null;
5546
5787
  this._changed(1033);
5547
5788
  },
@@ -5780,7 +6021,12 @@ var Raster = Item.extend({
5780
6021
 
5781
6022
  _draw: function(ctx, param, viewMatrix) {
5782
6023
  var element = this.getElement();
5783
- if (element && element.width > 0 && element.height > 0) {
6024
+ if (param.dontFinish || param.clip) {
6025
+ var rect = this.getInternalBounds();
6026
+ if (!param.dontStart)
6027
+ ctx.beginPath();
6028
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
6029
+ } else if (element && element.width > 0 && element.height > 0) {
5784
6030
  ctx.globalAlpha = Numerical.clamp(this._opacity, 0, 1);
5785
6031
 
5786
6032
  this._setStyles(ctx, param, viewMatrix);
@@ -5849,7 +6095,9 @@ var SymbolItem = Item.extend({
5849
6095
  },
5850
6096
 
5851
6097
  _hitTestSelf: function(point, options, viewMatrix) {
5852
- var opts = options.extend({ all: false });
6098
+ var opts = options.extend({
6099
+ all: false, match: null, class: null, type: null
6100
+ });
5853
6101
  var res = this._definition._item._hitTest(point, opts, viewMatrix);
5854
6102
  if (res)
5855
6103
  res.item = this;
@@ -5857,7 +6105,7 @@ var SymbolItem = Item.extend({
5857
6105
  },
5858
6106
 
5859
6107
  _draw: function(ctx, param) {
5860
- this._definition._item.draw(ctx, param);
6108
+ this._definition._item.draw(ctx, param.extend({ updateMatrix: false }));
5861
6109
  }
5862
6110
 
5863
6111
  });
@@ -6889,7 +7137,7 @@ statics: {
6889
7137
  }
6890
7138
  }
6891
7139
  }}, Base.each(
6892
- ['getBounds', 'getStrokeBounds', 'getHandleBounds'],
7140
+ ['getBounds', 'getStrokeBounds'],
6893
7141
  function(name) {
6894
7142
  this[name] = function() {
6895
7143
  if (!this._bounds)
@@ -6904,6 +7152,15 @@ statics: {
6904
7152
  },
6905
7153
  {
6906
7154
 
7155
+ getHandleBounds: function() {
7156
+ var v = this.getValues(),
7157
+ x1 = Math.min(v[0], v[2], v[4], v[6]),
7158
+ y1 = Math.min(v[1], v[3], v[5], v[7]),
7159
+ x2 = Math.max(v[0], v[2], v[4], v[6]),
7160
+ y2 = Math.max(v[1], v[3], v[5], v[7]);
7161
+ return new Rectangle(x1, y1, x2 - x1, y2 - y1);
7162
+ },
7163
+
6907
7164
  }), Base.each({
6908
7165
  isStraight: function(p1, h1, h2, p2) {
6909
7166
  if (h1.isZero() && h2.isZero()) {
@@ -7696,8 +7953,8 @@ new function() {
7696
7953
  den = 2 * ax * ty - 2 * ay * tx,
7697
7954
  times = [];
7698
7955
  if (Math.abs(den) < Numerical.CURVETIME_EPSILON) {
7699
- var num = ax * cy - ay * cx,
7700
- den = ax * by - ay * bx;
7956
+ var num = cx * ty - cy * tx,
7957
+ den = bx * ty - by * tx;
7701
7958
  if (den != 0) {
7702
7959
  var t = -num / den;
7703
7960
  if (t >= 0 && t <= 1) times.push(t);
@@ -8100,6 +8357,15 @@ var PathItem = Item.extend({
8100
8357
  initialize: function PathItem() {
8101
8358
  },
8102
8359
 
8360
+ _scaleStroke: function(matrix) {
8361
+ var style = this._style;
8362
+ if (this._project._scope.settings.scaleStrokes
8363
+ && style.getStrokeScaling()) {
8364
+ style.setStrokeWidth(style.getStrokeWidth()
8365
+ * matrix._getMeanScale());
8366
+ }
8367
+ },
8368
+
8103
8369
  statics: {
8104
8370
  create: function(arg) {
8105
8371
  var data,
@@ -8146,7 +8412,12 @@ var PathItem = Item.extend({
8146
8412
  previous,
8147
8413
  control,
8148
8414
  current = new Point(),
8149
- start = new Point();
8415
+ start = new Point(),
8416
+ number = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][+-]?\\d+)?)',
8417
+ numbers = new RegExp(number, 'g'),
8418
+ flag = '([01])',
8419
+ arcs = new RegExp([number, number, number, flag, flag, number,
8420
+ number].join('[\\s,]*'), 'g');
8150
8421
 
8151
8422
  function getCoord(index, coord) {
8152
8423
  var val = +coords[index];
@@ -8168,7 +8439,7 @@ var PathItem = Item.extend({
8168
8439
  var part = parts[i],
8169
8440
  command = part[0],
8170
8441
  lower = command.toLowerCase();
8171
- coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
8442
+ coords = part.match(numbers);
8172
8443
  var length = coords && coords.length;
8173
8444
  relative = command === lower;
8174
8445
  if (previous === 'z' && !/[mz]/.test(lower))
@@ -8233,10 +8504,10 @@ var PathItem = Item.extend({
8233
8504
  }
8234
8505
  break;
8235
8506
  case 'a':
8236
- for (var j = 0; j < length; j += 7) {
8237
- this.arcTo(current = getPoint(j + 5),
8238
- new Size(+coords[j], +coords[j + 1]),
8239
- +coords[j + 2], +coords[j + 4], +coords[j + 3]);
8507
+ while (coords = arcs.exec(part)) {
8508
+ this.arcTo(current = getPoint(6),
8509
+ new Size(+coords[1], +coords[2]),
8510
+ +coords[3], +coords[5], +coords[4]);
8240
8511
  }
8241
8512
  break;
8242
8513
  case 'z':
@@ -8563,6 +8834,7 @@ var Path = PathItem.extend({
8563
8834
  coords = new Array(6);
8564
8835
  for (var i = 0, l = segments.length; i < l; i++)
8565
8836
  segments[i]._transformCoordinates(matrix, coords, true);
8837
+ this._scaleStroke(matrix);
8566
8838
  return true;
8567
8839
  },
8568
8840
 
@@ -8670,6 +8942,9 @@ var Path = PathItem.extend({
8670
8942
  removeSegments: function(start, end, _includeCurves) {
8671
8943
  start = start || 0;
8672
8944
  end = Base.pick(end, this._segments.length);
8945
+ if (start > end && this._closed)
8946
+ return this.removeSegments(start).concat(
8947
+ this.removeSegments(0, end));
8673
8948
  var segments = this._segments,
8674
8949
  curves = this._curves,
8675
8950
  count = segments.length,
@@ -8798,7 +9073,7 @@ var Path = PathItem.extend({
8798
9073
  time = 0;
8799
9074
  }
8800
9075
  var curves = this.getCurves();
8801
- if (index >= 0 && index < curves.length) {
9076
+ if (loc && index < curves.length) {
8802
9077
  if (time >= tMin) {
8803
9078
  curves[index++].divideAtTime(time);
8804
9079
  }
@@ -9110,7 +9385,7 @@ var Path = PathItem.extend({
9110
9385
  shape.copyAttributes(this, true);
9111
9386
  shape._matrix.prepend(this._matrix);
9112
9387
  shape.rotate(topCenter.subtract(center).getAngle() + 90);
9113
- if (insert === undefined || insert)
9388
+ if (Base.pick(insert, paper.settings.insertItems))
9114
9389
  shape.insertAbove(this);
9115
9390
  return shape;
9116
9391
  }
@@ -9190,8 +9465,9 @@ var Path = PathItem.extend({
9190
9465
  strokePadding = tolerancePadding,
9191
9466
  join, cap, miterLimit,
9192
9467
  area, loc, res,
9468
+ hits = options.all && [],
9193
9469
  hitStroke = options.stroke && style.hasStroke(),
9194
- hitFill = options.fill && style.hasFill(),
9470
+ hitFill = options.fill && !!style.getFillColor(),
9195
9471
  hitCurves = options.curves,
9196
9472
  strokeRadius = hitStroke
9197
9473
  ? style.getStrokeWidth() / 2
@@ -9219,10 +9495,13 @@ var Path = PathItem.extend({
9219
9495
  if (pt !== anchor)
9220
9496
  pt = pt.add(anchor);
9221
9497
  if (isCloseEnough(pt, strokePadding)) {
9222
- return new HitResult(name, that, {
9498
+ var hit = new HitResult(name, that, {
9223
9499
  segment: seg,
9224
9500
  point: pt
9225
9501
  });
9502
+ if (!hits)
9503
+ return hit;
9504
+ hits.push(hit);
9226
9505
  }
9227
9506
  }
9228
9507
  }
@@ -9251,7 +9530,7 @@ var Path = PathItem.extend({
9251
9530
  Path._addBevelJoin(segment, join, strokeRadius,
9252
9531
  miterLimit, null, strokeMatrix, addToArea, true);
9253
9532
  }
9254
- } else if (cap === 'square') {
9533
+ } else {
9255
9534
  Path._addSquareCap(segment, cap, strokeRadius, null,
9256
9535
  strokeMatrix, addToArea, true);
9257
9536
  }
@@ -9296,7 +9575,7 @@ var Path = PathItem.extend({
9296
9575
  }
9297
9576
  }
9298
9577
  }
9299
- return !loc && hitFill && this._contains(point)
9578
+ res = !loc && hitFill && this._contains(point)
9300
9579
  || loc && !hitStroke && !hitCurves
9301
9580
  ? new HitResult('fill', this)
9302
9581
  : loc
@@ -9305,6 +9584,16 @@ var Path = PathItem.extend({
9305
9584
  point: loc.getPoint()
9306
9585
  })
9307
9586
  : null;
9587
+ if (hits) {
9588
+ hits.sort(function(hit1, hit2) {
9589
+ return hit1.point.getDistance(point)
9590
+ - hit2.point.getDistance(point);
9591
+ });
9592
+ if (res)
9593
+ hits.push(res);
9594
+ return hits;
9595
+ }
9596
+ return res;
9308
9597
  }
9309
9598
 
9310
9599
  }, Base.each(Curve._evaluateMethods,
@@ -9504,7 +9793,7 @@ new function() {
9504
9793
  }
9505
9794
 
9506
9795
  if (!dontPaint && (hasFill || hasStroke)) {
9507
- this._setStyles(ctx, param, viewMatrix);
9796
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
9508
9797
  if (hasFill) {
9509
9798
  ctx.fill(style.getFillRule());
9510
9799
  ctx.shadowColor = 'rgba(0,0,0,0)';
@@ -9884,18 +10173,12 @@ statics: {
9884
10173
  _getStrokePadding: function(radius, matrix) {
9885
10174
  if (!matrix)
9886
10175
  return [radius, radius];
9887
- var hor = new Point(radius, 0).transform(matrix),
9888
- ver = new Point(0, radius).transform(matrix),
9889
- phi = hor.getAngleInRadians(),
9890
- a = hor.getLength(),
9891
- b = ver.getLength();
9892
- var sin = Math.sin(phi),
9893
- cos = Math.cos(phi),
9894
- tan = Math.tan(phi),
9895
- tx = Math.atan2(b * tan, a),
9896
- ty = Math.atan2(b, tan * a);
9897
- return [Math.abs(a * Math.cos(tx) * cos + b * Math.sin(tx) * sin),
9898
- Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
10176
+ var a = matrix._a,
10177
+ b = matrix._b,
10178
+ c = matrix._c,
10179
+ d = matrix._d;
10180
+ return [radius * Math.sqrt(a * a + c * c),
10181
+ radius * Math.sqrt(b * b + d * d)];
9899
10182
  },
9900
10183
 
9901
10184
  _addBevelJoin: function(segment, join, radius, miterLimit, matrix,
@@ -9903,44 +10186,42 @@ statics: {
9903
10186
  var curve2 = segment.getCurve(),
9904
10187
  curve1 = curve2.getPrevious(),
9905
10188
  point = curve2.getPoint1().transform(matrix),
9906
- normal1 = curve1.getNormalAtTime(1).multiply(radius)
9907
- .transform(strokeMatrix),
9908
- normal2 = curve2.getNormalAtTime(0).multiply(radius)
9909
- .transform(strokeMatrix),
9910
- angle = normal1.getDirectedAngle(normal2);
10189
+ normal1 = curve1.getNormalAtTime(1).multiply(radius),
10190
+ normal2 = curve2.getNormalAtTime(0).multiply(radius),
10191
+ angle = normal1.getDirectedAngle(normal2);
9911
10192
  if (angle < 0 || angle >= 180) {
9912
10193
  normal1 = normal1.negate();
9913
10194
  normal2 = normal2.negate();
9914
10195
  }
9915
10196
  if (isArea)
9916
10197
  addPoint(point);
9917
- addPoint(point.add(normal1));
10198
+ addPoint(point.add(normal1.transform(strokeMatrix)));
9918
10199
  if (join === 'miter') {
9919
- var corner = new Line(point.add(normal1),
10200
+ var corner = new Line(normal1,
9920
10201
  new Point(-normal1.y, normal1.x), true
9921
- ).intersect(new Line(point.add(normal2),
10202
+ ).intersect(new Line(normal2,
9922
10203
  new Point(-normal2.y, normal2.x), true
9923
10204
  ), true);
9924
- if (corner && point.getDistance(corner) <= miterLimit * radius) {
9925
- addPoint(corner);
10205
+ if (corner && corner.getLength() <= miterLimit * radius) {
10206
+ addPoint(point.add(corner.transform(strokeMatrix)));
9926
10207
  }
9927
10208
  }
9928
- addPoint(point.add(normal2));
10209
+ addPoint(point.add(normal2.transform(strokeMatrix)));
9929
10210
  },
9930
10211
 
9931
10212
  _addSquareCap: function(segment, cap, radius, matrix, strokeMatrix,
9932
10213
  addPoint, isArea) {
9933
10214
  var point = segment._point.transform(matrix),
9934
10215
  loc = segment.getLocation(),
9935
- normal = loc.getNormal()
9936
- .multiply(loc.getTime() === 0 ? radius : -radius)
9937
- .transform(strokeMatrix);
10216
+ vector = loc.getNormal()
10217
+ .multiply(loc.getTime() === 0 ? radius : -radius),
10218
+ normal = vector.transform(strokeMatrix);
9938
10219
  if (cap === 'square') {
9939
10220
  if (isArea) {
9940
10221
  addPoint(point.subtract(normal));
9941
10222
  addPoint(point.add(normal));
9942
10223
  }
9943
- point = point.add(normal.rotate(-90));
10224
+ point = point.add(vector.rotate(-90).transform(strokeMatrix));
9944
10225
  }
9945
10226
  addPoint(point.add(normal));
9946
10227
  addPoint(point.subtract(normal));
@@ -10002,14 +10283,14 @@ Path.inject({ statics: new function() {
10002
10283
 
10003
10284
  function createPath(segments, closed, args) {
10004
10285
  var props = Base.getNamed(args),
10005
- path = new Path(props && (
10006
- props.insert == true ? Item.INSERT
10007
- : props.insert == false ? Item.NO_INSERT
10008
- : null
10009
- ));
10286
+ path = new Path(props && {
10287
+ insert: props.insert,
10288
+ project: props.project,
10289
+ parent: props.parent
10290
+ });
10010
10291
  path._add(segments);
10011
10292
  path._closed = closed;
10012
- return path.set(props, Item.INSERT);
10293
+ return path.set(props, { insert: true, project: true, parent: true });
10013
10294
  }
10014
10295
 
10015
10296
  function createEllipse(center, radius, args) {
@@ -10104,10 +10385,9 @@ Path.inject({ statics: new function() {
10104
10385
  center = Point.readNamed(args, 'center'),
10105
10386
  sides = Base.readNamed(args, 'sides'),
10106
10387
  radius = Base.readNamed(args, 'radius'),
10107
- step = 360 / sides,
10108
- three = sides % 3 === 0,
10109
- vector = new Point(0, three ? -radius : radius),
10110
- offset = three ? -1 : 0.5,
10388
+ step = -360 / sides,
10389
+ offset = sides % 2 ? 0 : -0.5,
10390
+ vector = new Point(0, -radius),
10111
10391
  segments = new Array(sides);
10112
10392
  for (var i = 0; i < sides; i++)
10113
10393
  segments[i] = new Segment(center.add(
@@ -10151,6 +10431,11 @@ var CompoundPath = PathItem.extend({
10151
10431
  }
10152
10432
  },
10153
10433
 
10434
+ _transformContent: function _transformContent(matrix) {
10435
+ this._scaleStroke(matrix);
10436
+ return _transformContent.base.apply(this, arguments);
10437
+ },
10438
+
10154
10439
  insertChildren: function insertChildren(index, items) {
10155
10440
  var list = items,
10156
10441
  first = list[0];
@@ -10278,7 +10563,7 @@ var CompoundPath = PathItem.extend({
10278
10563
  children[i].draw(ctx, param, strokeMatrix);
10279
10564
 
10280
10565
  if (!param.clip) {
10281
- this._setStyles(ctx, param, viewMatrix);
10566
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
10282
10567
  var style = this._style;
10283
10568
  if (style.hasFill()) {
10284
10569
  ctx.fill(style.getFillRule());
@@ -10391,7 +10676,7 @@ PathItem.inject(new function() {
10391
10676
  var result = new CompoundPath(Item.NO_INSERT);
10392
10677
  result.addChildren(paths, true);
10393
10678
  result = result.reduce({ simplify: simplify });
10394
- if (!(options && options.insert == false)) {
10679
+ if (Base.pick(options && options.insert, paper.settings.insertItems)) {
10395
10680
  result.insertAbove(path2 && path1.isSibling(path2)
10396
10681
  && path1.getIndex() < path2.getIndex() ? path2 : path1);
10397
10682
  }
@@ -10406,7 +10691,7 @@ PathItem.inject(new function() {
10406
10691
  function traceBoolean(path1, path2, operation, options) {
10407
10692
  if (options && (options.trace == false || options.stroke) &&
10408
10693
  /^(subtract|intersect)$/.test(operation))
10409
- return splitBoolean(path1, path2, operation);
10694
+ return splitBoolean(path1, path2, operation, options);
10410
10695
  var _path1 = preparePath(path1, true),
10411
10696
  _path2 = path2 && path1 !== path2 && preparePath(path2, true),
10412
10697
  operator = operators[operation];
@@ -10486,7 +10771,7 @@ PathItem.inject(new function() {
10486
10771
  return createResult(paths, true, path1, path2, options);
10487
10772
  }
10488
10773
 
10489
- function splitBoolean(path1, path2, operation) {
10774
+ function splitBoolean(path1, path2, operation, options) {
10490
10775
  var _path1 = preparePath(path1),
10491
10776
  _path2 = preparePath(path2),
10492
10777
  crossings = _path1.getIntersections(_path2, filterIntersection),
@@ -10513,7 +10798,7 @@ PathItem.inject(new function() {
10513
10798
  }
10514
10799
  }
10515
10800
  addPath(_path1);
10516
- return createResult(paths, false, path1, path2);
10801
+ return createResult(paths, false, path1, path2, options);
10517
10802
  }
10518
10803
 
10519
10804
  function linkIntersections(from, to) {
@@ -11100,7 +11385,7 @@ PathItem.inject(new function() {
11100
11385
 
11101
11386
  divide: function(path, options) {
11102
11387
  return options && (options.trace == false || options.stroke)
11103
- ? splitBoolean(this, path, 'divide')
11388
+ ? splitBoolean(this, path, 'divide', options)
11104
11389
  : createResult([
11105
11390
  this.subtract(path, options),
11106
11391
  this.intersect(path, options)
@@ -11568,7 +11853,7 @@ var TextItem = Item.extend({
11568
11853
  _serializeFields: {
11569
11854
  content: null
11570
11855
  },
11571
- _boundsOptions: { stroke: false, handle: false },
11856
+ _boundsOptions: { handle: false },
11572
11857
 
11573
11858
  initialize: function TextItem(arg) {
11574
11859
  this._content = '';
@@ -11625,27 +11910,43 @@ var PointText = TextItem.extend({
11625
11910
  },
11626
11911
 
11627
11912
  _draw: function(ctx, param, viewMatrix) {
11628
- if (!this._content)
11629
- return;
11630
- this._setStyles(ctx, param, viewMatrix);
11631
- var lines = this._lines,
11632
- style = this._style,
11633
- hasFill = style.hasFill(),
11634
- hasStroke = style.hasStroke(),
11635
- leading = style.getLeading(),
11636
- shadowColor = ctx.shadowColor;
11637
- ctx.font = style.getFontStyle();
11638
- ctx.textAlign = style.getJustification();
11639
- for (var i = 0, l = lines.length; i < l; i++) {
11640
- ctx.shadowColor = shadowColor;
11641
- var line = lines[i];
11642
- if (hasFill) {
11643
- ctx.fillText(line, 0, 0);
11644
- ctx.shadowColor = 'rgba(0,0,0,0)';
11913
+ if (param.dontFinish || param.clip) {
11914
+ var rect = this.getInternalBounds();
11915
+ if (!param.dontStart)
11916
+ ctx.beginPath();
11917
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
11918
+ } else if (this._content) {
11919
+ this._setStyles(ctx, param, viewMatrix);
11920
+ var lines = this._lines,
11921
+ style = this._style,
11922
+ hasFill = style.hasFill(),
11923
+ hasStroke = style.hasStroke(),
11924
+ leading = style.getLeading(),
11925
+ shadowColor = ctx.shadowColor;
11926
+ if (hasStroke && !style.getStrokeScaling()) {
11927
+ var scale = viewMatrix._getMeanScale();
11928
+ ctx.lineWidth /= scale;
11929
+ if ('setLineDash' in ctx) {
11930
+ ctx.setLineDash(ctx.getLineDash().map(function(dash) {
11931
+ return dash / scale;
11932
+ }));
11933
+ ctx.lineDashOffset /= scale;
11934
+ }
11935
+ }
11936
+ ctx.font = style.getFontStyle();
11937
+ ctx.textAlign = style.getJustification();
11938
+ ctx.letterSpacing = style.getTrackingStyle();
11939
+ for (var i = 0, l = lines.length; i < l; i++) {
11940
+ ctx.shadowColor = shadowColor;
11941
+ var line = lines[i];
11942
+ if (hasFill) {
11943
+ ctx.fillText(line, 0, 0);
11944
+ ctx.shadowColor = 'rgba(0,0,0,0)';
11945
+ }
11946
+ if (hasStroke)
11947
+ ctx.strokeText(line, 0, 0);
11948
+ ctx.translate(0, leading);
11645
11949
  }
11646
- if (hasStroke)
11647
- ctx.strokeText(line, 0, 0);
11648
- ctx.translate(0, leading);
11649
11950
  }
11650
11951
  },
11651
11952
 
@@ -11655,14 +11956,28 @@ var PointText = TextItem.extend({
11655
11956
  numLines = lines.length,
11656
11957
  justification = style.getJustification(),
11657
11958
  leading = style.getLeading(),
11658
- width = this.getView().getTextWidth(style.getFontStyle(), lines),
11959
+ width = this.getView().getTextWidth(style.getFontStyle(), lines,
11960
+ style.getTrackingStyle()),
11659
11961
  x = 0;
11660
11962
  if (justification !== 'left')
11661
11963
  x -= width / (justification === 'center' ? 2: 1);
11662
11964
  var rect = new Rectangle(x,
11663
11965
  numLines ? - 0.75 * leading : 0,
11664
11966
  width, numLines * leading);
11967
+ if (options.stroke && style.hasStroke())
11968
+ rect = rect.expand(style.getStrokeWidth());
11665
11969
  return matrix ? matrix._transformBounds(rect, rect) : rect;
11970
+ },
11971
+
11972
+ _hitTestSelf: function(point, options) {
11973
+ if (options.fill && this.getFillColor()) {
11974
+ var bounds = this.getInternalBounds(),
11975
+ closest = point.clamp(bounds.getTopLeft(true),
11976
+ bounds.getBottomRight(true));
11977
+ if (point.subtract(closest).divide(options._tolerancePadding)
11978
+ .getLength() <= 1)
11979
+ return new HitResult('fill', this);
11980
+ }
11666
11981
  }
11667
11982
  });
11668
11983
 
@@ -11723,13 +12038,14 @@ var Color = Base.extend(new function() {
11723
12038
  } else {
11724
12039
  var color = namedColors[string];
11725
12040
  if (!color) {
11726
- if (window) {
11727
- if (!colorCtx) {
11728
- colorCtx = CanvasProvider.getContext(1, 1, {
11729
- willReadFrequently: true
11730
- });
12041
+ if (!colorCtx) {
12042
+ colorCtx = CanvasProvider.getContext(1, 1, {
12043
+ willReadFrequently: true
12044
+ });
12045
+ if (colorCtx)
11731
12046
  colorCtx.globalCompositeOperation = 'copy';
11732
- }
12047
+ }
12048
+ if (colorCtx) {
11733
12049
  colorCtx.fillStyle = 'rgba(0,0,0,0)';
11734
12050
  colorCtx.fillStyle = string;
11735
12051
  colorCtx.fillRect(0, 0, 1, 1);
@@ -12136,8 +12452,9 @@ var Color = Base.extend(new function() {
12136
12452
  + components.join(',') + ')';
12137
12453
  },
12138
12454
 
12139
- toCanvasStyle: function(ctx, matrix) {
12140
- if (this._canvasStyle)
12455
+ toCanvasStyle: function(ctx, matrix, strokeMatrix) {
12456
+ var isCacheable = !(this._type === 'gradient' && strokeMatrix);
12457
+ if (this._canvasStyle && isCacheable)
12141
12458
  return this._canvasStyle;
12142
12459
  if (this._type !== 'gradient')
12143
12460
  return this._canvasStyle = this.toCSS();
@@ -12155,6 +12472,12 @@ var Color = Base.extend(new function() {
12155
12472
  if (highlight)
12156
12473
  highlight = inverse._transformPoint(highlight);
12157
12474
  }
12475
+ if (strokeMatrix) {
12476
+ origin = strokeMatrix._transformPoint(origin);
12477
+ destination = strokeMatrix._transformPoint(destination);
12478
+ if (highlight)
12479
+ highlight = strokeMatrix._transformPoint(highlight);
12480
+ }
12158
12481
  if (gradient._radial) {
12159
12482
  var radius = destination.getDistance(origin);
12160
12483
  if (highlight) {
@@ -12176,7 +12499,9 @@ var Color = Base.extend(new function() {
12176
12499
  offset == null ? i / (l - 1) : offset,
12177
12500
  stop._color.toCanvasStyle());
12178
12501
  }
12179
- return this._canvasStyle = canvasGradient;
12502
+ if (isCacheable)
12503
+ this._canvasStyle = canvasGradient;
12504
+ return canvasGradient;
12180
12505
  },
12181
12506
 
12182
12507
  transform: function(matrix) {
@@ -12440,6 +12765,7 @@ var Style = Base.extend(new function() {
12440
12765
  fontWeight: 'normal',
12441
12766
  fontSize: 12,
12442
12767
  leading: null,
12768
+ tracking: 0,
12443
12769
  justification: 'left'
12444
12770
  }),
12445
12771
  textDefaults = Base.set({}, groupDefaults, {
@@ -12456,6 +12782,7 @@ var Style = Base.extend(new function() {
12456
12782
  fontSize: 9,
12457
12783
  font: 9,
12458
12784
  leading: 9,
12785
+ tracking: 9,
12459
12786
  justification: 9
12460
12787
  },
12461
12788
  item = {
@@ -12500,7 +12827,7 @@ var Style = Base.extend(new function() {
12500
12827
  var old = this._values[key];
12501
12828
  if (old !== value) {
12502
12829
  if (isColor) {
12503
- if (old) {
12830
+ if (old && old.constructor === Color) {
12504
12831
  Color._setOwner(old, null);
12505
12832
  old._canvasStyle = null;
12506
12833
  }
@@ -12646,6 +12973,10 @@ var Style = Base.extend(new function() {
12646
12973
  + this.getFontFamily();
12647
12974
  },
12648
12975
 
12976
+ getTrackingStyle: function() {
12977
+ return this.getTracking() / 1000 + 'em';
12978
+ },
12979
+
12649
12980
  getFont: '#getFontFamily',
12650
12981
  setFont: '#setFontFamily',
12651
12982
 
@@ -12722,6 +13053,18 @@ var DomElement = new function() {
12722
13053
  return DomElement.getBounds(el, true).getSize();
12723
13054
  },
12724
13055
 
13056
+ getInsets: function(el, start) {
13057
+ var style = DomElement.getStyles(el);
13058
+ function get(side) {
13059
+ return parseFloat(style['padding' + side])
13060
+ + parseFloat(style['border' + side + 'Width']);
13061
+ }
13062
+ return start
13063
+ ? new Size(get('Left'), get('Top'))
13064
+ : new Size(get('Left') + get('Right'),
13065
+ get('Top') + get('Bottom'));
13066
+ },
13067
+
12725
13068
  isInvisible: function(el) {
12726
13069
  return DomElement.getSize(el).equals(new Size(0, 0));
12727
13070
  },
@@ -12780,12 +13123,20 @@ var DomEvent = {
12780
13123
  }
12781
13124
  },
12782
13125
 
12783
- getPoint: function(event) {
12784
- var pos = event.targetTouches
12785
- ? event.targetTouches.length
12786
- ? event.targetTouches[0]
12787
- : event.changedTouches[0]
12788
- : event;
13126
+ getPoint: function(event, target) {
13127
+ function find(touches) {
13128
+ for (var i = 0, l = touches.length; i < l; i++) {
13129
+ if (touches[i].target === target)
13130
+ return touches[i];
13131
+ }
13132
+ }
13133
+
13134
+ var touches = event.targetTouches,
13135
+ pos = event;
13136
+ if (touches) {
13137
+ pos = find(event.touches) || find(event.changedTouches)
13138
+ || (touches.length ? touches[0] : event.changedTouches[0]);
13139
+ }
12789
13140
  return new Point(
12790
13141
  pos.pageX || pos.clientX + document.documentElement.scrollLeft,
12791
13142
  pos.pageY || pos.clientY + document.documentElement.scrollTop
@@ -12801,8 +13152,9 @@ var DomEvent = {
12801
13152
  },
12802
13153
 
12803
13154
  getOffset: function(event, target) {
12804
- return DomEvent.getPoint(event).subtract(DomElement.getOffset(
12805
- target || DomEvent.getTarget(event)));
13155
+ target = target || DomEvent.getTarget(event);
13156
+ return DomEvent.getPoint(event, target).subtract(
13157
+ DomElement.getOffset(target));
12806
13158
  }
12807
13159
  };
12808
13160
 
@@ -12818,8 +13170,12 @@ DomEvent.requestAnimationFrame = new function() {
12818
13170
  for (var i = 0, l = functions.length; i < l; i++)
12819
13171
  functions[i]();
12820
13172
  requested = nativeRequest && callbacks.length;
12821
- if (requested)
13173
+ if (requested) {
12822
13174
  nativeRequest(handleCallbacks);
13175
+ } else if (timer && !callbacks.length) {
13176
+ clearInterval(timer);
13177
+ timer = null;
13178
+ }
12823
13179
  }
12824
13180
 
12825
13181
  return function(callback) {
@@ -12845,10 +13201,10 @@ var View = Base.extend(Emitter, {
12845
13201
  }
12846
13202
 
12847
13203
  function getCanvasSize() {
12848
- var size = DomElement.getSize(element);
13204
+ var size = new Size(element.offsetWidth, element.offsetHeight);
12849
13205
  return size.isNaN() || size.isZero()
12850
13206
  ? new Size(getSize('width'), getSize('height'))
12851
- : size;
13207
+ : size.subtract(DomElement.getInsets(element));
12852
13208
  }
12853
13209
 
12854
13210
  var size;
@@ -12961,7 +13317,7 @@ var View = Base.extend(Emitter, {
12961
13317
  this.requestUpdate();
12962
13318
  },
12963
13319
 
12964
- update: function() {
13320
+ update: function(force) {
12965
13321
  },
12966
13322
 
12967
13323
  draw: function() {
@@ -12976,7 +13332,8 @@ var View = Base.extend(Emitter, {
12976
13332
  if (that._animate) {
12977
13333
  that.requestUpdate();
12978
13334
  var element = that._element;
12979
- if ((!DomElement.getPrefixed(document, 'hidden')
13335
+ if (!element
13336
+ || (!DomElement.getPrefixed(document, 'hidden')
12980
13337
  || PaperScope.getAttribute(element, 'keepalive')
12981
13338
  === 'true') && DomElement.isInView(element)) {
12982
13339
  that._handleFrame();
@@ -13049,6 +13406,14 @@ var View = Base.extend(Emitter, {
13049
13406
  return this._element;
13050
13407
  },
13051
13408
 
13409
+ getProject: function() {
13410
+ return this._project;
13411
+ },
13412
+
13413
+ getScope: function() {
13414
+ return this._scope;
13415
+ },
13416
+
13052
13417
  getPixelRatio: function() {
13053
13418
  return this._pixelRatio;
13054
13419
  },
@@ -13154,12 +13519,14 @@ var View = Base.extend(Emitter, {
13154
13519
 
13155
13520
  getZoom: function() {
13156
13521
  var scaling = this._decompose().scaling;
13157
- return (scaling.x + scaling.y) / 2;
13522
+ return (Math.abs(scaling.x) + Math.abs(scaling.y)) / 2;
13158
13523
  },
13159
13524
 
13160
13525
  setZoom: function(zoom) {
13161
- this.transform(new Matrix().scale(zoom / this.getZoom(),
13162
- this.getCenter()));
13526
+ if (zoom > 0 && isFinite(zoom)) {
13527
+ this.transform(new Matrix().scale(zoom / this.getZoom(),
13528
+ this.getCenter()));
13529
+ }
13163
13530
  },
13164
13531
 
13165
13532
  getRotation: function() {
@@ -13169,7 +13536,8 @@ var View = Base.extend(Emitter, {
13169
13536
  setRotation: function(rotation) {
13170
13537
  var current = this.getRotation();
13171
13538
  if (current != null && rotation != null) {
13172
- this.rotate(rotation - current);
13539
+ this._matrix.prepend(new Matrix().rotate(rotation - current,
13540
+ this.projectToView(this.getCenter())));
13173
13541
  }
13174
13542
  },
13175
13543
 
@@ -13199,6 +13567,31 @@ var View = Base.extend(Emitter, {
13199
13567
  this._matrix.append(matrix);
13200
13568
  },
13201
13569
 
13570
+ fitBounds: function(rectangle, fit) {
13571
+ rectangle = Rectangle.read(arguments);
13572
+ fit = Base.read(arguments);
13573
+ var matrix = this._matrix,
13574
+ size = this._viewSize,
13575
+ bounds = matrix._transformBounds(rectangle),
13576
+ scaleX = new Point(matrix._a, matrix._c).getLength(),
13577
+ scaleY = new Point(matrix._b, matrix._d).getLength(),
13578
+ ratios = [
13579
+ size.width / bounds.width * scaleX,
13580
+ size.height / bounds.height * scaleY
13581
+ ].filter(function(ratio) {
13582
+ return ratio > 0 && isFinite(ratio);
13583
+ });
13584
+ this.setCenter(rectangle.getCenter());
13585
+ if (ratios.length) {
13586
+ var amount = fit === 'cover'
13587
+ ? Math.max.apply(Math, ratios)
13588
+ : Math.min.apply(Math, ratios);
13589
+ matrix.prepend(new Matrix().scale(
13590
+ new Point(amount / scaleX, amount / scaleY),
13591
+ size.divide(2)));
13592
+ }
13593
+ },
13594
+
13202
13595
  scrollBy: function() {
13203
13596
  this.translate(Point.read(arguments).negate());
13204
13597
  }
@@ -13213,7 +13606,17 @@ var View = Base.extend(Emitter, {
13213
13606
  },
13214
13607
 
13215
13608
  getEventPoint: function(event) {
13216
- return this.viewToProject(DomEvent.getOffset(event, this._element));
13609
+ var element = this._element,
13610
+ offset = DomEvent.getOffset(event, element),
13611
+ size = DomElement.getSize(element);
13612
+ if (!size.isZero()) {
13613
+ var cssSize = new Size(element.offsetWidth, element.offsetHeight);
13614
+ offset = offset.multiply(cssSize.divide(size))
13615
+ .subtract(DomElement.getInsets(element, true))
13616
+ .multiply(this.getViewSize().divide(
13617
+ cssSize.subtract(DomElement.getInsets(element))));
13618
+ }
13619
+ return this.viewToProject(offset);
13217
13620
  },
13218
13621
 
13219
13622
  }, {
@@ -13231,8 +13634,6 @@ var View = Base.extend(Emitter, {
13231
13634
  }
13232
13635
  },
13233
13636
  new function() {
13234
- if (!window)
13235
- return;
13236
13637
  var prevFocus,
13237
13638
  tempFocus,
13238
13639
  dragging = false,
@@ -13260,92 +13661,87 @@ new function() {
13260
13661
  view._handleMouseEvent('mousemove', event, point);
13261
13662
  }
13262
13663
 
13263
- var navigator = window.navigator,
13264
- mousedown, mousemove, mouseup;
13265
- if (navigator.pointerEnabled || navigator.msPointerEnabled) {
13266
- mousedown = 'pointerdown MSPointerDown';
13267
- mousemove = 'pointermove MSPointerMove';
13268
- mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
13269
- } else {
13270
- mousedown = 'touchstart';
13271
- mousemove = 'touchmove';
13272
- mouseup = 'touchend touchcancel';
13273
- if (!('ontouchstart' in window && navigator.userAgent.match(
13274
- /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
13275
- mousedown += ' mousedown';
13276
- mousemove += ' mousemove';
13277
- mouseup += ' mouseup';
13664
+ var viewEvents = {};
13665
+
13666
+ if (window) {
13667
+ var navigator = window.navigator,
13668
+ mousedown, mousemove, mouseup;
13669
+ if (navigator.pointerEnabled || navigator.msPointerEnabled) {
13670
+ mousedown = 'pointerdown MSPointerDown';
13671
+ mousemove = 'pointermove MSPointerMove';
13672
+ mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
13673
+ } else {
13674
+ mousedown = 'touchstart';
13675
+ mousemove = 'touchmove';
13676
+ mouseup = 'touchend touchcancel';
13677
+ if (!('ontouchstart' in window && navigator.userAgent.match(
13678
+ /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
13679
+ mousedown += ' mousedown';
13680
+ mousemove += ' mousemove';
13681
+ mouseup += ' mouseup';
13682
+ }
13278
13683
  }
13279
- }
13280
13684
 
13281
- var viewEvents = {},
13282
- docEvents = {
13685
+ var docEvents = {
13283
13686
  mouseout: function(event) {
13284
13687
  var view = View._focused,
13285
13688
  target = DomEvent.getRelatedTarget(event);
13286
- if (view && (!target || target.nodeName === 'HTML')) {
13287
- var offset = DomEvent.getOffset(event, view._element),
13288
- x = offset.x,
13289
- abs = Math.abs,
13290
- ax = abs(x),
13291
- max = 1 << 25,
13292
- diff = ax - max;
13293
- offset.x = abs(diff) < ax ? diff * (x < 0 ? -1 : 1) : x;
13294
- handleMouseMove(view, event, view.viewToProject(offset));
13295
- }
13689
+ if (view && (!target || target.nodeName === 'HTML'))
13690
+ handleMouseMove(view, event);
13296
13691
  },
13297
13692
 
13298
13693
  scroll: updateFocus
13299
13694
  };
13300
13695
 
13301
- viewEvents[mousedown] = function(event) {
13302
- var view = View._focused = getView(event);
13303
- if (!dragging) {
13304
- dragging = true;
13305
- view._handleMouseEvent('mousedown', event);
13306
- }
13307
- };
13696
+ viewEvents[mousedown] = function(event) {
13697
+ var view = View._focused = getView(event);
13698
+ if (!dragging) {
13699
+ dragging = true;
13700
+ view._handleMouseEvent('mousedown', event);
13701
+ }
13702
+ };
13308
13703
 
13309
- docEvents[mousemove] = function(event) {
13310
- var view = View._focused;
13311
- if (!mouseDown) {
13312
- var target = getView(event);
13313
- if (target) {
13314
- if (view !== target) {
13315
- if (view)
13316
- handleMouseMove(view, event);
13317
- if (!prevFocus)
13318
- prevFocus = view;
13319
- view = View._focused = tempFocus = target;
13320
- }
13321
- } else if (tempFocus && tempFocus === view) {
13322
- if (prevFocus && !prevFocus.isInserted())
13704
+ docEvents[mousemove] = function(event) {
13705
+ var view = View._focused;
13706
+ if (!mouseDown) {
13707
+ var target = getView(event);
13708
+ if (target) {
13709
+ if (view !== target) {
13710
+ if (view)
13711
+ handleMouseMove(view, event);
13712
+ if (!prevFocus)
13713
+ prevFocus = view;
13714
+ view = View._focused = tempFocus = target;
13715
+ }
13716
+ } else if (tempFocus && tempFocus === view) {
13717
+ if (prevFocus && !prevFocus.isInserted())
13718
+ prevFocus = null;
13719
+ view = View._focused = prevFocus;
13323
13720
  prevFocus = null;
13324
- view = View._focused = prevFocus;
13325
- prevFocus = null;
13326
- updateFocus();
13721
+ updateFocus();
13722
+ }
13327
13723
  }
13328
- }
13329
- if (view)
13330
- handleMouseMove(view, event);
13331
- };
13724
+ if (view)
13725
+ handleMouseMove(view, event);
13726
+ };
13332
13727
 
13333
- docEvents[mousedown] = function() {
13334
- mouseDown = true;
13335
- };
13728
+ docEvents[mousedown] = function() {
13729
+ mouseDown = true;
13730
+ };
13336
13731
 
13337
- docEvents[mouseup] = function(event) {
13338
- var view = View._focused;
13339
- if (view && dragging)
13340
- view._handleMouseEvent('mouseup', event);
13341
- mouseDown = dragging = false;
13342
- };
13732
+ docEvents[mouseup] = function(event) {
13733
+ var view = View._focused;
13734
+ if (view && dragging)
13735
+ view._handleMouseEvent('mouseup', event);
13736
+ mouseDown = dragging = false;
13737
+ };
13343
13738
 
13344
- DomEvent.add(document, docEvents);
13739
+ DomEvent.add(document, docEvents);
13345
13740
 
13346
- DomEvent.add(window, {
13347
- load: updateFocus
13348
- });
13741
+ DomEvent.add(window, {
13742
+ load: updateFocus
13743
+ });
13744
+ }
13349
13745
 
13350
13746
  var called = false,
13351
13747
  prevented = false,
@@ -13411,9 +13807,27 @@ new function() {
13411
13807
  point, prevPoint));
13412
13808
  }
13413
13809
 
13810
+ function createEvent(type, options) {
13811
+ if (options.modifiers)
13812
+ Key.modifiers.set(options.modifiers);
13813
+ return {
13814
+ type: type,
13815
+ timeStamp: options.timeStamp != null ? options.timeStamp
13816
+ : Date.now(),
13817
+ button: options.button || 0,
13818
+ cancelable: true,
13819
+ defaultPrevented: false,
13820
+ preventDefault: function() {
13821
+ this.defaultPrevented = true;
13822
+ },
13823
+ stopPropagation: function() {}
13824
+ };
13825
+ }
13826
+
13414
13827
  var itemEventsMap = {
13415
13828
  mousedown: {
13416
13829
  mousedown: 1,
13830
+ mouseup: 1,
13417
13831
  mousedrag: 1,
13418
13832
  click: 1,
13419
13833
  doubleclick: 1
@@ -13435,6 +13849,28 @@ new function() {
13435
13849
  return {
13436
13850
  _viewEvents: viewEvents,
13437
13851
 
13852
+ dispatchMouseEvent: function(type, point, options) {
13853
+ if (!/^mouse(down|move|up)$/.test(type))
13854
+ throw new Error('Unknown mouse event type: \'' + type + '\'.');
13855
+ var event = createEvent(type, options || {});
13856
+ if (type === 'mousedown') {
13857
+ View._focused = this;
13858
+ mouseDown = dragging = true;
13859
+ }
13860
+ this._handleMouseEvent(type, event, new Point(point));
13861
+ if (type === 'mouseup')
13862
+ mouseDown = dragging = false;
13863
+ return event.defaultPrevented;
13864
+ },
13865
+
13866
+ dispatchKeyEvent: function(type, key, options) {
13867
+ if (!/^key(down|up)$/.test(type))
13868
+ throw new Error('Unknown key event type: \'' + type + '\'.');
13869
+ options = options || {};
13870
+ Key._handleKey(type === 'keydown', key, options.character,
13871
+ createEvent(type, options), this);
13872
+ },
13873
+
13438
13874
  _handleMouseEvent: function(type, event, point) {
13439
13875
  var itemEvents = this._itemEvents,
13440
13876
  hitItems = itemEvents.native[type],
@@ -13447,12 +13883,37 @@ new function() {
13447
13883
  || tool && tool.responds(type);
13448
13884
  }
13449
13885
 
13886
+ function needsUp(hitItem) {
13887
+ if (view.responds('mouseup')
13888
+ || tool && tool.responds('mouseup'))
13889
+ return true;
13890
+ for (var item = hitItem; item; item = item._parent) {
13891
+ for (var key in itemEventsMap.mouseup) {
13892
+ if (item.responds(key))
13893
+ return true;
13894
+ }
13895
+ }
13896
+ return false;
13897
+ }
13898
+
13899
+ if (event.shiftKey !== undefined) {
13900
+ Key.modifiers.set({
13901
+ shift: event.shiftKey,
13902
+ control: event.ctrlKey,
13903
+ alt: event.altKey,
13904
+ meta: event.metaKey
13905
+ });
13906
+ }
13450
13907
  if (nativeMove && dragging && responds('mousedrag'))
13451
13908
  type = 'mousedrag';
13452
13909
  if (!point)
13453
13910
  point = this.getEventPoint(event);
13454
13911
 
13455
- var inView = this.getBounds().contains(point),
13912
+ var target = DomEvent.getTarget(event),
13913
+ isCovered = nativeMove && !dragging && target
13914
+ && target.nodeType === 1
13915
+ && !target.contains(this._element),
13916
+ inView = !isCovered && this.getBounds().contains(point),
13456
13917
  hit = hitItems && inView && view._project.hitTest(point, {
13457
13918
  tolerance: 0,
13458
13919
  fill: true,
@@ -13487,9 +13948,12 @@ new function() {
13487
13948
  if (mouse.down && inView || mouse.up && downPoint) {
13488
13949
  emitMouseEvents(this, hitItem, type, event, point, downPoint);
13489
13950
  if (mouse.down) {
13490
- dblClick = hitItem === clickItem
13491
- && (Date.now() - clickTime < 300);
13951
+ var time = Date.now();
13952
+ dblClick = !dblClick && hitItem === clickItem
13953
+ && time - clickTime < 300
13954
+ && point.getDistance(downPoint) < 3;
13492
13955
  downItem = clickItem = hitItem;
13956
+ clickTime = time;
13493
13957
  if (!prevented && hitItem) {
13494
13958
  var item = hitItem;
13495
13959
  while (item && !item.responds('mousedrag'))
@@ -13500,10 +13964,8 @@ new function() {
13500
13964
  downPoint = point;
13501
13965
  } else if (mouse.up) {
13502
13966
  if (!prevented && hitItem === downItem) {
13503
- clickTime = Date.now();
13504
13967
  emitMouseEvents(this, hitItem, dblClick ? 'doubleclick'
13505
13968
  : 'click', event, point, downPoint);
13506
- dblClick = false;
13507
13969
  }
13508
13970
  downItem = dragItem = null;
13509
13971
  }
@@ -13518,7 +13980,7 @@ new function() {
13518
13980
 
13519
13981
  if (
13520
13982
  event.cancelable !== false
13521
- && (called && !mouse.move || mouse.down && responds('mouseup'))
13983
+ && (called && !mouse.move || mouse.down && needsUp(hitItem))
13522
13984
  ) {
13523
13985
  event.preventDefault();
13524
13986
  }
@@ -13537,11 +13999,9 @@ new function() {
13537
13999
  }
13538
14000
  }
13539
14001
 
13540
- if (this.isVisible()) {
13541
- emit(this);
13542
- if (tool && tool.responds(type))
13543
- emit(tool);
13544
- }
14002
+ emit(this);
14003
+ if (tool && tool.responds(type))
14004
+ emit(tool);
13545
14005
  },
13546
14006
 
13547
14007
  _countItemEvent: function(type, sign) {
@@ -13599,16 +14059,20 @@ var CanvasView = View.extend({
13599
14059
  },
13600
14060
 
13601
14061
  _setElementSize: function _setElementSize(width, height) {
13602
- var pixelRatio = this._pixelRatio;
14062
+ var pixelRatio = this._pixelRatio,
14063
+ element = this._element;
13603
14064
  _setElementSize.base.call(this, width * pixelRatio, height * pixelRatio);
14065
+ if (!PaperScope.hasAttribute(element, 'resize')) {
14066
+ var style = element.style,
14067
+ insets = DomElement.getStyles(element).boxSizing
14068
+ === 'border-box'
14069
+ ? DomElement.getInsets(element)
14070
+ : new Size();
14071
+ style.width = (width + insets.width) + 'px';
14072
+ style.height = (height + insets.height) + 'px';
14073
+ }
13604
14074
  if (pixelRatio !== 1) {
13605
- var element = this._element,
13606
- ctx = this._context;
13607
- if (!PaperScope.hasAttribute(element, 'resize')) {
13608
- var style = element.style;
13609
- style.width = width + 'px';
13610
- style.height = height + 'px';
13611
- }
14075
+ var ctx = this._context;
13612
14076
  ctx.restore();
13613
14077
  ctx.save();
13614
14078
  ctx.scale(pixelRatio, pixelRatio);
@@ -13634,19 +14098,22 @@ var CanvasView = View.extend({
13634
14098
  return pixels;
13635
14099
  },
13636
14100
 
13637
- getTextWidth: function(font, lines) {
14101
+ getTextWidth: function(font, lines, letterSpacing) {
13638
14102
  var ctx = this._context,
13639
14103
  prevFont = ctx.font,
14104
+ prevLetterSpacing = ctx.letterSpacing,
13640
14105
  width = 0;
13641
14106
  ctx.font = font;
14107
+ ctx.letterSpacing = letterSpacing;
13642
14108
  for (var i = 0, l = lines.length; i < l; i++)
13643
14109
  width = Math.max(width, ctx.measureText(lines[i]).width);
13644
14110
  ctx.font = prevFont;
14111
+ ctx.letterSpacing = prevLetterSpacing;
13645
14112
  return width;
13646
14113
  },
13647
14114
 
13648
- update: function() {
13649
- if (!this._needsUpdate)
14115
+ update: function(force) {
14116
+ if (!this._needsUpdate && !force)
13650
14117
  return false;
13651
14118
  var project = this._project,
13652
14119
  ctx = this._context,
@@ -13717,6 +14184,7 @@ var Key = new function() {
13717
14184
  var keyLookup = {
13718
14185
  '\t': 'tab',
13719
14186
  ' ': 'space',
14187
+ '\xA0': 'space',
13720
14188
  '\b': 'backspace',
13721
14189
  '\x7f': 'delete',
13722
14190
  'Spacebar': 'space',
@@ -13770,10 +14238,16 @@ var Key = new function() {
13770
14238
  (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase());
13771
14239
  }
13772
14240
 
13773
- function handleKey(down, key, character, event) {
13774
- var type = down ? 'keydown' : 'keyup',
13775
- view = View._focused,
13776
- name;
14241
+ function handleKey(down, key, character, event, view) {
14242
+ if (!view) {
14243
+ View.updateFocus();
14244
+ view = View._focused;
14245
+ if (view && !view.isVisible())
14246
+ view = null;
14247
+ }
14248
+ if (character == null)
14249
+ character = down ? key.length > 1 ? '' : key : charMap[key] || '';
14250
+ var name;
13777
14251
  keyMap[key] = down;
13778
14252
  if (down) {
13779
14253
  charMap[key] = character;
@@ -13789,7 +14263,7 @@ var Key = new function() {
13789
14263
  } else {
13790
14264
  for (var k in metaFixMap) {
13791
14265
  if (k in charMap)
13792
- handleKey(false, k, metaFixMap[k], event);
14266
+ handleKey(false, k, metaFixMap[k], event, view);
13793
14267
  }
13794
14268
  metaFixMap = null;
13795
14269
  }
@@ -13805,11 +14279,9 @@ var Key = new function() {
13805
14279
 
13806
14280
  DomEvent.add(document, {
13807
14281
  keydown: function(event) {
13808
- var key = getKey(event),
13809
- agent = paper && paper.agent;
13810
- if (key.length > 1 || agent && (agent.chrome && (event.altKey
13811
- || agent.mac && event.metaKey
13812
- || !agent.mac && event.ctrlKey))) {
14282
+ var key = getKey(event);
14283
+ if (key.length > 1 || event.ctrlKey || event.altKey
14284
+ || event.metaKey) {
13813
14285
  handleKey(true, key,
13814
14286
  charLookup[key] || (key.length > 1 ? '' : key), event);
13815
14287
  } else {
@@ -13846,6 +14318,8 @@ var Key = new function() {
13846
14318
  });
13847
14319
 
13848
14320
  return {
14321
+ _handleKey: handleKey,
14322
+
13849
14323
  modifiers: modifiers,
13850
14324
 
13851
14325
  isDown: function(key) {
@@ -14012,6 +14486,14 @@ var Tool = PaperScopeItem.extend({
14012
14486
  }
14013
14487
  },
14014
14488
 
14489
+ getEventInterval: function() {
14490
+ return this._eventInterval;
14491
+ },
14492
+
14493
+ setEventInterval: function(interval) {
14494
+ this._eventInterval = interval;
14495
+ },
14496
+
14015
14497
  getFixedDistance: function() {
14016
14498
  return this._minDistance == this._maxDistance
14017
14499
  ? this._minDistance : null;
@@ -14068,7 +14550,24 @@ var Tool = PaperScopeItem.extend({
14068
14550
  if (mouse.down) {
14069
14551
  update();
14070
14552
  emit();
14553
+ if (this._eventInterval > 0) {
14554
+ clearInterval(this._timer);
14555
+ this._timer = setInterval(function() {
14556
+ if (tool.isActive()) {
14557
+ paper = tool._scope;
14558
+ tool._lastPoint = tool._point;
14559
+ tool._moveCount++;
14560
+ if (tool.responds('mousedrag')) {
14561
+ tool.emit('mousedrag',
14562
+ new ToolEvent(tool, 'mousedrag', event));
14563
+ }
14564
+ } else {
14565
+ clearInterval(tool._timer);
14566
+ }
14567
+ }, this._eventInterval);
14568
+ }
14071
14569
  } else if (mouse.up) {
14570
+ clearInterval(this._timer);
14072
14571
  update(null, this._maxDistance);
14073
14572
  emit();
14074
14573
  } else if (responds) {
@@ -14143,6 +14642,48 @@ var Tween = Base.extend(Emitter, {
14143
14642
  return t < 0.5
14144
14643
  ? 16 * t * t * t * t * t
14145
14644
  : 1 + 16 * (--t) * t * t * t * t;
14645
+ },
14646
+
14647
+ easeInSine: function(t) {
14648
+ return 1 - Math.cos(t * Math.PI / 2);
14649
+ },
14650
+
14651
+ easeOutSine: function(t) {
14652
+ return Math.sin(t * Math.PI / 2);
14653
+ },
14654
+
14655
+ easeInOutSine: function(t) {
14656
+ return (1 - Math.cos(t * Math.PI)) / 2;
14657
+ },
14658
+
14659
+ easeInExpo: function(t) {
14660
+ return t === 0 ? 0 : Math.pow(2, 10 * t - 10);
14661
+ },
14662
+
14663
+ easeOutExpo: function(t) {
14664
+ return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
14665
+ },
14666
+
14667
+ easeInOutExpo: function(t) {
14668
+ return t === 0 || t === 1
14669
+ ? t
14670
+ : t < 0.5
14671
+ ? Math.pow(2, 20 * t - 10) / 2
14672
+ : 1 - Math.pow(2, 10 - 20 * t) / 2;
14673
+ },
14674
+
14675
+ easeInCirc: function(t) {
14676
+ return 1 - Math.sqrt(1 - t * t);
14677
+ },
14678
+
14679
+ easeOutCirc: function(t) {
14680
+ return Math.sqrt(1 - (--t) * t);
14681
+ },
14682
+
14683
+ easeInOutCirc: function(t) {
14684
+ return t < 0.5
14685
+ ? (1 - Math.sqrt(1 - 4 * t * t)) / 2
14686
+ : (1 + Math.sqrt(1 - 4 * (--t) * t)) / 2;
14146
14687
  }
14147
14688
  })
14148
14689
  },
@@ -14363,7 +14904,7 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14363
14904
  canvases: [],
14364
14905
 
14365
14906
  getCanvas: function(width, height, options) {
14366
- if (!window)
14907
+ if (!window && !self.OffscreenCanvas)
14367
14908
  return null;
14368
14909
  var canvas,
14369
14910
  clear = true;
@@ -14374,7 +14915,9 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14374
14915
  if (this.canvases.length) {
14375
14916
  canvas = this.canvases.pop();
14376
14917
  } else {
14377
- canvas = document.createElement('canvas');
14918
+ canvas = window
14919
+ ? document.createElement('canvas')
14920
+ : new self.OffscreenCanvas(width, height);
14378
14921
  clear = false;
14379
14922
  }
14380
14923
  var ctx = canvas.getContext('2d', options || {});
@@ -14401,8 +14944,9 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14401
14944
  release: function(obj) {
14402
14945
  var canvas = obj && obj.canvas ? obj.canvas : obj;
14403
14946
  if (canvas && canvas.getContext) {
14404
- canvas.getContext('2d').restore();
14405
- this.canvases.push(canvas);
14947
+ canvas.width = canvas.height = 0;
14948
+ if (this.canvases.length < 8)
14949
+ this.canvases.push(canvas);
14406
14950
  }
14407
14951
  }
14408
14952
  };
@@ -14578,6 +15122,12 @@ var BlendMode = new function() {
14578
15122
  db = (bb + sb) / 2;
14579
15123
  },
14580
15124
 
15125
+ 'pin-light': function() {
15126
+ dr = sr < 128 ? min(br, 2 * sr) : max(br, 2 * sr - 255);
15127
+ dg = sg < 128 ? min(bg, 2 * sg) : max(bg, 2 * sg - 255);
15128
+ db = sb < 128 ? min(bb, 2 * sb) : max(bb, 2 * sb - 255);
15129
+ },
15130
+
14581
15131
  negation: function() {
14582
15132
  dr = 255 - abs(255 - sr - br);
14583
15133
  dg = 255 - abs(255 - sg - bg);
@@ -14676,7 +15226,8 @@ var SvgElement = new function() {
14676
15226
  function get(node, name) {
14677
15227
  var namespace = attributeNamespace[name],
14678
15228
  value = namespace
14679
- ? node.getAttributeNS(namespace, name)
15229
+ ? node.getAttribute(name)
15230
+ || node.getAttributeNS(namespace, name)
14680
15231
  : node.getAttribute(name);
14681
15232
  return value === 'null' ? null : value;
14682
15233
  }
@@ -14727,8 +15278,9 @@ var SvgStyles = Base.each({
14727
15278
  dashArray: ['stroke-dasharray', 'array'],
14728
15279
  dashOffset: ['stroke-dashoffset', 'number'],
14729
15280
  fontFamily: ['font-family', 'string'],
14730
- fontWeight: ['font-weight', 'string'],
15281
+ fontWeight: ['font-weight', 'font'],
14731
15282
  fontSize: ['font-size', 'number'],
15283
+ tracking: ['letter-spacing', 'tracking'],
14732
15284
  justification: ['text-anchor', 'lookup', {
14733
15285
  left: 'start',
14734
15286
  center: 'middle',
@@ -14754,7 +15306,8 @@ var SvgStyles = Base.each({
14754
15306
  }, {});
14755
15307
 
14756
15308
  new function() {
14757
- var formatter;
15309
+ var formatter,
15310
+ parentStyles;
14758
15311
 
14759
15312
  function getTransform(matrix, coordinates, center) {
14760
15313
  var attrs = new Base(),
@@ -14803,34 +15356,72 @@ new function() {
14803
15356
  var node = SvgElement.create('g', attrs, formatter);
14804
15357
  for (var i = 0, l = children.length; i < l; i++) {
14805
15358
  var child = children[i];
14806
- var childNode = exportSVG(child, options);
14807
- if (childNode) {
14808
- if (child.isClipMask()) {
14809
- var clip = SvgElement.create('clipPath');
14810
- clip.appendChild(childNode);
14811
- setDefinition(child, clip, 'clip');
14812
- SvgElement.set(node, {
14813
- 'clip-path': 'url(#' + clip.id + ')'
14814
- });
14815
- } else {
15359
+ if (child.isClipMask()) {
15360
+ var clip = exportClip(child, options);
15361
+ setDefinition(child, clip, 'clip', options);
15362
+ SvgElement.set(node, {
15363
+ 'clip-path': 'url(#' + clip.id + ')'
15364
+ });
15365
+ } else {
15366
+ var childNode = exportSVG(child, options);
15367
+ if (childNode)
14816
15368
  node.appendChild(childNode);
14817
- }
14818
15369
  }
14819
15370
  }
14820
15371
  return node;
14821
15372
  }
14822
15373
 
15374
+ function exportClip(item, options) {
15375
+ var clip = SvgElement.create('clipPath');
15376
+
15377
+ function addItem(item) {
15378
+ if (item instanceof Group) {
15379
+ var children = item._children;
15380
+ for (var i = 0, l = children.length; i < l; i++) {
15381
+ if (!children[i]._clipMask)
15382
+ addItem(children[i]);
15383
+ }
15384
+ } else {
15385
+ var node = exportSVG(item, options);
15386
+ if (node)
15387
+ clip.appendChild(node);
15388
+ }
15389
+ }
15390
+
15391
+ if (item instanceof Group) {
15392
+ item = item.clone({ insert: false });
15393
+ item.matrix.apply(true, true);
15394
+ }
15395
+ addItem(item);
15396
+ return clip;
15397
+ }
15398
+
14823
15399
  function exportRaster(item, options) {
14824
15400
  var attrs = getTransform(item._matrix, true),
14825
15401
  size = item.getSize(),
14826
- image = item.getImage();
15402
+ image = item.getImage(),
15403
+ href = options.embedImages == false && image && image.src
15404
+ || item.toDataURL();
14827
15405
  attrs.x -= size.width / 2;
14828
15406
  attrs.y -= size.height / 2;
14829
- attrs.width = size.width;
14830
- attrs.height = size.height;
14831
- attrs.href = options.embedImages == false && image && image.src
14832
- || item.toDataURL();
14833
- return SvgElement.create('image', attrs, formatter);
15407
+ if (options.reuseImages) {
15408
+ var node = getDefinition(href, 'image');
15409
+ if (!node) {
15410
+ node = SvgElement.create('image', {
15411
+ width: size.width,
15412
+ height: size.height,
15413
+ href: href
15414
+ }, formatter);
15415
+ setDefinition(href, node, 'image', options);
15416
+ }
15417
+ attrs.href = '#' + node.id;
15418
+ return SvgElement.create('use', attrs, formatter);
15419
+ } else {
15420
+ attrs.width = size.width;
15421
+ attrs.height = size.height;
15422
+ attrs.href = href;
15423
+ return SvgElement.create('image', attrs, formatter);
15424
+ }
14834
15425
  }
14835
15426
 
14836
15427
  function exportPath(item, options) {
@@ -14916,7 +15507,7 @@ new function() {
14916
15507
  viewBox: formatter.rectangle(bounds)
14917
15508
  });
14918
15509
  node.appendChild(exportSVG(definitionItem, options));
14919
- setDefinition(definition, node, 'symbol');
15510
+ setDefinition(definition, node, 'symbol', options);
14920
15511
  }
14921
15512
  attrs.href = '#' + node.id;
14922
15513
  attrs.x += bounds.x;
@@ -14927,7 +15518,7 @@ new function() {
14927
15518
  return SvgElement.create('use', attrs, formatter);
14928
15519
  }
14929
15520
 
14930
- function exportGradient(color) {
15521
+ function exportGradient(color, options) {
14931
15522
  var gradientNode = getDefinition(color, 'color');
14932
15523
  if (!gradientNode) {
14933
15524
  var gradient = color.getGradient(),
@@ -14973,15 +15564,28 @@ new function() {
14973
15564
  gradientNode.appendChild(
14974
15565
  SvgElement.create('stop', attrs, formatter));
14975
15566
  }
14976
- setDefinition(color, gradientNode, 'color');
15567
+ setDefinition(color, gradientNode, 'color', options);
14977
15568
  }
14978
15569
  return 'url(#' + gradientNode.id + ')';
14979
15570
  }
14980
15571
 
14981
15572
  function exportText(item) {
14982
- var node = SvgElement.create('text', getTransform(item._matrix, true),
14983
- formatter);
14984
- node.textContent = item._content;
15573
+ var attrs = getTransform(item._matrix, true),
15574
+ node = SvgElement.create('text', attrs, formatter),
15575
+ lines = item._lines;
15576
+ if (lines.length > 1) {
15577
+ var leading = item._style.getLeading();
15578
+ for (var i = 0, l = lines.length; i < l; i++) {
15579
+ var span = SvgElement.create('tspan', {
15580
+ x: attrs.x,
15581
+ y: attrs.y + i * leading
15582
+ }, formatter);
15583
+ span.textContent = lines[i];
15584
+ node.appendChild(span);
15585
+ }
15586
+ } else {
15587
+ node.textContent = item._content;
15588
+ }
14985
15589
  return node;
14986
15590
  }
14987
15591
 
@@ -14996,9 +15600,18 @@ new function() {
14996
15600
  PointText: exportText
14997
15601
  };
14998
15602
 
15603
+ var cssBlendModes = Base.each(['normal', 'multiply', 'screen', 'overlay',
15604
+ 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light',
15605
+ 'soft-light', 'difference', 'exclusion', 'hue', 'saturation',
15606
+ 'color', 'luminosity'], function(mode) {
15607
+ this[mode] = true;
15608
+ }, {});
15609
+
14999
15610
  function applyStyle(item, node, options, isRoot) {
15000
15611
  var attrs = {},
15001
15612
  parent = !isRoot && item.getParent(),
15613
+ parentStyle = parent && (parentStyles[parent._id] =
15614
+ parentStyles[parent._id] || {}),
15002
15615
  style = [];
15003
15616
 
15004
15617
  if (item._name != null)
@@ -15008,24 +15621,39 @@ new function() {
15008
15621
  var get = entry.get,
15009
15622
  type = entry.type,
15010
15623
  value = item[get]();
15011
- if (entry.exportFilter
15624
+ if (parent && !(get in parentStyle))
15625
+ parentStyle[get] = parent[get]();
15626
+ if (value !== undefined && (entry.exportFilter
15012
15627
  ? entry.exportFilter(item, value)
15013
15628
  : options.reduceAttributes == false
15014
- || !parent || !Base.equals(parent[get](), value)) {
15629
+ || !parent || !Base.equals(parentStyle[get], value))) {
15015
15630
  if (type === 'color' && value != null) {
15016
15631
  var alpha = value.getAlpha();
15017
15632
  if (alpha < 1)
15018
15633
  attrs[entry.attribute + '-opacity'] = alpha;
15019
15634
  }
15020
15635
  if (type === 'style') {
15021
- style.push(entry.attribute + ': ' + value);
15636
+ if (cssBlendModes[value]) {
15637
+ style.push(entry.attribute + ': ' + value);
15638
+ } else {
15639
+ attrs['data-paper-blend-mode'] = value;
15640
+ }
15641
+ } else if (type === 'font') {
15642
+ var font = ('' + value).match(
15643
+ /^(.*?)\s*\b(italic|oblique)\b\s*(.*)$/i);
15644
+ attrs[entry.attribute] = font
15645
+ ? (font[1] + ' ' + font[3]).trim() || 'normal'
15646
+ : value;
15647
+ if (font)
15648
+ attrs['font-style'] = font[2].toLowerCase();
15022
15649
  } else {
15023
15650
  attrs[entry.attribute] = value == null ? 'none'
15024
15651
  : type === 'color' ? value.gradient
15025
- ? exportGradient(value, item)
15652
+ ? exportGradient(value, options)
15026
15653
  : value.toCSS(true)
15027
15654
  : type === 'array' ? value.join(',')
15028
15655
  : type === 'lookup' ? entry.toSVG[value]
15656
+ : type === 'tracking' ? value && value / 1000 + 'em'
15029
15657
  : value;
15030
15658
  }
15031
15659
  }
@@ -15047,16 +15675,20 @@ new function() {
15047
15675
  function getDefinition(item, type) {
15048
15676
  if (!definitions)
15049
15677
  definitions = { ids: {}, svgs: {} };
15050
- return item && definitions.svgs[type + '-'
15051
- + (item._id || item.__id || (item.__id = UID.get('svg')))];
15678
+ return item && definitions.svgs[getDefinitionKey(item, type)];
15052
15679
  }
15053
15680
 
15054
- function setDefinition(item, node, type) {
15681
+ function setDefinition(item, node, type, options) {
15055
15682
  if (!definitions)
15056
15683
  getDefinition();
15057
15684
  var typeId = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
15058
- node.id = type + '-' + typeId;
15059
- definitions.svgs[type + '-' + (item._id || item.__id)] = node;
15685
+ node.id = (options.idPrefix || '') + type + '-' + typeId;
15686
+ definitions.svgs[getDefinitionKey(item, type)] = node;
15687
+ }
15688
+
15689
+ function getDefinitionKey(item, type) {
15690
+ return type + '-' + (typeof item === 'string' ? item
15691
+ : item._id || item.__id || (item.__id = UID.get('svg')));
15060
15692
  }
15061
15693
 
15062
15694
  function exportDefinitions(node, options) {
@@ -15077,6 +15709,7 @@ new function() {
15077
15709
  }
15078
15710
  definitions = null;
15079
15711
  }
15712
+ parentStyles = null;
15080
15713
  return options.asString
15081
15714
  ? new self.XMLSerializer().serializeToString(svg)
15082
15715
  : svg;
@@ -15100,6 +15733,7 @@ new function() {
15100
15733
  if (!options)
15101
15734
  options = {};
15102
15735
  formatter = new Formatter(options.precision);
15736
+ parentStyles = {};
15103
15737
  return options;
15104
15738
  }
15105
15739
 
@@ -15152,7 +15786,14 @@ new function() {
15152
15786
  new function() {
15153
15787
 
15154
15788
  var definitions = {},
15155
- rootSize;
15789
+ rootSize,
15790
+ units = { 'in': 96, pt: 96 / 72, pc: 96 / 6, cm: 96 / 2.54,
15791
+ mm: 96 / 25.4 };
15792
+
15793
+ function parseLength(value) {
15794
+ var unit = /(in|pt|pc|cm|mm)\s*$/.exec(value);
15795
+ return parseFloat(value) * (unit ? units[unit[1]] : 1);
15796
+ }
15156
15797
 
15157
15798
  function getValue(node, name, isString, allowNull, allowPercent,
15158
15799
  defaultValue) {
@@ -15163,7 +15804,7 @@ new function() {
15163
15804
  : isString ? '' : 0
15164
15805
  : isString
15165
15806
  ? value
15166
- : parseFloat(value);
15807
+ : parseLength(value);
15167
15808
  return /%\s*$/.test(value)
15168
15809
  ? (res / 100) * (allowPercent ? 1
15169
15810
  : rootSize[/x|^width/.test(name) ? 'width' : 'height'])
@@ -15184,12 +15825,12 @@ new function() {
15184
15825
  : new Size(w, h);
15185
15826
  }
15186
15827
 
15187
- function convertValue(value, type, lookup) {
15828
+ function convertValue(value, type, lookup, options) {
15188
15829
  return value === 'none' ? null
15189
- : type === 'number' ? parseFloat(value)
15830
+ : type === 'number' ? parseLength(value)
15190
15831
  : type === 'array' ?
15191
- value ? value.split(/[\s,]+/g).map(parseFloat) : []
15192
- : type === 'color' ? getDefinition(value) || value
15832
+ value ? value.split(/[\s,]+/g).map(parseLength) : []
15833
+ : type === 'color' ? getDefinition(value, options) || value
15193
15834
  : type === 'lookup' ? lookup[value]
15194
15835
  : value;
15195
15836
  }
@@ -15201,11 +15842,17 @@ new function() {
15201
15842
  item = new Group(),
15202
15843
  project = item._project,
15203
15844
  currentStyle = project._currentStyle,
15845
+ parentSize = rootSize,
15204
15846
  children = [];
15205
15847
  if (!isClip && !isDefs) {
15206
- item = applyAttributes(item, node, isRoot);
15848
+ item = applyAttributes(item, node, options, isRoot);
15207
15849
  project._currentStyle = item._style.clone();
15208
15850
  }
15851
+ if (type === 'svg' && !isRoot) {
15852
+ rootSize = new Size(
15853
+ getValue(node, 'width', false, false, false, '100%'),
15854
+ getValue(node, 'height', false, false, false, '100%'));
15855
+ }
15209
15856
  if (isRoot) {
15210
15857
  var defs = node.querySelectorAll('defs');
15211
15858
  for (var i = 0, l = defs.length; i < l; i++) {
@@ -15223,8 +15870,9 @@ new function() {
15223
15870
  }
15224
15871
  item.addChildren(children);
15225
15872
  if (isClip)
15226
- item = applyAttributes(item.reduce(), node, isRoot);
15873
+ item = applyAttributes(item.reduce(), node, options, isRoot);
15227
15874
  project._currentStyle = currentStyle;
15875
+ rootSize = parentSize;
15228
15876
  if (isClip || isDefs) {
15229
15877
  item.remove();
15230
15878
  item = null;
@@ -15250,12 +15898,13 @@ new function() {
15250
15898
  return PathItem.create(node.getAttribute('d'));
15251
15899
  }
15252
15900
 
15253
- function importGradient(node, type) {
15901
+ function importGradient(node, type, options) {
15254
15902
  var id = (getValue(node, 'href', true) || '').substring(1),
15903
+ definition = getDefinitionById(id, options),
15255
15904
  radial = type === 'radialgradient',
15256
15905
  gradient;
15257
- if (id) {
15258
- gradient = definitions[id].getGradient();
15906
+ if (definition) {
15907
+ gradient = definition.getGradient();
15259
15908
  if (gradient._radial ^ radial) {
15260
15909
  gradient = gradient.clone();
15261
15910
  gradient._radial = radial;
@@ -15266,7 +15915,8 @@ new function() {
15266
15915
  for (var i = 0, l = nodes.length; i < l; i++) {
15267
15916
  var child = nodes[i];
15268
15917
  if (child.nodeType === 1)
15269
- stops.push(applyAttributes(new GradientStop(), child));
15918
+ stops.push(applyAttributes(new GradientStop(), child,
15919
+ options));
15270
15920
  }
15271
15921
  gradient = new Gradient(stops, radial);
15272
15922
  }
@@ -15286,7 +15936,8 @@ new function() {
15286
15936
  '100%', '0%');
15287
15937
  }
15288
15938
  var color = applyAttributes(
15289
- new Color(gradient, origin, destination, highlight), node);
15939
+ new Color(gradient, origin, destination, highlight), node,
15940
+ options);
15290
15941
  color._scaleToBounds = scaleToBounds;
15291
15942
  return null;
15292
15943
  }
@@ -15309,13 +15960,30 @@ new function() {
15309
15960
  lineargradient: importGradient,
15310
15961
  radialgradient: importGradient,
15311
15962
 
15312
- image: function (node) {
15313
- var raster = new Raster(getValue(node, 'href', true));
15963
+ image: function (node, type, options) {
15964
+ var raster = new Raster({
15965
+ crossOrigin: options.crossOrigin,
15966
+ source: getValue(node, 'href', true)
15967
+ }),
15968
+ align = /none|x(Min|Mid|Max)Y(Min|Mid|Max)/.exec(
15969
+ getValue(node, 'preserveAspectRatio', true))
15970
+ || [null, 'Mid', 'Mid'];
15314
15971
  raster.on('load', function() {
15315
- var size = getSize(node);
15316
- this.setSize(size);
15317
- var center = getPoint(node).add(size.divide(2));
15318
- this._matrix.append(new Matrix().translate(center));
15972
+ var imageSize = this.getSize(),
15973
+ size = getSize(node, null, null, true) || imageSize,
15974
+ point = getPoint(node);
15975
+ if (align[0] !== 'none') {
15976
+ var factors = { Min: 0, Mid: 0.5, Max: 1 },
15977
+ fitted = imageSize.multiply(Math.min(
15978
+ size.width / imageSize.width,
15979
+ size.height / imageSize.height));
15980
+ point = point.add(size.subtract(fitted).multiply(
15981
+ [factors[align[1]], factors[align[2]]]));
15982
+ size = fitted;
15983
+ }
15984
+ var center = point.add(size.divide(2));
15985
+ this._matrix.append(new Matrix().translate(center)
15986
+ .scale(size.divide(imageSize)));
15319
15987
  });
15320
15988
  return raster;
15321
15989
  },
@@ -15327,13 +15995,17 @@ new function() {
15327
15995
 
15328
15996
  defs: importGroup,
15329
15997
 
15330
- use: function(node) {
15998
+ use: function(node, type, options) {
15331
15999
  var id = (getValue(node, 'href', true) || '').substring(1),
15332
- definition = definitions[id],
16000
+ definition = getDefinitionById(id, options),
15333
16001
  point = getPoint(node);
15334
16002
  return definition
15335
16003
  ? definition instanceof SymbolDefinition
15336
16004
  ? definition.place(point)
16005
+ : definition instanceof Raster
16006
+ ? importNode(getSandbox().ownerDocument
16007
+ .getElementById(id), options, false)
16008
+ .translate(point)
15337
16009
  : definition.clone().translate(point)
15338
16010
  : null;
15339
16011
  },
@@ -15352,11 +16024,16 @@ new function() {
15352
16024
  },
15353
16025
 
15354
16026
  rect: function(node) {
16027
+ var rx = getValue(node, 'rx', false, true),
16028
+ ry = getValue(node, 'ry', false, true);
15355
16029
  return new Shape.Rectangle(new Rectangle(
15356
16030
  getPoint(node),
15357
16031
  getSize(node)
15358
- ), getSize(node, 'rx', 'ry'));
15359
- },
16032
+ ), new Size(
16033
+ rx == null ? ry || 0 : rx,
16034
+ ry == null ? rx || 0 : ry
16035
+ ));
16036
+ },
15360
16037
 
15361
16038
  line: function(node) {
15362
16039
  return new Path.Line(
@@ -15366,8 +16043,14 @@ new function() {
15366
16043
 
15367
16044
  text: function(node) {
15368
16045
  var text = new PointText(getPoint(node).add(
15369
- getPoint(node, 'dx', 'dy')));
15370
- text.setContent(node.textContent.trim() || '');
16046
+ getPoint(node, 'dx', 'dy'))),
16047
+ lines = Base.each(node.childNodes, function(child) {
16048
+ if (/^tspan$/i.test(child.nodeName)
16049
+ && child.hasAttribute('y'))
16050
+ this.push(child.textContent.trim());
16051
+ }, []);
16052
+ text.setContent(lines.length > 1 ? lines.join('\n')
16053
+ : node.textContent.trim());
15371
16054
  return text;
15372
16055
  },
15373
16056
 
@@ -15421,9 +16104,11 @@ new function() {
15421
16104
  }
15422
16105
 
15423
16106
  var attributes = Base.set(Base.each(SvgStyles, function(entry) {
15424
- this[entry.attribute] = function(item, value) {
16107
+ this[entry.attribute] = function(item, value, name, node, styles,
16108
+ options) {
15425
16109
  if (item[entry.set]) {
15426
- item[entry.set](convertValue(value, entry.type, entry.fromSVG));
16110
+ item[entry.set](convertValue(value, entry.type, entry.fromSVG,
16111
+ options));
15427
16112
  if (entry.type === 'color') {
15428
16113
  var color = item[entry.get]();
15429
16114
  if (color) {
@@ -15440,12 +16125,12 @@ new function() {
15440
16125
  }, {}), {
15441
16126
  id: function(item, value) {
15442
16127
  definitions[value] = item;
15443
- if (item.setName)
16128
+ if (item.setName && value !== (+value) + '')
15444
16129
  item.setName(value);
15445
16130
  },
15446
16131
 
15447
- 'clip-path': function(item, value) {
15448
- var clip = getDefinition(value);
16132
+ 'clip-path': function(item, value, name, node, styles, options) {
16133
+ var clip = getDefinition(value, options);
15449
16134
  if (clip) {
15450
16135
  clip = clip.clone();
15451
16136
  clip.setClipMask(true);
@@ -15473,6 +16158,28 @@ new function() {
15473
16158
  item.setVisible(value !== null);
15474
16159
  },
15475
16160
 
16161
+ 'data-paper-blend-mode': function(item, value) {
16162
+ if (item.setBlendMode)
16163
+ item.setBlendMode(value);
16164
+ },
16165
+
16166
+ 'font-style': function(item, value) {
16167
+ if (item.setFontWeight && /^(italic|oblique)$/.test(value)) {
16168
+ var weight = ('' + item.getFontWeight())
16169
+ .replace(/\s*\b(italic|oblique)\b/gi, '');
16170
+ item.setFontWeight(/^(normal|400)$/.test(weight) ? value
16171
+ : weight + ' ' + value);
16172
+ }
16173
+ },
16174
+
16175
+ 'letter-spacing': function(item, value) {
16176
+ if (item.setTracking) {
16177
+ var amount = parseFloat(value) || 0;
16178
+ item.setTracking(1000 * (/em$/.test(value) ? amount
16179
+ : amount / parseFloat(item.getFontSize())));
16180
+ }
16181
+ },
16182
+
15476
16183
  'stop-color': function(item, value) {
15477
16184
  if (item.setColor)
15478
16185
  item.setColor(value);
@@ -15518,12 +16225,13 @@ new function() {
15518
16225
  });
15519
16226
 
15520
16227
  function getAttribute(node, name, styles) {
15521
- var attr = node.attributes[name],
16228
+ var style = Base.camelize(name),
16229
+ value = node.style && node.style[style];
16230
+ if (!value) {
16231
+ var attr = node.attributes[name];
15522
16232
  value = attr && attr.value;
15523
- if (!value && node.style) {
15524
- var style = Base.camelize(name);
15525
- value = node.style[style];
15526
- if (!value && styles.node[style] !== styles.parent[style])
16233
+ if (!value && node.style
16234
+ && styles.node[style] !== styles.parent[style])
15527
16235
  value = styles.node[style];
15528
16236
  }
15529
16237
  return !value ? undefined
@@ -15531,27 +16239,38 @@ new function() {
15531
16239
  : value;
15532
16240
  }
15533
16241
 
15534
- function applyAttributes(item, node, isRoot) {
16242
+ function applyAttributes(item, node, options, isRoot) {
15535
16243
  var parent = node.parentNode,
15536
16244
  styles = {
15537
16245
  node: DomElement.getStyles(node) || {},
15538
16246
  parent: !isRoot && !/^defs$/i.test(parent.tagName)
15539
16247
  && DomElement.getStyles(parent) || {}
15540
16248
  };
16249
+ if (isRoot && !styles.node.fill && item.setFillColor)
16250
+ item.setFillColor('black');
15541
16251
  Base.each(attributes, function(apply, name) {
15542
16252
  var value = getAttribute(node, name, styles);
15543
16253
  item = value !== undefined
15544
- && apply(item, value, name, node, styles) || item;
16254
+ && apply(item, value, name, node, styles, options) || item;
15545
16255
  });
15546
16256
  return item;
15547
16257
  }
15548
16258
 
15549
- function getDefinition(value) {
16259
+ function getDefinitionById(id, options) {
16260
+ if (id && !definitions[id]) {
16261
+ var node = getSandbox().ownerDocument.getElementById(id);
16262
+ if (node)
16263
+ importNode(node, options, false);
16264
+ }
16265
+ return definitions[id];
16266
+ }
16267
+
16268
+ function getDefinition(value, options) {
15550
16269
  var match = value && value.match(/\((?:["'#]*)([^"')]+)/),
15551
16270
  name = match && match[1],
15552
- res = name && definitions[window
16271
+ res = name && getDefinitionById(window
15553
16272
  ? name.replace(window.location.href.split('#')[0] + '#', '')
15554
- : name];
16273
+ : name, options);
15555
16274
  if (res && res._scaleToBounds) {
15556
16275
  res = res.clone();
15557
16276
  res._scaleToBounds = true;
@@ -15597,12 +16316,12 @@ new function() {
15597
16316
  var settings = paper.settings,
15598
16317
  applyMatrix = settings.applyMatrix,
15599
16318
  insertItems = settings.insertItems,
16319
+ importer = importers[type],
15600
16320
  item;
15601
16321
  try {
15602
16322
  settings.applyMatrix = false;
15603
16323
  settings.insertItems = false;
15604
16324
  try {
15605
- var importer = importers[type];
15606
16325
  item = importer && importer(node, type, options, isRoot)
15607
16326
  || null;
15608
16327
  } finally {
@@ -15610,8 +16329,8 @@ new function() {
15610
16329
  settings.applyMatrix = applyMatrix;
15611
16330
  }
15612
16331
  if (item) {
15613
- if (isElement && !(item instanceof Group))
15614
- item = applyAttributes(item, node, isRoot);
16332
+ if (isElement && importer !== importGroup)
16333
+ item = applyAttributes(item, node, options, isRoot);
15615
16334
  var onImport = options.onImport,
15616
16335
  data = isElement && node.getAttribute('data-paper-data');
15617
16336
  if (onImport)
@@ -15727,8 +16446,7 @@ new function() {
15727
16446
  };
15728
16447
 
15729
16448
  Base.exports.PaperScript = function() {
15730
- var global = this,
15731
- acorn = global.acorn;
16449
+ var acorn = self.acorn;
15732
16450
  if (!acorn && typeof require !== 'undefined') {
15733
16451
  try { acorn = require('acorn'); } catch(e) {}
15734
16452
  }
@@ -17050,7 +17768,7 @@ Base.exports.PaperScript = function() {
17050
17768
  }
17051
17769
 
17052
17770
  function parse(code, options) {
17053
- return (global.acorn || acorn).parse(code, options);
17771
+ return (self.acorn || acorn).parse(code, options);
17054
17772
  }
17055
17773
 
17056
17774
  var binaryOperators = {
@@ -17306,9 +18024,12 @@ Base.exports.PaperScript = function() {
17306
18024
  if (agent.node) {
17307
18025
  offset -= 2;
17308
18026
  } else if (window && url && !window.location.href.indexOf(url)) {
17309
- var html = document.getElementsByTagName('html')[0].innerHTML;
17310
- offset = html.substr(0, html.indexOf(code) + 1).match(
17311
- lineBreaks).length + 1;
18027
+ var html = document.getElementsByTagName('html')[0].innerHTML,
18028
+ index = html.indexOf(code);
18029
+ if (index >= 0) {
18030
+ offset = html.substr(0, index + 1).match(
18031
+ lineBreaks).length + 1;
18032
+ }
17312
18033
  }
17313
18034
  offsetCode = offset > 0 && !(
17314
18035
  agent.chrome && version >= 36 ||
@@ -17343,7 +18064,8 @@ Base.exports.PaperScript = function() {
17343
18064
  code = new Array(offset + 1).join('\n') + code;
17344
18065
  }
17345
18066
  if (/^(inline|both)$/.test(sourceMaps)) {
17346
- code += "\n//# sourceMappingURL=data:application/json;base64,"
18067
+ code += "\n//# " + "sourceMappingURL="
18068
+ + "data:application/json;base64,"
17347
18069
  + self.btoa(unescape(encodeURIComponent(
17348
18070
  JSON.stringify(map))));
17349
18071
  }
@@ -17360,11 +18082,13 @@ Base.exports.PaperScript = function() {
17360
18082
  function execute(code, scope, options) {
17361
18083
  paper = scope;
17362
18084
  var view = scope.getView(),
17363
- tool = /\btool\.\w+|\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/
17364
- .test(code) && !/\bnew\s+Tool\b/.test(code)
18085
+ tool = new RegExp('\\btool\\.\\w+|\\s+(?:'
18086
+ + Tool.prototype._events.join('|') + ')\\b').test(code)
18087
+ && !/\bnew\s+Tool\b/.test(code)
17365
18088
  ? new Tool() : null,
17366
18089
  toolHandlers = tool ? tool._events : [],
17367
- handlers = ['onFrame', 'onResize'].concat(toolHandlers),
18090
+ handlers = ['onFrame', 'onResize'].concat(toolHandlers,
18091
+ options && options.handlers || []),
17368
18092
  params = [],
17369
18093
  args = [],
17370
18094
  func,
@@ -17372,7 +18096,8 @@ Base.exports.PaperScript = function() {
17372
18096
  code = compiled.code;
17373
18097
  function expose(scope, hidden) {
17374
18098
  for (var key in scope) {
17375
- if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
18099
+ if ((hidden || !/^_/.test(key) && !(key in self))
18100
+ && new RegExp('([\\b\\s\\W]|^)'
17376
18101
  + key.replace(/\$/g, '\\$') + '\\b').test(code)) {
17377
18102
  params.push(key);
17378
18103
  args.push(scope[key]);
@@ -17453,11 +18178,17 @@ Base.exports.PaperScript = function() {
17453
18178
  async: async,
17454
18179
  mimeType: 'text/plain',
17455
18180
  onLoad: function(code) {
17456
- execute(code, scope, src);
18181
+ execute(code, scope, {
18182
+ url: src,
18183
+ sourceMaps: 'inline'
18184
+ });
17457
18185
  }
17458
18186
  });
17459
18187
  } else {
17460
- execute(script.innerHTML, scope, script.baseURI);
18188
+ execute(script.innerHTML, scope, {
18189
+ url: script.baseURI,
18190
+ sourceMaps: 'inline'
18191
+ });
17461
18192
  }
17462
18193
  script.setAttribute('data-paper-ignore', 'true');
17463
18194
  return scope;
@@ -17489,20 +18220,18 @@ Base.exports.PaperScript = function() {
17489
18220
  calculateBinary: __$__,
17490
18221
  calculateUnary: $__
17491
18222
  };
17492
-
17493
- }.call(this);
18223
+ }();
17494
18224
 
17495
18225
  var paper = new (PaperScope.inject(Base.exports, {
17496
18226
  Base: Base,
17497
18227
  Numerical: Numerical,
18228
+ ChangeFlag: ChangeFlag,
17498
18229
  Key: Key,
17499
18230
  DomEvent: DomEvent,
17500
18231
  DomElement: DomElement,
17501
18232
  document: document,
17502
18233
  window: window,
17503
- PlacedSymbol: SymbolItem
17504
- }).inject({
17505
- enumerable: false,
18234
+ PlacedSymbol: SymbolItem,
17506
18235
  Symbol: SymbolDefinition
17507
18236
  }))();
17508
18237
 
@@ -17517,4 +18246,4 @@ if (typeof define === 'function' && define.amd) {
17517
18246
  }
17518
18247
 
17519
18248
  return paper;
17520
- }.call(this, typeof self === 'object' ? self : null);
18249
+ }(typeof self === 'object' ? self : null);