fabric 7.0.0 → 7.2.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 (323) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/extensions/cropping_controls/croppingControls.d.ts +16 -0
  4. package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  5. package/dist/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  6. package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  7. package/dist/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  8. package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  9. package/dist/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  10. package/dist/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  11. package/dist/extensions/index.d.ts +3 -0
  12. package/dist/extensions/index.d.ts.map +1 -1
  13. package/dist/fabric.d.ts +1 -0
  14. package/dist/fabric.d.ts.map +1 -1
  15. package/dist/index.js +628 -537
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.min.js +1 -1
  18. package/dist/index.min.js.map +1 -1
  19. package/dist/index.min.mjs +1 -1
  20. package/dist/index.min.mjs.map +1 -1
  21. package/dist/index.mjs +628 -537
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.node.cjs +628 -537
  24. package/dist/index.node.cjs.map +1 -1
  25. package/dist/index.node.mjs +628 -537
  26. package/dist/index.node.mjs.map +1 -1
  27. package/dist/package.json.min.mjs +1 -1
  28. package/dist/package.json.mjs +1 -1
  29. package/dist/src/EventTypeDefs.d.ts +5 -0
  30. package/dist/src/EventTypeDefs.d.ts.map +1 -1
  31. package/dist/src/Pattern/Pattern.d.ts.map +1 -1
  32. package/dist/src/Pattern/Pattern.min.mjs +1 -1
  33. package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
  34. package/dist/src/Pattern/Pattern.mjs +2 -1
  35. package/dist/src/Pattern/Pattern.mjs.map +1 -1
  36. package/dist/src/Shadow.d.ts +1 -1
  37. package/dist/src/Shadow.d.ts.map +1 -1
  38. package/dist/src/Shadow.min.mjs +1 -1
  39. package/dist/src/Shadow.min.mjs.map +1 -1
  40. package/dist/src/Shadow.mjs +5 -4
  41. package/dist/src/Shadow.mjs.map +1 -1
  42. package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
  43. package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
  44. package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
  45. package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
  46. package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
  47. package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
  48. package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
  49. package/dist/src/canvas/SelectableCanvas.mjs +33 -13
  50. package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
  51. package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
  52. package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
  53. package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
  54. package/dist/src/canvas/StaticCanvas.mjs +3 -1
  55. package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
  56. package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  57. package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
  58. package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
  59. package/dist/src/constants.d.ts +1 -0
  60. package/dist/src/constants.d.ts.map +1 -1
  61. package/dist/src/constants.min.mjs.map +1 -1
  62. package/dist/src/constants.mjs.map +1 -1
  63. package/dist/src/controls/Control.d.ts +22 -1
  64. package/dist/src/controls/Control.d.ts.map +1 -1
  65. package/dist/src/controls/Control.min.mjs +1 -1
  66. package/dist/src/controls/Control.min.mjs.map +1 -1
  67. package/dist/src/controls/Control.mjs +45 -1
  68. package/dist/src/controls/Control.mjs.map +1 -1
  69. package/dist/src/controls/changeWidth.d.ts +22 -0
  70. package/dist/src/controls/changeWidth.d.ts.map +1 -1
  71. package/dist/src/controls/changeWidth.min.mjs +1 -1
  72. package/dist/src/controls/changeWidth.min.mjs.map +1 -1
  73. package/dist/src/controls/changeWidth.mjs +46 -18
  74. package/dist/src/controls/changeWidth.mjs.map +1 -1
  75. package/dist/src/controls/controlRendering.d.ts.map +1 -1
  76. package/dist/src/controls/controlRendering.min.mjs +1 -1
  77. package/dist/src/controls/controlRendering.min.mjs.map +1 -1
  78. package/dist/src/controls/controlRendering.mjs +18 -34
  79. package/dist/src/controls/controlRendering.mjs.map +1 -1
  80. package/dist/src/controls/index.d.ts +2 -1
  81. package/dist/src/controls/index.d.ts.map +1 -1
  82. package/dist/src/controls/index.min.mjs +1 -1
  83. package/dist/src/controls/index.mjs +1 -1
  84. package/dist/src/gradient/Gradient.d.ts.map +1 -1
  85. package/dist/src/gradient/Gradient.min.mjs +1 -1
  86. package/dist/src/gradient/Gradient.min.mjs.map +1 -1
  87. package/dist/src/gradient/Gradient.mjs +19 -6
  88. package/dist/src/gradient/Gradient.mjs.map +1 -1
  89. package/dist/src/shapes/Circle.d.ts.map +1 -1
  90. package/dist/src/shapes/Circle.min.mjs +1 -1
  91. package/dist/src/shapes/Circle.min.mjs.map +1 -1
  92. package/dist/src/shapes/Circle.mjs +10 -7
  93. package/dist/src/shapes/Circle.mjs.map +1 -1
  94. package/dist/src/shapes/Ellipse.d.ts.map +1 -1
  95. package/dist/src/shapes/Ellipse.min.mjs +1 -1
  96. package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
  97. package/dist/src/shapes/Ellipse.mjs +2 -1
  98. package/dist/src/shapes/Ellipse.mjs.map +1 -1
  99. package/dist/src/shapes/Group.d.ts.map +1 -1
  100. package/dist/src/shapes/Group.min.mjs +1 -1
  101. package/dist/src/shapes/Group.min.mjs.map +1 -1
  102. package/dist/src/shapes/Group.mjs +2 -1
  103. package/dist/src/shapes/Group.mjs.map +1 -1
  104. package/dist/src/shapes/IText/IText.d.ts.map +1 -1
  105. package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
  106. package/dist/src/shapes/IText/IText.mjs.map +1 -1
  107. package/dist/src/shapes/Image.d.ts +1 -1
  108. package/dist/src/shapes/Image.d.ts.map +1 -1
  109. package/dist/src/shapes/Image.min.mjs +1 -1
  110. package/dist/src/shapes/Image.min.mjs.map +1 -1
  111. package/dist/src/shapes/Image.mjs +4 -3
  112. package/dist/src/shapes/Image.mjs.map +1 -1
  113. package/dist/src/shapes/Line.d.ts.map +1 -1
  114. package/dist/src/shapes/Line.min.mjs +1 -1
  115. package/dist/src/shapes/Line.min.mjs.map +1 -1
  116. package/dist/src/shapes/Line.mjs +6 -10
  117. package/dist/src/shapes/Line.mjs.map +1 -1
  118. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  119. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
  120. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
  121. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
  122. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
  123. package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  124. package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
  125. package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
  126. package/dist/src/shapes/Object/Object.d.ts.map +1 -1
  127. package/dist/src/shapes/Object/Object.min.mjs +1 -1
  128. package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
  129. package/dist/src/shapes/Object/Object.mjs +3 -0
  130. package/dist/src/shapes/Object/Object.mjs.map +1 -1
  131. package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
  132. package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
  133. package/dist/src/shapes/Object/ObjectGeometry.mjs +1 -1
  134. package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
  135. package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  136. package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  137. package/dist/src/shapes/Path.d.ts.map +1 -1
  138. package/dist/src/shapes/Path.min.mjs.map +1 -1
  139. package/dist/src/shapes/Path.mjs +1 -2
  140. package/dist/src/shapes/Path.mjs.map +1 -1
  141. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  142. package/dist/src/shapes/Polyline.min.mjs +1 -1
  143. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  144. package/dist/src/shapes/Polyline.mjs +10 -6
  145. package/dist/src/shapes/Polyline.mjs.map +1 -1
  146. package/dist/src/shapes/Rect.d.ts.map +1 -1
  147. package/dist/src/shapes/Rect.min.mjs +1 -1
  148. package/dist/src/shapes/Rect.min.mjs.map +1 -1
  149. package/dist/src/shapes/Rect.mjs +2 -1
  150. package/dist/src/shapes/Rect.mjs.map +1 -1
  151. package/dist/src/shapes/Text/StyledText.d.ts.map +1 -1
  152. package/dist/src/shapes/Text/StyledText.min.mjs.map +1 -1
  153. package/dist/src/shapes/Text/StyledText.mjs +0 -3
  154. package/dist/src/shapes/Text/StyledText.mjs.map +1 -1
  155. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  156. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  157. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  158. package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  159. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
  160. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
  161. package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -6
  162. package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
  163. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  164. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  165. package/dist/src/shapes/Textbox.mjs.map +1 -1
  166. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  167. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  168. package/dist/src/shapes/Triangle.mjs.map +1 -1
  169. package/dist/src/util/lang_string.d.ts +1 -1
  170. package/dist/src/util/lang_string.d.ts.map +1 -1
  171. package/dist/src/util/lang_string.min.mjs +1 -1
  172. package/dist/src/util/lang_string.min.mjs.map +1 -1
  173. package/dist/src/util/lang_string.mjs +1 -1
  174. package/dist/src/util/lang_string.mjs.map +1 -1
  175. package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
  176. package/dist/src/util/misc/svgParsing.min.mjs +1 -1
  177. package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
  178. package/dist/src/util/misc/svgParsing.mjs +2 -1
  179. package/dist/src/util/misc/svgParsing.mjs.map +1 -1
  180. package/dist-extensions/cropping_controls/croppingControls.mjs +140 -0
  181. package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -0
  182. package/dist-extensions/cropping_controls/croppingHandlers.mjs +228 -0
  183. package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -0
  184. package/dist-extensions/cropping_controls/enterCropMode.mjs +38 -0
  185. package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -0
  186. package/dist-extensions/cropping_controls/renderCornerControl.mjs +45 -0
  187. package/dist-extensions/cropping_controls/renderCornerControl.mjs.map +1 -0
  188. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +16 -0
  189. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  190. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  191. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  192. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  193. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  194. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  195. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  196. package/dist-extensions/extensions/index.d.ts +3 -0
  197. package/dist-extensions/extensions/index.d.ts.map +1 -1
  198. package/dist-extensions/fabric-extensions.min.js +1 -1
  199. package/dist-extensions/fabric-extensions.min.js.map +1 -1
  200. package/dist-extensions/fabric.d.ts +1 -0
  201. package/dist-extensions/fabric.d.ts.map +1 -1
  202. package/dist-extensions/index.mjs +3 -0
  203. package/dist-extensions/index.mjs.map +1 -1
  204. package/dist-extensions/src/EventTypeDefs.d.ts +5 -0
  205. package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
  206. package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
  207. package/dist-extensions/src/Shadow.d.ts +1 -1
  208. package/dist-extensions/src/Shadow.d.ts.map +1 -1
  209. package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
  210. package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
  211. package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
  212. package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
  213. package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  214. package/dist-extensions/src/constants.d.ts +1 -0
  215. package/dist-extensions/src/constants.d.ts.map +1 -1
  216. package/dist-extensions/src/controls/Control.d.ts +22 -1
  217. package/dist-extensions/src/controls/Control.d.ts.map +1 -1
  218. package/dist-extensions/src/controls/changeWidth.d.ts +22 -0
  219. package/dist-extensions/src/controls/changeWidth.d.ts.map +1 -1
  220. package/dist-extensions/src/controls/controlRendering.d.ts.map +1 -1
  221. package/dist-extensions/src/controls/index.d.ts +2 -1
  222. package/dist-extensions/src/controls/index.d.ts.map +1 -1
  223. package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
  224. package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
  225. package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
  226. package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
  227. package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
  228. package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
  229. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  230. package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  231. package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  232. package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
  233. package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  234. package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  235. package/dist-extensions/src/shapes/Path.d.ts +1 -1
  236. package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
  237. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  238. package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
  239. package/dist-extensions/src/shapes/Text/StyledText.d.ts.map +1 -1
  240. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  241. package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  242. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  243. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  244. package/dist-extensions/src/util/lang_string.d.ts +1 -1
  245. package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
  246. package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
  247. package/eslint.config.mjs +2 -0
  248. package/extensions/cropping_controls/croppingControls.spec.ts +115 -0
  249. package/extensions/cropping_controls/croppingControls.ts +150 -0
  250. package/extensions/cropping_controls/croppingHandlers.spec.ts +579 -0
  251. package/extensions/cropping_controls/croppingHandlers.ts +285 -0
  252. package/extensions/cropping_controls/enterCropMode.ts +30 -0
  253. package/extensions/cropping_controls/renderCornerControl.ts +53 -0
  254. package/extensions/index.ts +9 -0
  255. package/fabric.ts +1 -0
  256. package/package.json +17 -8
  257. package/src/ClassRegistry.spec.ts +18 -19
  258. package/src/EventTypeDefs.ts +15 -11
  259. package/src/Pattern/Pattern.spec.ts +12 -0
  260. package/src/Pattern/Pattern.ts +3 -2
  261. package/src/Shadow.ts +9 -8
  262. package/src/brushes/PencilBrush.spec.ts +11 -11
  263. package/src/canvas/Canvas-dispose.spec.ts +8 -7
  264. package/src/canvas/Canvas.spec.ts +27 -29
  265. package/src/canvas/CanvasOptions.ts +2 -1
  266. package/src/canvas/SelectableCanvas.ts +38 -15
  267. package/src/canvas/StaticCanvas.spec.ts +20 -0
  268. package/src/canvas/StaticCanvas.ts +7 -4
  269. package/src/canvas/StaticCanvasOptions.ts +1 -3
  270. package/src/constants.ts +1 -0
  271. package/src/controls/Control.spec.ts +102 -0
  272. package/src/controls/Control.ts +71 -2
  273. package/src/controls/changeHeight.spec.ts +147 -0
  274. package/src/controls/changeWidth.ts +68 -35
  275. package/src/controls/controlRendering.ts +20 -48
  276. package/src/controls/index.ts +7 -1
  277. package/src/gradient/Gradient.spec.ts +101 -46
  278. package/src/gradient/Gradient.ts +27 -14
  279. package/src/shapes/Circle.spec.ts +10 -39
  280. package/src/shapes/Circle.ts +11 -11
  281. package/src/shapes/Ellipse.spec.ts +8 -37
  282. package/src/shapes/Ellipse.ts +7 -7
  283. package/src/shapes/Group.ts +3 -3
  284. package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
  285. package/src/shapes/IText/IText.ts +5 -6
  286. package/src/shapes/IText/ITextKeyBehavior.test.ts +0 -1
  287. package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
  288. package/src/shapes/Image.spec.ts +17 -33
  289. package/src/shapes/Image.ts +15 -11
  290. package/src/shapes/Line.spec.ts +4 -30
  291. package/src/shapes/Line.ts +11 -16
  292. package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
  293. package/src/shapes/Object/InteractiveObject.ts +4 -4
  294. package/src/shapes/Object/Object.ts +6 -5
  295. package/src/shapes/Object/ObjectGeometry.spec.ts +15 -0
  296. package/src/shapes/Object/ObjectGeometry.ts +1 -1
  297. package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
  298. package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
  299. package/src/shapes/Object/types/ObjectProps.ts +1 -3
  300. package/src/shapes/Path.spec.ts +4 -27
  301. package/src/shapes/Path.ts +2 -4
  302. package/src/shapes/Polygon.spec.ts +4 -31
  303. package/src/shapes/Polyline.spec.ts +4 -31
  304. package/src/shapes/Polyline.ts +11 -12
  305. package/src/shapes/Rect.spec.ts +25 -33
  306. package/src/shapes/Rect.ts +7 -7
  307. package/src/shapes/Text/StyledText.ts +0 -3
  308. package/src/shapes/Text/Text.spec.ts +3 -32
  309. package/src/shapes/Text/Text.ts +5 -6
  310. package/src/shapes/Text/TextSVGExportMixin.spec.ts +9 -0
  311. package/src/shapes/Text/TextSVGExportMixin.ts +14 -16
  312. package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
  313. package/src/shapes/Text/__snapshots__/TextSVGExportMixin.spec.ts.snap +1 -1
  314. package/src/shapes/Textbox.spec.ts +5 -5
  315. package/src/shapes/Textbox.ts +6 -5
  316. package/src/shapes/Triangle.ts +4 -4
  317. package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
  318. package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
  319. package/src/util/lang_string.ts +3 -2
  320. package/src/util/misc/svgParsing.ts +2 -1
  321. package/tsconfig.spec.json +1 -0
  322. package/vitest.config.ts +12 -2
  323. package/vitest.extend.ts +6 -2
package/dist/index.js CHANGED
@@ -362,7 +362,7 @@
362
362
  }
363
363
  const cache = new Cache();
364
364
 
365
- var version = "7.0.0";
365
+ var version = "7.2.0";
366
366
 
367
367
  // use this syntax so babel plugin see this import here
368
368
  const VERSION = version;
@@ -2197,6 +2197,111 @@
2197
2197
  patternQuality: 'best'
2198
2198
  };
2199
2199
 
2200
+ /**
2201
+ * Capitalizes a string
2202
+ * @param {String} string String to capitalize
2203
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
2204
+ * and other letters stay untouched, if false first letter is capitalized
2205
+ * and other letters are converted to lowercase.
2206
+ * @return {String} Capitalized version of a string
2207
+ */
2208
+ const capitalize = function (string) {
2209
+ let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2210
+ return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
2211
+ };
2212
+
2213
+ /**
2214
+ * Escapes XML in a string
2215
+ * @param {String} string String to escape
2216
+ * @return {String} Escaped version of a string
2217
+ */
2218
+ const escapeXml = stringOrNumber => stringOrNumber.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
2219
+ let segmenter;
2220
+ const getSegmenter = () => {
2221
+ if (!segmenter) {
2222
+ segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
2223
+ granularity: 'grapheme'
2224
+ });
2225
+ }
2226
+ return segmenter;
2227
+ };
2228
+
2229
+ /**
2230
+ * Divide a string in the user perceived single units
2231
+ * @param {String} textstring String to escape
2232
+ * @return {Array} array containing the graphemes
2233
+ */
2234
+ const graphemeSplit = textstring => {
2235
+ segmenter || getSegmenter();
2236
+ if (segmenter) {
2237
+ const segments = segmenter.segment(textstring);
2238
+ return Array.from(segments).map(_ref => {
2239
+ let {
2240
+ segment
2241
+ } = _ref;
2242
+ return segment;
2243
+ });
2244
+ }
2245
+
2246
+ //Fallback
2247
+ return graphemeSplitImpl(textstring);
2248
+ };
2249
+ const graphemeSplitImpl = textstring => {
2250
+ const graphemes = [];
2251
+ for (let i = 0, chr; i < textstring.length; i++) {
2252
+ if ((chr = getWholeChar(textstring, i)) === false) {
2253
+ continue;
2254
+ }
2255
+ graphemes.push(chr);
2256
+ }
2257
+ return graphemes;
2258
+ };
2259
+
2260
+ // taken from mdn in the charAt doc page.
2261
+ const getWholeChar = (str, i) => {
2262
+ const code = str.charCodeAt(i);
2263
+ if (isNaN(code)) {
2264
+ return ''; // Position not found
2265
+ }
2266
+ if (code < 0xd800 || code > 0xdfff) {
2267
+ return str.charAt(i);
2268
+ }
2269
+
2270
+ // High surrogate (could change last hex to 0xDB7F to treat high private
2271
+ // surrogates as single characters)
2272
+ if (0xd800 <= code && code <= 0xdbff) {
2273
+ if (str.length <= i + 1) {
2274
+ throw 'High surrogate without following low surrogate';
2275
+ }
2276
+ const next = str.charCodeAt(i + 1);
2277
+ if (0xdc00 > next || next > 0xdfff) {
2278
+ throw 'High surrogate without following low surrogate';
2279
+ }
2280
+ return str.charAt(i) + str.charAt(i + 1);
2281
+ }
2282
+ // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
2283
+ if (i === 0) {
2284
+ throw 'Low surrogate without preceding high surrogate';
2285
+ }
2286
+ const prev = str.charCodeAt(i - 1);
2287
+
2288
+ // (could change last hex to 0xDB7F to treat high private
2289
+ // surrogates as single characters)
2290
+ if (0xd800 > prev || prev > 0xdbff) {
2291
+ throw 'Low surrogate without preceding high surrogate';
2292
+ }
2293
+ // We can pass over low surrogates now as the second component
2294
+ // in a pair which we have already processed
2295
+ return false;
2296
+ };
2297
+
2298
+ var lang_string = /*#__PURE__*/Object.freeze({
2299
+ __proto__: null,
2300
+ capitalize: capitalize,
2301
+ escapeXml: escapeXml,
2302
+ graphemeSplit: graphemeSplit
2303
+ });
2304
+
2200
2305
  /**
2201
2306
  * Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
2202
2307
  * Better try to restrict with types to avoid confusion.
@@ -2966,7 +3071,8 @@
2966
3071
  this._setSVGPreamble(markup, options);
2967
3072
  this._setSVGHeader(markup, options);
2968
3073
  if (this.clipPath) {
2969
- markup.push(`<g clip-path="url(#${this.clipPath.clipPathId})" >\n`);
3074
+ var _this$clipPath$clipPa;
3075
+ markup.push(`<g clip-path="url(#${escapeXml((_this$clipPath$clipPa = this.clipPath.clipPathId) !== null && _this$clipPath$clipPa !== void 0 ? _this$clipPath$clipPa : '')})" >\n`);
2970
3076
  }
2971
3077
  this._setSVGBgOverlayColor(markup, 'background');
2972
3078
  this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
@@ -3645,20 +3751,6 @@
3645
3751
  return t;
3646
3752
  };
3647
3753
 
3648
- const fireEvent = (eventName, options) => {
3649
- var _target$canvas;
3650
- const {
3651
- transform: {
3652
- target
3653
- }
3654
- } = options;
3655
- (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
3656
- ...options,
3657
- target
3658
- });
3659
- target.fire(eventName, options);
3660
- };
3661
-
3662
3754
  const originOffset = {
3663
3755
  left: -0.5,
3664
3756
  top: -0.5,
@@ -3861,33 +3953,6 @@
3861
3953
  return localPoint;
3862
3954
  }
3863
3955
 
3864
- /**
3865
- * Action handler
3866
- * @private
3867
- * @param {Event} eventData javascript event that is doing the transform
3868
- * @param {Object} transform javascript object containing a series of information around the current transform
3869
- * @param {number} x current mouse x position, canvas normalized
3870
- * @param {number} y current mouse y position, canvas normalized
3871
- * @return {Boolean} true if the translation occurred
3872
- */
3873
- const dragHandler = (eventData, transform, x, y) => {
3874
- const {
3875
- target,
3876
- offsetX,
3877
- offsetY
3878
- } = transform,
3879
- newLeft = x - offsetX,
3880
- newTop = y - offsetY,
3881
- moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
3882
- moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
3883
- moveX && target.set(LEFT, newLeft);
3884
- moveY && target.set(TOP, newTop);
3885
- if (moveX || moveY) {
3886
- fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
3887
- }
3888
- return moveX || moveY;
3889
- };
3890
-
3891
3956
  const normalizeWs = value => value.replace(/\s+/g, ' ');
3892
3957
 
3893
3958
  /**
@@ -4634,7 +4699,7 @@
4634
4699
  if (!value) {
4635
4700
  colorValue = 'none';
4636
4701
  } else if (value.toLive) {
4637
- colorValue = `url(#SVGID_${value.id})`;
4702
+ colorValue = `url(#SVGID_${escapeXml(value.id)})`;
4638
4703
  } else {
4639
4704
  const color = new Color(value),
4640
4705
  opacity = color.getAlpha();
@@ -4687,7 +4752,7 @@
4687
4752
  filter = skipShadow ? '' : this.getSvgFilter(),
4688
4753
  fill = colorPropToSVG(FILL, this.fill),
4689
4754
  stroke = colorPropToSVG(STROKE, this.stroke);
4690
- return [stroke, 'stroke-width: ', strokeWidth, '; ', 'stroke-dasharray: ', strokeDashArray, '; ', 'stroke-linecap: ', strokeLineCap, '; ', 'stroke-dashoffset: ', strokeDashOffset, '; ', 'stroke-linejoin: ', strokeLineJoin, '; ', 'stroke-miterlimit: ', strokeMiterLimit, '; ', fill, 'fill-rule: ', fillRule, '; ', 'opacity: ', opacity, ';', filter, visibility].join('');
4755
+ return [stroke, 'stroke-width: ', strokeWidth, '; ', 'stroke-dasharray: ', strokeDashArray, '; ', 'stroke-linecap: ', strokeLineCap, '; ', 'stroke-dashoffset: ', strokeDashOffset, '; ', 'stroke-linejoin: ', strokeLineJoin, '; ', 'stroke-miterlimit: ', strokeMiterLimit, '; ', fill, 'fill-rule: ', fillRule, '; ', 'opacity: ', opacity, ';', filter, visibility].map(v => escapeXml(v)).join('');
4691
4756
  }
4692
4757
 
4693
4758
  /**
@@ -4695,7 +4760,7 @@
4695
4760
  * @return {String}
4696
4761
  */
4697
4762
  getSvgFilter() {
4698
- return this.shadow ? `filter: url(#SVGID_${this.shadow.id});` : '';
4763
+ return this.shadow ? `filter: url(#SVGID_${escapeXml(this.shadow.id)});` : '';
4699
4764
  }
4700
4765
 
4701
4766
  /**
@@ -4703,7 +4768,7 @@
4703
4768
  * @return {String}
4704
4769
  */
4705
4770
  getSvgCommons() {
4706
- return [this.id ? `id="${this.id}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4771
+ return [this.id ? `id="${escapeXml(String(this.id))}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4707
4772
  }
4708
4773
 
4709
4774
  /**
@@ -4816,7 +4881,7 @@
4816
4881
  return reviver ? reviver(markup.join('')) : markup.join('');
4817
4882
  }
4818
4883
  addPaintOrder() {
4819
- return this.paintFirst !== FILL ? ` paint-order="${this.paintFirst}" ` : '';
4884
+ return this.paintFirst !== FILL ? ` paint-order="${escapeXml(this.paintFirst)}" ` : '';
4820
4885
  }
4821
4886
  }
4822
4887
 
@@ -4954,7 +5019,6 @@
4954
5019
 
4955
5020
  (?:$|\s): This captures either the end of the line or a whitespace character. It ensures that the match ends either at the end of the string or with a whitespace character.
4956
5021
  */
4957
- // eslint-disable-next-line max-len
4958
5022
 
4959
5023
  const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
4960
5024
  const reOffsetsAndBlur = new RegExp('(?:\\s|^)' + shadowOffsetRegex + shadowOffsetRegex + '(' + reNum + '?(?:px)?)?(?:\\s?|$)(?:$|\\s)');
@@ -5013,14 +5077,15 @@
5013
5077
  toSVG(object) {
5014
5078
  const offset = rotateVector(new Point(this.offsetX, this.offsetY), degreesToRadians(-object.angle)),
5015
5079
  BLUR_BOX = 20,
5080
+ NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
5016
5081
  color = new Color(this.color);
5017
5082
  let fBoxX = 40,
5018
5083
  fBoxY = 40;
5019
5084
  if (object.width && object.height) {
5020
5085
  //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
5021
5086
  // we add some extra space to filter box to contain the blur ( 20 )
5022
- fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5023
- fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5087
+ fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5088
+ fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5024
5089
  }
5025
5090
  if (object.flipX) {
5026
5091
  offset.x *= -1;
@@ -5028,7 +5093,7 @@
5028
5093
  if (object.flipY) {
5029
5094
  offset.y *= -1;
5030
5095
  }
5031
- return `<filter id="SVGID_${this.id}" y="-${fBoxY}%" height="${100 + 2 * fBoxY}%" x="-${fBoxX}%" width="${100 + 2 * fBoxX}%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(this.blur ? this.blur / 2 : 0, config.NUM_FRACTION_DIGITS)}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(offset.x, config.NUM_FRACTION_DIGITS)}" dy="${toFixed(offset.y, config.NUM_FRACTION_DIGITS)}" result="oBlur" ></feOffset>\n\t<feFlood flood-color="${color.toRgb()}" flood-opacity="${color.getAlpha()}"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n`;
5096
+ return `<filter id="SVGID_${escapeXml(this.id)}" y="-${fBoxY}%" height="${100 + 2 * fBoxY}%" x="-${fBoxX}%" width="${100 + 2 * fBoxX}%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(this.blur ? this.blur / 2 : 0, NUM_FRACTION_DIGITS)}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(offset.x, NUM_FRACTION_DIGITS)}" dy="${toFixed(offset.y, NUM_FRACTION_DIGITS)}" result="oBlur" ></feOffset>\n\t<feFlood flood-color="${color.toRgb()}" flood-opacity="${color.getAlpha()}"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n`;
5032
5097
  }
5033
5098
 
5034
5099
  /**
@@ -6438,7 +6503,7 @@
6438
6503
  calcOwnMatrix() {
6439
6504
  const key = this.transformMatrixKey(true),
6440
6505
  cache = this.ownMatrixCache;
6441
- if (cache && cache.key === key) {
6506
+ if (cache && cache.key.every((x, i) => x === key[i])) {
6442
6507
  return cache.value;
6443
6508
  }
6444
6509
  const center = this.getRelativeCenterPoint(),
@@ -7179,6 +7244,9 @@
7179
7244
  } else {
7180
7245
  this._renderBackground(ctx);
7181
7246
  }
7247
+ this.fire('before:render', {
7248
+ ctx
7249
+ });
7182
7250
  this._render(ctx);
7183
7251
  this._drawClipPath(ctx, this.clipPath, context);
7184
7252
  this.fill = originalFill;
@@ -8179,6 +8247,20 @@
8179
8247
  classRegistry.setClass(FabricObject$1);
8180
8248
  classRegistry.setClass(FabricObject$1, 'object');
8181
8249
 
8250
+ const fireEvent = (eventName, options) => {
8251
+ var _target$canvas;
8252
+ const {
8253
+ transform: {
8254
+ target
8255
+ }
8256
+ } = options;
8257
+ (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
8258
+ ...options,
8259
+ target
8260
+ });
8261
+ target.fire(eventName, options);
8262
+ };
8263
+
8182
8264
  /**
8183
8265
  * Wrap an action handler with firing an event if the action is performed
8184
8266
  * @param {TModificationEvents} eventName the event we want to fire
@@ -8221,34 +8303,62 @@
8221
8303
  };
8222
8304
  }
8223
8305
 
8224
- /**
8225
- * Action handler to change object's width
8226
- * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8227
- * @param {Event} eventData javascript event that is doing the transform
8228
- * @param {Object} transform javascript object containing a series of information around the current transform
8229
- * @param {number} x current mouse x position, canvas normalized
8230
- * @param {number} y current mouse y position, canvas normalized
8231
- * @return {Boolean} true if some change happened
8232
- */
8233
- const changeObjectWidth = (eventData, transform, x, y) => {
8306
+ const changeObjectDimensionGen = (dimension, origin, xorY, scale) => (eventData, transform, x, y) => {
8234
8307
  const localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y);
8308
+ const localPointValue = localPoint[xorY];
8235
8309
  // make sure the control changes width ONLY from it's side of target
8236
- if (resolveOrigin(transform.originX) === resolveOrigin(CENTER) || resolveOrigin(transform.originX) === resolveOrigin(RIGHT) && localPoint.x < 0 || resolveOrigin(transform.originX) === resolveOrigin(LEFT) && localPoint.x > 0) {
8310
+ const originValue = resolveOrigin(transform[origin]);
8311
+ if (originValue === 0 || originValue > 0 && localPointValue < 0 || originValue < 0 && localPointValue > 0) {
8237
8312
  const {
8238
8313
  target
8239
8314
  } = transform,
8240
- strokePadding = target.strokeWidth / (target.strokeUniform ? target.scaleX : 1),
8315
+ strokePadding = target.strokeWidth / (target.strokeUniform ? target[scale] : 1),
8241
8316
  multiplier = isTransformCentered(transform) ? 2 : 1,
8242
- oldWidth = target.width,
8243
- newWidth = Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding;
8244
- target.set('width', Math.max(newWidth, 1));
8317
+ oldWidth = target[dimension],
8318
+ newWidth = Math.abs(localPointValue * multiplier / target[scale]) - strokePadding;
8319
+ target.set(dimension, Math.max(newWidth, 1));
8245
8320
  // check against actual target width in case `newWidth` was rejected
8246
- return oldWidth !== target.width;
8321
+ return oldWidth !== target[dimension];
8247
8322
  }
8248
8323
  return false;
8249
8324
  };
8325
+
8326
+ /**
8327
+ * Action handler to change object's width
8328
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8329
+ * You want to use this only if you are building a new control handler and you want
8330
+ * to reuse some logic. use "changeWidth" if you are looking to just use a control for width
8331
+ * @param {Event} eventData javascript event that is doing the transform
8332
+ * @param {Object} transform javascript object containing a series of information around the current transform
8333
+ * @param {number} x current mouse x position, canvas normalized
8334
+ * @param {number} y current mouse y position, canvas normalized
8335
+ * @return {Boolean} true if some change happened
8336
+ */
8337
+ const changeObjectWidth = changeObjectDimensionGen('width', 'originX', 'x', 'scaleX');
8338
+
8339
+ /**
8340
+ * Action handler to change object's height
8341
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8342
+ * You want to use this only if you are building a new control handler and you want
8343
+ * to reuse some logic. use "changeHeight" if you are looking to just use a control for height
8344
+ * @param {Event} eventData javascript event that is doing the transform
8345
+ * @param {Object} transform javascript object containing a series of information around the current transform
8346
+ * @param {number} x current mouse x position, canvas normalized
8347
+ * @param {number} y current mouse y position, canvas normalized
8348
+ * @return {Boolean} true if some change happened
8349
+ */
8350
+ const changeObjectHeight = changeObjectDimensionGen('height', 'originY', 'y', 'scaleY');
8351
+
8352
+ /**
8353
+ * Control handler for changing width
8354
+ */
8250
8355
  const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectWidth));
8251
8356
 
8357
+ /**
8358
+ * Control handler for changing height
8359
+ */
8360
+ const changeHeight = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectHeight));
8361
+
8252
8362
  /**
8253
8363
  * Render a round control, as per fabric features.
8254
8364
  * This function is written to respect object properties like transparentCorners, cornerSize
@@ -8261,33 +8371,24 @@
8261
8371
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8262
8372
  */
8263
8373
  function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8264
- styleOverride = styleOverride || {};
8265
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8266
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8267
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8268
- methodName = transparentCorners ? STROKE : FILL,
8269
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor);
8270
- let myLeft = left,
8271
- myTop = top,
8272
- size;
8273
8374
  ctx.save();
8274
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8275
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8375
+ const {
8376
+ stroke,
8377
+ xSize,
8378
+ ySize,
8379
+ opName
8380
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride);
8381
+ let size = xSize;
8276
8382
  // TODO: use proper ellipse code.
8277
8383
  if (xSize > ySize) {
8278
- size = xSize;
8279
8384
  ctx.scale(1.0, ySize / xSize);
8280
- myTop = top * xSize / ySize;
8281
8385
  } else if (ySize > xSize) {
8282
8386
  size = ySize;
8283
8387
  ctx.scale(xSize / ySize, 1.0);
8284
- myLeft = left * ySize / xSize;
8285
- } else {
8286
- size = xSize;
8287
8388
  }
8288
8389
  ctx.beginPath();
8289
- ctx.arc(myLeft, myTop, size / 2, 0, twoMathPi, false);
8290
- ctx[methodName]();
8390
+ ctx.arc(0, 0, size / 2, 0, twoMathPi, false);
8391
+ ctx[opName]();
8291
8392
  if (stroke) {
8292
8393
  ctx.stroke();
8293
8394
  }
@@ -8306,25 +8407,19 @@
8306
8407
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8307
8408
  */
8308
8409
  function renderSquareControl(ctx, left, top, styleOverride, fabricObject) {
8309
- styleOverride = styleOverride || {};
8310
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8311
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8312
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8313
- methodName = transparentCorners ? STROKE : FILL,
8314
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor),
8410
+ ctx.save();
8411
+ const {
8412
+ stroke,
8413
+ xSize,
8414
+ ySize,
8415
+ opName
8416
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride),
8315
8417
  xSizeBy2 = xSize / 2,
8316
8418
  ySizeBy2 = ySize / 2;
8317
- ctx.save();
8318
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8319
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8320
- ctx.translate(left, top);
8321
- // angle is relative to canvas plane
8322
- const angle = fabricObject.getTotalAngle();
8323
- ctx.rotate(degreesToRadians(angle));
8324
8419
  // this does not work, and fixed with ( && ) does not make sense.
8325
8420
  // to have real transparent corners we need the controls on upperCanvas
8326
8421
  // transparentCorners || ctx.clearRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8327
- ctx[`${methodName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8422
+ ctx[`${opName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8328
8423
  if (stroke) {
8329
8424
  ctx.strokeRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8330
8425
  }
@@ -8442,6 +8537,14 @@
8442
8537
  _defineProperty(this, "withConnection", false);
8443
8538
  Object.assign(this, options);
8444
8539
  }
8540
+ getTransformAnchorPoint() {
8541
+ var _this$transformAnchor;
8542
+ return (// return the control transformAnchorPoint
8543
+ (_this$transformAnchor = this.transformAnchorPoint) !== null && _this$transformAnchor !== void 0 ? _this$transformAnchor :
8544
+ // otherwise will return the opposite origin of where the control is located.
8545
+ new Point(-this.x + 0.5, -this.y + 0.5)
8546
+ );
8547
+ }
8445
8548
 
8446
8549
  /**
8447
8550
  * The control actionHandler, provide one to handle action ( control being moved )
@@ -8584,6 +8687,41 @@
8584
8687
  };
8585
8688
  }
8586
8689
 
8690
+ /**
8691
+ * This is an helper method to prepare the canvas to render a control
8692
+ * It detectes common control properties and sets the correct fill and
8693
+ * stroke styles on the context. It does not execute translations or
8694
+ * rotations since different controls need differnt combination of these.
8695
+ */
8696
+ commonRenderProps(ctx, left, top, fabricObject) {
8697
+ let styleOverride = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
8698
+ const {
8699
+ cornerSize,
8700
+ cornerColor,
8701
+ transparentCorners,
8702
+ cornerStrokeColor
8703
+ } = styleOverride,
8704
+ sizeFromProps = cornerSize || fabricObject.cornerSize,
8705
+ xSize = this.sizeX || sizeFromProps,
8706
+ ySize = this.sizeY || sizeFromProps,
8707
+ transparent = typeof transparentCorners !== 'undefined' ? transparentCorners : fabricObject.transparentCorners,
8708
+ opName = transparent ? STROKE : FILL,
8709
+ strokeColor = cornerStrokeColor || fabricObject.cornerStrokeColor,
8710
+ stroke = !transparent && !!strokeColor;
8711
+ ctx.fillStyle = cornerColor || fabricObject.cornerColor || '';
8712
+ ctx.strokeStyle = strokeColor || '';
8713
+ ctx.translate(left, top);
8714
+ // angle is relative to canvas plane
8715
+ ctx.rotate(degreesToRadians(fabricObject.getTotalAngle()));
8716
+ return {
8717
+ stroke,
8718
+ xSize,
8719
+ ySize,
8720
+ transparentCorners: transparent,
8721
+ opName
8722
+ };
8723
+ }
8724
+
8587
8725
  /**
8588
8726
  * Render function for the control.
8589
8727
  * When this function runs the context is unscaled. unrotate. Just retina scaled.
@@ -10223,136 +10361,31 @@
10223
10361
  };
10224
10362
 
10225
10363
  /**
10226
- * Capitalizes a string
10227
- * @param {String} string String to capitalize
10228
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
10229
- * and other letters stay untouched, if false first letter is capitalized
10230
- * and other letters are converted to lowercase.
10231
- * @return {String} Capitalized version of a string
10364
+ * @param {Object} prevStyle first style to compare
10365
+ * @param {Object} thisStyle second style to compare
10366
+ * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10367
+ * @return {boolean} true if the style changed
10232
10368
  */
10233
- const capitalize = function (string) {
10234
- let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
10235
- return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
10369
+ const hasStyleChanged = function (prevStyle, thisStyle) {
10370
+ let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10371
+ return prevStyle.fill !== thisStyle.fill || prevStyle.stroke !== thisStyle.stroke || prevStyle.strokeWidth !== thisStyle.strokeWidth || prevStyle.fontSize !== thisStyle.fontSize || prevStyle.fontFamily !== thisStyle.fontFamily || prevStyle.fontWeight !== thisStyle.fontWeight || prevStyle.fontStyle !== thisStyle.fontStyle || prevStyle.textDecorationThickness !== thisStyle.textDecorationThickness || prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor || prevStyle.deltaY !== thisStyle.deltaY || forTextSpans && (prevStyle.overline !== thisStyle.overline || prevStyle.underline !== thisStyle.underline || prevStyle.linethrough !== thisStyle.linethrough);
10236
10372
  };
10237
10373
 
10238
10374
  /**
10239
- * Escapes XML in a string
10240
- * @param {String} string String to escape
10241
- * @return {String} Escaped version of a string
10375
+ * Returns the array form of a text object's inline styles property with styles grouped in ranges
10376
+ * rather than per character. This format is less verbose, and is better suited for storage
10377
+ * so it is used in serialization (not during runtime).
10378
+ * @param {object} styles per character styles for a text object
10379
+ * @param {String} text the text string that the styles are applied to
10380
+ * @return {{start: number, end: number, style: object}[]}
10242
10381
  */
10243
- const escapeXml = string => string.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
10244
- let segmenter;
10245
- const getSegmenter = () => {
10246
- if (!segmenter) {
10247
- segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
10248
- granularity: 'grapheme'
10249
- });
10250
- }
10251
- return segmenter;
10252
- };
10253
-
10254
- /**
10255
- * Divide a string in the user perceived single units
10256
- * @param {String} textstring String to escape
10257
- * @return {Array} array containing the graphemes
10258
- */
10259
- const graphemeSplit = textstring => {
10260
- segmenter || getSegmenter();
10261
- if (segmenter) {
10262
- const segments = segmenter.segment(textstring);
10263
- return Array.from(segments).map(_ref => {
10264
- let {
10265
- segment
10266
- } = _ref;
10267
- return segment;
10268
- });
10269
- }
10270
-
10271
- //Fallback
10272
- return graphemeSplitImpl(textstring);
10273
- };
10274
- const graphemeSplitImpl = textstring => {
10275
- const graphemes = [];
10276
- for (let i = 0, chr; i < textstring.length; i++) {
10277
- if ((chr = getWholeChar(textstring, i)) === false) {
10278
- continue;
10279
- }
10280
- graphemes.push(chr);
10281
- }
10282
- return graphemes;
10283
- };
10284
-
10285
- // taken from mdn in the charAt doc page.
10286
- const getWholeChar = (str, i) => {
10287
- const code = str.charCodeAt(i);
10288
- if (isNaN(code)) {
10289
- return ''; // Position not found
10290
- }
10291
- if (code < 0xd800 || code > 0xdfff) {
10292
- return str.charAt(i);
10293
- }
10294
-
10295
- // High surrogate (could change last hex to 0xDB7F to treat high private
10296
- // surrogates as single characters)
10297
- if (0xd800 <= code && code <= 0xdbff) {
10298
- if (str.length <= i + 1) {
10299
- throw 'High surrogate without following low surrogate';
10300
- }
10301
- const next = str.charCodeAt(i + 1);
10302
- if (0xdc00 > next || next > 0xdfff) {
10303
- throw 'High surrogate without following low surrogate';
10304
- }
10305
- return str.charAt(i) + str.charAt(i + 1);
10306
- }
10307
- // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
10308
- if (i === 0) {
10309
- throw 'Low surrogate without preceding high surrogate';
10310
- }
10311
- const prev = str.charCodeAt(i - 1);
10312
-
10313
- // (could change last hex to 0xDB7F to treat high private
10314
- // surrogates as single characters)
10315
- if (0xd800 > prev || prev > 0xdbff) {
10316
- throw 'Low surrogate without preceding high surrogate';
10317
- }
10318
- // We can pass over low surrogates now as the second component
10319
- // in a pair which we have already processed
10320
- return false;
10321
- };
10322
-
10323
- var lang_string = /*#__PURE__*/Object.freeze({
10324
- __proto__: null,
10325
- capitalize: capitalize,
10326
- escapeXml: escapeXml,
10327
- graphemeSplit: graphemeSplit
10328
- });
10329
-
10330
- /**
10331
- * @param {Object} prevStyle first style to compare
10332
- * @param {Object} thisStyle second style to compare
10333
- * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10334
- * @return {boolean} true if the style changed
10335
- */
10336
- const hasStyleChanged = function (prevStyle, thisStyle) {
10337
- let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10338
- return prevStyle.fill !== thisStyle.fill || prevStyle.stroke !== thisStyle.stroke || prevStyle.strokeWidth !== thisStyle.strokeWidth || prevStyle.fontSize !== thisStyle.fontSize || prevStyle.fontFamily !== thisStyle.fontFamily || prevStyle.fontWeight !== thisStyle.fontWeight || prevStyle.fontStyle !== thisStyle.fontStyle || prevStyle.textDecorationThickness !== thisStyle.textDecorationThickness || prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor || prevStyle.deltaY !== thisStyle.deltaY || forTextSpans && (prevStyle.overline !== thisStyle.overline || prevStyle.underline !== thisStyle.underline || prevStyle.linethrough !== thisStyle.linethrough);
10339
- };
10340
-
10341
- /**
10342
- * Returns the array form of a text object's inline styles property with styles grouped in ranges
10343
- * rather than per character. This format is less verbose, and is better suited for storage
10344
- * so it is used in serialization (not during runtime).
10345
- * @param {object} styles per character styles for a text object
10346
- * @param {String} text the text string that the styles are applied to
10347
- * @return {{start: number, end: number, style: object}[]}
10348
- */
10349
- const stylesToArray = (styles, text) => {
10350
- const textLines = text.split('\n'),
10351
- stylesArray = [];
10352
- let charIndex = -1,
10353
- prevStyle = {};
10354
- // clone style structure to prevent mutation
10355
- styles = cloneStyles(styles);
10382
+ const stylesToArray = (styles, text) => {
10383
+ const textLines = text.split('\n'),
10384
+ stylesArray = [];
10385
+ let charIndex = -1,
10386
+ prevStyle = {};
10387
+ // clone style structure to prevent mutation
10388
+ styles = cloneStyles(styles);
10356
10389
 
10357
10390
  //loop through each textLine
10358
10391
  for (let i = 0; i < textLines.length; i++) {
@@ -10910,7 +10943,7 @@
10910
10943
  rx,
10911
10944
  ry
10912
10945
  } = this;
10913
- return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${rx}" ry="${ry}" width="${width}" height="${height}" />\n`];
10946
+ return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${escapeXml(rx)}" ry="${escapeXml(ry)}" width="${escapeXml(width)}" height="${escapeXml(height)}" />\n`];
10914
10947
  }
10915
10948
 
10916
10949
  /**
@@ -11875,7 +11908,7 @@
11875
11908
  * @return {String}
11876
11909
  */
11877
11910
  getSvgStyles() {
11878
- const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${this.opacity};` : '',
11911
+ const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${escapeXml(this.opacity)};` : '',
11879
11912
  visibility = this.visible ? '' : ' visibility: hidden;';
11880
11913
  return [opacity, this.getSvgFilter(), visibility].join('');
11881
11914
  }
@@ -13177,6 +13210,288 @@
13177
13210
  preserveObjectStacking: true
13178
13211
  };
13179
13212
 
13213
+ /**
13214
+ * Action handler
13215
+ * @private
13216
+ * @param {Event} eventData javascript event that is doing the transform
13217
+ * @param {Object} transform javascript object containing a series of information around the current transform
13218
+ * @param {number} x current mouse x position, canvas normalized
13219
+ * @param {number} y current mouse y position, canvas normalized
13220
+ * @return {Boolean} true if the translation occurred
13221
+ */
13222
+ const dragHandler = (eventData, transform, x, y) => {
13223
+ const {
13224
+ target,
13225
+ offsetX,
13226
+ offsetY
13227
+ } = transform,
13228
+ newLeft = x - offsetX,
13229
+ newTop = y - offsetY,
13230
+ moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
13231
+ moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
13232
+ moveX && target.set(LEFT, newLeft);
13233
+ moveY && target.set(TOP, newTop);
13234
+ if (moveX || moveY) {
13235
+ fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
13236
+ }
13237
+ return moveX || moveY;
13238
+ };
13239
+
13240
+ const ACTION_NAME$1 = MODIFY_POLY;
13241
+ /**
13242
+ * This function locates the controls.
13243
+ * It'll be used both for drawing and for interaction.
13244
+ */
13245
+ const createPolyPositionHandler = pointIndex => {
13246
+ return function (dim, finalMatrix, polyObject) {
13247
+ const {
13248
+ points,
13249
+ pathOffset
13250
+ } = polyObject;
13251
+ return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
13252
+ };
13253
+ };
13254
+
13255
+ /**
13256
+ * This function defines what the control does.
13257
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13258
+ * The function receives as argument the mouse event, the current transform object
13259
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13260
+ * current object being transformed.
13261
+ */
13262
+ const polyActionHandler = (eventData, transform, x, y) => {
13263
+ const {
13264
+ target,
13265
+ pointIndex
13266
+ } = transform;
13267
+ const poly = target;
13268
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
13269
+ poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
13270
+ poly.setDimensions();
13271
+ poly.set('dirty', true);
13272
+ return true;
13273
+ };
13274
+
13275
+ /**
13276
+ * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
13277
+ */
13278
+ const factoryPolyActionHandler = (pointIndex, fn) => {
13279
+ return function (eventData, transform, x, y) {
13280
+ const poly = transform.target,
13281
+ anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
13282
+ anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
13283
+ actionPerformed = fn(eventData, {
13284
+ ...transform,
13285
+ pointIndex
13286
+ }, x, y);
13287
+ const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
13288
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13289
+ poly.left -= diff.x;
13290
+ poly.top -= diff.y;
13291
+ return actionPerformed;
13292
+ };
13293
+ };
13294
+ const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
13295
+ function createPolyControls(arg0) {
13296
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13297
+ const controls = {};
13298
+ for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
13299
+ controls[`p${idx}`] = new Control({
13300
+ actionName: ACTION_NAME$1,
13301
+ positionHandler: createPolyPositionHandler(idx),
13302
+ actionHandler: createPolyActionHandler(idx),
13303
+ ...options
13304
+ });
13305
+ }
13306
+ return controls;
13307
+ }
13308
+
13309
+ const ACTION_NAME = 'modifyPath';
13310
+ const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
13311
+ const {
13312
+ path,
13313
+ pathOffset
13314
+ } = pathObject;
13315
+ const command = path[commandIndex];
13316
+ return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
13317
+ };
13318
+ const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
13319
+ const {
13320
+ path,
13321
+ pathOffset
13322
+ } = pathObject;
13323
+ const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
13324
+ const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
13325
+ const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
13326
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
13327
+ path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
13328
+ path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
13329
+ pathObject.setDimensions();
13330
+ const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
13331
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13332
+ pathObject.left -= diff.x;
13333
+ pathObject.top -= diff.y;
13334
+ pathObject.set('dirty', true);
13335
+ return true;
13336
+ };
13337
+
13338
+ /**
13339
+ * This function locates the controls.
13340
+ * It'll be used both for drawing and for interaction.
13341
+ */
13342
+ function pathPositionHandler(dim, finalMatrix, pathObject) {
13343
+ const {
13344
+ commandIndex,
13345
+ pointIndex
13346
+ } = this;
13347
+ return calcPathPointPosition(pathObject, commandIndex, pointIndex);
13348
+ }
13349
+
13350
+ /**
13351
+ * This function defines what the control does.
13352
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13353
+ * The function receives as argument the mouse event, the current transform object
13354
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13355
+ * current object being transformed.
13356
+ */
13357
+ function pathActionHandler(eventData, transform, x, y) {
13358
+ const {
13359
+ target
13360
+ } = transform;
13361
+ const {
13362
+ commandIndex,
13363
+ pointIndex
13364
+ } = this;
13365
+ const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
13366
+ {
13367
+ fireEvent(this.actionName, {
13368
+ ...commonEventInfo(eventData, transform, x, y),
13369
+ commandIndex,
13370
+ pointIndex
13371
+ });
13372
+ }
13373
+ return actionPerformed;
13374
+ }
13375
+ const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
13376
+ class PathPointControl extends Control {
13377
+ constructor(options) {
13378
+ super(options);
13379
+ }
13380
+ render(ctx, left, top, styleOverride, fabricObject) {
13381
+ const overrides = {
13382
+ ...styleOverride,
13383
+ cornerColor: this.controlFill,
13384
+ cornerStrokeColor: this.controlStroke,
13385
+ transparentCorners: !this.controlFill
13386
+ };
13387
+ super.render(ctx, left, top, overrides, fabricObject);
13388
+ }
13389
+ }
13390
+ class PathControlPointControl extends PathPointControl {
13391
+ constructor(options) {
13392
+ super(options);
13393
+ }
13394
+ render(ctx, left, top, styleOverride, fabricObject) {
13395
+ const {
13396
+ path
13397
+ } = fabricObject;
13398
+ const {
13399
+ commandIndex,
13400
+ pointIndex,
13401
+ connectToCommandIndex,
13402
+ connectToPointIndex
13403
+ } = this;
13404
+ ctx.save();
13405
+ ctx.strokeStyle = this.controlStroke;
13406
+ if (this.connectionDashArray) {
13407
+ ctx.setLineDash(this.connectionDashArray);
13408
+ }
13409
+ const [commandType] = path[commandIndex];
13410
+ const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
13411
+ if (commandType === 'Q') {
13412
+ // one control point connects to 2 points
13413
+ const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
13414
+ ctx.moveTo(point2.x, point2.y);
13415
+ ctx.lineTo(left, top);
13416
+ } else {
13417
+ ctx.moveTo(left, top);
13418
+ }
13419
+ ctx.lineTo(point.x, point.y);
13420
+ ctx.stroke();
13421
+ ctx.restore();
13422
+ super.render(ctx, left, top, styleOverride, fabricObject);
13423
+ }
13424
+ }
13425
+ const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
13426
+ commandIndex: commandIndexPos,
13427
+ pointIndex: pointIndexPos,
13428
+ actionName: ACTION_NAME,
13429
+ positionHandler: pathPositionHandler,
13430
+ actionHandler: pathActionHandler,
13431
+ connectToCommandIndex,
13432
+ connectToPointIndex,
13433
+ ...options,
13434
+ ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
13435
+ });
13436
+ function createPathControls(path) {
13437
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13438
+ const controls = {};
13439
+ let previousCommandType = 'M';
13440
+ path.path.forEach((command, commandIndex) => {
13441
+ const commandType = command[0];
13442
+ if (commandType !== 'Z') {
13443
+ controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
13444
+ }
13445
+ switch (commandType) {
13446
+ case 'C':
13447
+ controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
13448
+ controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
13449
+ break;
13450
+ case 'Q':
13451
+ controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
13452
+ break;
13453
+ }
13454
+ previousCommandType = commandType;
13455
+ });
13456
+ return controls;
13457
+ }
13458
+
13459
+ var index = /*#__PURE__*/Object.freeze({
13460
+ __proto__: null,
13461
+ changeHeight: changeHeight,
13462
+ changeObjectHeight: changeObjectHeight,
13463
+ changeObjectWidth: changeObjectWidth,
13464
+ changeWidth: changeWidth,
13465
+ createObjectDefaultControls: createObjectDefaultControls,
13466
+ createPathControls: createPathControls,
13467
+ createPolyActionHandler: createPolyActionHandler,
13468
+ createPolyControls: createPolyControls,
13469
+ createPolyPositionHandler: createPolyPositionHandler,
13470
+ createResizeControls: createResizeControls,
13471
+ createTextboxDefaultControls: createTextboxDefaultControls,
13472
+ dragHandler: dragHandler,
13473
+ factoryPolyActionHandler: factoryPolyActionHandler,
13474
+ getLocalPoint: getLocalPoint,
13475
+ polyActionHandler: polyActionHandler,
13476
+ renderCircleControl: renderCircleControl,
13477
+ renderSquareControl: renderSquareControl,
13478
+ rotationStyleHandler: rotationStyleHandler,
13479
+ rotationWithSnapping: rotationWithSnapping,
13480
+ scaleCursorStyleHandler: scaleCursorStyleHandler,
13481
+ scaleOrSkewActionName: scaleOrSkewActionName,
13482
+ scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
13483
+ scalingEqually: scalingEqually,
13484
+ scalingX: scalingX,
13485
+ scalingXOrSkewingY: scalingXOrSkewingY,
13486
+ scalingY: scalingY,
13487
+ scalingYOrSkewingX: scalingYOrSkewingX,
13488
+ skewCursorStyleHandler: skewCursorStyleHandler,
13489
+ skewHandlerX: skewHandlerX,
13490
+ skewHandlerY: skewHandlerY,
13491
+ wrapWithFireEvent: wrapWithFireEvent,
13492
+ wrapWithFixedAnchor: wrapWithFixedAnchor
13493
+ });
13494
+
13180
13495
  /**
13181
13496
  * Canvas class
13182
13497
  * @class Canvas
@@ -13547,11 +13862,13 @@
13547
13862
  * Given the control clicked, determine the origin of the transform.
13548
13863
  * This is bad because controls can totally have custom names
13549
13864
  * should disappear before release 4.0
13865
+ * Fabric 7.1, jan 2026 we are still using this.
13866
+ * Needs to go.
13550
13867
  * @private
13551
13868
  * @deprecated
13552
13869
  */
13553
13870
  _getOriginFromCorner(target, controlName) {
13554
- const origin = {
13871
+ const origin = controlName ? target.controls[controlName].getTransformAnchorPoint() : {
13555
13872
  x: target.originX,
13556
13873
  y: target.originY
13557
13874
  };
@@ -13559,6 +13876,9 @@
13559
13876
  return origin;
13560
13877
  }
13561
13878
 
13879
+ // this part down here is deprecated.
13880
+ // It is left to do not change the standard behavior in the middle of a major version
13881
+ // but when possible `getTransformAnchorPoint` will be the only source of truth
13562
13882
  // is a left control ?
13563
13883
  if (['ml', 'tl', 'bl'].includes(controlName)) {
13564
13884
  origin.x = RIGHT;
@@ -13598,37 +13918,52 @@
13598
13918
  x: CENTER,
13599
13919
  y: CENTER
13600
13920
  } : this._getOriginFromCorner(target, corner),
13921
+ {
13922
+ scaleX,
13923
+ scaleY,
13924
+ skewX,
13925
+ skewY,
13926
+ left,
13927
+ top,
13928
+ angle,
13929
+ width,
13930
+ height,
13931
+ cropX,
13932
+ cropY
13933
+ } = target,
13601
13934
  /**
13602
13935
  * relative to target's containing coordinate plane
13603
13936
  * both agree on every point
13604
13937
  **/
13605
13938
  transform = {
13606
- target: target,
13939
+ target,
13607
13940
  action,
13608
13941
  actionHandler,
13609
13942
  actionPerformed: false,
13610
13943
  corner,
13611
- scaleX: target.scaleX,
13612
- scaleY: target.scaleY,
13613
- skewX: target.skewX,
13614
- skewY: target.skewY,
13615
- offsetX: pointer.x - target.left,
13616
- offsetY: pointer.y - target.top,
13944
+ scaleX,
13945
+ scaleY,
13946
+ skewX,
13947
+ skewY,
13948
+ offsetX: pointer.x - left,
13949
+ offsetY: pointer.y - top,
13617
13950
  originX: origin.x,
13618
13951
  originY: origin.y,
13619
13952
  ex: pointer.x,
13620
13953
  ey: pointer.y,
13621
13954
  lastX: pointer.x,
13622
13955
  lastY: pointer.y,
13623
- theta: degreesToRadians(target.angle),
13624
- width: target.width,
13625
- height: target.height,
13956
+ theta: degreesToRadians(angle),
13957
+ width,
13958
+ height,
13626
13959
  shiftKey: e.shiftKey,
13627
13960
  altKey,
13628
13961
  original: {
13629
13962
  ...saveObjectTransform(target),
13630
13963
  originX: origin.x,
13631
- originY: origin.y
13964
+ originY: origin.y,
13965
+ cropX,
13966
+ cropY
13632
13967
  }
13633
13968
  };
13634
13969
  this._currentTransform = transform;
@@ -16041,7 +16376,8 @@
16041
16376
  }
16042
16377
  transform[4] -= offsetX;
16043
16378
  transform[5] -= offsetY;
16044
- const commonAttributes = [`id="SVGID_${this.id}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16379
+ const commonAttributes = [`id="SVGID_${escapeXml(String(this.id))}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16380
+ const sanitizeCoord = value => parseFloat(String(value));
16045
16381
  if (this.type === 'linear') {
16046
16382
  const {
16047
16383
  x1,
@@ -16049,7 +16385,11 @@
16049
16385
  x2,
16050
16386
  y2
16051
16387
  } = this.coords;
16052
- markup.push('<linearGradient ', commonAttributes, ' x1="', x1, '" y1="', y1, '" x2="', x2, '" y2="', y2, '">\n');
16388
+ const sx1 = sanitizeCoord(x1);
16389
+ const sy1 = sanitizeCoord(y1);
16390
+ const sx2 = sanitizeCoord(x2);
16391
+ const sy2 = sanitizeCoord(y2);
16392
+ markup.push('<linearGradient ', commonAttributes, ' x1="', sx1, '" y1="', sy1, '" x2="', sx2, '" y2="', sy2, '">\n');
16053
16393
  } else if (this.type === 'radial') {
16054
16394
  const {
16055
16395
  x1,
@@ -16059,9 +16399,15 @@
16059
16399
  r1,
16060
16400
  r2
16061
16401
  } = this.coords;
16062
- const needsSwap = r1 > r2;
16402
+ const sx1 = sanitizeCoord(x1);
16403
+ const sy1 = sanitizeCoord(y1);
16404
+ const sx2 = sanitizeCoord(x2);
16405
+ const sy2 = sanitizeCoord(y2);
16406
+ const sr1 = sanitizeCoord(r1);
16407
+ const sr2 = sanitizeCoord(r2);
16408
+ const needsSwap = sr1 > sr2;
16063
16409
  // svg radial gradient has just 1 radius. the biggest.
16064
- markup.push('<radialGradient ', commonAttributes, ' cx="', needsSwap ? x1 : x2, '" cy="', needsSwap ? y1 : y2, '" r="', needsSwap ? r1 : r2, '" fx="', needsSwap ? x2 : x1, '" fy="', needsSwap ? y2 : y1, '">\n');
16410
+ markup.push('<radialGradient ', commonAttributes, ' cx="', needsSwap ? sx1 : sx2, '" cy="', needsSwap ? sy1 : sy2, '" r="', needsSwap ? sr1 : sr2, '" fx="', needsSwap ? sx2 : sx1, '" fy="', needsSwap ? sy2 : sy1, '">\n');
16065
16411
  if (needsSwap) {
16066
16412
  // svg goes from internal to external radius. if radius are inverted, swap color stops.
16067
16413
  colorStops.reverse(); // mutates array
@@ -16069,16 +16415,17 @@
16069
16415
  colorStop.offset = 1 - colorStop.offset;
16070
16416
  });
16071
16417
  }
16072
- const minRadius = Math.min(r1, r2);
16418
+ const minRadius = Math.min(sr1, sr2);
16073
16419
  if (minRadius > 0) {
16074
16420
  // i have to shift all colorStops and add new one in 0.
16075
- const maxRadius = Math.max(r1, r2),
16421
+ const maxRadius = Math.max(sr1, sr2),
16076
16422
  percentageShift = minRadius / maxRadius;
16077
16423
  colorStops.forEach(colorStop => {
16078
16424
  colorStop.offset += percentageShift * (1 - colorStop.offset);
16079
16425
  });
16080
16426
  }
16081
16427
  }
16428
+ // todo make a malicious script tag injection test with color and also apply a fix with escapeXml
16082
16429
  colorStops.forEach(_ref => {
16083
16430
  let {
16084
16431
  color,
@@ -16394,7 +16741,7 @@
16394
16741
  patternOffsetY = ifNaN(this.offsetY / height, 0),
16395
16742
  patternWidth = repeat === 'repeat-y' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetX || 0) : ifNaN(patternSource.width / width, 0),
16396
16743
  patternHeight = repeat === 'repeat-x' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetY || 0) : ifNaN(patternSource.height / height, 0);
16397
- return [`<pattern id="SVGID_${id}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`, `<image x="0" y="0" width="${patternSource.width}" height="${patternSource.height}" xlink:href="${this.sourceToString()}"></image>`, `</pattern>`, ''].join('\n');
16744
+ return [`<pattern id="SVGID_${escapeXml(id)}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`, `<image x="0" y="0" width="${patternSource.width}" height="${patternSource.height}" xlink:href="${escapeXml(this.sourceToString())}"></image>`, `</pattern>`, ''].join('\n');
16398
16745
  }
16399
16746
  /* _TO_SVG_END_ */
16400
16747
 
@@ -16676,8 +17023,7 @@
16676
17023
  * of the instance
16677
17024
  */
16678
17025
  _toSVG() {
16679
- const path = joinPath(this.path, config.NUM_FRACTION_DIGITS);
16680
- return ['<path ', 'COMMON_PARTS', `d="${path}" stroke-linecap="round" />\n`];
17026
+ return ['<path ', 'COMMON_PARTS', `d="${joinPath(this.path, config.NUM_FRACTION_DIGITS)}" stroke-linecap="round" />\n`];
16681
17027
  }
16682
17028
 
16683
17029
  /**
@@ -17228,15 +17574,17 @@
17228
17574
  * of the instance
17229
17575
  */
17230
17576
  _toSVG() {
17231
- const angle = (this.endAngle - this.startAngle) % 360;
17577
+ const {
17578
+ radius,
17579
+ startAngle,
17580
+ endAngle
17581
+ } = this;
17582
+ const angle = (endAngle - startAngle) % 360;
17232
17583
  if (angle === 0) {
17233
- return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${this.radius}`, '" />\n'];
17584
+ return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${escapeXml(radius)}`, '" />\n'];
17234
17585
  } else {
17235
- const {
17236
- radius
17237
- } = this;
17238
- const start = degreesToRadians(this.startAngle),
17239
- end = degreesToRadians(this.endAngle),
17586
+ const start = degreesToRadians(startAngle),
17587
+ end = degreesToRadians(endAngle),
17240
17588
  startX = cos(start) * radius,
17241
17589
  startY = sin(start) * radius,
17242
17590
  endX = cos(end) * radius,
@@ -17808,17 +18156,13 @@
17808
18156
  width,
17809
18157
  height
17810
18158
  } = this;
17811
- const xMult = _x1 <= _x2 ? -1 : 1,
17812
- yMult = _y1 <= _y2 ? -1 : 1,
17813
- x1 = xMult * width / 2,
17814
- y1 = yMult * height / 2,
17815
- x2 = xMult * -width / 2,
17816
- y2 = yMult * -height / 2;
18159
+ const xMult = _x1 <= _x2 ? -0.5 : 0.5,
18160
+ yMult = _y1 <= _y2 ? -0.5 : 0.5;
17817
18161
  return {
17818
- x1,
17819
- x2,
17820
- y1,
17821
- y2
18162
+ x1: xMult * width,
18163
+ x2: xMult * -width,
18164
+ y1: yMult * height,
18165
+ y2: yMult * -height
17822
18166
  };
17823
18167
  }
17824
18168
 
@@ -18036,7 +18380,7 @@
18036
18380
  * of the instance
18037
18381
  */
18038
18382
  _toSVG() {
18039
- return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${this.rx}" ry="${this.ry}" />\n`];
18383
+ return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${escapeXml(this.rx)}" ry="${escapeXml(this.ry)}" />\n`];
18040
18384
  }
18041
18385
 
18042
18386
  /**
@@ -18354,14 +18698,17 @@
18354
18698
  * of the instance
18355
18699
  */
18356
18700
  _toSVG() {
18357
- const points = [],
18358
- diffX = this.pathOffset.x,
18701
+ const diffX = this.pathOffset.x,
18359
18702
  diffY = this.pathOffset.y,
18360
18703
  NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS;
18361
- for (let i = 0, len = this.points.length; i < len; i++) {
18362
- points.push(toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',', toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' ');
18363
- }
18364
- return [`<${this.constructor.type.toLowerCase()} `, 'COMMON_PARTS', `points="${points.join('')}" />\n`];
18704
+ const points = this.points.map(_ref2 => {
18705
+ let {
18706
+ x,
18707
+ y
18708
+ } = _ref2;
18709
+ return `${toFixed(x - diffX, NUM_FRACTION_DIGITS)},${toFixed(y - diffY, NUM_FRACTION_DIGITS)}`;
18710
+ }).join(' ');
18711
+ return [`<${escapeXml(this.constructor.type).toLowerCase()} `, 'COMMON_PARTS', `points="${points}" />\n`];
18365
18712
  }
18366
18713
 
18367
18714
  /**
@@ -18484,7 +18831,6 @@
18484
18831
  };
18485
18832
  for (const p1 in obj) {
18486
18833
  for (const p2 in obj[p1]) {
18487
- // eslint-disable-next-line no-unused-vars
18488
18834
  for (const p3 in obj[p1][p2]) {
18489
18835
  return false;
18490
18836
  }
@@ -18510,9 +18856,7 @@
18510
18856
  const obj = typeof lineIndex === 'undefined' ? this.styles : {
18511
18857
  0: this.styles[lineIndex]
18512
18858
  };
18513
- // eslint-disable-next-line
18514
18859
  for (const p1 in obj) {
18515
- // eslint-disable-next-line
18516
18860
  for (const p2 in obj[p1]) {
18517
18861
  if (typeof obj[p1][p2][property] !== 'undefined') {
18518
18862
  return true;
@@ -18788,7 +19132,7 @@
18788
19132
  } = _ref;
18789
19133
  const noShadow = true,
18790
19134
  textDecoration = this.getSvgTextDecoration(this);
18791
- return [textBgRects.join(''), '\t\t<text xml:space="preserve" ', `font-family="${this.fontFamily.replace(dblQuoteRegex, "'")}" `, `font-size="${this.fontSize}" `, this.fontStyle ? `font-style="${this.fontStyle}" ` : '', this.fontWeight ? `font-weight="${this.fontWeight}" ` : '', textDecoration ? `text-decoration="${textDecoration}" ` : '', this.direction === 'rtl' ? `direction="${this.direction}" ` : '', 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >', textSpans.join(''), '</text>\n'];
19135
+ return [textBgRects.join(''), '\t\t<text xml:space="preserve" ', `font-family="${escapeXml(this.fontFamily.replace(dblQuoteRegex, "'"))}" `, `font-size="${escapeXml(this.fontSize)}" `, this.fontStyle ? `font-style="${escapeXml(this.fontStyle)}" ` : '', this.fontWeight ? `font-weight="${escapeXml(this.fontWeight)}" ` : '', textDecoration ? `text-decoration="${textDecoration}" ` : '', this.direction === 'rtl' ? `direction="rtl" ` : '', 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >', textSpans.join(''), '</text>\n'];
18792
19136
  }
18793
19137
 
18794
19138
  /**
@@ -18804,7 +19148,7 @@
18804
19148
  lineOffset;
18805
19149
 
18806
19150
  // bounding-box background
18807
- this.backgroundColor && textBgRects.push(...createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
19151
+ this.backgroundColor && textBgRects.push(createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
18808
19152
 
18809
19153
  // text and text-background
18810
19154
  for (let i = 0, len = this._textLines.length; i < len; i++) {
@@ -18912,7 +19256,7 @@
18912
19256
  } = this.__charBounds[i][j];
18913
19257
  currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
18914
19258
  if (currentColor !== lastColor) {
18915
- lastColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19259
+ lastColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18916
19260
  boxStart = left;
18917
19261
  boxWidth = width;
18918
19262
  lastColor = currentColor;
@@ -18920,7 +19264,7 @@
18920
19264
  boxWidth += kernedWidth;
18921
19265
  }
18922
19266
  }
18923
- currentColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19267
+ currentColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18924
19268
  }
18925
19269
 
18926
19270
  /**
@@ -18947,7 +19291,6 @@
18947
19291
  fontSize,
18948
19292
  fontStyle,
18949
19293
  fontWeight,
18950
- deltaY,
18951
19294
  textDecorationThickness,
18952
19295
  linethrough,
18953
19296
  overline,
@@ -18959,7 +19302,7 @@
18959
19302
  linethrough: linethrough !== null && linethrough !== void 0 ? linethrough : this.linethrough
18960
19303
  });
18961
19304
  const thickness = textDecorationThickness || this.textDecorationThickness;
18962
- return [stroke ? colorPropToSVG(STROKE, stroke) : '', strokeWidth ? `stroke-width: ${strokeWidth}; ` : '', fontFamily ? `font-family: ${!fontFamily.includes("'") && !fontFamily.includes('"') ? `'${fontFamily}'` : fontFamily}; ` : '', fontSize ? `font-size: ${fontSize}px; ` : '', fontStyle ? `font-style: ${fontStyle}; ` : '', fontWeight ? `font-weight: ${fontWeight}; ` : '', textDecoration ? `text-decoration: ${textDecoration}; text-decoration-thickness: ${toFixed(thickness * this.getObjectScaling().y / 10, config.NUM_FRACTION_DIGITS)}%; ` : '', fill ? colorPropToSVG(FILL, fill) : '', deltaY ? `baseline-shift: ${-deltaY}; ` : '', useWhiteSpace ? 'white-space: pre; ' : ''].join('');
19305
+ return [stroke ? colorPropToSVG(STROKE, stroke) : '', strokeWidth ? `stroke-width: ${escapeXml(strokeWidth)}; ` : '', fontFamily ? `font-family: ${!fontFamily.includes("'") && !fontFamily.includes('"') ? `'${escapeXml(fontFamily)}'` : escapeXml(fontFamily)}; ` : '', fontSize ? `font-size: ${escapeXml(fontSize)}px; ` : '', fontStyle ? `font-style: ${escapeXml(fontStyle)}; ` : '', fontWeight ? `font-weight: ${escapeXml(fontWeight)}; ` : '', textDecoration ? `text-decoration: ${textDecoration}; text-decoration-thickness: ${toFixed(thickness * this.getObjectScaling().y / 10, config.NUM_FRACTION_DIGITS)}%; ` : '', fill ? colorPropToSVG(FILL, fill) : '', useWhiteSpace ? 'white-space: pre; ' : ''].join('');
18963
19306
  }
18964
19307
 
18965
19308
  /**
@@ -24523,13 +24866,13 @@
24523
24866
  }
24524
24867
  if (this.hasCrop()) {
24525
24868
  const clipPathId = uid();
24526
- svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n', '</clipPath>\n');
24869
+ svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + escapeXml(this.width) + '" height="' + escapeXml(this.height) + '" />\n', '</clipPath>\n');
24527
24870
  clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
24528
24871
  }
24529
24872
  if (!this.imageSmoothing) {
24530
24873
  imageRendering = ' image-rendering="optimizeSpeed"';
24531
24874
  }
24532
- imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${this.getSvgSrc(true)}" x="${x - this.cropX}" y="${y - this.cropY
24875
+ imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${escapeXml(this.getSrc(true))}" x="${x - this.cropX}" y="${y - this.cropY
24533
24876
  // we're essentially moving origin of transformation from top/left corner to the center of the shape
24534
24877
  // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
24535
24878
  // so that object's center aligns with container's left/top
@@ -24537,7 +24880,7 @@
24537
24880
  if (this.stroke || this.strokeDashArray) {
24538
24881
  const origFill = this.fill;
24539
24882
  this.fill = null;
24540
- strokeSvg = [`\t<rect x="${x}" y="${y}" width="${this.width}" height="${this.height}" style="${this.getSvgStyles()}" />\n`];
24883
+ strokeSvg = [`\t<rect x="${x}" y="${y}" width="${escapeXml(this.width)}" height="${escapeXml(this.height)}" style="${this.getSvgStyles()}" />\n`];
24541
24884
  this.fill = origFill;
24542
24885
  }
24543
24886
  if (this.paintFirst !== FILL) {
@@ -25520,258 +25863,6 @@
25520
25863
  });
25521
25864
  }
25522
25865
 
25523
- const ACTION_NAME$1 = MODIFY_POLY;
25524
- /**
25525
- * This function locates the controls.
25526
- * It'll be used both for drawing and for interaction.
25527
- */
25528
- const createPolyPositionHandler = pointIndex => {
25529
- return function (dim, finalMatrix, polyObject) {
25530
- const {
25531
- points,
25532
- pathOffset
25533
- } = polyObject;
25534
- return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
25535
- };
25536
- };
25537
-
25538
- /**
25539
- * This function defines what the control does.
25540
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25541
- * The function receives as argument the mouse event, the current transform object
25542
- * and the current position in canvas coordinate `transform.target` is a reference to the
25543
- * current object being transformed.
25544
- */
25545
- const polyActionHandler = (eventData, transform, x, y) => {
25546
- const {
25547
- target,
25548
- pointIndex
25549
- } = transform;
25550
- const poly = target;
25551
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
25552
- poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
25553
- poly.setDimensions();
25554
- poly.set('dirty', true);
25555
- return true;
25556
- };
25557
-
25558
- /**
25559
- * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
25560
- */
25561
- const factoryPolyActionHandler = (pointIndex, fn) => {
25562
- return function (eventData, transform, x, y) {
25563
- const poly = transform.target,
25564
- anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
25565
- anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
25566
- actionPerformed = fn(eventData, {
25567
- ...transform,
25568
- pointIndex
25569
- }, x, y);
25570
- const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
25571
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25572
- poly.left -= diff.x;
25573
- poly.top -= diff.y;
25574
- return actionPerformed;
25575
- };
25576
- };
25577
- const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
25578
- function createPolyControls(arg0) {
25579
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25580
- const controls = {};
25581
- for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
25582
- controls[`p${idx}`] = new Control({
25583
- actionName: ACTION_NAME$1,
25584
- positionHandler: createPolyPositionHandler(idx),
25585
- actionHandler: createPolyActionHandler(idx),
25586
- ...options
25587
- });
25588
- }
25589
- return controls;
25590
- }
25591
-
25592
- const ACTION_NAME = 'modifyPath';
25593
- const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
25594
- const {
25595
- path,
25596
- pathOffset
25597
- } = pathObject;
25598
- const command = path[commandIndex];
25599
- return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
25600
- };
25601
- const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
25602
- const {
25603
- path,
25604
- pathOffset
25605
- } = pathObject;
25606
- const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
25607
- const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
25608
- const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
25609
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
25610
- path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
25611
- path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
25612
- pathObject.setDimensions();
25613
- const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
25614
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25615
- pathObject.left -= diff.x;
25616
- pathObject.top -= diff.y;
25617
- pathObject.set('dirty', true);
25618
- return true;
25619
- };
25620
-
25621
- /**
25622
- * This function locates the controls.
25623
- * It'll be used both for drawing and for interaction.
25624
- */
25625
- function pathPositionHandler(dim, finalMatrix, pathObject) {
25626
- const {
25627
- commandIndex,
25628
- pointIndex
25629
- } = this;
25630
- return calcPathPointPosition(pathObject, commandIndex, pointIndex);
25631
- }
25632
-
25633
- /**
25634
- * This function defines what the control does.
25635
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25636
- * The function receives as argument the mouse event, the current transform object
25637
- * and the current position in canvas coordinate `transform.target` is a reference to the
25638
- * current object being transformed.
25639
- */
25640
- function pathActionHandler(eventData, transform, x, y) {
25641
- const {
25642
- target
25643
- } = transform;
25644
- const {
25645
- commandIndex,
25646
- pointIndex
25647
- } = this;
25648
- const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
25649
- {
25650
- fireEvent(this.actionName, {
25651
- ...commonEventInfo(eventData, transform, x, y),
25652
- commandIndex,
25653
- pointIndex
25654
- });
25655
- }
25656
- return actionPerformed;
25657
- }
25658
- const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
25659
- class PathPointControl extends Control {
25660
- constructor(options) {
25661
- super(options);
25662
- }
25663
- render(ctx, left, top, styleOverride, fabricObject) {
25664
- const overrides = {
25665
- ...styleOverride,
25666
- cornerColor: this.controlFill,
25667
- cornerStrokeColor: this.controlStroke,
25668
- transparentCorners: !this.controlFill
25669
- };
25670
- super.render(ctx, left, top, overrides, fabricObject);
25671
- }
25672
- }
25673
- class PathControlPointControl extends PathPointControl {
25674
- constructor(options) {
25675
- super(options);
25676
- }
25677
- render(ctx, left, top, styleOverride, fabricObject) {
25678
- const {
25679
- path
25680
- } = fabricObject;
25681
- const {
25682
- commandIndex,
25683
- pointIndex,
25684
- connectToCommandIndex,
25685
- connectToPointIndex
25686
- } = this;
25687
- ctx.save();
25688
- ctx.strokeStyle = this.controlStroke;
25689
- if (this.connectionDashArray) {
25690
- ctx.setLineDash(this.connectionDashArray);
25691
- }
25692
- const [commandType] = path[commandIndex];
25693
- const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
25694
- if (commandType === 'Q') {
25695
- // one control point connects to 2 points
25696
- const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
25697
- ctx.moveTo(point2.x, point2.y);
25698
- ctx.lineTo(left, top);
25699
- } else {
25700
- ctx.moveTo(left, top);
25701
- }
25702
- ctx.lineTo(point.x, point.y);
25703
- ctx.stroke();
25704
- ctx.restore();
25705
- super.render(ctx, left, top, styleOverride, fabricObject);
25706
- }
25707
- }
25708
- const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
25709
- commandIndex: commandIndexPos,
25710
- pointIndex: pointIndexPos,
25711
- actionName: ACTION_NAME,
25712
- positionHandler: pathPositionHandler,
25713
- actionHandler: pathActionHandler,
25714
- connectToCommandIndex,
25715
- connectToPointIndex,
25716
- ...options,
25717
- ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
25718
- });
25719
- function createPathControls(path) {
25720
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25721
- const controls = {};
25722
- let previousCommandType = 'M';
25723
- path.path.forEach((command, commandIndex) => {
25724
- const commandType = command[0];
25725
- if (commandType !== 'Z') {
25726
- controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
25727
- }
25728
- switch (commandType) {
25729
- case 'C':
25730
- controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
25731
- controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
25732
- break;
25733
- case 'Q':
25734
- controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
25735
- break;
25736
- }
25737
- previousCommandType = commandType;
25738
- });
25739
- return controls;
25740
- }
25741
-
25742
- var index = /*#__PURE__*/Object.freeze({
25743
- __proto__: null,
25744
- changeWidth: changeWidth,
25745
- createObjectDefaultControls: createObjectDefaultControls,
25746
- createPathControls: createPathControls,
25747
- createPolyActionHandler: createPolyActionHandler,
25748
- createPolyControls: createPolyControls,
25749
- createPolyPositionHandler: createPolyPositionHandler,
25750
- createResizeControls: createResizeControls,
25751
- createTextboxDefaultControls: createTextboxDefaultControls,
25752
- dragHandler: dragHandler,
25753
- factoryPolyActionHandler: factoryPolyActionHandler,
25754
- getLocalPoint: getLocalPoint,
25755
- polyActionHandler: polyActionHandler,
25756
- renderCircleControl: renderCircleControl,
25757
- renderSquareControl: renderSquareControl,
25758
- rotationStyleHandler: rotationStyleHandler,
25759
- rotationWithSnapping: rotationWithSnapping,
25760
- scaleCursorStyleHandler: scaleCursorStyleHandler,
25761
- scaleOrSkewActionName: scaleOrSkewActionName,
25762
- scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
25763
- scalingEqually: scalingEqually,
25764
- scalingX: scalingX,
25765
- scalingXOrSkewingY: scalingXOrSkewingY,
25766
- scalingY: scalingY,
25767
- scalingYOrSkewingX: scalingYOrSkewingX,
25768
- skewCursorStyleHandler: skewCursorStyleHandler,
25769
- skewHandlerX: skewHandlerX,
25770
- skewHandlerY: skewHandlerY,
25771
- wrapWithFireEvent: wrapWithFireEvent,
25772
- wrapWithFixedAnchor: wrapWithFixedAnchor
25773
- });
25774
-
25775
25866
  const isWebGLPipelineState = options => {
25776
25867
  return options.webgl !== undefined;
25777
25868
  };