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;
@@ -845,9 +849,10 @@ var PaperScope = Base.extend({
845
849
  }
846
850
  });
847
851
  });
848
- for (var key in this)
849
- if (!/^_/.test(key) && this[key])
852
+ for (var key in this) {
853
+ if (!/^_/.test(key) && this[key] && !(key in scope))
850
854
  scope[key] = this[key];
855
+ }
851
856
  },
852
857
 
853
858
  setup: function(element) {
@@ -924,6 +929,10 @@ var PaperScopeItem = Base.extend(Emitter, {
924
929
  return this._scope[this._reference] === this;
925
930
  },
926
931
 
932
+ getScope: function() {
933
+ return this._scope;
934
+ },
935
+
927
936
  remove: function() {
928
937
  if (this._index == null)
929
938
  return false;
@@ -1646,6 +1655,16 @@ var Point = Base.extend({
1646
1655
  );
1647
1656
  },
1648
1657
 
1658
+ clamp: function() {
1659
+ var args = arguments,
1660
+ min = Point.read(args),
1661
+ max = Point.read(args);
1662
+ return new Point(
1663
+ Numerical.clamp(this.x, min.x, max.x),
1664
+ Numerical.clamp(this.y, min.y, max.y)
1665
+ );
1666
+ },
1667
+
1649
1668
  statics: {
1650
1669
  min: function() {
1651
1670
  var args = arguments,
@@ -1927,10 +1946,11 @@ var Rectangle = Base.extend({
1927
1946
  arg0.width || 0, arg0.height || 0);
1928
1947
  read = 1;
1929
1948
  } else if (arg0.from === undefined && arg0.to === undefined) {
1930
- this._set(0, 0, 0, 0);
1931
- if (Base.readSupported(args, this)) {
1949
+ var builder = new RectangleBuilder();
1950
+ if (Base.readSupported(args, builder)) {
1932
1951
  read = 1;
1933
1952
  }
1953
+ this._set(builder.x, builder.y, builder.width, builder.height);
1934
1954
  }
1935
1955
  }
1936
1956
  if (read === undefined) {
@@ -2025,24 +2045,10 @@ var Rectangle = Base.extend({
2025
2045
  return new ctor(this.width, this.height, this, 'setSize');
2026
2046
  },
2027
2047
 
2028
- _fw: 1,
2029
- _fh: 1,
2030
-
2031
2048
  setSize: function() {
2032
- var size = Size.read(arguments),
2033
- sx = this._sx,
2034
- sy = this._sy,
2035
- w = size.width,
2036
- h = size.height;
2037
- if (sx) {
2038
- this.x += (this.width - w) * sx;
2039
- }
2040
- if (sy) {
2041
- this.y += (this.height - h) * sy;
2042
- }
2043
- this.width = w;
2044
- this.height = h;
2045
- this._fw = this._fh = 1;
2049
+ var size = Size.read(arguments);
2050
+ this.width = size.width;
2051
+ this.height = size.height;
2046
2052
  },
2047
2053
 
2048
2054
  getLeft: function() {
@@ -2050,12 +2056,8 @@ var Rectangle = Base.extend({
2050
2056
  },
2051
2057
 
2052
2058
  setLeft: function(left) {
2053
- if (!this._fw) {
2054
- var amount = left - this.x;
2055
- this.width -= this._sx === 0.5 ? amount * 2 : amount;
2056
- }
2059
+ this.width -= left - this.x;
2057
2060
  this.x = left;
2058
- this._sx = this._fw = 0;
2059
2061
  },
2060
2062
 
2061
2063
  getTop: function() {
@@ -2063,12 +2065,8 @@ var Rectangle = Base.extend({
2063
2065
  },
2064
2066
 
2065
2067
  setTop: function(top) {
2066
- if (!this._fh) {
2067
- var amount = top - this.y;
2068
- this.height -= this._sy === 0.5 ? amount * 2 : amount;
2069
- }
2068
+ this.height -= top - this.y;
2070
2069
  this.y = top;
2071
- this._sy = this._fh = 0;
2072
2070
  },
2073
2071
 
2074
2072
  getRight: function() {
@@ -2076,13 +2074,7 @@ var Rectangle = Base.extend({
2076
2074
  },
2077
2075
 
2078
2076
  setRight: function(right) {
2079
- if (!this._fw) {
2080
- var amount = right - this.x;
2081
- this.width = this._sx === 0.5 ? amount * 2 : amount;
2082
- }
2083
- this.x = right - this.width;
2084
- this._sx = 1;
2085
- this._fw = 0;
2077
+ this.width = right - this.x;
2086
2078
  },
2087
2079
 
2088
2080
  getBottom: function() {
@@ -2090,13 +2082,7 @@ var Rectangle = Base.extend({
2090
2082
  },
2091
2083
 
2092
2084
  setBottom: function(bottom) {
2093
- if (!this._fh) {
2094
- var amount = bottom - this.y;
2095
- this.height = this._sy === 0.5 ? amount * 2 : amount;
2096
- }
2097
- this.y = bottom - this.height;
2098
- this._sy = 1;
2099
- this._fh = 0;
2085
+ this.height = bottom - this.y;
2100
2086
  },
2101
2087
 
2102
2088
  getCenterX: function() {
@@ -2104,16 +2090,7 @@ var Rectangle = Base.extend({
2104
2090
  },
2105
2091
 
2106
2092
  setCenterX: function(x) {
2107
- if (this._fw || this._sx === 0.5) {
2108
- this.x = x - this.width / 2;
2109
- } else {
2110
- if (this._sx) {
2111
- this.x += (x - this.x) * 2 * this._sx;
2112
- }
2113
- this.width = (x - this.x) * 2;
2114
- }
2115
- this._sx = 0.5;
2116
- this._fw = 0;
2093
+ this.x = x - this.width / 2;
2117
2094
  },
2118
2095
 
2119
2096
  getCenterY: function() {
@@ -2121,16 +2098,7 @@ var Rectangle = Base.extend({
2121
2098
  },
2122
2099
 
2123
2100
  setCenterY: function(y) {
2124
- if (this._fh || this._sy === 0.5) {
2125
- this.y = y - this.height / 2;
2126
- } else {
2127
- if (this._sy) {
2128
- this.y += (y - this.y) * 2 * this._sy;
2129
- }
2130
- this.height = (y - this.y) * 2;
2131
- }
2132
- this._sy = 0.5;
2133
- this._fh = 0;
2101
+ this.y = y - this.height / 2;
2134
2102
  },
2135
2103
 
2136
2104
  getCenter: function(_dontLink) {
@@ -2316,6 +2284,94 @@ new function() {
2316
2284
  );
2317
2285
  });
2318
2286
 
2287
+ var RectangleBuilder = Rectangle.extend({
2288
+ beans: true,
2289
+
2290
+ _fw: 1,
2291
+ _fh: 1,
2292
+
2293
+ setSize: function() {
2294
+ var size = Size.read(arguments),
2295
+ sx = this._sx,
2296
+ sy = this._sy,
2297
+ w = size.width,
2298
+ h = size.height;
2299
+ if (sx) {
2300
+ this.x += (this.width - w) * sx;
2301
+ }
2302
+ if (sy) {
2303
+ this.y += (this.height - h) * sy;
2304
+ }
2305
+ this.width = w;
2306
+ this.height = h;
2307
+ this._fw = this._fh = 1;
2308
+ },
2309
+
2310
+ setLeft: function(left) {
2311
+ if (!this._fw) {
2312
+ var amount = left - this.x;
2313
+ this.width -= this._sx === 0.5 ? amount * 2 : amount;
2314
+ }
2315
+ this.x = left;
2316
+ this._sx = this._fw = 0;
2317
+ },
2318
+
2319
+ setTop: function(top) {
2320
+ if (!this._fh) {
2321
+ var amount = top - this.y;
2322
+ this.height -= this._sy === 0.5 ? amount * 2 : amount;
2323
+ }
2324
+ this.y = top;
2325
+ this._sy = this._fh = 0;
2326
+ },
2327
+
2328
+ setRight: function(right) {
2329
+ if (!this._fw) {
2330
+ var amount = right - this.x;
2331
+ this.width = this._sx === 0.5 ? amount * 2 : amount;
2332
+ }
2333
+ this.x = right - this.width;
2334
+ this._sx = 1;
2335
+ this._fw = 0;
2336
+ },
2337
+
2338
+ setBottom: function(bottom) {
2339
+ if (!this._fh) {
2340
+ var amount = bottom - this.y;
2341
+ this.height = this._sy === 0.5 ? amount * 2 : amount;
2342
+ }
2343
+ this.y = bottom - this.height;
2344
+ this._sy = 1;
2345
+ this._fh = 0;
2346
+ },
2347
+
2348
+ setCenterX: function(x) {
2349
+ if (this._fw || this._sx === 0.5) {
2350
+ this.x = x - this.width / 2;
2351
+ } else {
2352
+ if (this._sx) {
2353
+ this.x += (x - this.x) * 2 * this._sx;
2354
+ }
2355
+ this.width = (x - this.x) * 2;
2356
+ }
2357
+ this._sx = 0.5;
2358
+ this._fw = 0;
2359
+ },
2360
+
2361
+ setCenterY: function(y) {
2362
+ if (this._fh || this._sy === 0.5) {
2363
+ this.y = y - this.height / 2;
2364
+ } else {
2365
+ if (this._sy) {
2366
+ this.y += (y - this.y) * 2 * this._sy;
2367
+ }
2368
+ this.height = (y - this.y) * 2;
2369
+ }
2370
+ this._sy = 0.5;
2371
+ this._fh = 0;
2372
+ }
2373
+ });
2374
+
2319
2375
  var Matrix = Base.extend({
2320
2376
  _class: 'Matrix',
2321
2377
 
@@ -2593,6 +2649,10 @@ var Matrix = Base.extend({
2593
2649
  return det && !isNaN(det) && isFinite(this._tx) && isFinite(this._ty);
2594
2650
  },
2595
2651
 
2652
+ _getMeanScale: function() {
2653
+ return Math.sqrt(Math.abs(this._a * this._d - this._b * this._c));
2654
+ },
2655
+
2596
2656
  isSingular: function() {
2597
2657
  return !this.isInvertible();
2598
2658
  },
@@ -2867,8 +2927,10 @@ var Line = Base.extend({
2867
2927
  : vy === 0 ? (vx < 0 ? y - py : py - y)
2868
2928
  : ((x - px) * vy - (y - py) * vx) / (
2869
2929
  vy > vx
2870
- ? vy * Math.sqrt(1 + (vx * vx) / (vy * vy))
2871
- : vx * Math.sqrt(1 + (vy * vy) / (vx * vx))
2930
+ ? Math.abs(vy)
2931
+ * Math.sqrt(1 + (vx * vx) / (vy * vy))
2932
+ : Math.abs(vx)
2933
+ * Math.sqrt(1 + (vy * vy) / (vx * vx))
2872
2934
  );
2873
2935
  },
2874
2936
 
@@ -2879,6 +2941,48 @@ var Line = Base.extend({
2879
2941
  }
2880
2942
  });
2881
2943
 
2944
+ var ChangeFlag = {
2945
+ APPEARANCE: 0x1,
2946
+
2947
+ CHILDREN: 0x2,
2948
+
2949
+ INSERTION: 0x4,
2950
+
2951
+ GEOMETRY: 0x8,
2952
+
2953
+ MATRIX: 0x10,
2954
+
2955
+ SEGMENTS: 0x20,
2956
+
2957
+ STROKE: 0x40,
2958
+
2959
+ STYLE: 0x80,
2960
+
2961
+ ATTRIBUTE: 0x100,
2962
+
2963
+ CONTENT: 0x200,
2964
+
2965
+ PIXELS: 0x400,
2966
+
2967
+ CLIPPING: 0x800,
2968
+
2969
+ VIEW: 0x1000
2970
+ };
2971
+
2972
+ var Change = {
2973
+ CHILDREN: ChangeFlag.CHILDREN | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2974
+ INSERTION: ChangeFlag.INSERTION | ChangeFlag.APPEARANCE,
2975
+ GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2976
+ MATRIX: ChangeFlag.MATRIX | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2977
+ SEGMENTS: ChangeFlag.SEGMENTS | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2978
+ STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE,
2979
+ STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE,
2980
+ ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE,
2981
+ CONTENT: ChangeFlag.CONTENT | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
2982
+ PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE,
2983
+ VIEW: ChangeFlag.VIEW | ChangeFlag.APPEARANCE
2984
+ };
2985
+
2882
2986
  var Project = PaperScopeItem.extend({
2883
2987
  _class: 'Project',
2884
2988
  _list: 'projects',
@@ -2912,7 +3016,7 @@ var Project = PaperScopeItem.extend({
2912
3016
  }
2913
3017
  }
2914
3018
  var changes = this._changes;
2915
- if (changes && item) {
3019
+ if (changes && item && item.isInserted()) {
2916
3020
  var changesById = this._changesById,
2917
3021
  id = item._id,
2918
3022
  entry = changesById[id];
@@ -2924,6 +3028,26 @@ var Project = PaperScopeItem.extend({
2924
3028
  }
2925
3029
  },
2926
3030
 
3031
+ getTrackChanges: function() {
3032
+ return !!this._changes;
3033
+ },
3034
+
3035
+ setTrackChanges: function(trackChanges) {
3036
+ if (!this._changes ^ !trackChanges) {
3037
+ this._changes = trackChanges ? [] : null;
3038
+ this._changesById = trackChanges ? {} : null;
3039
+ }
3040
+ },
3041
+
3042
+ takeChanges: function() {
3043
+ var changes = this._changes;
3044
+ if (changes) {
3045
+ this._changes = [];
3046
+ this._changesById = {};
3047
+ }
3048
+ return changes || null;
3049
+ },
3050
+
2927
3051
  clear: function() {
2928
3052
  var children = this._children;
2929
3053
  for (var i = children.length - 1; i >= 0; i--)
@@ -3005,10 +3129,10 @@ var Project = PaperScopeItem.extend({
3005
3129
  return items;
3006
3130
  },
3007
3131
 
3008
- _updateSelection: function(item) {
3132
+ _updateSelection: function(item, remove) {
3009
3133
  var id = item._id,
3010
3134
  selectionItems = this._selectionItems;
3011
- if (item._selection) {
3135
+ if (item._selection && !remove && item.isInserted()) {
3012
3136
  if (selectionItems[id] !== item) {
3013
3137
  this._selectionCount++;
3014
3138
  selectionItems[id] = item;
@@ -3117,7 +3241,7 @@ var Project = PaperScopeItem.extend({
3117
3241
 
3118
3242
  if (this._selectionCount > 0) {
3119
3243
  ctx.save();
3120
- ctx.strokeWidth = 1;
3244
+ ctx.lineWidth = 1;
3121
3245
  var items = this._selectionItems,
3122
3246
  size = this._scope.settings.handleSize,
3123
3247
  version = this._updateVersion;
@@ -3180,11 +3304,13 @@ new function() {
3180
3304
  function(name) {
3181
3305
  this._events[name] = {
3182
3306
  install: function(type) {
3183
- this.getView()._countItemEvent(type, 1);
3307
+ if (this.isInserted())
3308
+ this.getView()._countItemEvent(type, 1);
3184
3309
  },
3185
3310
 
3186
3311
  uninstall: function(type) {
3187
- this.getView()._countItemEvent(type, -1);
3312
+ if (this.isInserted())
3313
+ this.getView()._countItemEvent(type, -1);
3188
3314
  }
3189
3315
  };
3190
3316
  }, {
@@ -3217,6 +3343,10 @@ new function() {
3217
3343
  matrix = this._matrix = new Matrix(),
3218
3344
  project = hasProps && props.project || paper.project,
3219
3345
  settings = paper.settings;
3346
+ if (!project) {
3347
+ throw new Error('Unable to create an item without a project. '
3348
+ + 'Call paper.setup() or create a Project first.');
3349
+ }
3220
3350
  this._id = internal ? null : UID.get();
3221
3351
  this._parent = this._index = null;
3222
3352
  this._applyMatrix = this._canApplyMatrix && settings.applyMatrix;
@@ -3493,8 +3623,8 @@ new function() {
3493
3623
  bounds = this.getBounds();
3494
3624
  }
3495
3625
  matrix.scale(
3496
- bounds.width !== 0 ? rect.width / bounds.width : 0,
3497
- bounds.height !== 0 ? rect.height / bounds.height : 0);
3626
+ bounds.width !== 0 ? rect.width / bounds.width : 1,
3627
+ bounds.height !== 0 ? rect.height / bounds.height : 1);
3498
3628
  }
3499
3629
  center = bounds.getCenter();
3500
3630
  matrix.translate(-center.x, -center.y);
@@ -3518,14 +3648,18 @@ new function() {
3518
3648
  },
3519
3649
 
3520
3650
  _getCachedBounds: function(matrix, options, noInternal) {
3521
- matrix = matrix && matrix._orNullIfIdentity();
3522
3651
  var internal = options.internal && !noInternal,
3523
3652
  cacheItem = options.cacheItem,
3524
- _matrix = internal ? null : this._matrix._orNullIfIdentity(),
3525
- cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
3653
+ _matrix = internal ? null : this._matrix._orNullIfIdentity();
3654
+ matrix = matrix ? matrix._orNullIfIdentity() : _matrix;
3655
+ var cacheKey = cacheItem && (matrix ? matrix.equals(_matrix) : !_matrix)
3526
3656
  && this._getBoundsCacheKey(options, internal),
3657
+ isIntermediate = cacheKey && cacheItem !== this
3658
+ && (this._children || this._definition),
3527
3659
  bounds = this._bounds;
3528
3660
  Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
3661
+ if (isIntermediate)
3662
+ Item._updateBoundsCache(this, cacheItem);
3529
3663
  if (cacheKey && bounds && cacheKey in bounds) {
3530
3664
  var cached = bounds[cacheKey];
3531
3665
  return {
@@ -3533,7 +3667,9 @@ new function() {
3533
3667
  nonscaling: cached.nonscaling
3534
3668
  };
3535
3669
  }
3536
- var res = this._getBounds(matrix || _matrix, options),
3670
+ var res = this._getBounds(matrix, isIntermediate
3671
+ ? Base.set({}, options, { cacheItem: this })
3672
+ : options),
3537
3673
  rect = res.rect || res,
3538
3674
  style = this._style,
3539
3675
  nonscaling = res.nonscaling || style.hasStroke()
@@ -3697,27 +3833,14 @@ new function() {
3697
3833
  },
3698
3834
 
3699
3835
  getGlobalMatrix: function(_dontClone) {
3700
- var matrix = this._globalMatrix;
3701
- if (matrix) {
3702
- var parent = this._parent;
3703
- var parents = [];
3704
- while (parent) {
3705
- if (!parent._globalMatrix) {
3706
- matrix = null;
3707
- for (var i = 0, l = parents.length; i < l; i++) {
3708
- parents[i]._globalMatrix = null;
3709
- }
3710
- break;
3711
- }
3712
- parents.push(parent);
3713
- parent = parent._parent;
3714
- }
3715
- }
3716
- if (!matrix) {
3836
+ var parent = this._parent,
3837
+ parentMatrix = parent && parent.getGlobalMatrix(true),
3838
+ matrix = this._globalMatrix;
3839
+ if (!matrix || this._parentGlobalMatrix !== parentMatrix) {
3717
3840
  matrix = this._globalMatrix = this._matrix.clone();
3718
- var parent = this._parent;
3719
- if (parent)
3720
- matrix.prepend(parent.getGlobalMatrix(true));
3841
+ if (parentMatrix)
3842
+ matrix.prepend(parentMatrix);
3843
+ this._parentGlobalMatrix = parentMatrix;
3721
3844
  }
3722
3845
  return _dontClone ? matrix : matrix.clone();
3723
3846
  },
@@ -3744,13 +3867,10 @@ new function() {
3744
3867
 
3745
3868
  _setProject: function(project, installEvents) {
3746
3869
  if (this._project !== project) {
3747
- if (this._project)
3748
- this._installEvents(false);
3749
3870
  this._project = project;
3750
3871
  var children = this._children;
3751
3872
  for (var i = 0, l = children && children.length; i < l; i++)
3752
3873
  children[i]._setProject(project);
3753
- installEvents = true;
3754
3874
  }
3755
3875
  if (installEvents)
3756
3876
  this._installEvents(true);
@@ -3760,8 +3880,14 @@ new function() {
3760
3880
  return this._project._view;
3761
3881
  },
3762
3882
 
3883
+ getScope: function() {
3884
+ return this._project._scope;
3885
+ },
3886
+
3763
3887
  _installEvents: function _installEvents(install) {
3764
3888
  _installEvents.base.call(this, install);
3889
+ if (this._selection)
3890
+ this._project._updateSelection(this, !install);
3765
3891
  var children = this._children;
3766
3892
  for (var i = 0, l = children && children.length; i < l; i++)
3767
3893
  children[i]._installEvents(install);
@@ -3849,7 +3975,8 @@ new function() {
3849
3975
  var copy = new this.constructor(Item.NO_INSERT),
3850
3976
  children = this._children,
3851
3977
  insert = Base.pick(options ? options.insert : undefined,
3852
- options === undefined || options === true),
3978
+ typeof options === 'boolean' ? options
3979
+ : paper.settings.insertItems),
3853
3980
  deep = Base.pick(options ? options.deep : undefined, true);
3854
3981
  if (children)
3855
3982
  copy.copyAttributes(this);
@@ -3937,7 +4064,7 @@ new function() {
3937
4064
  .translate(topLeft.add(boundsSize.divide(2)))
3938
4065
  .scale(1 / scale)
3939
4066
  );
3940
- if (insert === undefined || insert) {
4067
+ if (Base.pick(insert, paper.settings.insertItems)) {
3941
4068
  raster.insertAbove(this);
3942
4069
  }
3943
4070
  return raster;
@@ -3970,6 +4097,7 @@ new function() {
3970
4097
  _asPathItem: function() {
3971
4098
  return new Path.Rectangle({
3972
4099
  rectangle: this.getInternalBounds(),
4100
+ applyMatrix: false,
3973
4101
  matrix: this._matrix,
3974
4102
  insert: false,
3975
4103
  });
@@ -4028,20 +4156,22 @@ new function() {
4028
4156
 
4029
4157
  _hitTest: function(point, options, parentViewMatrix) {
4030
4158
  if (this._locked || !this._visible || this._guide && !options.guides
4031
- || this.isEmpty()) {
4159
+ || !this._shell && this.isEmpty()) {
4032
4160
  return null;
4033
4161
  }
4034
4162
 
4035
4163
  var matrix = this._matrix,
4164
+ pointMatrix = parentViewMatrix ? matrix : this.getGlobalMatrix(),
4036
4165
  viewMatrix = parentViewMatrix
4037
4166
  ? parentViewMatrix.appended(matrix)
4038
- : this.getGlobalMatrix().prepend(this.getView()._matrix),
4167
+ : pointMatrix.prepended(this.getView()._matrix),
4039
4168
  tolerance = Math.max(options.tolerance, 1e-12),
4040
4169
  tolerancePadding = options._tolerancePadding = new Size(
4041
- Path._getStrokePadding(tolerance,
4042
- matrix._shiftless().invert()));
4043
- point = matrix._inverseTransform(point);
4044
- if (!point || !this._children &&
4170
+ Path._getStrokePadding(tolerance, viewMatrix
4171
+ .prepended(this.getView()._matrix.inverted())
4172
+ ._shiftless().invert()));
4173
+ point = pointMatrix._inverseTransform(point);
4174
+ if (!point || (this._shell || !this._children) &&
4045
4175
  !this.getBounds({ internal: true, stroke: true, handle: true })
4046
4176
  .expand(tolerancePadding.multiply(2))._containsPoint(point)) {
4047
4177
  return null;
@@ -4057,10 +4187,21 @@ new function() {
4057
4187
  res;
4058
4188
 
4059
4189
  function filter(hit) {
4190
+ if (Array.isArray(hit)) {
4191
+ var first = null;
4192
+ for (var i = 0, l = hit.length; i < l; i++) {
4193
+ var res = filter(hit[i]);
4194
+ first = first || res;
4195
+ }
4196
+ return first;
4197
+ }
4060
4198
  if (hit && match && !match(hit))
4061
4199
  hit = null;
4062
- if (hit && options.all)
4200
+ if (hit && options.all) {
4201
+ if (hit.point)
4202
+ hit.point = that.getGlobalMatrix().transform(hit.point);
4063
4203
  options.all.push(hit);
4204
+ }
4064
4205
  return hit;
4065
4206
  }
4066
4207
 
@@ -4104,14 +4245,14 @@ new function() {
4104
4245
  : viewMatrix._shiftless().invert()))
4105
4246
  || null;
4106
4247
  }
4107
- if (res && res.point) {
4108
- res.point = matrix.transform(res.point);
4248
+ if (res && res.point && !options.all) {
4249
+ res.point = pointMatrix.transform(res.point);
4109
4250
  }
4110
4251
  return res;
4111
4252
  },
4112
4253
 
4113
4254
  _hitTestSelf: function(point, options) {
4114
- if (options.fill && this.hasFill() && this._contains(point))
4255
+ if (options.fill && this.getFillColor() && this._contains(point))
4115
4256
  return new HitResult('fill', this);
4116
4257
  },
4117
4258
 
@@ -4185,7 +4326,7 @@ new function() {
4185
4326
  rect = bounds && Rectangle.read([bounds]);
4186
4327
  param = {
4187
4328
  items: [],
4188
- recursive: obj && obj.recursive !== false,
4329
+ recursive: !obj || obj.recursive !== false,
4189
4330
  inside: !!inside,
4190
4331
  overlapping: !!overlapping,
4191
4332
  rect: rect,
@@ -4234,6 +4375,19 @@ new function() {
4234
4375
  }, {
4235
4376
 
4236
4377
  importJSON: function(json) {
4378
+ json = typeof json === 'string' ? JSON.parse(json) : json;
4379
+ var props = json && json[0] === this._class && json[1];
4380
+ if (Base.isPlainObject(props)) {
4381
+ var item = new this.constructor(Item.NO_INSERT),
4382
+ fields = Base.set({}, this._serializeFields,
4383
+ this instanceof Group ? null : this._style._defaults),
4384
+ defaults = {};
4385
+ for (var key in fields) {
4386
+ if (!(key in props) && !Base.equals(this[key], item[key]))
4387
+ defaults[key] = item[key];
4388
+ }
4389
+ this.set(defaults);
4390
+ }
4237
4391
  var res = Base.importJSON(json, this);
4238
4392
  return res !== this ? this.addChild(res) : res;
4239
4393
  },
@@ -4379,15 +4533,15 @@ new function() {
4379
4533
  if (owner) {
4380
4534
  if (this._name)
4381
4535
  this._removeNamed();
4536
+ this._installEvents(false);
4537
+ if (notifySelf && project._changes)
4538
+ this._changed(5);
4382
4539
  if (index != null) {
4383
4540
  if (project._activeLayer === this)
4384
4541
  project._activeLayer = this.getNextSibling()
4385
4542
  || this.getPreviousSibling();
4386
4543
  Base.splice(owner._children, null, index, 1);
4387
4544
  }
4388
- this._installEvents(false);
4389
- if (notifySelf && project._changes)
4390
- this._changed(5);
4391
4545
  if (notifyParent)
4392
4546
  owner._changed(11, this);
4393
4547
  this._parent = null;
@@ -4644,30 +4798,46 @@ new function() {
4644
4798
  return this._matrix._transformPoint(Point.read(arguments));
4645
4799
  },
4646
4800
 
4647
- fitBounds: function(rectangle, fill) {
4801
+ fitBounds: function(rectangle, fit) {
4648
4802
  rectangle = Rectangle.read(arguments);
4649
- fill = Base.read(arguments);
4803
+ fit = Base.read(arguments);
4804
+ if (fit === true)
4805
+ fit = 'cover';
4650
4806
  var bounds = this.getBounds(),
4651
- itemRatio = bounds.height / bounds.width,
4652
- rectRatio = rectangle.height / rectangle.width,
4653
- scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
4654
- ? rectangle.width / bounds.width
4655
- : rectangle.height / bounds.height,
4656
- newBounds = new Rectangle(new Point(),
4657
- new Size(bounds.width * scale, bounds.height * scale));
4658
- newBounds.setCenter(rectangle.getCenter());
4659
- this.setBounds(newBounds);
4807
+ hor = rectangle.width / bounds.width,
4808
+ ver = rectangle.height / bounds.height,
4809
+ size;
4810
+
4811
+ function isScaling(ratio) {
4812
+ return ratio > 0 && isFinite(ratio);
4813
+ }
4814
+
4815
+ if (fit === 'fill') {
4816
+ size = new Size(
4817
+ isScaling(hor) ? rectangle.width : bounds.width,
4818
+ isScaling(ver) ? rectangle.height : bounds.height);
4819
+ } else {
4820
+ var ratios = [hor, ver].filter(isScaling),
4821
+ scale = !ratios.length ? 1
4822
+ : fit === 'cover' ? Math.max.apply(Math, ratios)
4823
+ : Math.min.apply(Math, ratios);
4824
+ size = bounds.getSize().multiply(scale);
4825
+ }
4826
+ this.setBounds(new Rectangle(new Point(), size)
4827
+ .setCenter(rectangle.getCenter()));
4660
4828
  }
4661
4829
  }), {
4662
4830
 
4663
- _setStyles: function(ctx, param, viewMatrix) {
4831
+ _setStyles: function(ctx, param, viewMatrix, strokeMatrix) {
4664
4832
  var style = this._style,
4665
4833
  matrix = this._matrix;
4666
4834
  if (style.hasFill()) {
4667
- ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
4835
+ ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix,
4836
+ strokeMatrix);
4668
4837
  }
4669
4838
  if (style.hasStroke()) {
4670
- ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix);
4839
+ ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx,
4840
+ matrix, strokeMatrix);
4671
4841
  ctx.lineWidth = style.getStrokeWidth();
4672
4842
  var strokeJoin = style.getStrokeJoin(),
4673
4843
  strokeCap = style.getStrokeCap(),
@@ -4712,7 +4882,8 @@ new function() {
4712
4882
  var matrices = param.matrices,
4713
4883
  viewMatrix = param.viewMatrix,
4714
4884
  matrix = this._matrix,
4715
- globalMatrix = matrices[matrices.length - 1].appended(matrix);
4885
+ parentMatrix = matrices[matrices.length - 1],
4886
+ globalMatrix = parentMatrix.appended(matrix);
4716
4887
  if (!globalMatrix.isInvertible())
4717
4888
  return;
4718
4889
 
@@ -4722,13 +4893,14 @@ new function() {
4722
4893
  matrices.push(globalMatrix);
4723
4894
  if (param.updateMatrix) {
4724
4895
  this._globalMatrix = globalMatrix;
4896
+ this._parentGlobalMatrix = this._parent && parentMatrix;
4725
4897
  }
4726
4898
 
4727
4899
  var blendMode = this._blendMode,
4728
4900
  opacity = Numerical.clamp(this._opacity, 0, 1),
4729
4901
  normalBlend = blendMode === 'normal',
4730
4902
  nativeBlend = BlendMode.nativeModes[blendMode],
4731
- direct = normalBlend && opacity === 1
4903
+ direct = normalBlend && opacity === 1 && !this._isolated
4732
4904
  || param.dontStart
4733
4905
  || param.clip
4734
4906
  || (nativeBlend || normalBlend && opacity < 1)
@@ -4736,8 +4908,23 @@ new function() {
4736
4908
  pixelRatio = param.pixelRatio || 1,
4737
4909
  mainCtx, itemOffset, prevOffset;
4738
4910
  if (!direct) {
4739
- var bounds = this.getStrokeBounds(viewMatrix);
4740
- if (!bounds.width || !bounds.height) {
4911
+ var bounds = this.getStrokeBounds(viewMatrix),
4912
+ style = this._style;
4913
+ if (style.getShadowColor(true)) {
4914
+ var mx = viewMatrix._shiftless(),
4915
+ blur = mx.transform(
4916
+ new Point(style.getShadowBlur(true), 0)),
4917
+ offset = mx.transform(style.getShadowOffset(true));
4918
+ bounds = bounds.unite(
4919
+ new Rectangle(bounds.getPoint().add(offset),
4920
+ bounds.getSize()).expand(blur.getLength() * 3));
4921
+ }
4922
+ var canvas = ctx.canvas;
4923
+ bounds = bounds.intersect(
4924
+ new Rectangle(new Point(param.offset),
4925
+ new Size(canvas.width, canvas.height)
4926
+ .divide(pixelRatio)));
4927
+ if (bounds.width <= 0 || bounds.height <= 0) {
4741
4928
  matrices.pop();
4742
4929
  return;
4743
4930
  }
@@ -4763,11 +4950,16 @@ new function() {
4763
4950
  } else if (transform) {
4764
4951
  ctx.translate(-itemOffset.x, -itemOffset.y);
4765
4952
  }
4766
- if (transform) {
4767
- (direct ? matrix : viewMatrix).applyToContext(ctx);
4768
- }
4769
4953
  if (clip) {
4770
- param.clipItem.draw(ctx, param.extend({ clip: true }));
4954
+ (param.viewMatrix ? param.viewMatrix.appended(parentMatrix)
4955
+ : parentMatrix).applyToContext(ctx);
4956
+ param.clipItem.draw(ctx, param.extend({
4957
+ clip: true,
4958
+ matrices: matrices.slice(0, -1)
4959
+ }));
4960
+ matrix.applyToContext(ctx);
4961
+ } else if (transform) {
4962
+ (direct ? matrix : viewMatrix).applyToContext(ctx);
4771
4963
  }
4772
4964
  if (strokeMatrix) {
4773
4965
  ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -4811,7 +5003,7 @@ new function() {
4811
5003
  if (!this._drawSelected)
4812
5004
  itemSelected = false;
4813
5005
  if ((itemSelected || boundsSelected || positionSelected)
4814
- && this._isUpdated(updateVersion)) {
5006
+ && this._isUpdated(updateVersion) && this.isInserted()) {
4815
5007
  var layer,
4816
5008
  color = this.getSelectedColor(true) || (layer = this.getLayer())
4817
5009
  && layer.getSelectedColor(true),
@@ -4924,8 +5116,10 @@ var Group = Item.extend({
4924
5116
  _selectBounds: false,
4925
5117
  _selectChildren: true,
4926
5118
  _serializeFields: {
5119
+ isolated: false,
4927
5120
  children: []
4928
5121
  },
5122
+ _isolated: false,
4929
5123
 
4930
5124
  initialize: function Group(arg) {
4931
5125
  this._children = [];
@@ -4967,6 +5161,15 @@ var Group = Item.extend({
4967
5161
  child.setClipMask(clipped);
4968
5162
  },
4969
5163
 
5164
+ isIsolated: function() {
5165
+ return this._isolated;
5166
+ },
5167
+
5168
+ setIsolated: function(isolated) {
5169
+ this._isolated = !!isolated;
5170
+ this._changed(257);
5171
+ },
5172
+
4970
5173
  _getBounds: function _getBounds(matrix, options) {
4971
5174
  var clipItem = this._getClipItem();
4972
5175
  return clipItem
@@ -4982,10 +5185,21 @@ var Group = Item.extend({
4982
5185
  clipItem);
4983
5186
  },
4984
5187
 
5188
+ _hitTestSelf: function(point, options, viewMatrix) {
5189
+ if (options.match || options.class || options.type || options.all) {
5190
+ var res = this._hitTestChildren(point, options.extend({
5191
+ all: null, match: null, class: null, type: null
5192
+ }), viewMatrix);
5193
+ if (res)
5194
+ res.item = this;
5195
+ return res;
5196
+ }
5197
+ },
5198
+
4985
5199
  _draw: function(ctx, param) {
4986
5200
  var clip = param.clip,
4987
- clipItem = !clip && this._getClipItem();
4988
- param = param.extend({ clipItem: clipItem, clip: false });
5201
+ clipItem = this._getClipItem();
5202
+ param = param.extend({ clipItem: !clip && clipItem, clip: false });
4989
5203
  if (clip) {
4990
5204
  ctx.beginPath();
4991
5205
  param.dontStart = param.dontFinish = true;
@@ -5034,6 +5248,7 @@ var Shape = Item.extend({
5034
5248
  size: null,
5035
5249
  radius: null
5036
5250
  },
5251
+ _prioritize: ['type'],
5037
5252
 
5038
5253
  initialize: function Shape(props, point) {
5039
5254
  this._initialize(props, point);
@@ -5131,16 +5346,10 @@ var Shape = Item.extend({
5131
5346
  },
5132
5347
 
5133
5348
  toPath: function(insert) {
5134
- var path = new Path[Base.capitalize(this._type)]({
5135
- center: new Point(),
5136
- size: this._size,
5137
- radius: this._radius,
5138
- insert: false
5139
- });
5140
- path.copyAttributes(this);
5349
+ var path = this._asPathItem();
5141
5350
  if (paper.settings.applyMatrix)
5142
5351
  path.setApplyMatrix(true);
5143
- if (insert === undefined || insert)
5352
+ if (Base.pick(insert, paper.settings.insertItems))
5144
5353
  path.insertAbove(this);
5145
5354
  return path;
5146
5355
  },
@@ -5148,7 +5357,14 @@ var Shape = Item.extend({
5148
5357
  toShape: '#clone',
5149
5358
 
5150
5359
  _asPathItem: function() {
5151
- return this.toPath(false);
5360
+ var path = new Path[Base.capitalize(this._type)]({
5361
+ center: new Point(),
5362
+ size: this._size,
5363
+ radius: this._radius,
5364
+ insert: false
5365
+ });
5366
+ path.copyAttributes(this);
5367
+ return path;
5152
5368
  },
5153
5369
 
5154
5370
  _draw: function(ctx, param, viewMatrix, strokeMatrix) {
@@ -5215,7 +5431,7 @@ var Shape = Item.extend({
5215
5431
  ctx.closePath();
5216
5432
  }
5217
5433
  if (!dontPaint && (hasFill || hasStroke)) {
5218
- this._setStyles(ctx, param, viewMatrix);
5434
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
5219
5435
  if (hasFill) {
5220
5436
  ctx.fill(style.getFillRule());
5221
5437
  ctx.shadowColor = 'rgba(0,0,0,0)';
@@ -5230,12 +5446,25 @@ var Shape = Item.extend({
5230
5446
  },
5231
5447
 
5232
5448
  _getBounds: function(matrix, options) {
5233
- var rect = new Rectangle(this._size).setCenter(0, 0),
5449
+ var size = this._size,
5450
+ rect = new Rectangle(size).setCenter(0, 0),
5234
5451
  style = this._style,
5235
5452
  strokeWidth = options.stroke && style.hasStroke()
5236
5453
  && style.getStrokeWidth();
5237
- if (matrix)
5238
- rect = matrix._transformBounds(rect);
5454
+ if (matrix) {
5455
+ if (this._type === 'rectangle') {
5456
+ rect = matrix._transformBounds(rect);
5457
+ } else {
5458
+ var rx = size.width / 2,
5459
+ ry = size.height / 2,
5460
+ dx = Math.sqrt(Math.pow(matrix._a * rx, 2)
5461
+ + Math.pow(matrix._c * ry, 2)),
5462
+ dy = Math.sqrt(Math.pow(matrix._b * rx, 2)
5463
+ + Math.pow(matrix._d * ry, 2));
5464
+ rect = new Rectangle(matrix._tx - dx, matrix._ty - dy,
5465
+ 2 * dx, 2 * dy);
5466
+ }
5467
+ }
5239
5468
  return strokeWidth
5240
5469
  ? rect.expand(Path._getStrokePadding(strokeWidth,
5241
5470
  this._getStrokeMatrix(matrix, options)))
@@ -5285,7 +5514,7 @@ new function() {
5285
5514
  var hit = false,
5286
5515
  style = this._style,
5287
5516
  hitStroke = options.stroke && style.hasStroke(),
5288
- hitFill = options.fill && style.hasFill();
5517
+ hitFill = options.fill && !!style.getFillColor();
5289
5518
  if (hitStroke || hitFill) {
5290
5519
  var type = this._type,
5291
5520
  radius = this._radius,
@@ -5375,6 +5604,7 @@ var Raster = Item.extend({
5375
5604
  _boundsOptions: { stroke: false, handle: false },
5376
5605
  _serializeFields: {
5377
5606
  crossOrigin: null,
5607
+ size: new Size(),
5378
5608
  source: null
5379
5609
  },
5380
5610
  _prioritize: ['crossOrigin'],
@@ -5524,6 +5754,14 @@ var Raster = Item.extend({
5524
5754
  }
5525
5755
  },
5526
5756
 
5757
+ set: function set(props, exclude) {
5758
+ if (props && props.source && props.size) {
5759
+ this._size = new Size(props.size);
5760
+ exclude = Base.set({ size: true }, exclude);
5761
+ }
5762
+ return set.base.call(this, props, exclude);
5763
+ },
5764
+
5527
5765
  _setImage: function(image) {
5528
5766
  if (this._canvas)
5529
5767
  CanvasProvider.release(this._canvas);
@@ -5536,9 +5774,11 @@ var Raster = Item.extend({
5536
5774
  this._canvas = null;
5537
5775
  this._loaded = !!(image && image.src && image.complete);
5538
5776
  }
5539
- this._size = new Size(
5540
- image ? image.naturalWidth || image.width : 0,
5541
- image ? image.naturalHeight || image.height : 0);
5777
+ this._size = image && !this._loaded
5778
+ ? this._size || new Size()
5779
+ : new Size(
5780
+ image ? image.naturalWidth || image.width : 0,
5781
+ image ? image.naturalHeight || image.height : 0);
5542
5782
  this._context = null;
5543
5783
  this._changed(1033);
5544
5784
  },
@@ -5777,7 +6017,12 @@ var Raster = Item.extend({
5777
6017
 
5778
6018
  _draw: function(ctx, param, viewMatrix) {
5779
6019
  var element = this.getElement();
5780
- if (element && element.width > 0 && element.height > 0) {
6020
+ if (param.dontFinish || param.clip) {
6021
+ var rect = this.getInternalBounds();
6022
+ if (!param.dontStart)
6023
+ ctx.beginPath();
6024
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
6025
+ } else if (element && element.width > 0 && element.height > 0) {
5781
6026
  ctx.globalAlpha = Numerical.clamp(this._opacity, 0, 1);
5782
6027
 
5783
6028
  this._setStyles(ctx, param, viewMatrix);
@@ -5846,7 +6091,9 @@ var SymbolItem = Item.extend({
5846
6091
  },
5847
6092
 
5848
6093
  _hitTestSelf: function(point, options, viewMatrix) {
5849
- var opts = options.extend({ all: false });
6094
+ var opts = options.extend({
6095
+ all: false, match: null, class: null, type: null
6096
+ });
5850
6097
  var res = this._definition._item._hitTest(point, opts, viewMatrix);
5851
6098
  if (res)
5852
6099
  res.item = this;
@@ -5854,7 +6101,7 @@ var SymbolItem = Item.extend({
5854
6101
  },
5855
6102
 
5856
6103
  _draw: function(ctx, param) {
5857
- this._definition._item.draw(ctx, param);
6104
+ this._definition._item.draw(ctx, param.extend({ updateMatrix: false }));
5858
6105
  }
5859
6106
 
5860
6107
  });
@@ -6886,7 +7133,7 @@ statics: {
6886
7133
  }
6887
7134
  }
6888
7135
  }}, Base.each(
6889
- ['getBounds', 'getStrokeBounds', 'getHandleBounds'],
7136
+ ['getBounds', 'getStrokeBounds'],
6890
7137
  function(name) {
6891
7138
  this[name] = function() {
6892
7139
  if (!this._bounds)
@@ -6901,6 +7148,15 @@ statics: {
6901
7148
  },
6902
7149
  {
6903
7150
 
7151
+ getHandleBounds: function() {
7152
+ var v = this.getValues(),
7153
+ x1 = Math.min(v[0], v[2], v[4], v[6]),
7154
+ y1 = Math.min(v[1], v[3], v[5], v[7]),
7155
+ x2 = Math.max(v[0], v[2], v[4], v[6]),
7156
+ y2 = Math.max(v[1], v[3], v[5], v[7]);
7157
+ return new Rectangle(x1, y1, x2 - x1, y2 - y1);
7158
+ },
7159
+
6904
7160
  }), Base.each({
6905
7161
  isStraight: function(p1, h1, h2, p2) {
6906
7162
  if (h1.isZero() && h2.isZero()) {
@@ -7693,8 +7949,8 @@ new function() {
7693
7949
  den = 2 * ax * ty - 2 * ay * tx,
7694
7950
  times = [];
7695
7951
  if (Math.abs(den) < Numerical.CURVETIME_EPSILON) {
7696
- var num = ax * cy - ay * cx,
7697
- den = ax * by - ay * bx;
7952
+ var num = cx * ty - cy * tx,
7953
+ den = bx * ty - by * tx;
7698
7954
  if (den != 0) {
7699
7955
  var t = -num / den;
7700
7956
  if (t >= 0 && t <= 1) times.push(t);
@@ -8097,6 +8353,15 @@ var PathItem = Item.extend({
8097
8353
  initialize: function PathItem() {
8098
8354
  },
8099
8355
 
8356
+ _scaleStroke: function(matrix) {
8357
+ var style = this._style;
8358
+ if (this._project._scope.settings.scaleStrokes
8359
+ && style.getStrokeScaling()) {
8360
+ style.setStrokeWidth(style.getStrokeWidth()
8361
+ * matrix._getMeanScale());
8362
+ }
8363
+ },
8364
+
8100
8365
  statics: {
8101
8366
  create: function(arg) {
8102
8367
  var data,
@@ -8143,7 +8408,12 @@ var PathItem = Item.extend({
8143
8408
  previous,
8144
8409
  control,
8145
8410
  current = new Point(),
8146
- start = new Point();
8411
+ start = new Point(),
8412
+ number = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][+-]?\\d+)?)',
8413
+ numbers = new RegExp(number, 'g'),
8414
+ flag = '([01])',
8415
+ arcs = new RegExp([number, number, number, flag, flag, number,
8416
+ number].join('[\\s,]*'), 'g');
8147
8417
 
8148
8418
  function getCoord(index, coord) {
8149
8419
  var val = +coords[index];
@@ -8165,7 +8435,7 @@ var PathItem = Item.extend({
8165
8435
  var part = parts[i],
8166
8436
  command = part[0],
8167
8437
  lower = command.toLowerCase();
8168
- coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
8438
+ coords = part.match(numbers);
8169
8439
  var length = coords && coords.length;
8170
8440
  relative = command === lower;
8171
8441
  if (previous === 'z' && !/[mz]/.test(lower))
@@ -8230,10 +8500,10 @@ var PathItem = Item.extend({
8230
8500
  }
8231
8501
  break;
8232
8502
  case 'a':
8233
- for (var j = 0; j < length; j += 7) {
8234
- this.arcTo(current = getPoint(j + 5),
8235
- new Size(+coords[j], +coords[j + 1]),
8236
- +coords[j + 2], +coords[j + 4], +coords[j + 3]);
8503
+ while (coords = arcs.exec(part)) {
8504
+ this.arcTo(current = getPoint(6),
8505
+ new Size(+coords[1], +coords[2]),
8506
+ +coords[3], +coords[5], +coords[4]);
8237
8507
  }
8238
8508
  break;
8239
8509
  case 'z':
@@ -8560,6 +8830,7 @@ var Path = PathItem.extend({
8560
8830
  coords = new Array(6);
8561
8831
  for (var i = 0, l = segments.length; i < l; i++)
8562
8832
  segments[i]._transformCoordinates(matrix, coords, true);
8833
+ this._scaleStroke(matrix);
8563
8834
  return true;
8564
8835
  },
8565
8836
 
@@ -8667,6 +8938,9 @@ var Path = PathItem.extend({
8667
8938
  removeSegments: function(start, end, _includeCurves) {
8668
8939
  start = start || 0;
8669
8940
  end = Base.pick(end, this._segments.length);
8941
+ if (start > end && this._closed)
8942
+ return this.removeSegments(start).concat(
8943
+ this.removeSegments(0, end));
8670
8944
  var segments = this._segments,
8671
8945
  curves = this._curves,
8672
8946
  count = segments.length,
@@ -8795,7 +9069,7 @@ var Path = PathItem.extend({
8795
9069
  time = 0;
8796
9070
  }
8797
9071
  var curves = this.getCurves();
8798
- if (index >= 0 && index < curves.length) {
9072
+ if (loc && index < curves.length) {
8799
9073
  if (time >= tMin) {
8800
9074
  curves[index++].divideAtTime(time);
8801
9075
  }
@@ -9107,7 +9381,7 @@ var Path = PathItem.extend({
9107
9381
  shape.copyAttributes(this, true);
9108
9382
  shape._matrix.prepend(this._matrix);
9109
9383
  shape.rotate(topCenter.subtract(center).getAngle() + 90);
9110
- if (insert === undefined || insert)
9384
+ if (Base.pick(insert, paper.settings.insertItems))
9111
9385
  shape.insertAbove(this);
9112
9386
  return shape;
9113
9387
  }
@@ -9187,8 +9461,9 @@ var Path = PathItem.extend({
9187
9461
  strokePadding = tolerancePadding,
9188
9462
  join, cap, miterLimit,
9189
9463
  area, loc, res,
9464
+ hits = options.all && [],
9190
9465
  hitStroke = options.stroke && style.hasStroke(),
9191
- hitFill = options.fill && style.hasFill(),
9466
+ hitFill = options.fill && !!style.getFillColor(),
9192
9467
  hitCurves = options.curves,
9193
9468
  strokeRadius = hitStroke
9194
9469
  ? style.getStrokeWidth() / 2
@@ -9216,10 +9491,13 @@ var Path = PathItem.extend({
9216
9491
  if (pt !== anchor)
9217
9492
  pt = pt.add(anchor);
9218
9493
  if (isCloseEnough(pt, strokePadding)) {
9219
- return new HitResult(name, that, {
9494
+ var hit = new HitResult(name, that, {
9220
9495
  segment: seg,
9221
9496
  point: pt
9222
9497
  });
9498
+ if (!hits)
9499
+ return hit;
9500
+ hits.push(hit);
9223
9501
  }
9224
9502
  }
9225
9503
  }
@@ -9248,7 +9526,7 @@ var Path = PathItem.extend({
9248
9526
  Path._addBevelJoin(segment, join, strokeRadius,
9249
9527
  miterLimit, null, strokeMatrix, addToArea, true);
9250
9528
  }
9251
- } else if (cap === 'square') {
9529
+ } else {
9252
9530
  Path._addSquareCap(segment, cap, strokeRadius, null,
9253
9531
  strokeMatrix, addToArea, true);
9254
9532
  }
@@ -9293,7 +9571,7 @@ var Path = PathItem.extend({
9293
9571
  }
9294
9572
  }
9295
9573
  }
9296
- return !loc && hitFill && this._contains(point)
9574
+ res = !loc && hitFill && this._contains(point)
9297
9575
  || loc && !hitStroke && !hitCurves
9298
9576
  ? new HitResult('fill', this)
9299
9577
  : loc
@@ -9302,6 +9580,16 @@ var Path = PathItem.extend({
9302
9580
  point: loc.getPoint()
9303
9581
  })
9304
9582
  : null;
9583
+ if (hits) {
9584
+ hits.sort(function(hit1, hit2) {
9585
+ return hit1.point.getDistance(point)
9586
+ - hit2.point.getDistance(point);
9587
+ });
9588
+ if (res)
9589
+ hits.push(res);
9590
+ return hits;
9591
+ }
9592
+ return res;
9305
9593
  }
9306
9594
 
9307
9595
  }, Base.each(Curve._evaluateMethods,
@@ -9501,7 +9789,7 @@ new function() {
9501
9789
  }
9502
9790
 
9503
9791
  if (!dontPaint && (hasFill || hasStroke)) {
9504
- this._setStyles(ctx, param, viewMatrix);
9792
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
9505
9793
  if (hasFill) {
9506
9794
  ctx.fill(style.getFillRule());
9507
9795
  ctx.shadowColor = 'rgba(0,0,0,0)';
@@ -9881,18 +10169,12 @@ statics: {
9881
10169
  _getStrokePadding: function(radius, matrix) {
9882
10170
  if (!matrix)
9883
10171
  return [radius, radius];
9884
- var hor = new Point(radius, 0).transform(matrix),
9885
- ver = new Point(0, radius).transform(matrix),
9886
- phi = hor.getAngleInRadians(),
9887
- a = hor.getLength(),
9888
- b = ver.getLength();
9889
- var sin = Math.sin(phi),
9890
- cos = Math.cos(phi),
9891
- tan = Math.tan(phi),
9892
- tx = Math.atan2(b * tan, a),
9893
- ty = Math.atan2(b, tan * a);
9894
- return [Math.abs(a * Math.cos(tx) * cos + b * Math.sin(tx) * sin),
9895
- Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
10172
+ var a = matrix._a,
10173
+ b = matrix._b,
10174
+ c = matrix._c,
10175
+ d = matrix._d;
10176
+ return [radius * Math.sqrt(a * a + c * c),
10177
+ radius * Math.sqrt(b * b + d * d)];
9896
10178
  },
9897
10179
 
9898
10180
  _addBevelJoin: function(segment, join, radius, miterLimit, matrix,
@@ -9900,44 +10182,42 @@ statics: {
9900
10182
  var curve2 = segment.getCurve(),
9901
10183
  curve1 = curve2.getPrevious(),
9902
10184
  point = curve2.getPoint1().transform(matrix),
9903
- normal1 = curve1.getNormalAtTime(1).multiply(radius)
9904
- .transform(strokeMatrix),
9905
- normal2 = curve2.getNormalAtTime(0).multiply(radius)
9906
- .transform(strokeMatrix),
9907
- angle = normal1.getDirectedAngle(normal2);
10185
+ normal1 = curve1.getNormalAtTime(1).multiply(radius),
10186
+ normal2 = curve2.getNormalAtTime(0).multiply(radius),
10187
+ angle = normal1.getDirectedAngle(normal2);
9908
10188
  if (angle < 0 || angle >= 180) {
9909
10189
  normal1 = normal1.negate();
9910
10190
  normal2 = normal2.negate();
9911
10191
  }
9912
10192
  if (isArea)
9913
10193
  addPoint(point);
9914
- addPoint(point.add(normal1));
10194
+ addPoint(point.add(normal1.transform(strokeMatrix)));
9915
10195
  if (join === 'miter') {
9916
- var corner = new Line(point.add(normal1),
10196
+ var corner = new Line(normal1,
9917
10197
  new Point(-normal1.y, normal1.x), true
9918
- ).intersect(new Line(point.add(normal2),
10198
+ ).intersect(new Line(normal2,
9919
10199
  new Point(-normal2.y, normal2.x), true
9920
10200
  ), true);
9921
- if (corner && point.getDistance(corner) <= miterLimit * radius) {
9922
- addPoint(corner);
10201
+ if (corner && corner.getLength() <= miterLimit * radius) {
10202
+ addPoint(point.add(corner.transform(strokeMatrix)));
9923
10203
  }
9924
10204
  }
9925
- addPoint(point.add(normal2));
10205
+ addPoint(point.add(normal2.transform(strokeMatrix)));
9926
10206
  },
9927
10207
 
9928
10208
  _addSquareCap: function(segment, cap, radius, matrix, strokeMatrix,
9929
10209
  addPoint, isArea) {
9930
10210
  var point = segment._point.transform(matrix),
9931
10211
  loc = segment.getLocation(),
9932
- normal = loc.getNormal()
9933
- .multiply(loc.getTime() === 0 ? radius : -radius)
9934
- .transform(strokeMatrix);
10212
+ vector = loc.getNormal()
10213
+ .multiply(loc.getTime() === 0 ? radius : -radius),
10214
+ normal = vector.transform(strokeMatrix);
9935
10215
  if (cap === 'square') {
9936
10216
  if (isArea) {
9937
10217
  addPoint(point.subtract(normal));
9938
10218
  addPoint(point.add(normal));
9939
10219
  }
9940
- point = point.add(normal.rotate(-90));
10220
+ point = point.add(vector.rotate(-90).transform(strokeMatrix));
9941
10221
  }
9942
10222
  addPoint(point.add(normal));
9943
10223
  addPoint(point.subtract(normal));
@@ -9999,14 +10279,14 @@ Path.inject({ statics: new function() {
9999
10279
 
10000
10280
  function createPath(segments, closed, args) {
10001
10281
  var props = Base.getNamed(args),
10002
- path = new Path(props && (
10003
- props.insert == true ? Item.INSERT
10004
- : props.insert == false ? Item.NO_INSERT
10005
- : null
10006
- ));
10282
+ path = new Path(props && {
10283
+ insert: props.insert,
10284
+ project: props.project,
10285
+ parent: props.parent
10286
+ });
10007
10287
  path._add(segments);
10008
10288
  path._closed = closed;
10009
- return path.set(props, Item.INSERT);
10289
+ return path.set(props, { insert: true, project: true, parent: true });
10010
10290
  }
10011
10291
 
10012
10292
  function createEllipse(center, radius, args) {
@@ -10101,10 +10381,9 @@ Path.inject({ statics: new function() {
10101
10381
  center = Point.readNamed(args, 'center'),
10102
10382
  sides = Base.readNamed(args, 'sides'),
10103
10383
  radius = Base.readNamed(args, 'radius'),
10104
- step = 360 / sides,
10105
- three = sides % 3 === 0,
10106
- vector = new Point(0, three ? -radius : radius),
10107
- offset = three ? -1 : 0.5,
10384
+ step = -360 / sides,
10385
+ offset = sides % 2 ? 0 : -0.5,
10386
+ vector = new Point(0, -radius),
10108
10387
  segments = new Array(sides);
10109
10388
  for (var i = 0; i < sides; i++)
10110
10389
  segments[i] = new Segment(center.add(
@@ -10148,6 +10427,11 @@ var CompoundPath = PathItem.extend({
10148
10427
  }
10149
10428
  },
10150
10429
 
10430
+ _transformContent: function _transformContent(matrix) {
10431
+ this._scaleStroke(matrix);
10432
+ return _transformContent.base.apply(this, arguments);
10433
+ },
10434
+
10151
10435
  insertChildren: function insertChildren(index, items) {
10152
10436
  var list = items,
10153
10437
  first = list[0];
@@ -10275,7 +10559,7 @@ var CompoundPath = PathItem.extend({
10275
10559
  children[i].draw(ctx, param, strokeMatrix);
10276
10560
 
10277
10561
  if (!param.clip) {
10278
- this._setStyles(ctx, param, viewMatrix);
10562
+ this._setStyles(ctx, param, viewMatrix, strokeMatrix);
10279
10563
  var style = this._style;
10280
10564
  if (style.hasFill()) {
10281
10565
  ctx.fill(style.getFillRule());
@@ -10388,7 +10672,7 @@ PathItem.inject(new function() {
10388
10672
  var result = new CompoundPath(Item.NO_INSERT);
10389
10673
  result.addChildren(paths, true);
10390
10674
  result = result.reduce({ simplify: simplify });
10391
- if (!(options && options.insert == false)) {
10675
+ if (Base.pick(options && options.insert, paper.settings.insertItems)) {
10392
10676
  result.insertAbove(path2 && path1.isSibling(path2)
10393
10677
  && path1.getIndex() < path2.getIndex() ? path2 : path1);
10394
10678
  }
@@ -10403,7 +10687,7 @@ PathItem.inject(new function() {
10403
10687
  function traceBoolean(path1, path2, operation, options) {
10404
10688
  if (options && (options.trace == false || options.stroke) &&
10405
10689
  /^(subtract|intersect)$/.test(operation))
10406
- return splitBoolean(path1, path2, operation);
10690
+ return splitBoolean(path1, path2, operation, options);
10407
10691
  var _path1 = preparePath(path1, true),
10408
10692
  _path2 = path2 && path1 !== path2 && preparePath(path2, true),
10409
10693
  operator = operators[operation];
@@ -10483,7 +10767,7 @@ PathItem.inject(new function() {
10483
10767
  return createResult(paths, true, path1, path2, options);
10484
10768
  }
10485
10769
 
10486
- function splitBoolean(path1, path2, operation) {
10770
+ function splitBoolean(path1, path2, operation, options) {
10487
10771
  var _path1 = preparePath(path1),
10488
10772
  _path2 = preparePath(path2),
10489
10773
  crossings = _path1.getIntersections(_path2, filterIntersection),
@@ -10510,7 +10794,7 @@ PathItem.inject(new function() {
10510
10794
  }
10511
10795
  }
10512
10796
  addPath(_path1);
10513
- return createResult(paths, false, path1, path2);
10797
+ return createResult(paths, false, path1, path2, options);
10514
10798
  }
10515
10799
 
10516
10800
  function linkIntersections(from, to) {
@@ -11097,7 +11381,7 @@ PathItem.inject(new function() {
11097
11381
 
11098
11382
  divide: function(path, options) {
11099
11383
  return options && (options.trace == false || options.stroke)
11100
- ? splitBoolean(this, path, 'divide')
11384
+ ? splitBoolean(this, path, 'divide', options)
11101
11385
  : createResult([
11102
11386
  this.subtract(path, options),
11103
11387
  this.intersect(path, options)
@@ -11565,7 +11849,7 @@ var TextItem = Item.extend({
11565
11849
  _serializeFields: {
11566
11850
  content: null
11567
11851
  },
11568
- _boundsOptions: { stroke: false, handle: false },
11852
+ _boundsOptions: { handle: false },
11569
11853
 
11570
11854
  initialize: function TextItem(arg) {
11571
11855
  this._content = '';
@@ -11622,27 +11906,43 @@ var PointText = TextItem.extend({
11622
11906
  },
11623
11907
 
11624
11908
  _draw: function(ctx, param, viewMatrix) {
11625
- if (!this._content)
11626
- return;
11627
- this._setStyles(ctx, param, viewMatrix);
11628
- var lines = this._lines,
11629
- style = this._style,
11630
- hasFill = style.hasFill(),
11631
- hasStroke = style.hasStroke(),
11632
- leading = style.getLeading(),
11633
- shadowColor = ctx.shadowColor;
11634
- ctx.font = style.getFontStyle();
11635
- ctx.textAlign = style.getJustification();
11636
- for (var i = 0, l = lines.length; i < l; i++) {
11637
- ctx.shadowColor = shadowColor;
11638
- var line = lines[i];
11639
- if (hasFill) {
11640
- ctx.fillText(line, 0, 0);
11641
- ctx.shadowColor = 'rgba(0,0,0,0)';
11909
+ if (param.dontFinish || param.clip) {
11910
+ var rect = this.getInternalBounds();
11911
+ if (!param.dontStart)
11912
+ ctx.beginPath();
11913
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
11914
+ } else if (this._content) {
11915
+ this._setStyles(ctx, param, viewMatrix);
11916
+ var lines = this._lines,
11917
+ style = this._style,
11918
+ hasFill = style.hasFill(),
11919
+ hasStroke = style.hasStroke(),
11920
+ leading = style.getLeading(),
11921
+ shadowColor = ctx.shadowColor;
11922
+ if (hasStroke && !style.getStrokeScaling()) {
11923
+ var scale = viewMatrix._getMeanScale();
11924
+ ctx.lineWidth /= scale;
11925
+ if ('setLineDash' in ctx) {
11926
+ ctx.setLineDash(ctx.getLineDash().map(function(dash) {
11927
+ return dash / scale;
11928
+ }));
11929
+ ctx.lineDashOffset /= scale;
11930
+ }
11931
+ }
11932
+ ctx.font = style.getFontStyle();
11933
+ ctx.textAlign = style.getJustification();
11934
+ ctx.letterSpacing = style.getTrackingStyle();
11935
+ for (var i = 0, l = lines.length; i < l; i++) {
11936
+ ctx.shadowColor = shadowColor;
11937
+ var line = lines[i];
11938
+ if (hasFill) {
11939
+ ctx.fillText(line, 0, 0);
11940
+ ctx.shadowColor = 'rgba(0,0,0,0)';
11941
+ }
11942
+ if (hasStroke)
11943
+ ctx.strokeText(line, 0, 0);
11944
+ ctx.translate(0, leading);
11642
11945
  }
11643
- if (hasStroke)
11644
- ctx.strokeText(line, 0, 0);
11645
- ctx.translate(0, leading);
11646
11946
  }
11647
11947
  },
11648
11948
 
@@ -11652,14 +11952,28 @@ var PointText = TextItem.extend({
11652
11952
  numLines = lines.length,
11653
11953
  justification = style.getJustification(),
11654
11954
  leading = style.getLeading(),
11655
- width = this.getView().getTextWidth(style.getFontStyle(), lines),
11955
+ width = this.getView().getTextWidth(style.getFontStyle(), lines,
11956
+ style.getTrackingStyle()),
11656
11957
  x = 0;
11657
11958
  if (justification !== 'left')
11658
11959
  x -= width / (justification === 'center' ? 2: 1);
11659
11960
  var rect = new Rectangle(x,
11660
11961
  numLines ? - 0.75 * leading : 0,
11661
11962
  width, numLines * leading);
11963
+ if (options.stroke && style.hasStroke())
11964
+ rect = rect.expand(style.getStrokeWidth());
11662
11965
  return matrix ? matrix._transformBounds(rect, rect) : rect;
11966
+ },
11967
+
11968
+ _hitTestSelf: function(point, options) {
11969
+ if (options.fill && this.getFillColor()) {
11970
+ var bounds = this.getInternalBounds(),
11971
+ closest = point.clamp(bounds.getTopLeft(true),
11972
+ bounds.getBottomRight(true));
11973
+ if (point.subtract(closest).divide(options._tolerancePadding)
11974
+ .getLength() <= 1)
11975
+ return new HitResult('fill', this);
11976
+ }
11663
11977
  }
11664
11978
  });
11665
11979
 
@@ -11720,13 +12034,14 @@ var Color = Base.extend(new function() {
11720
12034
  } else {
11721
12035
  var color = namedColors[string];
11722
12036
  if (!color) {
11723
- if (window) {
11724
- if (!colorCtx) {
11725
- colorCtx = CanvasProvider.getContext(1, 1, {
11726
- willReadFrequently: true
11727
- });
12037
+ if (!colorCtx) {
12038
+ colorCtx = CanvasProvider.getContext(1, 1, {
12039
+ willReadFrequently: true
12040
+ });
12041
+ if (colorCtx)
11728
12042
  colorCtx.globalCompositeOperation = 'copy';
11729
- }
12043
+ }
12044
+ if (colorCtx) {
11730
12045
  colorCtx.fillStyle = 'rgba(0,0,0,0)';
11731
12046
  colorCtx.fillStyle = string;
11732
12047
  colorCtx.fillRect(0, 0, 1, 1);
@@ -12133,8 +12448,9 @@ var Color = Base.extend(new function() {
12133
12448
  + components.join(',') + ')';
12134
12449
  },
12135
12450
 
12136
- toCanvasStyle: function(ctx, matrix) {
12137
- if (this._canvasStyle)
12451
+ toCanvasStyle: function(ctx, matrix, strokeMatrix) {
12452
+ var isCacheable = !(this._type === 'gradient' && strokeMatrix);
12453
+ if (this._canvasStyle && isCacheable)
12138
12454
  return this._canvasStyle;
12139
12455
  if (this._type !== 'gradient')
12140
12456
  return this._canvasStyle = this.toCSS();
@@ -12152,6 +12468,12 @@ var Color = Base.extend(new function() {
12152
12468
  if (highlight)
12153
12469
  highlight = inverse._transformPoint(highlight);
12154
12470
  }
12471
+ if (strokeMatrix) {
12472
+ origin = strokeMatrix._transformPoint(origin);
12473
+ destination = strokeMatrix._transformPoint(destination);
12474
+ if (highlight)
12475
+ highlight = strokeMatrix._transformPoint(highlight);
12476
+ }
12155
12477
  if (gradient._radial) {
12156
12478
  var radius = destination.getDistance(origin);
12157
12479
  if (highlight) {
@@ -12173,7 +12495,9 @@ var Color = Base.extend(new function() {
12173
12495
  offset == null ? i / (l - 1) : offset,
12174
12496
  stop._color.toCanvasStyle());
12175
12497
  }
12176
- return this._canvasStyle = canvasGradient;
12498
+ if (isCacheable)
12499
+ this._canvasStyle = canvasGradient;
12500
+ return canvasGradient;
12177
12501
  },
12178
12502
 
12179
12503
  transform: function(matrix) {
@@ -12437,6 +12761,7 @@ var Style = Base.extend(new function() {
12437
12761
  fontWeight: 'normal',
12438
12762
  fontSize: 12,
12439
12763
  leading: null,
12764
+ tracking: 0,
12440
12765
  justification: 'left'
12441
12766
  }),
12442
12767
  textDefaults = Base.set({}, groupDefaults, {
@@ -12453,6 +12778,7 @@ var Style = Base.extend(new function() {
12453
12778
  fontSize: 9,
12454
12779
  font: 9,
12455
12780
  leading: 9,
12781
+ tracking: 9,
12456
12782
  justification: 9
12457
12783
  },
12458
12784
  item = {
@@ -12497,7 +12823,7 @@ var Style = Base.extend(new function() {
12497
12823
  var old = this._values[key];
12498
12824
  if (old !== value) {
12499
12825
  if (isColor) {
12500
- if (old) {
12826
+ if (old && old.constructor === Color) {
12501
12827
  Color._setOwner(old, null);
12502
12828
  old._canvasStyle = null;
12503
12829
  }
@@ -12643,6 +12969,10 @@ var Style = Base.extend(new function() {
12643
12969
  + this.getFontFamily();
12644
12970
  },
12645
12971
 
12972
+ getTrackingStyle: function() {
12973
+ return this.getTracking() / 1000 + 'em';
12974
+ },
12975
+
12646
12976
  getFont: '#getFontFamily',
12647
12977
  setFont: '#setFontFamily',
12648
12978
 
@@ -12719,6 +13049,18 @@ var DomElement = new function() {
12719
13049
  return DomElement.getBounds(el, true).getSize();
12720
13050
  },
12721
13051
 
13052
+ getInsets: function(el, start) {
13053
+ var style = DomElement.getStyles(el);
13054
+ function get(side) {
13055
+ return parseFloat(style['padding' + side])
13056
+ + parseFloat(style['border' + side + 'Width']);
13057
+ }
13058
+ return start
13059
+ ? new Size(get('Left'), get('Top'))
13060
+ : new Size(get('Left') + get('Right'),
13061
+ get('Top') + get('Bottom'));
13062
+ },
13063
+
12722
13064
  isInvisible: function(el) {
12723
13065
  return DomElement.getSize(el).equals(new Size(0, 0));
12724
13066
  },
@@ -12777,12 +13119,20 @@ var DomEvent = {
12777
13119
  }
12778
13120
  },
12779
13121
 
12780
- getPoint: function(event) {
12781
- var pos = event.targetTouches
12782
- ? event.targetTouches.length
12783
- ? event.targetTouches[0]
12784
- : event.changedTouches[0]
12785
- : event;
13122
+ getPoint: function(event, target) {
13123
+ function find(touches) {
13124
+ for (var i = 0, l = touches.length; i < l; i++) {
13125
+ if (touches[i].target === target)
13126
+ return touches[i];
13127
+ }
13128
+ }
13129
+
13130
+ var touches = event.targetTouches,
13131
+ pos = event;
13132
+ if (touches) {
13133
+ pos = find(event.touches) || find(event.changedTouches)
13134
+ || (touches.length ? touches[0] : event.changedTouches[0]);
13135
+ }
12786
13136
  return new Point(
12787
13137
  pos.pageX || pos.clientX + document.documentElement.scrollLeft,
12788
13138
  pos.pageY || pos.clientY + document.documentElement.scrollTop
@@ -12798,8 +13148,9 @@ var DomEvent = {
12798
13148
  },
12799
13149
 
12800
13150
  getOffset: function(event, target) {
12801
- return DomEvent.getPoint(event).subtract(DomElement.getOffset(
12802
- target || DomEvent.getTarget(event)));
13151
+ target = target || DomEvent.getTarget(event);
13152
+ return DomEvent.getPoint(event, target).subtract(
13153
+ DomElement.getOffset(target));
12803
13154
  }
12804
13155
  };
12805
13156
 
@@ -12815,8 +13166,12 @@ DomEvent.requestAnimationFrame = new function() {
12815
13166
  for (var i = 0, l = functions.length; i < l; i++)
12816
13167
  functions[i]();
12817
13168
  requested = nativeRequest && callbacks.length;
12818
- if (requested)
13169
+ if (requested) {
12819
13170
  nativeRequest(handleCallbacks);
13171
+ } else if (timer && !callbacks.length) {
13172
+ clearInterval(timer);
13173
+ timer = null;
13174
+ }
12820
13175
  }
12821
13176
 
12822
13177
  return function(callback) {
@@ -12842,10 +13197,10 @@ var View = Base.extend(Emitter, {
12842
13197
  }
12843
13198
 
12844
13199
  function getCanvasSize() {
12845
- var size = DomElement.getSize(element);
13200
+ var size = new Size(element.offsetWidth, element.offsetHeight);
12846
13201
  return size.isNaN() || size.isZero()
12847
13202
  ? new Size(getSize('width'), getSize('height'))
12848
- : size;
13203
+ : size.subtract(DomElement.getInsets(element));
12849
13204
  }
12850
13205
 
12851
13206
  var size;
@@ -12958,7 +13313,7 @@ var View = Base.extend(Emitter, {
12958
13313
  this.requestUpdate();
12959
13314
  },
12960
13315
 
12961
- update: function() {
13316
+ update: function(force) {
12962
13317
  },
12963
13318
 
12964
13319
  draw: function() {
@@ -12973,7 +13328,8 @@ var View = Base.extend(Emitter, {
12973
13328
  if (that._animate) {
12974
13329
  that.requestUpdate();
12975
13330
  var element = that._element;
12976
- if ((!DomElement.getPrefixed(document, 'hidden')
13331
+ if (!element
13332
+ || (!DomElement.getPrefixed(document, 'hidden')
12977
13333
  || PaperScope.getAttribute(element, 'keepalive')
12978
13334
  === 'true') && DomElement.isInView(element)) {
12979
13335
  that._handleFrame();
@@ -13046,6 +13402,14 @@ var View = Base.extend(Emitter, {
13046
13402
  return this._element;
13047
13403
  },
13048
13404
 
13405
+ getProject: function() {
13406
+ return this._project;
13407
+ },
13408
+
13409
+ getScope: function() {
13410
+ return this._scope;
13411
+ },
13412
+
13049
13413
  getPixelRatio: function() {
13050
13414
  return this._pixelRatio;
13051
13415
  },
@@ -13151,12 +13515,14 @@ var View = Base.extend(Emitter, {
13151
13515
 
13152
13516
  getZoom: function() {
13153
13517
  var scaling = this._decompose().scaling;
13154
- return (scaling.x + scaling.y) / 2;
13518
+ return (Math.abs(scaling.x) + Math.abs(scaling.y)) / 2;
13155
13519
  },
13156
13520
 
13157
13521
  setZoom: function(zoom) {
13158
- this.transform(new Matrix().scale(zoom / this.getZoom(),
13159
- this.getCenter()));
13522
+ if (zoom > 0 && isFinite(zoom)) {
13523
+ this.transform(new Matrix().scale(zoom / this.getZoom(),
13524
+ this.getCenter()));
13525
+ }
13160
13526
  },
13161
13527
 
13162
13528
  getRotation: function() {
@@ -13166,7 +13532,8 @@ var View = Base.extend(Emitter, {
13166
13532
  setRotation: function(rotation) {
13167
13533
  var current = this.getRotation();
13168
13534
  if (current != null && rotation != null) {
13169
- this.rotate(rotation - current);
13535
+ this._matrix.prepend(new Matrix().rotate(rotation - current,
13536
+ this.projectToView(this.getCenter())));
13170
13537
  }
13171
13538
  },
13172
13539
 
@@ -13196,6 +13563,31 @@ var View = Base.extend(Emitter, {
13196
13563
  this._matrix.append(matrix);
13197
13564
  },
13198
13565
 
13566
+ fitBounds: function(rectangle, fit) {
13567
+ rectangle = Rectangle.read(arguments);
13568
+ fit = Base.read(arguments);
13569
+ var matrix = this._matrix,
13570
+ size = this._viewSize,
13571
+ bounds = matrix._transformBounds(rectangle),
13572
+ scaleX = new Point(matrix._a, matrix._c).getLength(),
13573
+ scaleY = new Point(matrix._b, matrix._d).getLength(),
13574
+ ratios = [
13575
+ size.width / bounds.width * scaleX,
13576
+ size.height / bounds.height * scaleY
13577
+ ].filter(function(ratio) {
13578
+ return ratio > 0 && isFinite(ratio);
13579
+ });
13580
+ this.setCenter(rectangle.getCenter());
13581
+ if (ratios.length) {
13582
+ var amount = fit === 'cover'
13583
+ ? Math.max.apply(Math, ratios)
13584
+ : Math.min.apply(Math, ratios);
13585
+ matrix.prepend(new Matrix().scale(
13586
+ new Point(amount / scaleX, amount / scaleY),
13587
+ size.divide(2)));
13588
+ }
13589
+ },
13590
+
13199
13591
  scrollBy: function() {
13200
13592
  this.translate(Point.read(arguments).negate());
13201
13593
  }
@@ -13210,7 +13602,17 @@ var View = Base.extend(Emitter, {
13210
13602
  },
13211
13603
 
13212
13604
  getEventPoint: function(event) {
13213
- return this.viewToProject(DomEvent.getOffset(event, this._element));
13605
+ var element = this._element,
13606
+ offset = DomEvent.getOffset(event, element),
13607
+ size = DomElement.getSize(element);
13608
+ if (!size.isZero()) {
13609
+ var cssSize = new Size(element.offsetWidth, element.offsetHeight);
13610
+ offset = offset.multiply(cssSize.divide(size))
13611
+ .subtract(DomElement.getInsets(element, true))
13612
+ .multiply(this.getViewSize().divide(
13613
+ cssSize.subtract(DomElement.getInsets(element))));
13614
+ }
13615
+ return this.viewToProject(offset);
13214
13616
  },
13215
13617
 
13216
13618
  }, {
@@ -13228,8 +13630,6 @@ var View = Base.extend(Emitter, {
13228
13630
  }
13229
13631
  },
13230
13632
  new function() {
13231
- if (!window)
13232
- return;
13233
13633
  var prevFocus,
13234
13634
  tempFocus,
13235
13635
  dragging = false,
@@ -13257,92 +13657,87 @@ new function() {
13257
13657
  view._handleMouseEvent('mousemove', event, point);
13258
13658
  }
13259
13659
 
13260
- var navigator = window.navigator,
13261
- mousedown, mousemove, mouseup;
13262
- if (navigator.pointerEnabled || navigator.msPointerEnabled) {
13263
- mousedown = 'pointerdown MSPointerDown';
13264
- mousemove = 'pointermove MSPointerMove';
13265
- mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
13266
- } else {
13267
- mousedown = 'touchstart';
13268
- mousemove = 'touchmove';
13269
- mouseup = 'touchend touchcancel';
13270
- if (!('ontouchstart' in window && navigator.userAgent.match(
13271
- /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
13272
- mousedown += ' mousedown';
13273
- mousemove += ' mousemove';
13274
- mouseup += ' mouseup';
13660
+ var viewEvents = {};
13661
+
13662
+ if (window) {
13663
+ var navigator = window.navigator,
13664
+ mousedown, mousemove, mouseup;
13665
+ if (navigator.pointerEnabled || navigator.msPointerEnabled) {
13666
+ mousedown = 'pointerdown MSPointerDown';
13667
+ mousemove = 'pointermove MSPointerMove';
13668
+ mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
13669
+ } else {
13670
+ mousedown = 'touchstart';
13671
+ mousemove = 'touchmove';
13672
+ mouseup = 'touchend touchcancel';
13673
+ if (!('ontouchstart' in window && navigator.userAgent.match(
13674
+ /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
13675
+ mousedown += ' mousedown';
13676
+ mousemove += ' mousemove';
13677
+ mouseup += ' mouseup';
13678
+ }
13275
13679
  }
13276
- }
13277
13680
 
13278
- var viewEvents = {},
13279
- docEvents = {
13681
+ var docEvents = {
13280
13682
  mouseout: function(event) {
13281
13683
  var view = View._focused,
13282
13684
  target = DomEvent.getRelatedTarget(event);
13283
- if (view && (!target || target.nodeName === 'HTML')) {
13284
- var offset = DomEvent.getOffset(event, view._element),
13285
- x = offset.x,
13286
- abs = Math.abs,
13287
- ax = abs(x),
13288
- max = 1 << 25,
13289
- diff = ax - max;
13290
- offset.x = abs(diff) < ax ? diff * (x < 0 ? -1 : 1) : x;
13291
- handleMouseMove(view, event, view.viewToProject(offset));
13292
- }
13685
+ if (view && (!target || target.nodeName === 'HTML'))
13686
+ handleMouseMove(view, event);
13293
13687
  },
13294
13688
 
13295
13689
  scroll: updateFocus
13296
13690
  };
13297
13691
 
13298
- viewEvents[mousedown] = function(event) {
13299
- var view = View._focused = getView(event);
13300
- if (!dragging) {
13301
- dragging = true;
13302
- view._handleMouseEvent('mousedown', event);
13303
- }
13304
- };
13692
+ viewEvents[mousedown] = function(event) {
13693
+ var view = View._focused = getView(event);
13694
+ if (!dragging) {
13695
+ dragging = true;
13696
+ view._handleMouseEvent('mousedown', event);
13697
+ }
13698
+ };
13305
13699
 
13306
- docEvents[mousemove] = function(event) {
13307
- var view = View._focused;
13308
- if (!mouseDown) {
13309
- var target = getView(event);
13310
- if (target) {
13311
- if (view !== target) {
13312
- if (view)
13313
- handleMouseMove(view, event);
13314
- if (!prevFocus)
13315
- prevFocus = view;
13316
- view = View._focused = tempFocus = target;
13317
- }
13318
- } else if (tempFocus && tempFocus === view) {
13319
- if (prevFocus && !prevFocus.isInserted())
13700
+ docEvents[mousemove] = function(event) {
13701
+ var view = View._focused;
13702
+ if (!mouseDown) {
13703
+ var target = getView(event);
13704
+ if (target) {
13705
+ if (view !== target) {
13706
+ if (view)
13707
+ handleMouseMove(view, event);
13708
+ if (!prevFocus)
13709
+ prevFocus = view;
13710
+ view = View._focused = tempFocus = target;
13711
+ }
13712
+ } else if (tempFocus && tempFocus === view) {
13713
+ if (prevFocus && !prevFocus.isInserted())
13714
+ prevFocus = null;
13715
+ view = View._focused = prevFocus;
13320
13716
  prevFocus = null;
13321
- view = View._focused = prevFocus;
13322
- prevFocus = null;
13323
- updateFocus();
13717
+ updateFocus();
13718
+ }
13324
13719
  }
13325
- }
13326
- if (view)
13327
- handleMouseMove(view, event);
13328
- };
13720
+ if (view)
13721
+ handleMouseMove(view, event);
13722
+ };
13329
13723
 
13330
- docEvents[mousedown] = function() {
13331
- mouseDown = true;
13332
- };
13724
+ docEvents[mousedown] = function() {
13725
+ mouseDown = true;
13726
+ };
13333
13727
 
13334
- docEvents[mouseup] = function(event) {
13335
- var view = View._focused;
13336
- if (view && dragging)
13337
- view._handleMouseEvent('mouseup', event);
13338
- mouseDown = dragging = false;
13339
- };
13728
+ docEvents[mouseup] = function(event) {
13729
+ var view = View._focused;
13730
+ if (view && dragging)
13731
+ view._handleMouseEvent('mouseup', event);
13732
+ mouseDown = dragging = false;
13733
+ };
13340
13734
 
13341
- DomEvent.add(document, docEvents);
13735
+ DomEvent.add(document, docEvents);
13342
13736
 
13343
- DomEvent.add(window, {
13344
- load: updateFocus
13345
- });
13737
+ DomEvent.add(window, {
13738
+ load: updateFocus
13739
+ });
13740
+ }
13346
13741
 
13347
13742
  var called = false,
13348
13743
  prevented = false,
@@ -13408,9 +13803,27 @@ new function() {
13408
13803
  point, prevPoint));
13409
13804
  }
13410
13805
 
13806
+ function createEvent(type, options) {
13807
+ if (options.modifiers)
13808
+ Key.modifiers.set(options.modifiers);
13809
+ return {
13810
+ type: type,
13811
+ timeStamp: options.timeStamp != null ? options.timeStamp
13812
+ : Date.now(),
13813
+ button: options.button || 0,
13814
+ cancelable: true,
13815
+ defaultPrevented: false,
13816
+ preventDefault: function() {
13817
+ this.defaultPrevented = true;
13818
+ },
13819
+ stopPropagation: function() {}
13820
+ };
13821
+ }
13822
+
13411
13823
  var itemEventsMap = {
13412
13824
  mousedown: {
13413
13825
  mousedown: 1,
13826
+ mouseup: 1,
13414
13827
  mousedrag: 1,
13415
13828
  click: 1,
13416
13829
  doubleclick: 1
@@ -13432,6 +13845,28 @@ new function() {
13432
13845
  return {
13433
13846
  _viewEvents: viewEvents,
13434
13847
 
13848
+ dispatchMouseEvent: function(type, point, options) {
13849
+ if (!/^mouse(down|move|up)$/.test(type))
13850
+ throw new Error('Unknown mouse event type: \'' + type + '\'.');
13851
+ var event = createEvent(type, options || {});
13852
+ if (type === 'mousedown') {
13853
+ View._focused = this;
13854
+ mouseDown = dragging = true;
13855
+ }
13856
+ this._handleMouseEvent(type, event, new Point(point));
13857
+ if (type === 'mouseup')
13858
+ mouseDown = dragging = false;
13859
+ return event.defaultPrevented;
13860
+ },
13861
+
13862
+ dispatchKeyEvent: function(type, key, options) {
13863
+ if (!/^key(down|up)$/.test(type))
13864
+ throw new Error('Unknown key event type: \'' + type + '\'.');
13865
+ options = options || {};
13866
+ Key._handleKey(type === 'keydown', key, options.character,
13867
+ createEvent(type, options), this);
13868
+ },
13869
+
13435
13870
  _handleMouseEvent: function(type, event, point) {
13436
13871
  var itemEvents = this._itemEvents,
13437
13872
  hitItems = itemEvents.native[type],
@@ -13444,12 +13879,37 @@ new function() {
13444
13879
  || tool && tool.responds(type);
13445
13880
  }
13446
13881
 
13882
+ function needsUp(hitItem) {
13883
+ if (view.responds('mouseup')
13884
+ || tool && tool.responds('mouseup'))
13885
+ return true;
13886
+ for (var item = hitItem; item; item = item._parent) {
13887
+ for (var key in itemEventsMap.mouseup) {
13888
+ if (item.responds(key))
13889
+ return true;
13890
+ }
13891
+ }
13892
+ return false;
13893
+ }
13894
+
13895
+ if (event.shiftKey !== undefined) {
13896
+ Key.modifiers.set({
13897
+ shift: event.shiftKey,
13898
+ control: event.ctrlKey,
13899
+ alt: event.altKey,
13900
+ meta: event.metaKey
13901
+ });
13902
+ }
13447
13903
  if (nativeMove && dragging && responds('mousedrag'))
13448
13904
  type = 'mousedrag';
13449
13905
  if (!point)
13450
13906
  point = this.getEventPoint(event);
13451
13907
 
13452
- var inView = this.getBounds().contains(point),
13908
+ var target = DomEvent.getTarget(event),
13909
+ isCovered = nativeMove && !dragging && target
13910
+ && target.nodeType === 1
13911
+ && !target.contains(this._element),
13912
+ inView = !isCovered && this.getBounds().contains(point),
13453
13913
  hit = hitItems && inView && view._project.hitTest(point, {
13454
13914
  tolerance: 0,
13455
13915
  fill: true,
@@ -13484,9 +13944,12 @@ new function() {
13484
13944
  if (mouse.down && inView || mouse.up && downPoint) {
13485
13945
  emitMouseEvents(this, hitItem, type, event, point, downPoint);
13486
13946
  if (mouse.down) {
13487
- dblClick = hitItem === clickItem
13488
- && (Date.now() - clickTime < 300);
13947
+ var time = Date.now();
13948
+ dblClick = !dblClick && hitItem === clickItem
13949
+ && time - clickTime < 300
13950
+ && point.getDistance(downPoint) < 3;
13489
13951
  downItem = clickItem = hitItem;
13952
+ clickTime = time;
13490
13953
  if (!prevented && hitItem) {
13491
13954
  var item = hitItem;
13492
13955
  while (item && !item.responds('mousedrag'))
@@ -13497,10 +13960,8 @@ new function() {
13497
13960
  downPoint = point;
13498
13961
  } else if (mouse.up) {
13499
13962
  if (!prevented && hitItem === downItem) {
13500
- clickTime = Date.now();
13501
13963
  emitMouseEvents(this, hitItem, dblClick ? 'doubleclick'
13502
13964
  : 'click', event, point, downPoint);
13503
- dblClick = false;
13504
13965
  }
13505
13966
  downItem = dragItem = null;
13506
13967
  }
@@ -13515,7 +13976,7 @@ new function() {
13515
13976
 
13516
13977
  if (
13517
13978
  event.cancelable !== false
13518
- && (called && !mouse.move || mouse.down && responds('mouseup'))
13979
+ && (called && !mouse.move || mouse.down && needsUp(hitItem))
13519
13980
  ) {
13520
13981
  event.preventDefault();
13521
13982
  }
@@ -13534,11 +13995,9 @@ new function() {
13534
13995
  }
13535
13996
  }
13536
13997
 
13537
- if (this.isVisible()) {
13538
- emit(this);
13539
- if (tool && tool.responds(type))
13540
- emit(tool);
13541
- }
13998
+ emit(this);
13999
+ if (tool && tool.responds(type))
14000
+ emit(tool);
13542
14001
  },
13543
14002
 
13544
14003
  _countItemEvent: function(type, sign) {
@@ -13596,16 +14055,20 @@ var CanvasView = View.extend({
13596
14055
  },
13597
14056
 
13598
14057
  _setElementSize: function _setElementSize(width, height) {
13599
- var pixelRatio = this._pixelRatio;
14058
+ var pixelRatio = this._pixelRatio,
14059
+ element = this._element;
13600
14060
  _setElementSize.base.call(this, width * pixelRatio, height * pixelRatio);
14061
+ if (!PaperScope.hasAttribute(element, 'resize')) {
14062
+ var style = element.style,
14063
+ insets = DomElement.getStyles(element).boxSizing
14064
+ === 'border-box'
14065
+ ? DomElement.getInsets(element)
14066
+ : new Size();
14067
+ style.width = (width + insets.width) + 'px';
14068
+ style.height = (height + insets.height) + 'px';
14069
+ }
13601
14070
  if (pixelRatio !== 1) {
13602
- var element = this._element,
13603
- ctx = this._context;
13604
- if (!PaperScope.hasAttribute(element, 'resize')) {
13605
- var style = element.style;
13606
- style.width = width + 'px';
13607
- style.height = height + 'px';
13608
- }
14071
+ var ctx = this._context;
13609
14072
  ctx.restore();
13610
14073
  ctx.save();
13611
14074
  ctx.scale(pixelRatio, pixelRatio);
@@ -13631,19 +14094,22 @@ var CanvasView = View.extend({
13631
14094
  return pixels;
13632
14095
  },
13633
14096
 
13634
- getTextWidth: function(font, lines) {
14097
+ getTextWidth: function(font, lines, letterSpacing) {
13635
14098
  var ctx = this._context,
13636
14099
  prevFont = ctx.font,
14100
+ prevLetterSpacing = ctx.letterSpacing,
13637
14101
  width = 0;
13638
14102
  ctx.font = font;
14103
+ ctx.letterSpacing = letterSpacing;
13639
14104
  for (var i = 0, l = lines.length; i < l; i++)
13640
14105
  width = Math.max(width, ctx.measureText(lines[i]).width);
13641
14106
  ctx.font = prevFont;
14107
+ ctx.letterSpacing = prevLetterSpacing;
13642
14108
  return width;
13643
14109
  },
13644
14110
 
13645
- update: function() {
13646
- if (!this._needsUpdate)
14111
+ update: function(force) {
14112
+ if (!this._needsUpdate && !force)
13647
14113
  return false;
13648
14114
  var project = this._project,
13649
14115
  ctx = this._context,
@@ -13714,6 +14180,7 @@ var Key = new function() {
13714
14180
  var keyLookup = {
13715
14181
  '\t': 'tab',
13716
14182
  ' ': 'space',
14183
+ '\xA0': 'space',
13717
14184
  '\b': 'backspace',
13718
14185
  '\x7f': 'delete',
13719
14186
  'Spacebar': 'space',
@@ -13767,10 +14234,16 @@ var Key = new function() {
13767
14234
  (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase());
13768
14235
  }
13769
14236
 
13770
- function handleKey(down, key, character, event) {
13771
- var type = down ? 'keydown' : 'keyup',
13772
- view = View._focused,
13773
- name;
14237
+ function handleKey(down, key, character, event, view) {
14238
+ if (!view) {
14239
+ View.updateFocus();
14240
+ view = View._focused;
14241
+ if (view && !view.isVisible())
14242
+ view = null;
14243
+ }
14244
+ if (character == null)
14245
+ character = down ? key.length > 1 ? '' : key : charMap[key] || '';
14246
+ var name;
13774
14247
  keyMap[key] = down;
13775
14248
  if (down) {
13776
14249
  charMap[key] = character;
@@ -13786,7 +14259,7 @@ var Key = new function() {
13786
14259
  } else {
13787
14260
  for (var k in metaFixMap) {
13788
14261
  if (k in charMap)
13789
- handleKey(false, k, metaFixMap[k], event);
14262
+ handleKey(false, k, metaFixMap[k], event, view);
13790
14263
  }
13791
14264
  metaFixMap = null;
13792
14265
  }
@@ -13802,11 +14275,9 @@ var Key = new function() {
13802
14275
 
13803
14276
  DomEvent.add(document, {
13804
14277
  keydown: function(event) {
13805
- var key = getKey(event),
13806
- agent = paper && paper.agent;
13807
- if (key.length > 1 || agent && (agent.chrome && (event.altKey
13808
- || agent.mac && event.metaKey
13809
- || !agent.mac && event.ctrlKey))) {
14278
+ var key = getKey(event);
14279
+ if (key.length > 1 || event.ctrlKey || event.altKey
14280
+ || event.metaKey) {
13810
14281
  handleKey(true, key,
13811
14282
  charLookup[key] || (key.length > 1 ? '' : key), event);
13812
14283
  } else {
@@ -13843,6 +14314,8 @@ var Key = new function() {
13843
14314
  });
13844
14315
 
13845
14316
  return {
14317
+ _handleKey: handleKey,
14318
+
13846
14319
  modifiers: modifiers,
13847
14320
 
13848
14321
  isDown: function(key) {
@@ -14009,6 +14482,14 @@ var Tool = PaperScopeItem.extend({
14009
14482
  }
14010
14483
  },
14011
14484
 
14485
+ getEventInterval: function() {
14486
+ return this._eventInterval;
14487
+ },
14488
+
14489
+ setEventInterval: function(interval) {
14490
+ this._eventInterval = interval;
14491
+ },
14492
+
14012
14493
  getFixedDistance: function() {
14013
14494
  return this._minDistance == this._maxDistance
14014
14495
  ? this._minDistance : null;
@@ -14065,7 +14546,24 @@ var Tool = PaperScopeItem.extend({
14065
14546
  if (mouse.down) {
14066
14547
  update();
14067
14548
  emit();
14549
+ if (this._eventInterval > 0) {
14550
+ clearInterval(this._timer);
14551
+ this._timer = setInterval(function() {
14552
+ if (tool.isActive()) {
14553
+ paper = tool._scope;
14554
+ tool._lastPoint = tool._point;
14555
+ tool._moveCount++;
14556
+ if (tool.responds('mousedrag')) {
14557
+ tool.emit('mousedrag',
14558
+ new ToolEvent(tool, 'mousedrag', event));
14559
+ }
14560
+ } else {
14561
+ clearInterval(tool._timer);
14562
+ }
14563
+ }, this._eventInterval);
14564
+ }
14068
14565
  } else if (mouse.up) {
14566
+ clearInterval(this._timer);
14069
14567
  update(null, this._maxDistance);
14070
14568
  emit();
14071
14569
  } else if (responds) {
@@ -14140,6 +14638,48 @@ var Tween = Base.extend(Emitter, {
14140
14638
  return t < 0.5
14141
14639
  ? 16 * t * t * t * t * t
14142
14640
  : 1 + 16 * (--t) * t * t * t * t;
14641
+ },
14642
+
14643
+ easeInSine: function(t) {
14644
+ return 1 - Math.cos(t * Math.PI / 2);
14645
+ },
14646
+
14647
+ easeOutSine: function(t) {
14648
+ return Math.sin(t * Math.PI / 2);
14649
+ },
14650
+
14651
+ easeInOutSine: function(t) {
14652
+ return (1 - Math.cos(t * Math.PI)) / 2;
14653
+ },
14654
+
14655
+ easeInExpo: function(t) {
14656
+ return t === 0 ? 0 : Math.pow(2, 10 * t - 10);
14657
+ },
14658
+
14659
+ easeOutExpo: function(t) {
14660
+ return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
14661
+ },
14662
+
14663
+ easeInOutExpo: function(t) {
14664
+ return t === 0 || t === 1
14665
+ ? t
14666
+ : t < 0.5
14667
+ ? Math.pow(2, 20 * t - 10) / 2
14668
+ : 1 - Math.pow(2, 10 - 20 * t) / 2;
14669
+ },
14670
+
14671
+ easeInCirc: function(t) {
14672
+ return 1 - Math.sqrt(1 - t * t);
14673
+ },
14674
+
14675
+ easeOutCirc: function(t) {
14676
+ return Math.sqrt(1 - (--t) * t);
14677
+ },
14678
+
14679
+ easeInOutCirc: function(t) {
14680
+ return t < 0.5
14681
+ ? (1 - Math.sqrt(1 - 4 * t * t)) / 2
14682
+ : (1 + Math.sqrt(1 - 4 * (--t) * t)) / 2;
14143
14683
  }
14144
14684
  })
14145
14685
  },
@@ -14360,7 +14900,7 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14360
14900
  canvases: [],
14361
14901
 
14362
14902
  getCanvas: function(width, height, options) {
14363
- if (!window)
14903
+ if (!window && !self.OffscreenCanvas)
14364
14904
  return null;
14365
14905
  var canvas,
14366
14906
  clear = true;
@@ -14371,7 +14911,9 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14371
14911
  if (this.canvases.length) {
14372
14912
  canvas = this.canvases.pop();
14373
14913
  } else {
14374
- canvas = document.createElement('canvas');
14914
+ canvas = window
14915
+ ? document.createElement('canvas')
14916
+ : new self.OffscreenCanvas(width, height);
14375
14917
  clear = false;
14376
14918
  }
14377
14919
  var ctx = canvas.getContext('2d', options || {});
@@ -14398,8 +14940,9 @@ var CanvasProvider = Base.exports.CanvasProvider = {
14398
14940
  release: function(obj) {
14399
14941
  var canvas = obj && obj.canvas ? obj.canvas : obj;
14400
14942
  if (canvas && canvas.getContext) {
14401
- canvas.getContext('2d').restore();
14402
- this.canvases.push(canvas);
14943
+ canvas.width = canvas.height = 0;
14944
+ if (this.canvases.length < 8)
14945
+ this.canvases.push(canvas);
14403
14946
  }
14404
14947
  }
14405
14948
  };
@@ -14575,6 +15118,12 @@ var BlendMode = new function() {
14575
15118
  db = (bb + sb) / 2;
14576
15119
  },
14577
15120
 
15121
+ 'pin-light': function() {
15122
+ dr = sr < 128 ? min(br, 2 * sr) : max(br, 2 * sr - 255);
15123
+ dg = sg < 128 ? min(bg, 2 * sg) : max(bg, 2 * sg - 255);
15124
+ db = sb < 128 ? min(bb, 2 * sb) : max(bb, 2 * sb - 255);
15125
+ },
15126
+
14578
15127
  negation: function() {
14579
15128
  dr = 255 - abs(255 - sr - br);
14580
15129
  dg = 255 - abs(255 - sg - bg);
@@ -14673,7 +15222,8 @@ var SvgElement = new function() {
14673
15222
  function get(node, name) {
14674
15223
  var namespace = attributeNamespace[name],
14675
15224
  value = namespace
14676
- ? node.getAttributeNS(namespace, name)
15225
+ ? node.getAttribute(name)
15226
+ || node.getAttributeNS(namespace, name)
14677
15227
  : node.getAttribute(name);
14678
15228
  return value === 'null' ? null : value;
14679
15229
  }
@@ -14724,8 +15274,9 @@ var SvgStyles = Base.each({
14724
15274
  dashArray: ['stroke-dasharray', 'array'],
14725
15275
  dashOffset: ['stroke-dashoffset', 'number'],
14726
15276
  fontFamily: ['font-family', 'string'],
14727
- fontWeight: ['font-weight', 'string'],
15277
+ fontWeight: ['font-weight', 'font'],
14728
15278
  fontSize: ['font-size', 'number'],
15279
+ tracking: ['letter-spacing', 'tracking'],
14729
15280
  justification: ['text-anchor', 'lookup', {
14730
15281
  left: 'start',
14731
15282
  center: 'middle',
@@ -14751,7 +15302,8 @@ var SvgStyles = Base.each({
14751
15302
  }, {});
14752
15303
 
14753
15304
  new function() {
14754
- var formatter;
15305
+ var formatter,
15306
+ parentStyles;
14755
15307
 
14756
15308
  function getTransform(matrix, coordinates, center) {
14757
15309
  var attrs = new Base(),
@@ -14800,34 +15352,72 @@ new function() {
14800
15352
  var node = SvgElement.create('g', attrs, formatter);
14801
15353
  for (var i = 0, l = children.length; i < l; i++) {
14802
15354
  var child = children[i];
14803
- var childNode = exportSVG(child, options);
14804
- if (childNode) {
14805
- if (child.isClipMask()) {
14806
- var clip = SvgElement.create('clipPath');
14807
- clip.appendChild(childNode);
14808
- setDefinition(child, clip, 'clip');
14809
- SvgElement.set(node, {
14810
- 'clip-path': 'url(#' + clip.id + ')'
14811
- });
14812
- } else {
15355
+ if (child.isClipMask()) {
15356
+ var clip = exportClip(child, options);
15357
+ setDefinition(child, clip, 'clip', options);
15358
+ SvgElement.set(node, {
15359
+ 'clip-path': 'url(#' + clip.id + ')'
15360
+ });
15361
+ } else {
15362
+ var childNode = exportSVG(child, options);
15363
+ if (childNode)
14813
15364
  node.appendChild(childNode);
14814
- }
14815
15365
  }
14816
15366
  }
14817
15367
  return node;
14818
15368
  }
14819
15369
 
15370
+ function exportClip(item, options) {
15371
+ var clip = SvgElement.create('clipPath');
15372
+
15373
+ function addItem(item) {
15374
+ if (item instanceof Group) {
15375
+ var children = item._children;
15376
+ for (var i = 0, l = children.length; i < l; i++) {
15377
+ if (!children[i]._clipMask)
15378
+ addItem(children[i]);
15379
+ }
15380
+ } else {
15381
+ var node = exportSVG(item, options);
15382
+ if (node)
15383
+ clip.appendChild(node);
15384
+ }
15385
+ }
15386
+
15387
+ if (item instanceof Group) {
15388
+ item = item.clone({ insert: false });
15389
+ item.matrix.apply(true, true);
15390
+ }
15391
+ addItem(item);
15392
+ return clip;
15393
+ }
15394
+
14820
15395
  function exportRaster(item, options) {
14821
15396
  var attrs = getTransform(item._matrix, true),
14822
15397
  size = item.getSize(),
14823
- image = item.getImage();
15398
+ image = item.getImage(),
15399
+ href = options.embedImages == false && image && image.src
15400
+ || item.toDataURL();
14824
15401
  attrs.x -= size.width / 2;
14825
15402
  attrs.y -= size.height / 2;
14826
- attrs.width = size.width;
14827
- attrs.height = size.height;
14828
- attrs.href = options.embedImages == false && image && image.src
14829
- || item.toDataURL();
14830
- return SvgElement.create('image', attrs, formatter);
15403
+ if (options.reuseImages) {
15404
+ var node = getDefinition(href, 'image');
15405
+ if (!node) {
15406
+ node = SvgElement.create('image', {
15407
+ width: size.width,
15408
+ height: size.height,
15409
+ href: href
15410
+ }, formatter);
15411
+ setDefinition(href, node, 'image', options);
15412
+ }
15413
+ attrs.href = '#' + node.id;
15414
+ return SvgElement.create('use', attrs, formatter);
15415
+ } else {
15416
+ attrs.width = size.width;
15417
+ attrs.height = size.height;
15418
+ attrs.href = href;
15419
+ return SvgElement.create('image', attrs, formatter);
15420
+ }
14831
15421
  }
14832
15422
 
14833
15423
  function exportPath(item, options) {
@@ -14913,7 +15503,7 @@ new function() {
14913
15503
  viewBox: formatter.rectangle(bounds)
14914
15504
  });
14915
15505
  node.appendChild(exportSVG(definitionItem, options));
14916
- setDefinition(definition, node, 'symbol');
15506
+ setDefinition(definition, node, 'symbol', options);
14917
15507
  }
14918
15508
  attrs.href = '#' + node.id;
14919
15509
  attrs.x += bounds.x;
@@ -14924,7 +15514,7 @@ new function() {
14924
15514
  return SvgElement.create('use', attrs, formatter);
14925
15515
  }
14926
15516
 
14927
- function exportGradient(color) {
15517
+ function exportGradient(color, options) {
14928
15518
  var gradientNode = getDefinition(color, 'color');
14929
15519
  if (!gradientNode) {
14930
15520
  var gradient = color.getGradient(),
@@ -14970,15 +15560,28 @@ new function() {
14970
15560
  gradientNode.appendChild(
14971
15561
  SvgElement.create('stop', attrs, formatter));
14972
15562
  }
14973
- setDefinition(color, gradientNode, 'color');
15563
+ setDefinition(color, gradientNode, 'color', options);
14974
15564
  }
14975
15565
  return 'url(#' + gradientNode.id + ')';
14976
15566
  }
14977
15567
 
14978
15568
  function exportText(item) {
14979
- var node = SvgElement.create('text', getTransform(item._matrix, true),
14980
- formatter);
14981
- node.textContent = item._content;
15569
+ var attrs = getTransform(item._matrix, true),
15570
+ node = SvgElement.create('text', attrs, formatter),
15571
+ lines = item._lines;
15572
+ if (lines.length > 1) {
15573
+ var leading = item._style.getLeading();
15574
+ for (var i = 0, l = lines.length; i < l; i++) {
15575
+ var span = SvgElement.create('tspan', {
15576
+ x: attrs.x,
15577
+ y: attrs.y + i * leading
15578
+ }, formatter);
15579
+ span.textContent = lines[i];
15580
+ node.appendChild(span);
15581
+ }
15582
+ } else {
15583
+ node.textContent = item._content;
15584
+ }
14982
15585
  return node;
14983
15586
  }
14984
15587
 
@@ -14993,9 +15596,18 @@ new function() {
14993
15596
  PointText: exportText
14994
15597
  };
14995
15598
 
15599
+ var cssBlendModes = Base.each(['normal', 'multiply', 'screen', 'overlay',
15600
+ 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light',
15601
+ 'soft-light', 'difference', 'exclusion', 'hue', 'saturation',
15602
+ 'color', 'luminosity'], function(mode) {
15603
+ this[mode] = true;
15604
+ }, {});
15605
+
14996
15606
  function applyStyle(item, node, options, isRoot) {
14997
15607
  var attrs = {},
14998
15608
  parent = !isRoot && item.getParent(),
15609
+ parentStyle = parent && (parentStyles[parent._id] =
15610
+ parentStyles[parent._id] || {}),
14999
15611
  style = [];
15000
15612
 
15001
15613
  if (item._name != null)
@@ -15005,24 +15617,39 @@ new function() {
15005
15617
  var get = entry.get,
15006
15618
  type = entry.type,
15007
15619
  value = item[get]();
15008
- if (entry.exportFilter
15620
+ if (parent && !(get in parentStyle))
15621
+ parentStyle[get] = parent[get]();
15622
+ if (value !== undefined && (entry.exportFilter
15009
15623
  ? entry.exportFilter(item, value)
15010
15624
  : options.reduceAttributes == false
15011
- || !parent || !Base.equals(parent[get](), value)) {
15625
+ || !parent || !Base.equals(parentStyle[get], value))) {
15012
15626
  if (type === 'color' && value != null) {
15013
15627
  var alpha = value.getAlpha();
15014
15628
  if (alpha < 1)
15015
15629
  attrs[entry.attribute + '-opacity'] = alpha;
15016
15630
  }
15017
15631
  if (type === 'style') {
15018
- style.push(entry.attribute + ': ' + value);
15632
+ if (cssBlendModes[value]) {
15633
+ style.push(entry.attribute + ': ' + value);
15634
+ } else {
15635
+ attrs['data-paper-blend-mode'] = value;
15636
+ }
15637
+ } else if (type === 'font') {
15638
+ var font = ('' + value).match(
15639
+ /^(.*?)\s*\b(italic|oblique)\b\s*(.*)$/i);
15640
+ attrs[entry.attribute] = font
15641
+ ? (font[1] + ' ' + font[3]).trim() || 'normal'
15642
+ : value;
15643
+ if (font)
15644
+ attrs['font-style'] = font[2].toLowerCase();
15019
15645
  } else {
15020
15646
  attrs[entry.attribute] = value == null ? 'none'
15021
15647
  : type === 'color' ? value.gradient
15022
- ? exportGradient(value, item)
15648
+ ? exportGradient(value, options)
15023
15649
  : value.toCSS(true)
15024
15650
  : type === 'array' ? value.join(',')
15025
15651
  : type === 'lookup' ? entry.toSVG[value]
15652
+ : type === 'tracking' ? value && value / 1000 + 'em'
15026
15653
  : value;
15027
15654
  }
15028
15655
  }
@@ -15044,16 +15671,20 @@ new function() {
15044
15671
  function getDefinition(item, type) {
15045
15672
  if (!definitions)
15046
15673
  definitions = { ids: {}, svgs: {} };
15047
- return item && definitions.svgs[type + '-'
15048
- + (item._id || item.__id || (item.__id = UID.get('svg')))];
15674
+ return item && definitions.svgs[getDefinitionKey(item, type)];
15049
15675
  }
15050
15676
 
15051
- function setDefinition(item, node, type) {
15677
+ function setDefinition(item, node, type, options) {
15052
15678
  if (!definitions)
15053
15679
  getDefinition();
15054
15680
  var typeId = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
15055
- node.id = type + '-' + typeId;
15056
- definitions.svgs[type + '-' + (item._id || item.__id)] = node;
15681
+ node.id = (options.idPrefix || '') + type + '-' + typeId;
15682
+ definitions.svgs[getDefinitionKey(item, type)] = node;
15683
+ }
15684
+
15685
+ function getDefinitionKey(item, type) {
15686
+ return type + '-' + (typeof item === 'string' ? item
15687
+ : item._id || item.__id || (item.__id = UID.get('svg')));
15057
15688
  }
15058
15689
 
15059
15690
  function exportDefinitions(node, options) {
@@ -15074,6 +15705,7 @@ new function() {
15074
15705
  }
15075
15706
  definitions = null;
15076
15707
  }
15708
+ parentStyles = null;
15077
15709
  return options.asString
15078
15710
  ? new self.XMLSerializer().serializeToString(svg)
15079
15711
  : svg;
@@ -15097,6 +15729,7 @@ new function() {
15097
15729
  if (!options)
15098
15730
  options = {};
15099
15731
  formatter = new Formatter(options.precision);
15732
+ parentStyles = {};
15100
15733
  return options;
15101
15734
  }
15102
15735
 
@@ -15149,7 +15782,14 @@ new function() {
15149
15782
  new function() {
15150
15783
 
15151
15784
  var definitions = {},
15152
- rootSize;
15785
+ rootSize,
15786
+ units = { 'in': 96, pt: 96 / 72, pc: 96 / 6, cm: 96 / 2.54,
15787
+ mm: 96 / 25.4 };
15788
+
15789
+ function parseLength(value) {
15790
+ var unit = /(in|pt|pc|cm|mm)\s*$/.exec(value);
15791
+ return parseFloat(value) * (unit ? units[unit[1]] : 1);
15792
+ }
15153
15793
 
15154
15794
  function getValue(node, name, isString, allowNull, allowPercent,
15155
15795
  defaultValue) {
@@ -15160,7 +15800,7 @@ new function() {
15160
15800
  : isString ? '' : 0
15161
15801
  : isString
15162
15802
  ? value
15163
- : parseFloat(value);
15803
+ : parseLength(value);
15164
15804
  return /%\s*$/.test(value)
15165
15805
  ? (res / 100) * (allowPercent ? 1
15166
15806
  : rootSize[/x|^width/.test(name) ? 'width' : 'height'])
@@ -15181,12 +15821,12 @@ new function() {
15181
15821
  : new Size(w, h);
15182
15822
  }
15183
15823
 
15184
- function convertValue(value, type, lookup) {
15824
+ function convertValue(value, type, lookup, options) {
15185
15825
  return value === 'none' ? null
15186
- : type === 'number' ? parseFloat(value)
15826
+ : type === 'number' ? parseLength(value)
15187
15827
  : type === 'array' ?
15188
- value ? value.split(/[\s,]+/g).map(parseFloat) : []
15189
- : type === 'color' ? getDefinition(value) || value
15828
+ value ? value.split(/[\s,]+/g).map(parseLength) : []
15829
+ : type === 'color' ? getDefinition(value, options) || value
15190
15830
  : type === 'lookup' ? lookup[value]
15191
15831
  : value;
15192
15832
  }
@@ -15198,11 +15838,17 @@ new function() {
15198
15838
  item = new Group(),
15199
15839
  project = item._project,
15200
15840
  currentStyle = project._currentStyle,
15841
+ parentSize = rootSize,
15201
15842
  children = [];
15202
15843
  if (!isClip && !isDefs) {
15203
- item = applyAttributes(item, node, isRoot);
15844
+ item = applyAttributes(item, node, options, isRoot);
15204
15845
  project._currentStyle = item._style.clone();
15205
15846
  }
15847
+ if (type === 'svg' && !isRoot) {
15848
+ rootSize = new Size(
15849
+ getValue(node, 'width', false, false, false, '100%'),
15850
+ getValue(node, 'height', false, false, false, '100%'));
15851
+ }
15206
15852
  if (isRoot) {
15207
15853
  var defs = node.querySelectorAll('defs');
15208
15854
  for (var i = 0, l = defs.length; i < l; i++) {
@@ -15220,8 +15866,9 @@ new function() {
15220
15866
  }
15221
15867
  item.addChildren(children);
15222
15868
  if (isClip)
15223
- item = applyAttributes(item.reduce(), node, isRoot);
15869
+ item = applyAttributes(item.reduce(), node, options, isRoot);
15224
15870
  project._currentStyle = currentStyle;
15871
+ rootSize = parentSize;
15225
15872
  if (isClip || isDefs) {
15226
15873
  item.remove();
15227
15874
  item = null;
@@ -15247,12 +15894,13 @@ new function() {
15247
15894
  return PathItem.create(node.getAttribute('d'));
15248
15895
  }
15249
15896
 
15250
- function importGradient(node, type) {
15897
+ function importGradient(node, type, options) {
15251
15898
  var id = (getValue(node, 'href', true) || '').substring(1),
15899
+ definition = getDefinitionById(id, options),
15252
15900
  radial = type === 'radialgradient',
15253
15901
  gradient;
15254
- if (id) {
15255
- gradient = definitions[id].getGradient();
15902
+ if (definition) {
15903
+ gradient = definition.getGradient();
15256
15904
  if (gradient._radial ^ radial) {
15257
15905
  gradient = gradient.clone();
15258
15906
  gradient._radial = radial;
@@ -15263,7 +15911,8 @@ new function() {
15263
15911
  for (var i = 0, l = nodes.length; i < l; i++) {
15264
15912
  var child = nodes[i];
15265
15913
  if (child.nodeType === 1)
15266
- stops.push(applyAttributes(new GradientStop(), child));
15914
+ stops.push(applyAttributes(new GradientStop(), child,
15915
+ options));
15267
15916
  }
15268
15917
  gradient = new Gradient(stops, radial);
15269
15918
  }
@@ -15283,7 +15932,8 @@ new function() {
15283
15932
  '100%', '0%');
15284
15933
  }
15285
15934
  var color = applyAttributes(
15286
- new Color(gradient, origin, destination, highlight), node);
15935
+ new Color(gradient, origin, destination, highlight), node,
15936
+ options);
15287
15937
  color._scaleToBounds = scaleToBounds;
15288
15938
  return null;
15289
15939
  }
@@ -15306,13 +15956,30 @@ new function() {
15306
15956
  lineargradient: importGradient,
15307
15957
  radialgradient: importGradient,
15308
15958
 
15309
- image: function (node) {
15310
- var raster = new Raster(getValue(node, 'href', true));
15959
+ image: function (node, type, options) {
15960
+ var raster = new Raster({
15961
+ crossOrigin: options.crossOrigin,
15962
+ source: getValue(node, 'href', true)
15963
+ }),
15964
+ align = /none|x(Min|Mid|Max)Y(Min|Mid|Max)/.exec(
15965
+ getValue(node, 'preserveAspectRatio', true))
15966
+ || [null, 'Mid', 'Mid'];
15311
15967
  raster.on('load', function() {
15312
- var size = getSize(node);
15313
- this.setSize(size);
15314
- var center = getPoint(node).add(size.divide(2));
15315
- this._matrix.append(new Matrix().translate(center));
15968
+ var imageSize = this.getSize(),
15969
+ size = getSize(node, null, null, true) || imageSize,
15970
+ point = getPoint(node);
15971
+ if (align[0] !== 'none') {
15972
+ var factors = { Min: 0, Mid: 0.5, Max: 1 },
15973
+ fitted = imageSize.multiply(Math.min(
15974
+ size.width / imageSize.width,
15975
+ size.height / imageSize.height));
15976
+ point = point.add(size.subtract(fitted).multiply(
15977
+ [factors[align[1]], factors[align[2]]]));
15978
+ size = fitted;
15979
+ }
15980
+ var center = point.add(size.divide(2));
15981
+ this._matrix.append(new Matrix().translate(center)
15982
+ .scale(size.divide(imageSize)));
15316
15983
  });
15317
15984
  return raster;
15318
15985
  },
@@ -15324,13 +15991,17 @@ new function() {
15324
15991
 
15325
15992
  defs: importGroup,
15326
15993
 
15327
- use: function(node) {
15994
+ use: function(node, type, options) {
15328
15995
  var id = (getValue(node, 'href', true) || '').substring(1),
15329
- definition = definitions[id],
15996
+ definition = getDefinitionById(id, options),
15330
15997
  point = getPoint(node);
15331
15998
  return definition
15332
15999
  ? definition instanceof SymbolDefinition
15333
16000
  ? definition.place(point)
16001
+ : definition instanceof Raster
16002
+ ? importNode(getSandbox().ownerDocument
16003
+ .getElementById(id), options, false)
16004
+ .translate(point)
15334
16005
  : definition.clone().translate(point)
15335
16006
  : null;
15336
16007
  },
@@ -15349,11 +16020,16 @@ new function() {
15349
16020
  },
15350
16021
 
15351
16022
  rect: function(node) {
16023
+ var rx = getValue(node, 'rx', false, true),
16024
+ ry = getValue(node, 'ry', false, true);
15352
16025
  return new Shape.Rectangle(new Rectangle(
15353
16026
  getPoint(node),
15354
16027
  getSize(node)
15355
- ), getSize(node, 'rx', 'ry'));
15356
- },
16028
+ ), new Size(
16029
+ rx == null ? ry || 0 : rx,
16030
+ ry == null ? rx || 0 : ry
16031
+ ));
16032
+ },
15357
16033
 
15358
16034
  line: function(node) {
15359
16035
  return new Path.Line(
@@ -15363,8 +16039,14 @@ new function() {
15363
16039
 
15364
16040
  text: function(node) {
15365
16041
  var text = new PointText(getPoint(node).add(
15366
- getPoint(node, 'dx', 'dy')));
15367
- text.setContent(node.textContent.trim() || '');
16042
+ getPoint(node, 'dx', 'dy'))),
16043
+ lines = Base.each(node.childNodes, function(child) {
16044
+ if (/^tspan$/i.test(child.nodeName)
16045
+ && child.hasAttribute('y'))
16046
+ this.push(child.textContent.trim());
16047
+ }, []);
16048
+ text.setContent(lines.length > 1 ? lines.join('\n')
16049
+ : node.textContent.trim());
15368
16050
  return text;
15369
16051
  },
15370
16052
 
@@ -15418,9 +16100,11 @@ new function() {
15418
16100
  }
15419
16101
 
15420
16102
  var attributes = Base.set(Base.each(SvgStyles, function(entry) {
15421
- this[entry.attribute] = function(item, value) {
16103
+ this[entry.attribute] = function(item, value, name, node, styles,
16104
+ options) {
15422
16105
  if (item[entry.set]) {
15423
- item[entry.set](convertValue(value, entry.type, entry.fromSVG));
16106
+ item[entry.set](convertValue(value, entry.type, entry.fromSVG,
16107
+ options));
15424
16108
  if (entry.type === 'color') {
15425
16109
  var color = item[entry.get]();
15426
16110
  if (color) {
@@ -15437,12 +16121,12 @@ new function() {
15437
16121
  }, {}), {
15438
16122
  id: function(item, value) {
15439
16123
  definitions[value] = item;
15440
- if (item.setName)
16124
+ if (item.setName && value !== (+value) + '')
15441
16125
  item.setName(value);
15442
16126
  },
15443
16127
 
15444
- 'clip-path': function(item, value) {
15445
- var clip = getDefinition(value);
16128
+ 'clip-path': function(item, value, name, node, styles, options) {
16129
+ var clip = getDefinition(value, options);
15446
16130
  if (clip) {
15447
16131
  clip = clip.clone();
15448
16132
  clip.setClipMask(true);
@@ -15470,6 +16154,28 @@ new function() {
15470
16154
  item.setVisible(value !== null);
15471
16155
  },
15472
16156
 
16157
+ 'data-paper-blend-mode': function(item, value) {
16158
+ if (item.setBlendMode)
16159
+ item.setBlendMode(value);
16160
+ },
16161
+
16162
+ 'font-style': function(item, value) {
16163
+ if (item.setFontWeight && /^(italic|oblique)$/.test(value)) {
16164
+ var weight = ('' + item.getFontWeight())
16165
+ .replace(/\s*\b(italic|oblique)\b/gi, '');
16166
+ item.setFontWeight(/^(normal|400)$/.test(weight) ? value
16167
+ : weight + ' ' + value);
16168
+ }
16169
+ },
16170
+
16171
+ 'letter-spacing': function(item, value) {
16172
+ if (item.setTracking) {
16173
+ var amount = parseFloat(value) || 0;
16174
+ item.setTracking(1000 * (/em$/.test(value) ? amount
16175
+ : amount / parseFloat(item.getFontSize())));
16176
+ }
16177
+ },
16178
+
15473
16179
  'stop-color': function(item, value) {
15474
16180
  if (item.setColor)
15475
16181
  item.setColor(value);
@@ -15515,12 +16221,13 @@ new function() {
15515
16221
  });
15516
16222
 
15517
16223
  function getAttribute(node, name, styles) {
15518
- var attr = node.attributes[name],
16224
+ var style = Base.camelize(name),
16225
+ value = node.style && node.style[style];
16226
+ if (!value) {
16227
+ var attr = node.attributes[name];
15519
16228
  value = attr && attr.value;
15520
- if (!value && node.style) {
15521
- var style = Base.camelize(name);
15522
- value = node.style[style];
15523
- if (!value && styles.node[style] !== styles.parent[style])
16229
+ if (!value && node.style
16230
+ && styles.node[style] !== styles.parent[style])
15524
16231
  value = styles.node[style];
15525
16232
  }
15526
16233
  return !value ? undefined
@@ -15528,27 +16235,38 @@ new function() {
15528
16235
  : value;
15529
16236
  }
15530
16237
 
15531
- function applyAttributes(item, node, isRoot) {
16238
+ function applyAttributes(item, node, options, isRoot) {
15532
16239
  var parent = node.parentNode,
15533
16240
  styles = {
15534
16241
  node: DomElement.getStyles(node) || {},
15535
16242
  parent: !isRoot && !/^defs$/i.test(parent.tagName)
15536
16243
  && DomElement.getStyles(parent) || {}
15537
16244
  };
16245
+ if (isRoot && !styles.node.fill && item.setFillColor)
16246
+ item.setFillColor('black');
15538
16247
  Base.each(attributes, function(apply, name) {
15539
16248
  var value = getAttribute(node, name, styles);
15540
16249
  item = value !== undefined
15541
- && apply(item, value, name, node, styles) || item;
16250
+ && apply(item, value, name, node, styles, options) || item;
15542
16251
  });
15543
16252
  return item;
15544
16253
  }
15545
16254
 
15546
- function getDefinition(value) {
16255
+ function getDefinitionById(id, options) {
16256
+ if (id && !definitions[id]) {
16257
+ var node = getSandbox().ownerDocument.getElementById(id);
16258
+ if (node)
16259
+ importNode(node, options, false);
16260
+ }
16261
+ return definitions[id];
16262
+ }
16263
+
16264
+ function getDefinition(value, options) {
15547
16265
  var match = value && value.match(/\((?:["'#]*)([^"')]+)/),
15548
16266
  name = match && match[1],
15549
- res = name && definitions[window
16267
+ res = name && getDefinitionById(window
15550
16268
  ? name.replace(window.location.href.split('#')[0] + '#', '')
15551
- : name];
16269
+ : name, options);
15552
16270
  if (res && res._scaleToBounds) {
15553
16271
  res = res.clone();
15554
16272
  res._scaleToBounds = true;
@@ -15594,12 +16312,12 @@ new function() {
15594
16312
  var settings = paper.settings,
15595
16313
  applyMatrix = settings.applyMatrix,
15596
16314
  insertItems = settings.insertItems,
16315
+ importer = importers[type],
15597
16316
  item;
15598
16317
  try {
15599
16318
  settings.applyMatrix = false;
15600
16319
  settings.insertItems = false;
15601
16320
  try {
15602
- var importer = importers[type];
15603
16321
  item = importer && importer(node, type, options, isRoot)
15604
16322
  || null;
15605
16323
  } finally {
@@ -15607,8 +16325,8 @@ new function() {
15607
16325
  settings.applyMatrix = applyMatrix;
15608
16326
  }
15609
16327
  if (item) {
15610
- if (isElement && !(item instanceof Group))
15611
- item = applyAttributes(item, node, isRoot);
16328
+ if (isElement && importer !== importGroup)
16329
+ item = applyAttributes(item, node, options, isRoot);
15612
16330
  var onImport = options.onImport,
15613
16331
  data = isElement && node.getAttribute('data-paper-data');
15614
16332
  if (onImport)
@@ -15726,14 +16444,13 @@ new function() {
15726
16444
  var paper = new (PaperScope.inject(Base.exports, {
15727
16445
  Base: Base,
15728
16446
  Numerical: Numerical,
16447
+ ChangeFlag: ChangeFlag,
15729
16448
  Key: Key,
15730
16449
  DomEvent: DomEvent,
15731
16450
  DomElement: DomElement,
15732
16451
  document: document,
15733
16452
  window: window,
15734
- PlacedSymbol: SymbolItem
15735
- }).inject({
15736
- enumerable: false,
16453
+ PlacedSymbol: SymbolItem,
15737
16454
  Symbol: SymbolDefinition
15738
16455
  }))();
15739
16456
 
@@ -15748,4 +16465,4 @@ if (typeof define === 'function' && define.amd) {
15748
16465
  }
15749
16466
 
15750
16467
  return paper;
15751
- }.call(this, typeof self === 'object' ? self : null);
16468
+ }(typeof self === 'object' ? self : null);