From 0bb65f95ae226eefa1aa00cd7f0c9261a7335ebc Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 22:20:12 +0200 Subject: [PATCH 01/58] init --- src/mixins/object_ancestry.mixin.ts | 252 +++++++++++++++------------- src/util/applymixins.ts | 10 +- 2 files changed, 139 insertions(+), 123 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index c41eb5d3528..9e12b275bb3 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,134 +1,144 @@ -//@ts-nocheck +import { fabric } from '../../HEADER'; import { FabricObject } from '../shapes/fabricObject.class'; +import { Group } from '../shapes/group.class'; +import { Canvas, StaticCanvas } from '../__types__'; +import { applyMixins } from '../util/applyMixins'; -(function (global) { - var fabric = global.fabric; - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * Checks if object is decendant of target - * Should be used instead of @link {fabric.Collection.contains} for performance reasons - * @param {fabric.Object|fabric.StaticCanvas} target - * @returns {boolean} - */ - isDescendantOf: function (target) { - var parent = this.group || this.canvas; - while (parent) { - if (target === parent) { - return true; - } else if (parent instanceof fabric.StaticCanvas) { - // happens after all parents were traversed through without a match - return false; - } - parent = parent.group || parent.canvas; - } +export class FabricObjectAncestryMixin { + group?: Group; + canvas?: StaticCanvas; + /** + * Checks if object is descendant of target + * Should be used instead of @link {fabric.Collection.contains} for performance reasons + * @param {FabricObject|fabric.StaticCanvas} target + * @returns {boolean} + */ + isDescendantOf(target: FabricObject | StaticCanvas) { + let parent = this.group || this.canvas; + while (parent) { + if (target === parent) { + return true; + } else if (parent instanceof fabric.StaticCanvas) { + // happens after all parents were traversed through without a match return false; - }, + } + parent = parent.group || parent.canvas; + } + return false; + } - /** - * - * @typedef {fabric.Object[] | [...fabric.Object[], fabric.StaticCanvas]} Ancestors - * - * @param {boolean} [strict] returns only ancestors that are objects (without canvas) - * @returns {Ancestors} ancestors from bottom to top - */ - getAncestors: function (strict) { - var ancestors = []; - var parent = this.group || (strict ? undefined : this.canvas); - while (parent) { - ancestors.push(parent); - parent = parent.group || (strict ? undefined : parent.canvas); - } - return ancestors; - }, + /** + * + * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors + * + * @param {boolean} [strict] returns only ancestors that are objects (without canvas) + * @returns {Ancestors} ancestors from bottom to top + */ + getAncestors(strict?: boolean) { + const ancestors: (Group | StaticCanvas | Canvas)[] = []; + let parent = this.group || (strict ? undefined : this.canvas); + while (parent) { + ancestors.push(parent); + parent = parent.group || (strict ? undefined : parent.canvas); + } + return ancestors; + } - /** - * Returns an object that represent the ancestry situation. - * - * @typedef {object} AncestryComparison - * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) - * @property {Ancestors} fork ancestors that are of `this` only - * @property {Ancestors} otherFork ancestors that are of `other` only - * - * @param {fabric.Object} other - * @param {boolean} [strict] finds only ancestors that are objects (without canvas) - * @returns {AncestryComparison | undefined} - * - */ - findCommonAncestors: function (other, strict) { - if (this === other) { + /** + * Returns an object that represent the ancestry situation. + * + * @typedef {object} AncestryComparison + * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) + * @property {Ancestors} fork ancestors that are of `this` only + * @property {Ancestors} otherFork ancestors that are of `other` only + * + * @param {FabricObject} other + * @param {boolean} [strict] finds only ancestors that are objects (without canvas) + * @returns {AncestryComparison | undefined} + * + */ + findCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean + ) { + if (this === other) { + return { + fork: [], + otherFork: [], + common: [this, ...this.getAncestors(strict)], + }; + } else if (!other) { + // meh, warn and inform, and not my issue. + // the argument is NOT optional, we can't end up here. + return undefined; + } + const ancestors = this.getAncestors(strict); + const otherAncestors = other.getAncestors(strict); + // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case + if ( + ancestors.length === 0 && + otherAncestors.length > 0 && + this === otherAncestors[otherAncestors.length - 1] + ) { + return { + fork: [], + otherFork: [ + other, + ...otherAncestors.slice(0, otherAncestors.length - 1), + ], + common: [this], + }; + } + // compare ancestors + for (let i = 0, ancestor; i < ancestors.length; i++) { + ancestor = ancestors[i]; + if (ancestor === other) { + return { + fork: [this, ...ancestors.slice(0, i)], + otherFork: [], + common: ancestors.slice(i), + }; + } + for (let j = 0; j < otherAncestors.length; j++) { + if (this === otherAncestors[j]) { return { fork: [], - otherFork: [], - common: [this].concat(this.getAncestors(strict)), + otherFork: [other, ...otherAncestors.slice(0, j)], + common: [this, ...ancestors], }; - } else if (!other) { - // meh, warn and inform, and not my issue. - // the argument is NOT optional, we can't end up here. - return undefined; } - var ancestors = this.getAncestors(strict); - var otherAncestors = other.getAncestors(strict); - // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case - if ( - ancestors.length === 0 && - otherAncestors.length > 0 && - this === otherAncestors[otherAncestors.length - 1] - ) { + if (ancestor === otherAncestors[j]) { return { - fork: [], - otherFork: [other].concat( - otherAncestors.slice(0, otherAncestors.length - 1) - ), - common: [this], + fork: [this, ...ancestors.slice(0, i)], + otherFork: [other, ...otherAncestors.slice(0, j)], + common: ancestors.slice(i), }; } - // compare ancestors - for (var i = 0, ancestor; i < ancestors.length; i++) { - ancestor = ancestors[i]; - if (ancestor === other) { - return { - fork: [this].concat(ancestors.slice(0, i)), - otherFork: [], - common: ancestors.slice(i), - }; - } - for (var j = 0; j < otherAncestors.length; j++) { - if (this === otherAncestors[j]) { - return { - fork: [], - otherFork: [other].concat(otherAncestors.slice(0, j)), - common: [this].concat(ancestors), - }; - } - if (ancestor === otherAncestors[j]) { - return { - fork: [this].concat(ancestors.slice(0, i)), - otherFork: [other].concat(otherAncestors.slice(0, j)), - common: ancestors.slice(i), - }; - } - } - } - // nothing shared - return { - fork: [this].concat(ancestors), - otherFork: [other].concat(otherAncestors), - common: [], - }; - }, - - /** - * - * @param {fabric.Object} other - * @param {boolean} [strict] checks only ancestors that are objects (without canvas) - * @returns {boolean} - */ - hasCommonAncestors: function (other, strict) { - var commonAncestors = this.findCommonAncestors(other, strict); - return commonAncestors && !!commonAncestors.ancestors.length; - }, + } } - ); -})(typeof exports !== 'undefined' ? exports : window); + // nothing shared + return { + fork: [this, ...ancestors], + otherFork: [other, ...otherAncestors], + common: [], + }; + } + + /** + * + * @param {FabricObject} other + * @param {boolean} [strict] checks only ancestors that are objects (without canvas) + * @returns {boolean} + */ + hasCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean + ) { + const commonAncestors = this.findCommonAncestors(other, strict); + return commonAncestors && !!commonAncestors.common.length; + } +} + +// applyMixins(FabricObject, [FabricObjectAncestryMixin]); diff --git a/src/util/applymixins.ts b/src/util/applymixins.ts index 4314c28fdc9..d5322b9fc60 100644 --- a/src/util/applymixins.ts +++ b/src/util/applymixins.ts @@ -1,7 +1,12 @@ -/** +type TClass = { new (...args: any[]): any }; + +/*** * https://www.typescriptlang.org/docs/handbook/mixins.html#alternative-pattern */ -export function applyMixins(derivedCtor: any, constructors: any[]) { +export function applyMixins( + derivedCtor: T, + constructors: S[] +) { constructors.forEach((baseCtor) => { Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { Object.defineProperty( @@ -12,4 +17,5 @@ export function applyMixins(derivedCtor: any, constructors: any[]) { ); }); }); + return derivedCtor as T & S; } From 39aa82902eb06cabaa4204277774a8bacbf079c4 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 22:33:05 +0200 Subject: [PATCH 02/58] Update object_ancestry.mixin.ts --- src/mixins/object_ancestry.mixin.ts | 250 ++++++++++++++-------------- 1 file changed, 128 insertions(+), 122 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 9e12b275bb3..8ce600b4878 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,144 +1,150 @@ +// @ts-nocheck import { fabric } from '../../HEADER'; -import { FabricObject } from '../shapes/fabricObject.class'; -import { Group } from '../shapes/group.class'; -import { Canvas, StaticCanvas } from '../__types__'; -import { applyMixins } from '../util/applyMixins'; -export class FabricObjectAncestryMixin { - group?: Group; - canvas?: StaticCanvas; - /** - * Checks if object is descendant of target - * Should be used instead of @link {fabric.Collection.contains} for performance reasons - * @param {FabricObject|fabric.StaticCanvas} target - * @returns {boolean} - */ - isDescendantOf(target: FabricObject | StaticCanvas) { - let parent = this.group || this.canvas; - while (parent) { - if (target === parent) { - return true; - } else if (parent instanceof fabric.StaticCanvas) { - // happens after all parents were traversed through without a match - return false; +import type { FabricObject } from '../shapes/fabricObject.class'; +import type { Group } from '../shapes/group.class'; +import type { Canvas, StaticCanvas } from '../__types__'; + +export function FabricObjectAncestryMixinGenerator(Klass: any) { + return class FabricObjectAncestryMixin extends Klass { + group?: Group; + canvas?: StaticCanvas; + + constructor(...args: any[]) { + super(...args); + } + /** + * Checks if object is descendant of target + * Should be used instead of @link {fabric.Collection.contains} for performance reasons + * @param {FabricObject|fabric.StaticCanvas} target + * @returns {boolean} + */ + isDescendantOf(target: FabricObject | StaticCanvas) { + let parent = this.group || this.canvas; + while (parent) { + if (target === parent) { + return true; + } else if (parent instanceof fabric.StaticCanvas) { + // happens after all parents were traversed through without a match + return false; + } + parent = parent.group || parent.canvas; } - parent = parent.group || parent.canvas; + return false; } - return false; - } - /** - * - * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors - * - * @param {boolean} [strict] returns only ancestors that are objects (without canvas) - * @returns {Ancestors} ancestors from bottom to top - */ - getAncestors(strict?: boolean) { - const ancestors: (Group | StaticCanvas | Canvas)[] = []; - let parent = this.group || (strict ? undefined : this.canvas); - while (parent) { - ancestors.push(parent); - parent = parent.group || (strict ? undefined : parent.canvas); + /** + * + * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors + * + * @param {boolean} [strict] returns only ancestors that are objects (without canvas) + * @returns {Ancestors} ancestors from bottom to top + */ + getAncestors(strict?: boolean) { + const ancestors: (Group | StaticCanvas | Canvas)[] = []; + let parent = this.group || (strict ? undefined : this.canvas); + while (parent) { + ancestors.push(parent); + parent = parent.group || (strict ? undefined : parent.canvas); + } + return ancestors; } - return ancestors; - } - /** - * Returns an object that represent the ancestry situation. - * - * @typedef {object} AncestryComparison - * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) - * @property {Ancestors} fork ancestors that are of `this` only - * @property {Ancestors} otherFork ancestors that are of `other` only - * - * @param {FabricObject} other - * @param {boolean} [strict] finds only ancestors that are objects (without canvas) - * @returns {AncestryComparison | undefined} - * - */ - findCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, - strict?: boolean - ) { - if (this === other) { - return { - fork: [], - otherFork: [], - common: [this, ...this.getAncestors(strict)], - }; - } else if (!other) { - // meh, warn and inform, and not my issue. - // the argument is NOT optional, we can't end up here. - return undefined; - } - const ancestors = this.getAncestors(strict); - const otherAncestors = other.getAncestors(strict); - // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case - if ( - ancestors.length === 0 && - otherAncestors.length > 0 && - this === otherAncestors[otherAncestors.length - 1] + /** + * Returns an object that represent the ancestry situation. + * + * @typedef {object} AncestryComparison + * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) + * @property {Ancestors} fork ancestors that are of `this` only + * @property {Ancestors} otherFork ancestors that are of `other` only + * + * @param {FabricObject} other + * @param {boolean} [strict] finds only ancestors that are objects (without canvas) + * @returns {AncestryComparison | undefined} + * + */ + findCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean ) { - return { - fork: [], - otherFork: [ - other, - ...otherAncestors.slice(0, otherAncestors.length - 1), - ], - common: [this], - }; - } - // compare ancestors - for (let i = 0, ancestor; i < ancestors.length; i++) { - ancestor = ancestors[i]; - if (ancestor === other) { + if (this === other) { return { - fork: [this, ...ancestors.slice(0, i)], + fork: [], otherFork: [], - common: ancestors.slice(i), + common: [this, ...this.getAncestors(strict)], }; + } else if (!other) { + // meh, warn and inform, and not my issue. + // the argument is NOT optional, we can't end up here. + return undefined; } - for (let j = 0; j < otherAncestors.length; j++) { - if (this === otherAncestors[j]) { - return { - fork: [], - otherFork: [other, ...otherAncestors.slice(0, j)], - common: [this, ...ancestors], - }; - } - if (ancestor === otherAncestors[j]) { + const ancestors = this.getAncestors(strict); + const otherAncestors = other.getAncestors(strict); + // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case + if ( + ancestors.length === 0 && + otherAncestors.length > 0 && + this === otherAncestors[otherAncestors.length - 1] + ) { + return { + fork: [], + otherFork: [ + other, + ...otherAncestors.slice(0, otherAncestors.length - 1), + ], + common: [this], + }; + } + // compare ancestors + for (let i = 0, ancestor; i < ancestors.length; i++) { + ancestor = ancestors[i]; + if (ancestor === other) { return { fork: [this, ...ancestors.slice(0, i)], - otherFork: [other, ...otherAncestors.slice(0, j)], + otherFork: [], common: ancestors.slice(i), }; } + for (let j = 0; j < otherAncestors.length; j++) { + if (this === otherAncestors[j]) { + return { + fork: [], + otherFork: [other, ...otherAncestors.slice(0, j)], + common: [this, ...ancestors], + }; + } + if (ancestor === otherAncestors[j]) { + return { + fork: [this, ...ancestors.slice(0, i)], + otherFork: [other, ...otherAncestors.slice(0, j)], + common: ancestors.slice(i), + }; + } + } } + // nothing shared + return { + fork: [this, ...ancestors], + otherFork: [other, ...otherAncestors], + common: [], + }; } - // nothing shared - return { - fork: [this, ...ancestors], - otherFork: [other, ...otherAncestors], - common: [], - }; - } - /** - * - * @param {FabricObject} other - * @param {boolean} [strict] checks only ancestors that are objects (without canvas) - * @returns {boolean} - */ - hasCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, - strict?: boolean - ) { - const commonAncestors = this.findCommonAncestors(other, strict); - return commonAncestors && !!commonAncestors.common.length; - } + /** + * + * @param {FabricObject} other + * @param {boolean} [strict] checks only ancestors that are objects (without canvas) + * @returns {boolean} + */ + hasCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean + ) { + const commonAncestors = this.findCommonAncestors(other, strict); + return commonAncestors && !!commonAncestors.common.length; + } + }; } - // applyMixins(FabricObject, [FabricObjectAncestryMixin]); From c4f72d33b414b825d3d141ac4f9f843f7c167c10 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 22:48:29 +0200 Subject: [PATCH 03/58] apply mixins --- src/mixins/object_ancestry.mixin.ts | 242 ++++++++++++++-------------- src/shapes/fabricObject.class.js | 4 + src/util/applymixins.ts | 13 +- 3 files changed, 128 insertions(+), 131 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 8ce600b4878..5ede2bece34 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,150 +1,142 @@ -// @ts-nocheck import { fabric } from '../../HEADER'; - import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; import type { Canvas, StaticCanvas } from '../__types__'; -export function FabricObjectAncestryMixinGenerator(Klass: any) { - return class FabricObjectAncestryMixin extends Klass { - group?: Group; - canvas?: StaticCanvas; +export class FabricObjectAncestryMixin { + group?: Group; + canvas?: StaticCanvas; - constructor(...args: any[]) { - super(...args); - } - /** - * Checks if object is descendant of target - * Should be used instead of @link {fabric.Collection.contains} for performance reasons - * @param {FabricObject|fabric.StaticCanvas} target - * @returns {boolean} - */ - isDescendantOf(target: FabricObject | StaticCanvas) { - let parent = this.group || this.canvas; - while (parent) { - if (target === parent) { - return true; - } else if (parent instanceof fabric.StaticCanvas) { - // happens after all parents were traversed through without a match - return false; - } - parent = parent.group || parent.canvas; + /** + * Checks if object is descendant of target + * Should be used instead of @link {fabric.Collection.contains} for performance reasons + * @param {FabricObject|fabric.StaticCanvas} target + * @returns {boolean} + */ + isDescendantOf(target: FabricObject | StaticCanvas) { + let parent = this.group || this.canvas; + while (parent) { + if (target === parent) { + return true; + } else if (parent instanceof fabric.StaticCanvas) { + // happens after all parents were traversed through without a match + return false; } - return false; + parent = parent.group || parent.canvas; } + return false; + } - /** - * - * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors - * - * @param {boolean} [strict] returns only ancestors that are objects (without canvas) - * @returns {Ancestors} ancestors from bottom to top - */ - getAncestors(strict?: boolean) { - const ancestors: (Group | StaticCanvas | Canvas)[] = []; - let parent = this.group || (strict ? undefined : this.canvas); - while (parent) { - ancestors.push(parent); - parent = parent.group || (strict ? undefined : parent.canvas); - } - return ancestors; + /** + * + * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors + * + * @param {boolean} [strict] returns only ancestors that are objects (without canvas) + * @returns {Ancestors} ancestors from bottom to top + */ + getAncestors(strict?: boolean) { + const ancestors: (Group | StaticCanvas | Canvas)[] = []; + let parent = this.group || (strict ? undefined : this.canvas); + while (parent) { + ancestors.push(parent); + parent = parent.group || (strict ? undefined : parent.canvas); } + return ancestors; + } - /** - * Returns an object that represent the ancestry situation. - * - * @typedef {object} AncestryComparison - * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) - * @property {Ancestors} fork ancestors that are of `this` only - * @property {Ancestors} otherFork ancestors that are of `other` only - * - * @param {FabricObject} other - * @param {boolean} [strict] finds only ancestors that are objects (without canvas) - * @returns {AncestryComparison | undefined} - * - */ - findCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, - strict?: boolean + /** + * Returns an object that represent the ancestry situation. + * + * @typedef {object} AncestryComparison + * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) + * @property {Ancestors} fork ancestors that are of `this` only + * @property {Ancestors} otherFork ancestors that are of `other` only + * + * @param {FabricObject} other + * @param {boolean} [strict] finds only ancestors that are objects (without canvas) + * @returns {AncestryComparison | undefined} + * + */ + findCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean + ) { + if (this === other) { + return { + fork: [], + otherFork: [], + common: [this, ...this.getAncestors(strict)], + }; + } else if (!other) { + // meh, warn and inform, and not my issue. + // the argument is NOT optional, we can't end up here. + return undefined; + } + const ancestors = this.getAncestors(strict); + const otherAncestors = other.getAncestors(strict); + // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case + if ( + ancestors.length === 0 && + otherAncestors.length > 0 && + this === otherAncestors[otherAncestors.length - 1] ) { - if (this === other) { + return { + fork: [], + otherFork: [ + other, + ...otherAncestors.slice(0, otherAncestors.length - 1), + ], + common: [this], + }; + } + // compare ancestors + for (let i = 0, ancestor; i < ancestors.length; i++) { + ancestor = ancestors[i]; + if (ancestor === other) { return { - fork: [], + fork: [this, ...ancestors.slice(0, i)], otherFork: [], - common: [this, ...this.getAncestors(strict)], + common: ancestors.slice(i), }; - } else if (!other) { - // meh, warn and inform, and not my issue. - // the argument is NOT optional, we can't end up here. - return undefined; } - const ancestors = this.getAncestors(strict); - const otherAncestors = other.getAncestors(strict); - // if `this` has no ancestors and `this` is top ancestor of `other` we must handle the following case - if ( - ancestors.length === 0 && - otherAncestors.length > 0 && - this === otherAncestors[otherAncestors.length - 1] - ) { - return { - fork: [], - otherFork: [ - other, - ...otherAncestors.slice(0, otherAncestors.length - 1), - ], - common: [this], - }; - } - // compare ancestors - for (let i = 0, ancestor; i < ancestors.length; i++) { - ancestor = ancestors[i]; - if (ancestor === other) { + for (let j = 0; j < otherAncestors.length; j++) { + if (this === otherAncestors[j]) { + return { + fork: [], + otherFork: [other, ...otherAncestors.slice(0, j)], + common: [this, ...ancestors], + }; + } + if (ancestor === otherAncestors[j]) { return { fork: [this, ...ancestors.slice(0, i)], - otherFork: [], + otherFork: [other, ...otherAncestors.slice(0, j)], common: ancestors.slice(i), }; } - for (let j = 0; j < otherAncestors.length; j++) { - if (this === otherAncestors[j]) { - return { - fork: [], - otherFork: [other, ...otherAncestors.slice(0, j)], - common: [this, ...ancestors], - }; - } - if (ancestor === otherAncestors[j]) { - return { - fork: [this, ...ancestors.slice(0, i)], - otherFork: [other, ...otherAncestors.slice(0, j)], - common: ancestors.slice(i), - }; - } - } } - // nothing shared - return { - fork: [this, ...ancestors], - otherFork: [other, ...otherAncestors], - common: [], - }; } + // nothing shared + return { + fork: [this, ...ancestors], + otherFork: [other, ...otherAncestors], + common: [], + }; + } - /** - * - * @param {FabricObject} other - * @param {boolean} [strict] checks only ancestors that are objects (without canvas) - * @returns {boolean} - */ - hasCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, - strict?: boolean - ) { - const commonAncestors = this.findCommonAncestors(other, strict); - return commonAncestors && !!commonAncestors.common.length; - } - }; + /** + * + * @param {FabricObject} other + * @param {boolean} [strict] checks only ancestors that are objects (without canvas) + * @returns {boolean} + */ + hasCommonAncestors( + this: FabricObject & this, + other: FabricObject & this, + strict?: boolean + ) { + const commonAncestors = this.findCommonAncestors(other, strict); + return commonAncestors && !!commonAncestors.common.length; + } } -// applyMixins(FabricObject, [FabricObjectAncestryMixin]); diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.js index d860333e20d..a5b72f025fd 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.js @@ -1,7 +1,11 @@ +import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; +import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import +applyMixins(InteractiveFabricObject, [FabricObjectAncestryMixin]); + export { InteractiveFabricObject as FabricObject }; (function (global) { diff --git a/src/util/applymixins.ts b/src/util/applymixins.ts index d5322b9fc60..d7d2d122ff1 100644 --- a/src/util/applymixins.ts +++ b/src/util/applymixins.ts @@ -9,12 +9,13 @@ export function applyMixins( ) { constructors.forEach((baseCtor) => { Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { - Object.defineProperty( - derivedCtor.prototype, - name, - Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || - Object.create(null) - ); + name !== 'constructor' && + Object.defineProperty( + derivedCtor.prototype, + name, + Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || + Object.create(null) + ); }); }); return derivedCtor as T & S; From 0333f3864d52b1ebd2af300b43ba66e1adb79b90 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:10:15 +0200 Subject: [PATCH 04/58] mixins --- src/__types__.ts | 2 + src/mixins/object_ancestry.mixin.ts | 36 ++++- src/mixins/object_stacking.mixin.ts | 195 ++++++++++++---------------- src/shapes/fabricObject.class.js | 6 +- src/shapes/group.class.ts | 4 +- 5 files changed, 125 insertions(+), 118 deletions(-) diff --git a/src/__types__.ts b/src/__types__.ts index 2ac2cd9623c..f91a6af0528 100644 --- a/src/__types__.ts +++ b/src/__types__.ts @@ -1,5 +1,6 @@ import type { Observable } from './mixins/observable.mixin'; import type { Point } from './point.class'; +import type { FabricObject } from './shapes/fabricObject.class'; import { ModifierKey, TMat2D } from './typedefs'; /** @@ -19,6 +20,7 @@ export type StaticCanvas = Record & { br: Point; }; getRetinaScaling(): number; + _objects: FabricObject[]; } & Observable; export type Rect = any; export type TObject = any; diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 5ede2bece34..e3810b2605b 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,3 +1,5 @@ +// @ts-nocheck + import { fabric } from '../../HEADER'; import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; @@ -64,8 +66,8 @@ export class FabricObjectAncestryMixin { ) { if (this === other) { return { - fork: [], - otherFork: [], + fork: [] as FabricObject[], + otherFork: [] as FabricObject[], common: [this, ...this.getAncestors(strict)], }; } else if (!other) { @@ -139,4 +141,34 @@ export class FabricObjectAncestryMixin { const commonAncestors = this.findCommonAncestors(other, strict); return commonAncestors && !!commonAncestors.common.length; } + + /** + * + * @param {FabricObject} other object to compare against + * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` + */ + isInFrontOf(this: FabricObject & this, other: FabricObject & this) { + if (this === other) { + return undefined; + } + const ancestorData = this.findCommonAncestors(other); + if (!ancestorData) { + return undefined; + } + if (ancestorData.fork.includes(other)) { + return true; + } + if (ancestorData.otherFork.includes(this)) { + return false; + } + const firstCommonAncestor = ancestorData.common[0]; + if (!firstCommonAncestor) { + return undefined; + } + const headOfFork = ancestorData.fork.pop(), + headOfOtherFork = ancestorData.otherFork.pop(), + thisIndex = firstCommonAncestor._objects.indexOf(headOfFork), + otherIndex = firstCommonAncestor._objects.indexOf(headOfOtherFork); + return thisIndex > -1 && thisIndex > otherIndex; + } } diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts index ef6e9ef8ce3..25a93d94483 100644 --- a/src/mixins/object_stacking.mixin.ts +++ b/src/mixins/object_stacking.mixin.ts @@ -1,121 +1,88 @@ -//@ts-nocheck -import { FabricObject } from '../shapes/fabricObject.class'; +// @ts-nocheck -(function (global) { - var fabric = global.fabric; - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * Moves an object to the bottom of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - sendToBack: function () { - if (this.group) { - fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); - } else if (this.canvas) { - this.canvas.sendToBack(this); - } - return this; - }, +import { fabric } from '../../HEADER'; +import type { FabricObject } from '../shapes/fabricObject.class'; +import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; - /** - * Moves an object to the top of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - bringToFront: function () { - if (this.group) { - fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); - } else if (this.canvas) { - this.canvas.bringToFront(this); - } - return this; - }, +export class FabricObjectObjectStackingMixin extends FabricObjectAncestryMixin { + /** + * Moves an object to the bottom of the stack of drawn objects + * @return {FabricObject} thisArg + * @chainable + */ + sendToBack() { + if (this.group) { + fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); + } else if (this.canvas) { + this.canvas.sendToBack(this); + } + return this; + } - /** - * Moves an object down in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - sendBackwards: function (intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.sendBackwards.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.sendBackwards(this, intersecting); - } - return this; - }, + /** + * Moves an object to the top of the stack of drawn objects + * @return {FabricObject} thisArg + * @chainable + */ + bringToFront() { + if (this.group) { + fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); + } else if (this.canvas) { + this.canvas.bringToFront(this); + } + return this; + } - /** - * Moves an object up in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - bringForward: function (intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.bringForward.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.bringForward(this, intersecting); - } - return this; - }, + /** + * Moves an object down in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object + * @return {FabricObject} thisArg + * @chainable + */ + sendBackwards(intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.sendBackwards.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.sendBackwards(this, intersecting); + } + return this; + } - /** - * Moves an object to specified level in stack of drawn objects - * @param {Number} index New position of object - * @return {fabric.Object} thisArg - * @chainable - */ - moveTo: function (index) { - if (this.group && this.group.type !== 'activeSelection') { - fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); - } else if (this.canvas) { - this.canvas.moveTo(this, index); - } - return this; - }, + /** + * Moves an object up in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object + * @return {FabricObject} thisArg + * @chainable + */ + bringForward(intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.bringForward.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.bringForward(this, intersecting); + } + return this; + } - /** - * - * @param {fabric.Object} other object to compare against - * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` - */ - isInFrontOf: function (other) { - if (this === other) { - return undefined; - } - var ancestorData = this.findCommonAncestors(other); - if (!ancestorData) { - return undefined; - } - if (ancestorData.fork.includes(other)) { - return true; - } - if (ancestorData.otherFork.includes(this)) { - return false; - } - var firstCommonAncestor = ancestorData.common[0]; - if (!firstCommonAncestor) { - return undefined; - } - var headOfFork = ancestorData.fork.pop(), - headOfOtherFork = ancestorData.otherFork.pop(), - thisIndex = firstCommonAncestor._objects.indexOf(headOfFork), - otherIndex = firstCommonAncestor._objects.indexOf(headOfOtherFork); - return thisIndex > -1 && thisIndex > otherIndex; - }, + /** + * Moves an object to specified level in stack of drawn objects + * @param {Number} index New position of object + * @return {FabricObject} thisArg + * @chainable + */ + moveTo(index) { + if (this.group && this.group.type !== 'activeSelection') { + fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); + } else if (this.canvas) { + this.canvas.moveTo(this, index); } - ); -})(typeof exports !== 'undefined' ? exports : window); + return this; + } +} diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.js index a5b72f025fd..b7635d8337f 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.js @@ -1,10 +1,14 @@ import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; +import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import -applyMixins(InteractiveFabricObject, [FabricObjectAncestryMixin]); +applyMixins(InteractiveFabricObject, [ + FabricObjectAncestryMixin, + FabricObjectObjectStackingMixin, +]); export { InteractiveFabricObject as FabricObject }; diff --git a/src/shapes/group.class.ts b/src/shapes/group.class.ts index caa22f366fe..f38269f150d 100644 --- a/src/shapes/group.class.ts +++ b/src/shapes/group.class.ts @@ -3,7 +3,9 @@ import { Point } from '../point.class'; import { FabricObject } from './fabricObject.class'; import { resolveOrigin } from '../mixins/object_origin.mixin'; -export class Group extends FabricObject {} +export class Group extends FabricObject { + _objects: FabricObject[]; +} (function (global) { var fabric = global.fabric || (global.fabric = {}), From 4a87cccf85b4b37484da549a0e2da21a99625dd1 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:11:17 +0200 Subject: [PATCH 05/58] split --- src/mixins/CanvasStraighteningMixin.ts | 27 +++++++++++++++++++++++ src/mixins/object_straightening.mixin.ts | 28 ------------------------ 2 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 src/mixins/CanvasStraighteningMixin.ts diff --git a/src/mixins/CanvasStraighteningMixin.ts b/src/mixins/CanvasStraighteningMixin.ts new file mode 100644 index 00000000000..43cad2932f3 --- /dev/null +++ b/src/mixins/CanvasStraighteningMixin.ts @@ -0,0 +1,27 @@ +fabric.util.object.extend( + fabric.StaticCanvas.prototype, + /** @lends fabric.StaticCanvas.prototype */ { + /** + * Straightens object, then rerenders canvas + * @param {fabric.Object} object Object to straighten + * @return {fabric.Canvas} thisArg + * @chainable + */ + straightenObject: function (object) { + object.straighten(); + this.requestRenderAll(); + return this; + }, + + /** + * Same as {@link fabric.Canvas.prototype.straightenObject}, but animated + * @param {fabric.Object} object Object to straighten + * @return {fabric.Canvas} thisArg + */ + fxStraightenObject: function (object) { + return object.fxStraighten({ + onChange: this.requestRenderAllBound, + }); + }, + } +); diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts index 7f320e56f4b..e63ac3ef6c6 100644 --- a/src/mixins/object_straightening.mixin.ts +++ b/src/mixins/object_straightening.mixin.ts @@ -59,32 +59,4 @@ import { FabricObject } from '../shapes/fabricObject.class'; }, } ); - - fabric.util.object.extend( - fabric.StaticCanvas.prototype, - /** @lends fabric.StaticCanvas.prototype */ { - /** - * Straightens object, then rerenders canvas - * @param {fabric.Object} object Object to straighten - * @return {fabric.Canvas} thisArg - * @chainable - */ - straightenObject: function (object) { - object.straighten(); - this.requestRenderAll(); - return this; - }, - - /** - * Same as {@link fabric.Canvas.prototype.straightenObject}, but animated - * @param {fabric.Object} object Object to straighten - * @return {fabric.Canvas} thisArg - */ - fxStraightenObject: function (object) { - return object.fxStraighten({ - onChange: this.requestRenderAllBound, - }); - }, - } - ); })(typeof exports !== 'undefined' ? exports : window); From a1db53e7ed70b8cbdb0b7c4b74c36fafc3632a47 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:11:31 +0200 Subject: [PATCH 06/58] Squashed commit of the following: commit f9071cffbffe0fe2353ecb05bc4da378513e522f Author: ShaMan123 Date: Mon Oct 31 21:52:32 2022 +0200 Update transform_files.mjs commit 6ebf5ffe52a9e6ddb8c0b1572a6002dbca79e632 Author: ShaMan123 Date: Mon Oct 31 20:31:18 2022 +0200 Update transform_files.mjs commit 6433c0b18ecbd7e47af85e95c2e974e62c53c2db Author: ShaMan123 Date: Mon Oct 31 20:09:43 2022 +0200 Update transform_files.mjs commit 57934893e2fd81884159d2416c566ea087cbca3f Author: ShaMan123 Date: Mon Oct 31 20:01:55 2022 +0200 Update transform_files.mjs commit 06a12b03418be0e1d23a7825b7da082678563cd8 Author: ShaMan123 Date: Mon Oct 31 19:39:23 2022 +0200 Update transform_files.mjs commit 411158707a39ef945a6b891a553e3aa730cab7ba Author: ShaMan123 Date: Mon Oct 31 19:37:41 2022 +0200 Create out.ts commit a2f29c0dca1c91a85f809426d03340f76885bc81 Author: ShaMan123 Date: Mon Oct 31 18:56:48 2022 +0200 Update transform_files.mjs commit 6587d19629d656b5f53c65eaf1556aa976ea9ad7 Author: ShaMan123 Date: Mon Oct 31 18:49:32 2022 +0200 Update transform_files.mjs commit c3bf999319f32ebfd30c732098cfef6bb204bf3b Author: ShaMan123 Date: Mon Oct 31 18:45:46 2022 +0200 Update transform_files.mjs commit 81051677b094673fa4fd7a2786d7b8203a0f0d28 Author: ShaMan123 Date: Mon Oct 31 18:37:17 2022 +0200 Update transform_files.mjs commit b8de8aa2b98c480e5283f722f9a650d13734639e Author: ShaMan123 Date: Mon Oct 31 18:34:27 2022 +0200 Update transform_files.mjs commit 2d1d1da20f701272c87751a73550311b9fd3b8d4 Author: ShaMan123 Date: Mon Oct 31 18:22:47 2022 +0200 Update transform_files.mjs commit 781fe55b2802b58bd1909c0e7b3a4b845502b5e7 Author: ShaMan123 Date: Mon Oct 31 18:18:01 2022 +0200 Update transform_files.mjs commit daf443fab6dddc5343218830b53bb6f25ec8656a Author: ShaMan123 Date: Mon Oct 31 18:16:48 2022 +0200 Update transform_files.mjs commit 3c554551f749b477f3f1d4075f8847de8029dfd7 Author: ShaMan123 Date: Mon Oct 31 17:41:30 2022 +0200 Update transform_files.mjs commit f567fcf90d2999a2aeee88a0c56e62e2656b9894 Author: ShaMan123 Date: Mon Oct 31 17:24:54 2022 +0200 Update transform_files.mjs commit 9ab2ea30b5b3198cfb7be977c3e99d3cad16677c Author: ShaMan123 Date: Mon Oct 31 17:16:13 2022 +0200 Update transform_files.mjs commit b3c73d15c40e2dda678419333cb976c285c82dca Author: ShaMan123 Date: Mon Oct 31 17:10:30 2022 +0200 Update transform_files.mjs commit f6d9031a1f6550ac3ca160b2e95182e124bd50fb Author: ShaMan123 Date: Mon Oct 31 16:51:10 2022 +0200 Update transform_files.mjs commit 62084991604a4e59ec2f38694f5818c77ee874e7 Author: ShaMan123 Date: Mon Oct 31 15:51:00 2022 +0200 Update transform_files.mjs commit 483379a309fd913ef8c6af0ca30b921819144ad1 Author: ShaMan123 Date: Mon Oct 31 13:38:10 2022 +0200 static commit 316ba1ad3f0d989261b7ee89b8077f47010fcaaf Author: ShaMan123 Date: Mon Oct 31 13:28:03 2022 +0200 Update transform_files.mjs commit bcb31240c75ada91951be3e5294dabded0e90c58 Author: ShaMan123 Date: Mon Oct 31 12:00:43 2022 +0200 Update transform_files.mjs commit 1d555ad00f7fc3e62a222d1d012169cbc5c3780c Author: ShaMan123 Date: Mon Oct 31 11:53:37 2022 +0200 Update transform_files.mjs commit 5a04341854bdd24c578004543744c1157098633a Author: ShaMan123 Date: Mon Oct 31 11:38:11 2022 +0200 Update transform_files.mjs commit 322c27107bb07c52768eb9b3b6ca6b48b0f52225 Author: ShaMan123 Date: Mon Oct 31 10:13:37 2022 +0200 Update transform_files.mjs commit 9b7f6f918413b3c5c50b07d0ffb5ce21402d51ca Author: ShaMan123 Date: Mon Oct 31 09:57:55 2022 +0200 Update transform_files.mjs commit e86aa95e0eb7632094133f592b517d5fb440021b Author: ShaMan123 Date: Mon Oct 31 08:32:27 2022 +0200 Update transform_files.mjs commit f23227fbcbf994c3ee334aa9e84aa3079b2ba0c0 Author: ShaMan123 Date: Mon Oct 31 08:26:14 2022 +0200 ast! --- package-lock.json | 45 ++- package.json | 2 + scripts/transform_files.mjs | 647 +++++++++++++++++++++--------------- 3 files changed, 416 insertions(+), 278 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f6d9d79dac..9e0c56417b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", "abort-controller": "^3.0.0", + "acorn": "^8.8.1", + "acorn-walk": "^8.2.0", "auto-changelog": "^2.3.0", "busboy": "^1.6.0", "chalk": "^2.4.1", @@ -1460,9 +1462,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "devOptional": true, "bin": { "acorn": "bin/acorn" @@ -1493,6 +1495,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1503,10 +1514,10 @@ } }, "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "optional": true, + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -13149,9 +13160,9 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "devOptional": true }, "acorn-globals": { @@ -13169,6 +13180,12 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "optional": true + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true } } }, @@ -13180,10 +13197,10 @@ "requires": {} }, "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "optional": true + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true }, "agent-base": { "version": "6.0.2", diff --git a/package.json b/package.json index 78f26c4695e..33fdcffb1f8 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,8 @@ "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", "abort-controller": "^3.0.0", + "acorn": "^8.8.1", + "acorn-walk": "^8.2.0", "auto-changelog": "^2.3.0", "busboy": "^1.6.0", "chalk": "^2.4.1", diff --git a/scripts/transform_files.mjs b/scripts/transform_files.mjs index 343dbca7cac..4ac64e04492 100644 --- a/scripts/transform_files.mjs +++ b/scripts/transform_files.mjs @@ -4,6 +4,9 @@ import fs from 'fs-extra'; import _ from 'lodash'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import * as acorn from 'acorn'; +import * as walk from 'acorn-walk'; +import * as cp from 'node:child_process'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -14,202 +17,201 @@ function readFile(file) { return fs.readFileSync(path.resolve(wd, file)).toString('utf-8'); } -function getVariableNameOfNS(raw, namespace) { - const regex = new RegExp( - `\\s*(.+)=\\s*${namespace.replaceAll('.', '\\.')}[^(]+`, - 'm' - ); - const result = regex.exec(raw); - return result ? result[1].trim() : namespace; -} - -function getNSFromVariableName(raw, varname) { - if (varname === 'fabric') return 'fabric'; - const regex = new RegExp(`\\s*${varname}\\s*=\\s*(.*)\\s*?,\\s*`, 'gm'); - const result = regex.exec(raw); - return result ? result[1].trim() : null; +function printASTNode(raw, node, removeTrailingComma = true) { + if (node.type === 'Literal') + return typeof node.value === 'string' ? `'${node.value}'` : `${node.value}`; + if (Array.isArray(node) && node.length === 0) return ''; + const out = ( + Array.isArray(node) + ? raw.slice(node[0].start, node[node.length - 1].end + 1) + : raw.slice(node.start, node.end + 1) + ).trim(); + return removeTrailingComma && out.endsWith(',') + ? out.slice(0, out.length - 1) + : out; } -function findObject(raw, charStart, charEnd, startFrom = 0) { - const start = raw.indexOf(charStart, startFrom); - let index = start; - let counter = 0; - while (index < raw.length) { - if (raw[index] === charStart) { - counter++; - } else if (raw[index] === charEnd) { - counter--; - if (counter === 0) { - break; - } - } - index++; - } - return start > -1 - ? { +/** + * + * @param {string} raw + * @param {(tree: acorn.Node) => {found:acorn.Node, parent:acorn.Node,variableName:string}} find + * @returns + */ +function parseClassBase(raw, find) { + const comments = []; + const ast = acorn.parse(raw, { + ecmaVersion: 2022, + sourceType: 'module', + locations: true, + onComment(block, value, start, end, locStart, locEnd) { + comments.push({ + block, + value, start, - end: index, - raw: raw.slice(start, index + charEnd.length), - } - : null; -} - -function parseRawClass(raw) { - let index = 0; - const pairs = [ - { - opening: '{', - closing: '}', - test(key, index, input) { - return input[index] === this[key]; - }, - }, - { - opening: '[', - closing: ']', - test(key, index, input) { - return input[index] === this[key]; - }, - }, - { - opening: '(', - closing: ')', - test(key, index, input) { - return input[index] === this[key]; - }, - }, - { - blocking: true, - opening: '/*', - closing: '*/', - test(key, index, input) { - return key === 'closing' - ? input[index - 1] === this[key][0] && input[index] === this[key][1] - : input[index] === this[key][0] && input[index + 1] === this[key][1]; - }, + end, + loc: { + start: locStart, + end: locEnd, + }, + }); }, - ]; - const stack = []; - const commas = []; - const fields = []; - while (index < raw.length) { - const top = stack[stack.length - 1]; - //console.log(raw[index],top) - if (top && top.test('closing', index, raw)) { - stack.pop(); - } else if (!top || !top.blocking) { - const found = pairs.find((t) => t.test('opening', index, raw)); - if (found) { - stack.push(found); - } else if (pairs.some((t) => t.test('closing', index, raw))) { - stack.pop(); - } else if (raw[index] === ',' && stack.length === 1) { - commas.push(index); - } else if (raw[index] === ':' && stack.length === 1) { - const trim = raw.slice(0, index).trim(); - const result = /\s*(.+)$/.exec(trim); - result && fields.push(result[1]); - } - } + }); + // fs.writeFileSync('./ast.json', JSON.stringify(ast, null, 2)); - index++; + function printNode(node, removeTrailingComma) { + return printASTNode(raw, node, removeTrailingComma); } - commas.reverse().forEach((pos) => { - raw = raw.slice(0, pos) + raw.slice(pos + 1); + + function findNodeComment(node) { + return comments.find( + (comment) => comment.loc.end.line === node.loc.start.line - 1 + ); + } + + const { found, parent, variableName } = find(ast); + + const declaration = found.arguments.pop(); + const superClasses = found.arguments.map((node) => printNode(node, true)); + const [methods, properties] = _.partition( + declaration.properties.map((node) => ({ + node, + comment: findNodeComment(node), + })), + ({ node }) => + node.type === 'Property' && node.value.type === 'FunctionExpression' + ); + const name = variableName.slice(variableName.lastIndexOf('.') + 1); + const defaultValues = _.fromPairs( + properties.map(({ node }) => { + return [node.key.name, printNode(node.value)]; + }) + ); + + const statics = []; + walk.simple(ast, { + ExpressionStatement(node) { + const { + expression: { left, right }, + } = node; + if ( + left?.type === 'MemberExpression' && + printNode(left.object).slice(0, -1) === variableName + ) { + statics.push({ + type: right.type === 'FunctionExpression' ? 'method' : 'property', + key: printNode(left.property), + value: right, + node, + comment: findNodeComment(left), + }); + } + }, }); - return { raw, fields }; -} -/** - * - * @param {RegExpExecArray | null} regex - * @returns - */ -function findClassBase(raw, regex) { - const result = regex.exec(raw); - if (!result) throw new Error('FAILED TO PARSE'); - const [match, classNSRaw, superClassRaw] = result; - const [first, ...rest] = classNSRaw.trim().split('.'); - const namespace = [getNSFromVariableName(raw, first), ...rest].join('.'); - const name = namespace.slice(namespace.lastIndexOf('.') + 1); - const superClasses = - superClassRaw - ?.trim() - .split(',') - .filter((raw) => !raw.match(/\/\*+/) && raw) - .map((key) => key.trim()) - .map((val) => { - const [first, ...rest] = val.split('.'); - return [getNSFromVariableName(raw, first), ...rest].join('.'); - }) || []; - const rawObject = findObject(raw, '{', '}', result.index); - // const NS = namespace.slice(0, namespace.lastIndexOf('.')); - // const { fabric } = require(wd); - // const klass = fabric.util.resolveNamespace(NS === 'fabric' ? null : NS)[name]; + const [staticMethods, staticProperties] = _.partition( + statics, + ({ type }) => type === 'method' + ); + return { + ast, name, - namespace, + namespace: variableName, superClasses, superClass: superClasses.length > 0 ? superClasses[superClasses.length - 1] : undefined, requiresSuperClassResolution: superClasses.length > 0, - match: { - index: result.index, - value: match, - }, - ...rawObject, + start: declaration.start, + end: declaration.end, + variableNode: parent, + declaration, + methods, + properties, + defaultValues, + staticMethods, + staticProperties, + comments, + body: raw.slice(declaration.start, declaration.end + 1), + printNode, }; } -function findClass(raw) { - const keyWord = getVariableNameOfNS(raw, 'fabric.util.createClass'); - const regex = new RegExp( - `(.+)=\\s*${keyWord.replaceAll('.', '\\.')}\\((\.*)\\{`, - 'm' - ); - return findClassBase(raw, regex); -} +function parseClass(raw) { + return parseClassBase(raw, (ast) => { + const { node: found } = walk.findNodeAt( + ast, + undefined, + undefined, + (nodeType, node) => { + return ( + nodeType === 'CallExpression' && + printASTNode(raw, node.callee).endsWith('createClass(') + ); + } + ); + const { node: parent } = walk.findNodeAt( + ast, + undefined, + undefined, + (nodeType, node) => { + return ( + (nodeType === 'VariableDeclaration' || + nodeType === 'ExpressionStatement') && + node.start < found.start && + node.end > found.end && + !!walk.findNodeAt( + node, + undefined, + undefined, + (nodeType, node) => node === found + ) + ); + } + ); -function findMixin(raw) { - const keyWord = getVariableNameOfNS(raw, 'fabric.util.object.extend'); - const regex = new RegExp( - `${keyWord.replaceAll('.', '\\.')}\\((.+)\\.prototype,\.*\\{`, - 'm' - ); - return findClassBase(raw, regex); + const variableNode = + parent.type === 'ExpressionStatement' + ? parent.expression.left + : parent.declarations[0].id; + + return { + found, + parent, + variableName: printASTNode(raw, variableNode), + }; + }); } -function transformSuperCall(raw) { - const regex = /this.callSuper\((.+)\)/g; - const result = regex.exec(raw); - if (!result) { - if (raw.indexOf('callSuper') > -1) - throw new Error(`failed to replace 'callSuper'`); - return raw; - } - const [rawMethodName, ...args] = result[1].split(','); - const methodName = rawMethodName.replace(/'|"/g, ''); - const firstArgIndex = result[1].indexOf(args[0]); - const rest = - firstArgIndex > -1 - ? result[1].slice(firstArgIndex, result[1].length).trim() - : ''; - const transformedCall = `super${ - methodName === 'initialize' ? '' : `.${methodName}` - }(${rest})`; - return ( - raw.slice(0, result.index) + - transformedCall + - raw.slice(result.index + result[0].length) - ); +function parseMixin(raw) { + return parseClassBase(raw, (ast) => { + const { node: found } = walk.findNodeAt( + ast, + undefined, + undefined, + (nodeType, node) => { + return ( + nodeType === 'CallExpression' && + printASTNode(raw, node.callee).endsWith('extend(') + ); + } + ); + return { + found, + parent: found, + variableName: printASTNode(raw, found.arguments[0]).replace( + '.prototype', + '' + ), + }; + }); } function generateClass(rawClass, className, superClass, useExports) { return `${useExports ? 'export ' : ''}class ${className}${ superClass ? ` extends ${superClass}` : '' - } ${rawClass}`; + } {\n${rawClass}\n}`; } /** @@ -219,7 +221,7 @@ function generateMixin(rawClass, mixinName, baseClassNS, useExports) { const funcName = `${mixinName}Generator`; return ` ${useExports ? 'export ' : ''}function ${funcName}(Klass) { - return class ${mixinName || ''} extends Klass ${rawClass} + return class ${mixinName || ''} extends Klass {\n${rawClass}\n}\n } ${baseClassNS ? `${baseClassNS} = ${funcName}(${baseClassNS});` : ''} @@ -236,24 +238,6 @@ function getMixinName(file) { return name.replace('Itext', 'IText') + 'Mixin'; } -function transformFile(raw, { namespace, name } = {}) { - if (raw.replace(/\/\*.*\*\\s*/).startsWith('(function')) { - const wrapper = findObject(raw, '{', '}'); - raw = wrapper.raw.slice(1, wrapper.raw.length - 1); - } - - const annoyingCheck = new RegExp( - `if\\s*\\(\\s*(global.)?${namespace.replace(/\./g, '\\.')}\\s*\\)\\s*{` - ); - const result = annoyingCheck.exec(raw); - if (result) { - const found = findObject(raw, '{', '}', result.index); - raw = raw.slice(0, result.index) + raw.slice(found.end + 1); - } - raw = `//@ts-nocheck\n${raw}`; - return raw; -} - /** * * @param {string} file @@ -264,90 +248,219 @@ function transformClass(type, raw, options = {}) { const { className, useExports } = options; if (!type) throw new Error(`INVALID_ARGUMENT type`); const { - match, + ast, name, namespace, superClass, - raw: _rawClass, end, requiresSuperClassResolution, superClasses, - } = type === 'mixin' ? findMixin(raw) : findClass(raw); - let { raw: rawClass, fields } = parseRawClass(_rawClass); - const getPropStart = (key, raw) => { - const searchPhrase = `^(\\s*)${key}\\s*:\\s*`; - const regex = new RegExp(searchPhrase, 'm'); - const result = regex.exec(raw); - const whitespace = result[1]; - return { - start: result?.index + whitespace.length || -1, - regex, - value: `${whitespace}${key} = `, - }; - }; - const staticCandidantes = []; - fields.forEach((key) => { - const searchPhrase = `^(\\s*)${key}\\s*:\\s*function\\s*\\(`; - const regex = new RegExp(searchPhrase, 'm'); - const result = regex.exec(rawClass); - if (result) { - const whitespace = result[1]; - const start = result.index + whitespace.length; - const func = findObject(rawClass, '{', '}', start); - start && func.raw.indexOf('this') === -1 && staticCandidantes.push(key); - rawClass = rawClass.replace( - regex, - `${whitespace}${key === 'initialize' ? 'constructor' : key}(` - ); - if (regex.exec(rawClass)) { - throw new Error(`dupliate method found ${name}#${key}`); - } - } else { - const start = getPropStart(key, rawClass); - rawClass = rawClass.replace(start.regex, start.value); - } + methods, + properties, + defaultValues, + staticMethods, + staticProperties, + declaration, + variableNode, + printNode, + } = type === 'mixin' ? parseMixin(raw) : parseClass(raw); + + // safety + const duplicateMethods = _.differenceWith( + methods, + _.uniqBy(methods, 'node.key.name'), + (a, b) => a === b + ); + if (duplicateMethods.length > 0) { + throw new Error( + `${name}: duplicate methods found: ${_.map( + duplicateMethods, + 'node.key.name' + )}` + ); + } + const duplicateProps = _.differenceWith( + properties, + _.uniqBy(properties, 'node.key.name'), + (a, b) => a === b + ); + + if (duplicateProps.length > 0) { + throw new Error( + `${name}: duplicate properties found: ${_.map( + duplicateProps, + 'node.key.name' + )}` + ); + } + + const classBody = []; + + properties.forEach(({ node, comment }) => { + const key = node.key.name; + const typeable = + node.value.type === 'Literal' && + ['boolean', 'number', 'string'].includes(typeof node.value.value); + const typed = typeable ? `${key}: ${typeof node.value.value}` : key; + classBody.push((comment ? printNode(comment) : '') + '\n' + typed); + // replaceNode(node, typeable ? `${key}: ${typeof node.value.value}` : key); }); - let transformed = rawClass; - do { - rawClass = transformed; - try { - transformed = transformSuperCall(rawClass); - } catch (error) { - console.error(error); - } - } while (transformed !== rawClass); - const classDirective = + + const staticCandidates = []; + + methods.forEach(({ node, comment }) => { + const key = node.key.name; + const value = printNode(node.value.body.body); + const methodAST = acorn.parse(value, { + ecmaVersion: 2022, + allowReturnOutsideFunction: true, + }); + const superTransforms = []; + walk.simple(methodAST, { + CallExpression(node) { + if ( + node.callee.object?.type === 'ThisExpression' && + node.callee.property?.name === 'callSuper' + ) { + const [methodNameArg, ...args] = node.arguments; + const out = `${ + methodNameArg.value === 'initialize' + ? 'super' + : `super.${methodNameArg.value}` + }(${args + .map((arg) => { + const out = printASTNode(value, arg); + return out.replace(/[^\(|\)]/gm, '').length % 2 === 1 + ? out.slice(0, -1) + : out; + }) + .join(', ')})`; + superTransforms.push({ + node, + methodName: methodNameArg.value, + value: out, + }); + } + }, + }); + + value.indexOf('this') === -1 && staticCandidates.push(key); + classBody.push( + (comment ? printNode(comment) : '') + + '\n' + + (node.value.async ? 'async ' : '') + + (key === 'initialize' ? 'constructor' : key) + + `(${printNode(node.value.params).slice(0, -1)}) {\n` + + superTransforms.reduceRight((value, { node, value: out }) => { + return value.replace(printASTNode(value, node), out); + }, value) + + '\n}' + ); + }); + + staticProperties.forEach(({ key, value, comment }) => { + const out = + (comment ? printNode(comment) : '') + + '\r\n' + + 'static ' + + key + + '=' + + printNode(value); + classBody.push(out); + }); + + staticMethods.forEach(({ key, value, comment }) => { + classBody.push( + (comment ? printNode(comment) : '') + + '\n' + + 'static ' + + (value.async ? 'async ' : '') + + key + + `(${printNode(value.params).slice(0, -1)}) {\n` + + printNode(value.body.body) + + '\n}' + ); + }); + + const body = classBody.join('\r\n\r\n'); + + const finalName = + type === 'mixin' + ? `${_.upperFirst(name)}${className.replace( + new RegExp( + name.toLowerCase() === 'staticcanvas' ? 'canvas' : name, + 'i' + ), + '' + )}` || name + : className || name; + + let classDirective = type === 'mixin' - ? generateMixin( - rawClass, - `${_.upperFirst(name)}${className.replace( - new RegExp( - name.toLowerCase() === 'staticcanvas' ? 'canvas' : name, - 'i' - ), - '' - )}` || name, - namespace, - useExports - ) - : generateClass(rawClass, className || name, superClass, useExports); - raw = `${raw.slice(0, match.index)}${classDirective}${raw - .slice(end + 1) - .replace(/\s*\)\s*;?/, '')}`; - if (type === 'mixin') { - // in case of multiple mixins in one file - try { - return transformClass(type, raw, options); - } catch (error) {} + ? generateMixin(body, finalName, namespace, useExports) + : generateClass(body, finalName, superClass, useExports); + + if (_.size(defaultValues) > 0) { + const defaultsKey = `${_.lowerFirst(finalName)}DefaultValues`; + classDirective += + '\n\n' + + `export const ${defaultsKey}: Partial> = {\n${_.map( + defaultValues, + (value, key) => [key, value].join(':') + ).join(',\n')}\n};` + + '\n\n' + + `Object.assign(${finalName}.prototype, ${defaultsKey})`; + } + + let rawFile; + + const lastNode = ast.body[ast.body.length - 1]; + if ( + lastNode.type === 'ExpressionStatement' && + lastNode.expression.callee.params[0].name === 'global' + ) { + const bodyNodes = lastNode.expression.callee.body.body; + rawFile = + raw.slice(0, lastNode.start) + + printNode(bodyNodes).replace( + printNode(variableNode, false), + classDirective + ) + + raw.slice(lastNode.end + 1); + } else { + rawFile = `${raw.slice(0, variableNode.start)}${classDirective}${raw + .slice(end + 1) + .replace(/\s*\)\s*;?/, '')}`; } - if (type === 'class' && !useExports) { - raw = `${raw}\n/** @todo TODO_JS_MIGRATION remove next line after refactoring build */\n${namespace} = ${name};\n`; + + [...staticMethods, ...staticProperties].forEach(({ node, comment }) => { + if (comment) { + rawFile = rawFile.replace( + printNode({ + start: comment.start, + end: node.end, + }), + '' + ); + } + }); + + rawFile = rawFile + .replace(new RegExp(namespace.replace(/\./g, '\\.'), 'g'), name) + .replace(/fabric\.Object/g, 'FabricObject') + .replace(/fabric\.util\./g, ''); + + rawFile.indexOf('//@ts-nocheck') === -1 && + (rawFile = `//@ts-nocheck\n${rawFile}`); + + if (type === 'class' /*&& !useExports*/) { + classDirective += `\n\n/** @todo TODO_JS_MIGRATION remove next line after refactoring build */\n${namespace} = ${name};\n`; } - raw = transformFile(raw, { namespace, name }); + return { name, - raw, - staticCandidantes, + raw: rawFile, + staticCandidates, requiresSuperClassResolution, superClasses, }; @@ -358,7 +471,7 @@ function convertFile(type, source, dest, options) { const { name, raw, - staticCandidantes, + staticCandidates, requiresSuperClassResolution, superClasses, } = transformClass(type, readFile(source), { @@ -377,8 +490,8 @@ function convertFile(type, source, dest, options) { requiresSuperClassResolution: requiresSuperClassResolution ? superClasses : false, - staticCandidantes: - staticCandidantes.length > 0 ? staticCandidantes : 'none', + staticCandidates: + staticCandidates.length > 0 ? staticCandidates : 'none', }); return dest; } catch (error) { @@ -484,6 +597,12 @@ export function transform(options = {}) { ); }); + cp.execSync( + `prettier --write ${result + .map(({ dir, file }) => path.relative('.', path.resolve(dir, file))) + .join(' ')}` + ); + const [errors, files] = _.partition(result, (file) => file instanceof Error); const dirs = files.reduce((dirs, { dir, file }) => { (dirs[dir] || (dirs[dir] = [])).push(file); @@ -497,7 +616,7 @@ export function transform(options = {}) { fs.removeSync(path.resolve(dir, file.replace('.ts', '.js'))) ); - if (!options.verbose) { + if (!options.verbose && errors.length > 0) { console.error(`failed files:`); errors.map(console.error); } From 1e6c012dd4b6e676737f0e1886d424ada31c34a3 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:16:44 +0200 Subject: [PATCH 07/58] s --- src/mixins/object_straightening.mixin.ts | 112 ++++++++++----------- src/shapes/fabricObject.class.js | 4 +- src/util/{applymixins.ts => applyixins.ts} | 0 3 files changed, 59 insertions(+), 57 deletions(-) rename src/util/{applymixins.ts => applyixins.ts} (100%) diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts index e63ac3ef6c6..b8a58f4ddd2 100644 --- a/src/mixins/object_straightening.mixin.ts +++ b/src/mixins/object_straightening.mixin.ts @@ -1,62 +1,62 @@ -//@ts-nocheck -import { FabricObject } from '../shapes/fabricObject.class'; +import type { FabricObject } from '../shapes/fabricObject.class'; +import { TDegree } from '../typedefs'; +import { animate } from '../util/animate'; -(function (global) { - var fabric = global.fabric; - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * @private - * @return {Number} angle value - */ - _getAngleValueForStraighten: function () { - var angle = this.angle % 360; - if (angle > 0) { - return Math.round((angle - 1) / 90) * 90; - } - return Math.round(angle / 90) * 90; - }, +export class FabricObjectObjectStraighteningMixin { + /** + * @private + * @return {Number} angle value + */ + _getAngleValueForStraighten(this: FabricObject) { + const angle = this.angle % 360; + if (angle > 0) { + return Math.round((angle - 1) / 90) * 90; + } + return Math.round(angle / 90) * 90; + } - /** - * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) - * @return {fabric.Object} thisArg - * @chainable - */ - straighten: function () { - return this.rotate(this._getAngleValueForStraighten()); - }, + /** + * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) + * @return {FabricObject} thisArg + * @chainable + */ + straighten(this: FabricObject & this) { + return this.rotate(this._getAngleValueForStraighten()); + } - /** - * Same as {@link FabricObject.prototype.straighten} but with animation - * @param {Object} callbacks Object with callback functions - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - * @return {fabric.Object} thisArg - */ - fxStraighten: function (callbacks) { - callbacks = callbacks || {}; + /** + * Same as {@link FabricObject.prototype.straighten} but with animation + * @param {Object} callbacks Object with callback functions + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + * @return {FabricObject} thisArg + */ + fxStraighten( + this: FabricObject & this, + callbacks: { + onChange(value: TDegree): any; + onComplete(): any; + } + ) { + callbacks = callbacks || {}; - var empty = function () {}, - onComplete = callbacks.onComplete || empty, - onChange = callbacks.onChange || empty, - _this = this; + const empty = function () {}, + onComplete = callbacks.onComplete || empty, + onChange = callbacks.onChange || empty; - return fabric.util.animate({ - target: this, - startValue: this.get('angle'), - endValue: this._getAngleValueForStraighten(), - duration: this.FX_DURATION, - onChange: function (value) { - _this.rotate(value); - onChange(); - }, - onComplete: function () { - _this.setCoords(); - onComplete(); - }, - }); + return animate({ + target: this, + startValue: this.get('angle'), + endValue: this._getAngleValueForStraighten(), + duration: this.FX_DURATION, + onChange: (value: TDegree) => { + this.rotate(value); + onChange(value); }, - } - ); -})(typeof exports !== 'undefined' ? exports : window); + onComplete: () => { + this.setCoords(); + onComplete(); + }, + }); + } +} diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.js index b7635d8337f..4422bbf64df 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.js @@ -1,13 +1,15 @@ import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; -import { applyMixins } from '../util/applyMixins'; +import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; +import { applyMixins } from '../util/applyixins'; // TODO somehow we have to make a tree-shakeable import applyMixins(InteractiveFabricObject, [ FabricObjectAncestryMixin, FabricObjectObjectStackingMixin, + FabricObjectObjectStraighteningMixin, ]); export { InteractiveFabricObject as FabricObject }; diff --git a/src/util/applymixins.ts b/src/util/applyixins.ts similarity index 100% rename from src/util/applymixins.ts rename to src/util/applyixins.ts From b83df40f3eab4a5e6fd56ebb21f62f5829690731 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:16:57 +0200 Subject: [PATCH 08/58] r --- src/shapes/fabricObject.class.js | 2 +- src/util/{applyixins.ts => applyMixins.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/util/{applyixins.ts => applyMixins.ts} (100%) diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.js index 4422bbf64df..f633eb5f4be 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.js @@ -2,7 +2,7 @@ import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; -import { applyMixins } from '../util/applyixins'; +import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import diff --git a/src/util/applyixins.ts b/src/util/applyMixins.ts similarity index 100% rename from src/util/applyixins.ts rename to src/util/applyMixins.ts From d252e3651d0d448c881db822897321724ab7847c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:21:05 +0200 Subject: [PATCH 09/58] imports --- index.js | 5 +---- src/mixins/CanvasStraighteningMixin.ts | 8 ++++++-- src/mixins/object_straightening.mixin.ts | 2 ++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index ad04eeb283d..147f90c9d47 100644 --- a/index.js +++ b/index.js @@ -17,9 +17,7 @@ import './src/mixins/canvas_grouping.mixin'; // optional interaction import './src/mixins/canvas_dataurl_exporter.mixin'; import './src/mixins/canvas_serialization.mixin'; // optiona serialization import './src/mixins/canvas_gestures.mixin'; // optional gesturesv -import './src/mixins/object_interactivity.mixin'; // optional interaction -import './src/mixins/object_ancestry.mixin'; -import './src/mixins/object_stacking.mixin'; +import './src/mixins/CanvasStraighteningMixin'; // optional gesturesv import './src/mixins/object.svg_export'; import './src/mixins/stateful.mixin'; import './src/mixins/animation.mixin'; // optional animation @@ -34,7 +32,6 @@ import './src/shapes/path.class'; import './src/shapes/group.class'; import './src/shapes/active_selection.class'; // optional interaction import './src/shapes/image.class'; -import './src/mixins/object_straightening.mixin'; // optional objectstraightening import './src/filters/webgl_backend.class'; // optional image_filters import './src/filters/2d_backend.class'; // optional image_filters import './src/filters/base_filter.class'; // optional image_filters diff --git a/src/mixins/CanvasStraighteningMixin.ts b/src/mixins/CanvasStraighteningMixin.ts index 43cad2932f3..68199fe46ae 100644 --- a/src/mixins/CanvasStraighteningMixin.ts +++ b/src/mixins/CanvasStraighteningMixin.ts @@ -1,3 +1,7 @@ +// @ts-nocheck +import { fabric } from '../../HEADER'; +import type { FabricObject } from '../shapes/fabricObject.class'; + fabric.util.object.extend( fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ { @@ -7,7 +11,7 @@ fabric.util.object.extend( * @return {fabric.Canvas} thisArg * @chainable */ - straightenObject: function (object) { + straightenObject: function (object: FabricObject) { object.straighten(); this.requestRenderAll(); return this; @@ -18,7 +22,7 @@ fabric.util.object.extend( * @param {fabric.Object} object Object to straighten * @return {fabric.Canvas} thisArg */ - fxStraightenObject: function (object) { + fxStraightenObject: function (object: FabricObject) { return object.fxStraighten({ onChange: this.requestRenderAllBound, }); diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts index b8a58f4ddd2..7cf47a3b889 100644 --- a/src/mixins/object_straightening.mixin.ts +++ b/src/mixins/object_straightening.mixin.ts @@ -1,3 +1,5 @@ +// @ts-nocheck + import type { FabricObject } from '../shapes/fabricObject.class'; import { TDegree } from '../typedefs'; import { animate } from '../util/animate'; From eebf7596ea0dd7e9d1fb8cf3a1a28769a598f53c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:21:35 +0200 Subject: [PATCH 10/58] Revert "Squashed commit of the following:" This reverts commit a1db53e7ed70b8cbdb0b7c4b74c36fafc3632a47. --- package-lock.json | 45 +-- package.json | 2 - scripts/transform_files.mjs | 647 +++++++++++++++--------------------- 3 files changed, 278 insertions(+), 416 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e0c56417b7..4f6d9d79dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,8 +17,6 @@ "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", "abort-controller": "^3.0.0", - "acorn": "^8.8.1", - "acorn-walk": "^8.2.0", "auto-changelog": "^2.3.0", "busboy": "^1.6.0", "chalk": "^2.4.1", @@ -1462,9 +1460,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "devOptional": true, "bin": { "acorn": "bin/acorn" @@ -1495,15 +1493,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1514,10 +1503,10 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true, "engines": { "node": ">=0.4.0" } @@ -13160,9 +13149,9 @@ } }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "devOptional": true }, "acorn-globals": { @@ -13180,12 +13169,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "optional": true - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "optional": true } } }, @@ -13197,10 +13180,10 @@ "requires": {} }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true }, "agent-base": { "version": "6.0.2", diff --git a/package.json b/package.json index 33fdcffb1f8..78f26c4695e 100644 --- a/package.json +++ b/package.json @@ -84,8 +84,6 @@ "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", "abort-controller": "^3.0.0", - "acorn": "^8.8.1", - "acorn-walk": "^8.2.0", "auto-changelog": "^2.3.0", "busboy": "^1.6.0", "chalk": "^2.4.1", diff --git a/scripts/transform_files.mjs b/scripts/transform_files.mjs index 4ac64e04492..343dbca7cac 100644 --- a/scripts/transform_files.mjs +++ b/scripts/transform_files.mjs @@ -4,9 +4,6 @@ import fs from 'fs-extra'; import _ from 'lodash'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import * as acorn from 'acorn'; -import * as walk from 'acorn-walk'; -import * as cp from 'node:child_process'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -17,201 +14,202 @@ function readFile(file) { return fs.readFileSync(path.resolve(wd, file)).toString('utf-8'); } -function printASTNode(raw, node, removeTrailingComma = true) { - if (node.type === 'Literal') - return typeof node.value === 'string' ? `'${node.value}'` : `${node.value}`; - if (Array.isArray(node) && node.length === 0) return ''; - const out = ( - Array.isArray(node) - ? raw.slice(node[0].start, node[node.length - 1].end + 1) - : raw.slice(node.start, node.end + 1) - ).trim(); - return removeTrailingComma && out.endsWith(',') - ? out.slice(0, out.length - 1) - : out; +function getVariableNameOfNS(raw, namespace) { + const regex = new RegExp( + `\\s*(.+)=\\s*${namespace.replaceAll('.', '\\.')}[^(]+`, + 'm' + ); + const result = regex.exec(raw); + return result ? result[1].trim() : namespace; } -/** - * - * @param {string} raw - * @param {(tree: acorn.Node) => {found:acorn.Node, parent:acorn.Node,variableName:string}} find - * @returns - */ -function parseClassBase(raw, find) { - const comments = []; - const ast = acorn.parse(raw, { - ecmaVersion: 2022, - sourceType: 'module', - locations: true, - onComment(block, value, start, end, locStart, locEnd) { - comments.push({ - block, - value, - start, - end, - loc: { - start: locStart, - end: locEnd, - }, - }); - }, - }); - // fs.writeFileSync('./ast.json', JSON.stringify(ast, null, 2)); - - function printNode(node, removeTrailingComma) { - return printASTNode(raw, node, removeTrailingComma); - } +function getNSFromVariableName(raw, varname) { + if (varname === 'fabric') return 'fabric'; + const regex = new RegExp(`\\s*${varname}\\s*=\\s*(.*)\\s*?,\\s*`, 'gm'); + const result = regex.exec(raw); + return result ? result[1].trim() : null; +} - function findNodeComment(node) { - return comments.find( - (comment) => comment.loc.end.line === node.loc.start.line - 1 - ); +function findObject(raw, charStart, charEnd, startFrom = 0) { + const start = raw.indexOf(charStart, startFrom); + let index = start; + let counter = 0; + while (index < raw.length) { + if (raw[index] === charStart) { + counter++; + } else if (raw[index] === charEnd) { + counter--; + if (counter === 0) { + break; + } + } + index++; } - - const { found, parent, variableName } = find(ast); - - const declaration = found.arguments.pop(); - const superClasses = found.arguments.map((node) => printNode(node, true)); - const [methods, properties] = _.partition( - declaration.properties.map((node) => ({ - node, - comment: findNodeComment(node), - })), - ({ node }) => - node.type === 'Property' && node.value.type === 'FunctionExpression' - ); - const name = variableName.slice(variableName.lastIndexOf('.') + 1); - const defaultValues = _.fromPairs( - properties.map(({ node }) => { - return [node.key.name, printNode(node.value)]; - }) - ); - - const statics = []; - walk.simple(ast, { - ExpressionStatement(node) { - const { - expression: { left, right }, - } = node; - if ( - left?.type === 'MemberExpression' && - printNode(left.object).slice(0, -1) === variableName - ) { - statics.push({ - type: right.type === 'FunctionExpression' ? 'method' : 'property', - key: printNode(left.property), - value: right, - node, - comment: findNodeComment(left), - }); + return start > -1 + ? { + start, + end: index, + raw: raw.slice(start, index + charEnd.length), } + : null; +} + +function parseRawClass(raw) { + let index = 0; + const pairs = [ + { + opening: '{', + closing: '}', + test(key, index, input) { + return input[index] === this[key]; + }, }, - }); + { + opening: '[', + closing: ']', + test(key, index, input) { + return input[index] === this[key]; + }, + }, + { + opening: '(', + closing: ')', + test(key, index, input) { + return input[index] === this[key]; + }, + }, + { + blocking: true, + opening: '/*', + closing: '*/', + test(key, index, input) { + return key === 'closing' + ? input[index - 1] === this[key][0] && input[index] === this[key][1] + : input[index] === this[key][0] && input[index + 1] === this[key][1]; + }, + }, + ]; + const stack = []; + const commas = []; + const fields = []; + while (index < raw.length) { + const top = stack[stack.length - 1]; + //console.log(raw[index],top) + if (top && top.test('closing', index, raw)) { + stack.pop(); + } else if (!top || !top.blocking) { + const found = pairs.find((t) => t.test('opening', index, raw)); + if (found) { + stack.push(found); + } else if (pairs.some((t) => t.test('closing', index, raw))) { + stack.pop(); + } else if (raw[index] === ',' && stack.length === 1) { + commas.push(index); + } else if (raw[index] === ':' && stack.length === 1) { + const trim = raw.slice(0, index).trim(); + const result = /\s*(.+)$/.exec(trim); + result && fields.push(result[1]); + } + } - const [staticMethods, staticProperties] = _.partition( - statics, - ({ type }) => type === 'method' - ); + index++; + } + commas.reverse().forEach((pos) => { + raw = raw.slice(0, pos) + raw.slice(pos + 1); + }); + return { raw, fields }; +} +/** + * + * @param {RegExpExecArray | null} regex + * @returns + */ +function findClassBase(raw, regex) { + const result = regex.exec(raw); + if (!result) throw new Error('FAILED TO PARSE'); + const [match, classNSRaw, superClassRaw] = result; + const [first, ...rest] = classNSRaw.trim().split('.'); + const namespace = [getNSFromVariableName(raw, first), ...rest].join('.'); + const name = namespace.slice(namespace.lastIndexOf('.') + 1); + const superClasses = + superClassRaw + ?.trim() + .split(',') + .filter((raw) => !raw.match(/\/\*+/) && raw) + .map((key) => key.trim()) + .map((val) => { + const [first, ...rest] = val.split('.'); + return [getNSFromVariableName(raw, first), ...rest].join('.'); + }) || []; + const rawObject = findObject(raw, '{', '}', result.index); + // const NS = namespace.slice(0, namespace.lastIndexOf('.')); + // const { fabric } = require(wd); + // const klass = fabric.util.resolveNamespace(NS === 'fabric' ? null : NS)[name]; return { - ast, name, - namespace: variableName, + namespace, superClasses, superClass: superClasses.length > 0 ? superClasses[superClasses.length - 1] : undefined, requiresSuperClassResolution: superClasses.length > 0, - start: declaration.start, - end: declaration.end, - variableNode: parent, - declaration, - methods, - properties, - defaultValues, - staticMethods, - staticProperties, - comments, - body: raw.slice(declaration.start, declaration.end + 1), - printNode, + match: { + index: result.index, + value: match, + }, + ...rawObject, }; } -function parseClass(raw) { - return parseClassBase(raw, (ast) => { - const { node: found } = walk.findNodeAt( - ast, - undefined, - undefined, - (nodeType, node) => { - return ( - nodeType === 'CallExpression' && - printASTNode(raw, node.callee).endsWith('createClass(') - ); - } - ); - const { node: parent } = walk.findNodeAt( - ast, - undefined, - undefined, - (nodeType, node) => { - return ( - (nodeType === 'VariableDeclaration' || - nodeType === 'ExpressionStatement') && - node.start < found.start && - node.end > found.end && - !!walk.findNodeAt( - node, - undefined, - undefined, - (nodeType, node) => node === found - ) - ); - } - ); - - const variableNode = - parent.type === 'ExpressionStatement' - ? parent.expression.left - : parent.declarations[0].id; +function findClass(raw) { + const keyWord = getVariableNameOfNS(raw, 'fabric.util.createClass'); + const regex = new RegExp( + `(.+)=\\s*${keyWord.replaceAll('.', '\\.')}\\((\.*)\\{`, + 'm' + ); + return findClassBase(raw, regex); +} - return { - found, - parent, - variableName: printASTNode(raw, variableNode), - }; - }); +function findMixin(raw) { + const keyWord = getVariableNameOfNS(raw, 'fabric.util.object.extend'); + const regex = new RegExp( + `${keyWord.replaceAll('.', '\\.')}\\((.+)\\.prototype,\.*\\{`, + 'm' + ); + return findClassBase(raw, regex); } -function parseMixin(raw) { - return parseClassBase(raw, (ast) => { - const { node: found } = walk.findNodeAt( - ast, - undefined, - undefined, - (nodeType, node) => { - return ( - nodeType === 'CallExpression' && - printASTNode(raw, node.callee).endsWith('extend(') - ); - } - ); - return { - found, - parent: found, - variableName: printASTNode(raw, found.arguments[0]).replace( - '.prototype', - '' - ), - }; - }); +function transformSuperCall(raw) { + const regex = /this.callSuper\((.+)\)/g; + const result = regex.exec(raw); + if (!result) { + if (raw.indexOf('callSuper') > -1) + throw new Error(`failed to replace 'callSuper'`); + return raw; + } + const [rawMethodName, ...args] = result[1].split(','); + const methodName = rawMethodName.replace(/'|"/g, ''); + const firstArgIndex = result[1].indexOf(args[0]); + const rest = + firstArgIndex > -1 + ? result[1].slice(firstArgIndex, result[1].length).trim() + : ''; + const transformedCall = `super${ + methodName === 'initialize' ? '' : `.${methodName}` + }(${rest})`; + return ( + raw.slice(0, result.index) + + transformedCall + + raw.slice(result.index + result[0].length) + ); } function generateClass(rawClass, className, superClass, useExports) { return `${useExports ? 'export ' : ''}class ${className}${ superClass ? ` extends ${superClass}` : '' - } {\n${rawClass}\n}`; + } ${rawClass}`; } /** @@ -221,7 +219,7 @@ function generateMixin(rawClass, mixinName, baseClassNS, useExports) { const funcName = `${mixinName}Generator`; return ` ${useExports ? 'export ' : ''}function ${funcName}(Klass) { - return class ${mixinName || ''} extends Klass {\n${rawClass}\n}\n + return class ${mixinName || ''} extends Klass ${rawClass} } ${baseClassNS ? `${baseClassNS} = ${funcName}(${baseClassNS});` : ''} @@ -238,6 +236,24 @@ function getMixinName(file) { return name.replace('Itext', 'IText') + 'Mixin'; } +function transformFile(raw, { namespace, name } = {}) { + if (raw.replace(/\/\*.*\*\\s*/).startsWith('(function')) { + const wrapper = findObject(raw, '{', '}'); + raw = wrapper.raw.slice(1, wrapper.raw.length - 1); + } + + const annoyingCheck = new RegExp( + `if\\s*\\(\\s*(global.)?${namespace.replace(/\./g, '\\.')}\\s*\\)\\s*{` + ); + const result = annoyingCheck.exec(raw); + if (result) { + const found = findObject(raw, '{', '}', result.index); + raw = raw.slice(0, result.index) + raw.slice(found.end + 1); + } + raw = `//@ts-nocheck\n${raw}`; + return raw; +} + /** * * @param {string} file @@ -248,219 +264,90 @@ function transformClass(type, raw, options = {}) { const { className, useExports } = options; if (!type) throw new Error(`INVALID_ARGUMENT type`); const { - ast, + match, name, namespace, superClass, + raw: _rawClass, end, requiresSuperClassResolution, superClasses, - methods, - properties, - defaultValues, - staticMethods, - staticProperties, - declaration, - variableNode, - printNode, - } = type === 'mixin' ? parseMixin(raw) : parseClass(raw); - - // safety - const duplicateMethods = _.differenceWith( - methods, - _.uniqBy(methods, 'node.key.name'), - (a, b) => a === b - ); - if (duplicateMethods.length > 0) { - throw new Error( - `${name}: duplicate methods found: ${_.map( - duplicateMethods, - 'node.key.name' - )}` - ); - } - const duplicateProps = _.differenceWith( - properties, - _.uniqBy(properties, 'node.key.name'), - (a, b) => a === b - ); - - if (duplicateProps.length > 0) { - throw new Error( - `${name}: duplicate properties found: ${_.map( - duplicateProps, - 'node.key.name' - )}` - ); - } - - const classBody = []; - - properties.forEach(({ node, comment }) => { - const key = node.key.name; - const typeable = - node.value.type === 'Literal' && - ['boolean', 'number', 'string'].includes(typeof node.value.value); - const typed = typeable ? `${key}: ${typeof node.value.value}` : key; - classBody.push((comment ? printNode(comment) : '') + '\n' + typed); - // replaceNode(node, typeable ? `${key}: ${typeof node.value.value}` : key); - }); - - const staticCandidates = []; - - methods.forEach(({ node, comment }) => { - const key = node.key.name; - const value = printNode(node.value.body.body); - const methodAST = acorn.parse(value, { - ecmaVersion: 2022, - allowReturnOutsideFunction: true, - }); - const superTransforms = []; - walk.simple(methodAST, { - CallExpression(node) { - if ( - node.callee.object?.type === 'ThisExpression' && - node.callee.property?.name === 'callSuper' - ) { - const [methodNameArg, ...args] = node.arguments; - const out = `${ - methodNameArg.value === 'initialize' - ? 'super' - : `super.${methodNameArg.value}` - }(${args - .map((arg) => { - const out = printASTNode(value, arg); - return out.replace(/[^\(|\)]/gm, '').length % 2 === 1 - ? out.slice(0, -1) - : out; - }) - .join(', ')})`; - superTransforms.push({ - node, - methodName: methodNameArg.value, - value: out, - }); - } - }, - }); - - value.indexOf('this') === -1 && staticCandidates.push(key); - classBody.push( - (comment ? printNode(comment) : '') + - '\n' + - (node.value.async ? 'async ' : '') + - (key === 'initialize' ? 'constructor' : key) + - `(${printNode(node.value.params).slice(0, -1)}) {\n` + - superTransforms.reduceRight((value, { node, value: out }) => { - return value.replace(printASTNode(value, node), out); - }, value) + - '\n}' - ); - }); - - staticProperties.forEach(({ key, value, comment }) => { - const out = - (comment ? printNode(comment) : '') + - '\r\n' + - 'static ' + - key + - '=' + - printNode(value); - classBody.push(out); - }); - - staticMethods.forEach(({ key, value, comment }) => { - classBody.push( - (comment ? printNode(comment) : '') + - '\n' + - 'static ' + - (value.async ? 'async ' : '') + - key + - `(${printNode(value.params).slice(0, -1)}) {\n` + - printNode(value.body.body) + - '\n}' - ); - }); - - const body = classBody.join('\r\n\r\n'); - - const finalName = - type === 'mixin' - ? `${_.upperFirst(name)}${className.replace( - new RegExp( - name.toLowerCase() === 'staticcanvas' ? 'canvas' : name, - 'i' - ), - '' - )}` || name - : className || name; - - let classDirective = - type === 'mixin' - ? generateMixin(body, finalName, namespace, useExports) - : generateClass(body, finalName, superClass, useExports); - - if (_.size(defaultValues) > 0) { - const defaultsKey = `${_.lowerFirst(finalName)}DefaultValues`; - classDirective += - '\n\n' + - `export const ${defaultsKey}: Partial> = {\n${_.map( - defaultValues, - (value, key) => [key, value].join(':') - ).join(',\n')}\n};` + - '\n\n' + - `Object.assign(${finalName}.prototype, ${defaultsKey})`; - } - - let rawFile; - - const lastNode = ast.body[ast.body.length - 1]; - if ( - lastNode.type === 'ExpressionStatement' && - lastNode.expression.callee.params[0].name === 'global' - ) { - const bodyNodes = lastNode.expression.callee.body.body; - rawFile = - raw.slice(0, lastNode.start) + - printNode(bodyNodes).replace( - printNode(variableNode, false), - classDirective - ) + - raw.slice(lastNode.end + 1); - } else { - rawFile = `${raw.slice(0, variableNode.start)}${classDirective}${raw - .slice(end + 1) - .replace(/\s*\)\s*;?/, '')}`; - } - - [...staticMethods, ...staticProperties].forEach(({ node, comment }) => { - if (comment) { - rawFile = rawFile.replace( - printNode({ - start: comment.start, - end: node.end, - }), - '' + } = type === 'mixin' ? findMixin(raw) : findClass(raw); + let { raw: rawClass, fields } = parseRawClass(_rawClass); + const getPropStart = (key, raw) => { + const searchPhrase = `^(\\s*)${key}\\s*:\\s*`; + const regex = new RegExp(searchPhrase, 'm'); + const result = regex.exec(raw); + const whitespace = result[1]; + return { + start: result?.index + whitespace.length || -1, + regex, + value: `${whitespace}${key} = `, + }; + }; + const staticCandidantes = []; + fields.forEach((key) => { + const searchPhrase = `^(\\s*)${key}\\s*:\\s*function\\s*\\(`; + const regex = new RegExp(searchPhrase, 'm'); + const result = regex.exec(rawClass); + if (result) { + const whitespace = result[1]; + const start = result.index + whitespace.length; + const func = findObject(rawClass, '{', '}', start); + start && func.raw.indexOf('this') === -1 && staticCandidantes.push(key); + rawClass = rawClass.replace( + regex, + `${whitespace}${key === 'initialize' ? 'constructor' : key}(` ); + if (regex.exec(rawClass)) { + throw new Error(`dupliate method found ${name}#${key}`); + } + } else { + const start = getPropStart(key, rawClass); + rawClass = rawClass.replace(start.regex, start.value); } }); - - rawFile = rawFile - .replace(new RegExp(namespace.replace(/\./g, '\\.'), 'g'), name) - .replace(/fabric\.Object/g, 'FabricObject') - .replace(/fabric\.util\./g, ''); - - rawFile.indexOf('//@ts-nocheck') === -1 && - (rawFile = `//@ts-nocheck\n${rawFile}`); - - if (type === 'class' /*&& !useExports*/) { - classDirective += `\n\n/** @todo TODO_JS_MIGRATION remove next line after refactoring build */\n${namespace} = ${name};\n`; + let transformed = rawClass; + do { + rawClass = transformed; + try { + transformed = transformSuperCall(rawClass); + } catch (error) { + console.error(error); + } + } while (transformed !== rawClass); + const classDirective = + type === 'mixin' + ? generateMixin( + rawClass, + `${_.upperFirst(name)}${className.replace( + new RegExp( + name.toLowerCase() === 'staticcanvas' ? 'canvas' : name, + 'i' + ), + '' + )}` || name, + namespace, + useExports + ) + : generateClass(rawClass, className || name, superClass, useExports); + raw = `${raw.slice(0, match.index)}${classDirective}${raw + .slice(end + 1) + .replace(/\s*\)\s*;?/, '')}`; + if (type === 'mixin') { + // in case of multiple mixins in one file + try { + return transformClass(type, raw, options); + } catch (error) {} } - + if (type === 'class' && !useExports) { + raw = `${raw}\n/** @todo TODO_JS_MIGRATION remove next line after refactoring build */\n${namespace} = ${name};\n`; + } + raw = transformFile(raw, { namespace, name }); return { name, - raw: rawFile, - staticCandidates, + raw, + staticCandidantes, requiresSuperClassResolution, superClasses, }; @@ -471,7 +358,7 @@ function convertFile(type, source, dest, options) { const { name, raw, - staticCandidates, + staticCandidantes, requiresSuperClassResolution, superClasses, } = transformClass(type, readFile(source), { @@ -490,8 +377,8 @@ function convertFile(type, source, dest, options) { requiresSuperClassResolution: requiresSuperClassResolution ? superClasses : false, - staticCandidates: - staticCandidates.length > 0 ? staticCandidates : 'none', + staticCandidantes: + staticCandidantes.length > 0 ? staticCandidantes : 'none', }); return dest; } catch (error) { @@ -597,12 +484,6 @@ export function transform(options = {}) { ); }); - cp.execSync( - `prettier --write ${result - .map(({ dir, file }) => path.relative('.', path.resolve(dir, file))) - .join(' ')}` - ); - const [errors, files] = _.partition(result, (file) => file instanceof Error); const dirs = files.reduce((dirs, { dir, file }) => { (dirs[dir] || (dirs[dir] = [])).push(file); @@ -616,7 +497,7 @@ export function transform(options = {}) { fs.removeSync(path.resolve(dir, file.replace('.ts', '.js'))) ); - if (!options.verbose && errors.length > 0) { + if (!options.verbose) { console.error(`failed files:`); errors.map(console.error); } From 9b16b187093307c1ed3af604a8a78334d44df128 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:32:56 +0200 Subject: [PATCH 11/58] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a25afe7662b..2a1daa0e58a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [next] +- chore(TS): migrate object mixins to TS [#8414](https://github.com/fabricjs/fabric.js/pull/8414) - chore(TS): migrate Triangle to TS [#8410](https://github.com/fabricjs/fabric.js/pull/8410) - chore(TS): migrate Circle to TS [#8406](https://github.com/fabricjs/fabric.js/pull/8406) - chore(TS): convert Object interactivity mixin to its own class [#8401](https://github.com/fabricjs/fabric.js/pull/8401) From 198596273192ae61825eb1b059a9e96d441b35ff Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:38:05 +0200 Subject: [PATCH 12/58] svg mix --- index.js | 2 +- src/mixins/object.svg_export.ts | 595 +++++++++++++++---------------- src/shapes/fabricObject.class.js | 2 + 3 files changed, 293 insertions(+), 306 deletions(-) diff --git a/index.js b/index.js index 147f90c9d47..77ba587982d 100644 --- a/index.js +++ b/index.js @@ -18,7 +18,7 @@ import './src/mixins/canvas_dataurl_exporter.mixin'; import './src/mixins/canvas_serialization.mixin'; // optiona serialization import './src/mixins/canvas_gestures.mixin'; // optional gesturesv import './src/mixins/CanvasStraighteningMixin'; // optional gesturesv -import './src/mixins/object.svg_export'; + import './src/mixins/stateful.mixin'; import './src/mixins/animation.mixin'; // optional animation import './src/shapes/line.class'; diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index 3226589a2d8..07000667cc7 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -5,326 +5,311 @@ import { config } from '../config'; import { FabricObject } from '../shapes/fabricObject.class'; /* _TO_SVG_START_ */ -(function (global) { - var fabric = global.fabric; - function getSvgColorString(prop, value) { - if (!value) { - return prop + ': none; '; - } else if (value.toLive) { - return prop + ': url(#SVGID_' + value.id + '); '; - } else { - var color = new Color(value), - str = prop + ': ' + color.toRgb() + '; ', - opacity = color.getAlpha(); - if (opacity !== 1) { - //change the color in rgb + opacity - str += prop + '-opacity: ' + opacity.toString() + '; '; - } - return str; + +function getSvgColorString(prop, value) { + if (!value) { + return prop + ': none; '; + } else if (value.toLive) { + return prop + ': url(#SVGID_' + value.id + '); '; + } else { + const color = new Color(value), + opacity = color.getAlpha(); + + let str = `${prop}: ${color.toRgb()}; `; + + if (opacity !== 1) { + //change the color in rgb + opacity + str += `${prop}-opacity: ${opacity.toString()}; `; } + return str; } +} - var toFixed = (fabric = global.fabric), - toFixed = fabric.util.toFixed; +export class FabricObjectSVGExportMixin { + /** + * Returns styles-string for svg-export + * @param {Boolean} skipShadow a boolean to skip shadow filter output + * @return {String} + */ + getSvgStyles(skipShadow) { + const fillRule = this.fillRule ? this.fillRule : 'nonzero', + strokeWidth = this.strokeWidth ? this.strokeWidth : '0', + strokeDashArray = this.strokeDashArray + ? this.strokeDashArray.join(' ') + : 'none', + strokeDashOffset = this.strokeDashOffset ? this.strokeDashOffset : '0', + strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt', + strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter', + strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4', + opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1', + visibility = this.visible ? '' : ' visibility: hidden;', + filter = skipShadow ? '' : this.getSvgFilter(), + fill = getSvgColorString('fill', this.fill), + stroke = getSvgColorString('stroke', this.stroke); - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * Returns styles-string for svg-export - * @param {Boolean} skipShadow a boolean to skip shadow filter output - * @return {String} - */ - getSvgStyles: function (skipShadow) { - var fillRule = this.fillRule ? this.fillRule : 'nonzero', - strokeWidth = this.strokeWidth ? this.strokeWidth : '0', - strokeDashArray = this.strokeDashArray - ? this.strokeDashArray.join(' ') - : 'none', - strokeDashOffset = this.strokeDashOffset - ? this.strokeDashOffset - : '0', - strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt', - strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter', - strokeMiterLimit = this.strokeMiterLimit - ? this.strokeMiterLimit - : '4', - opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1', - visibility = this.visible ? '' : ' visibility: hidden;', - filter = skipShadow ? '' : this.getSvgFilter(), - fill = getSvgColorString('fill', this.fill), - stroke = getSvgColorString('stroke', this.stroke); + 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(''); + } - 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(''); - }, + /** + * Returns styles-string for svg-export + * @param {Object} style the object from which to retrieve style properties + * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the style. + * @return {String} + */ + getSvgSpanStyles(style, useWhiteSpace) { + const term = '; '; + var fontFamily = style.fontFamily + ? 'font-family: ' + + (style.fontFamily.indexOf("'") === -1 && + style.fontFamily.indexOf('"') === -1 + ? "'" + style.fontFamily + "'" + : style.fontFamily) + + term + : ''; + var strokeWidth = style.strokeWidth + ? 'stroke-width: ' + style.strokeWidth + term + : '', + fontFamily = fontFamily, + fontSize = style.fontSize + ? 'font-size: ' + style.fontSize + 'px' + term + : '', + fontStyle = style.fontStyle + ? 'font-style: ' + style.fontStyle + term + : '', + fontWeight = style.fontWeight + ? 'font-weight: ' + style.fontWeight + term + : '', + fill = style.fill ? getSvgColorString('fill', style.fill) : '', + stroke = style.stroke ? getSvgColorString('stroke', style.stroke) : '', + textDecoration = this.getSvgTextDecoration(style), + deltaY = style.deltaY ? 'baseline-shift: ' + -style.deltaY + '; ' : ''; + if (textDecoration) { + textDecoration = 'text-decoration: ' + textDecoration + term; + } - /** - * Returns styles-string for svg-export - * @param {Object} style the object from which to retrieve style properties - * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the style. - * @return {String} - */ - getSvgSpanStyles: function (style, useWhiteSpace) { - var term = '; '; - var fontFamily = style.fontFamily - ? 'font-family: ' + - (style.fontFamily.indexOf("'") === -1 && - style.fontFamily.indexOf('"') === -1 - ? "'" + style.fontFamily + "'" - : style.fontFamily) + - term - : ''; - var strokeWidth = style.strokeWidth - ? 'stroke-width: ' + style.strokeWidth + term - : '', - fontFamily = fontFamily, - fontSize = style.fontSize - ? 'font-size: ' + style.fontSize + 'px' + term - : '', - fontStyle = style.fontStyle - ? 'font-style: ' + style.fontStyle + term - : '', - fontWeight = style.fontWeight - ? 'font-weight: ' + style.fontWeight + term - : '', - fill = style.fill ? getSvgColorString('fill', style.fill) : '', - stroke = style.stroke - ? getSvgColorString('stroke', style.stroke) - : '', - textDecoration = this.getSvgTextDecoration(style), - deltaY = style.deltaY - ? 'baseline-shift: ' + -style.deltaY + '; ' - : ''; - if (textDecoration) { - textDecoration = 'text-decoration: ' + textDecoration + term; - } + return [ + stroke, + strokeWidth, + fontFamily, + fontSize, + fontStyle, + fontWeight, + textDecoration, + fill, + deltaY, + useWhiteSpace ? 'white-space: pre; ' : '', + ].join(''); + } - return [ - stroke, - strokeWidth, - fontFamily, - fontSize, - fontStyle, - fontWeight, - textDecoration, - fill, - deltaY, - useWhiteSpace ? 'white-space: pre; ' : '', - ].join(''); - }, + /** + * Returns text-decoration property for svg-export + * @param {Object} style the object from which to retrieve style properties + * @return {String} + */ + getSvgTextDecoration(style) { + return ['overline', 'underline', 'line-through'] + .filter(function (decoration) { + return style[decoration.replace('-', '')]; + }) + .join(' '); + } - /** - * Returns text-decoration property for svg-export - * @param {Object} style the object from which to retrieve style properties - * @return {String} - */ - getSvgTextDecoration: function (style) { - return ['overline', 'underline', 'line-through'] - .filter(function (decoration) { - return style[decoration.replace('-', '')]; - }) - .join(' '); - }, + /** + * Returns filter for svg shadow + * @return {String} + */ + getSvgFilter() { + return this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : ''; + } - /** - * Returns filter for svg shadow - * @return {String} - */ - getSvgFilter: function () { - return this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : ''; - }, + /** + * Returns id attribute for svg output + * @return {String} + */ + getSvgCommons() { + return [ + this.id ? 'id="' + this.id + '" ' : '', + this.clipPath + ? 'clip-path="url(#' + this.clipPath.clipPathId + ')" ' + : '', + ].join(''); + } - /** - * Returns id attribute for svg output - * @return {String} - */ - getSvgCommons: function () { - return [ - this.id ? 'id="' + this.id + '" ' : '', - this.clipPath - ? 'clip-path="url(#' + this.clipPath.clipPathId + ')" ' - : '', - ].join(''); - }, + /** + * Returns transform-string for svg-export + * @param {Boolean} use the full transform or the single object one. + * @return {String} + */ + getSvgTransform(full, additionalTransform) { + const transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(), + svgTransform = 'transform="' + matrixToSVG(transform); + return svgTransform + (additionalTransform || '') + '" '; + } - /** - * Returns transform-string for svg-export - * @param {Boolean} use the full transform or the single object one. - * @return {String} - */ - getSvgTransform: function (full, additionalTransform) { - var transform = full - ? this.calcTransformMatrix() - : this.calcOwnMatrix(), - svgTransform = 'transform="' + fabric.util.matrixToSVG(transform); - return svgTransform + (additionalTransform || '') + '" '; - }, + _setSVGBg(textBgRects) { + if (this.backgroundColor) { + const NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; + textBgRects.push( + '\t\t\n' + ); + } + } - _setSVGBg: function (textBgRects) { - if (this.backgroundColor) { - var NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; - textBgRects.push( - '\t\t\n' - ); - } - }, + /** + * Returns svg representation of an instance + * @param {Function} [reviver] Method for further parsing of svg representation. + * @return {String} svg representation of an instance + */ + toSVG(reviver) { + return this._createBaseSVGMarkup(this._toSVG(reviver), { + reviver: reviver, + }); + } - /** - * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toSVG: function (reviver) { - return this._createBaseSVGMarkup(this._toSVG(reviver), { - reviver: reviver, - }); - }, + /** + * Returns svg clipPath representation of an instance + * @param {Function} [reviver] Method for further parsing of svg representation. + * @return {String} svg representation of an instance + */ + toClipPathSVG(reviver) { + return ( + '\t' + + this._createBaseClipPathSVGMarkup(this._toSVG(reviver), { + reviver: reviver, + }) + ); + } - /** - * Returns svg clipPath representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. - * @return {String} svg representation of an instance - */ - toClipPathSVG: function (reviver) { - return ( - '\t' + - this._createBaseClipPathSVGMarkup(this._toSVG(reviver), { - reviver: reviver, - }) - ); - }, + /** + * @private + */ + _createBaseClipPathSVGMarkup(objectMarkup, options) { + options = options || {}; + const reviver = options.reviver, + additionalTransform = options.additionalTransform || '', + commonPieces = [ + this.getSvgTransform(true, additionalTransform), + this.getSvgCommons(), + ].join(''), + // insert commons in the markup, style and svgCommons + index = objectMarkup.indexOf('COMMON_PARTS'); + objectMarkup[index] = commonPieces; + return reviver ? reviver(objectMarkup.join('')) : objectMarkup.join(''); + } - /** - * @private - */ - _createBaseClipPathSVGMarkup: function (objectMarkup, options) { - options = options || {}; - var reviver = options.reviver, - additionalTransform = options.additionalTransform || '', - commonPieces = [ - this.getSvgTransform(true, additionalTransform), - this.getSvgCommons(), - ].join(''), - // insert commons in the markup, style and svgCommons - index = objectMarkup.indexOf('COMMON_PARTS'); - objectMarkup[index] = commonPieces; - return reviver ? reviver(objectMarkup.join('')) : objectMarkup.join(''); - }, + /** + * @private + */ + _createBaseSVGMarkup(objectMarkup, options) { + options = options || {}; + let noStyle = options.noStyle, + reviver = options.reviver, + styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ', + shadowInfo = options.withShadow + ? 'style="' + this.getSvgFilter() + '" ' + : '', + clipPath = this.clipPath, + vectorEffect = this.strokeUniform + ? 'vector-effect="non-scaling-stroke" ' + : '', + absoluteClipPath = clipPath && clipPath.absolutePositioned, + stroke = this.stroke, + fill = this.fill, + shadow = this.shadow, + commonPieces, + markup = [], + clipPathMarkup, + // insert commons in the markup, style and svgCommons + index = objectMarkup.indexOf('COMMON_PARTS'), + additionalTransform = options.additionalTransform; + if (clipPath) { + clipPath.clipPathId = 'CLIPPATH_' + FabricObject.__uid++; + clipPathMarkup = + '\n' + + clipPath.toClipPathSVG(reviver) + + '\n'; + } + if (absoluteClipPath) { + markup.push('\n'); + } + markup.push( + '\n' + ); + commonPieces = [ + styleInfo, + vectorEffect, + noStyle ? '' : this.addPaintOrder(), + ' ', + additionalTransform ? 'transform="' + additionalTransform + '" ' : '', + ].join(''); + objectMarkup[index] = commonPieces; + if (fill && fill.toLive) { + markup.push(fill.toSVG(this)); + } + if (stroke && stroke.toLive) { + markup.push(stroke.toSVG(this)); + } + if (shadow) { + markup.push(shadow.toSVG(this)); + } + if (clipPath) { + markup.push(clipPathMarkup); + } + markup.push(objectMarkup.join('')); + markup.push('\n'); + absoluteClipPath && markup.push('\n'); + return reviver ? reviver(markup.join('')) : markup.join(''); + } - /** - * @private - */ - _createBaseSVGMarkup: function (objectMarkup, options) { - options = options || {}; - var noStyle = options.noStyle, - reviver = options.reviver, - styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ', - shadowInfo = options.withShadow - ? 'style="' + this.getSvgFilter() + '" ' - : '', - clipPath = this.clipPath, - vectorEffect = this.strokeUniform - ? 'vector-effect="non-scaling-stroke" ' - : '', - absoluteClipPath = clipPath && clipPath.absolutePositioned, - stroke = this.stroke, - fill = this.fill, - shadow = this.shadow, - commonPieces, - markup = [], - clipPathMarkup, - // insert commons in the markup, style and svgCommons - index = objectMarkup.indexOf('COMMON_PARTS'), - additionalTransform = options.additionalTransform; - if (clipPath) { - clipPath.clipPathId = 'CLIPPATH_' + FabricObject.__uid++; - clipPathMarkup = - '\n' + - clipPath.toClipPathSVG(reviver) + - '\n'; - } - if (absoluteClipPath) { - markup.push('\n'); - } - markup.push( - '\n' - ); - commonPieces = [ - styleInfo, - vectorEffect, - noStyle ? '' : this.addPaintOrder(), - ' ', - additionalTransform ? 'transform="' + additionalTransform + '" ' : '', - ].join(''); - objectMarkup[index] = commonPieces; - if (fill && fill.toLive) { - markup.push(fill.toSVG(this)); - } - if (stroke && stroke.toLive) { - markup.push(stroke.toSVG(this)); - } - if (shadow) { - markup.push(shadow.toSVG(this)); - } - if (clipPath) { - markup.push(clipPathMarkup); - } - markup.push(objectMarkup.join('')); - markup.push('\n'); - absoluteClipPath && markup.push('\n'); - return reviver ? reviver(markup.join('')) : markup.join(''); - }, + addPaintOrder() { + return this.paintFirst !== 'fill' + ? ' paint-order="' + this.paintFirst + '" ' + : ''; + } +} - addPaintOrder: function () { - return this.paintFirst !== 'fill' - ? ' paint-order="' + this.paintFirst + '" ' - : ''; - }, - } - ); -})(typeof exports !== 'undefined' ? exports : window); /* _TO_SVG_END_ */ diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.js index f633eb5f4be..44044fe97ac 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.js @@ -2,6 +2,7 @@ import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; +import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import @@ -10,6 +11,7 @@ applyMixins(InteractiveFabricObject, [ FabricObjectAncestryMixin, FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, + FabricObjectSVGExportMixin, ]); export { InteractiveFabricObject as FabricObject }; From 50f14da530c4f295c423a3ce5d2eb5e0ef7acd3e Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:45:50 +0200 Subject: [PATCH 13/58] rename --- index.js | 2 +- ...teningMixin.ts => canvas_straightening.mixin.ts} | 0 ...{fabricObject.class.js => fabricObject.class.ts} | 13 +++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) rename src/mixins/{CanvasStraighteningMixin.ts => canvas_straightening.mixin.ts} (100%) rename src/shapes/{fabricObject.class.js => fabricObject.class.ts} (69%) diff --git a/index.js b/index.js index 77ba587982d..2a0778a08d2 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,7 @@ import './src/mixins/canvas_grouping.mixin'; // optional interaction import './src/mixins/canvas_dataurl_exporter.mixin'; import './src/mixins/canvas_serialization.mixin'; // optiona serialization import './src/mixins/canvas_gestures.mixin'; // optional gesturesv -import './src/mixins/CanvasStraighteningMixin'; // optional gesturesv +import './src/mixins/canvas_straightening.mixin'; // optional gesturesv import './src/mixins/stateful.mixin'; import './src/mixins/animation.mixin'; // optional animation diff --git a/src/mixins/CanvasStraighteningMixin.ts b/src/mixins/canvas_straightening.mixin.ts similarity index 100% rename from src/mixins/CanvasStraighteningMixin.ts rename to src/mixins/canvas_straightening.mixin.ts diff --git a/src/shapes/fabricObject.class.js b/src/shapes/fabricObject.class.ts similarity index 69% rename from src/shapes/fabricObject.class.js rename to src/shapes/fabricObject.class.ts index 44044fe97ac..fef9bf2a997 100644 --- a/src/shapes/fabricObject.class.js +++ b/src/shapes/fabricObject.class.ts @@ -4,6 +4,8 @@ import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { applyMixins } from '../util/applyMixins'; +import { fabric } from '../../HEADER'; +import { FabricObject } from './object.class'; // TODO somehow we have to make a tree-shakeable import @@ -14,9 +16,12 @@ applyMixins(InteractiveFabricObject, [ FabricObjectSVGExportMixin, ]); +// export interface InteractiveFabricObject +// extends FabricObjectAncestryMixin, +// FabricObjectObjectStackingMixin, +// FabricObjectObjectStraighteningMixin, +// FabricObjectSVGExportMixin {} + export { InteractiveFabricObject as FabricObject }; -(function (global) { - const fabric = global.fabric; - fabric.Object = InteractiveFabricObject; -})(typeof exports !== 'undefined' ? exports : window); +fabric.Object = FabricObject; From f0e24f236960d2e052fc6165f71bc059806670af Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 31 Oct 2022 23:46:15 +0200 Subject: [PATCH 14/58] Update object_stacking.mixin.ts --- src/mixins/object_stacking.mixin.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts index 25a93d94483..ff99a3aa2d9 100644 --- a/src/mixins/object_stacking.mixin.ts +++ b/src/mixins/object_stacking.mixin.ts @@ -2,9 +2,8 @@ import { fabric } from '../../HEADER'; import type { FabricObject } from '../shapes/fabricObject.class'; -import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; -export class FabricObjectObjectStackingMixin extends FabricObjectAncestryMixin { +export class FabricObjectObjectStackingMixin { /** * Moves an object to the bottom of the stack of drawn objects * @return {FabricObject} thisArg From 537d056c1c6889c291dcc0d07e914c691a67a884 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:04:23 +0200 Subject: [PATCH 15/58] split --- index.js | 4 +- src/mixins/animation.mixin.ts | 258 --------------------------- src/mixins/canvas_animation.mixin.ts | 114 ++++++++++++ src/mixins/object_animation.mixin.ts | 130 ++++++++++++++ src/shapes/fabricObject.class.ts | 4 +- 5 files changed, 249 insertions(+), 261 deletions(-) delete mode 100644 src/mixins/animation.mixin.ts create mode 100644 src/mixins/canvas_animation.mixin.ts create mode 100644 src/mixins/object_animation.mixin.ts diff --git a/index.js b/index.js index 2a0778a08d2..831c468cc53 100644 --- a/index.js +++ b/index.js @@ -18,9 +18,9 @@ import './src/mixins/canvas_dataurl_exporter.mixin'; import './src/mixins/canvas_serialization.mixin'; // optiona serialization import './src/mixins/canvas_gestures.mixin'; // optional gesturesv import './src/mixins/canvas_straightening.mixin'; // optional gesturesv - import './src/mixins/stateful.mixin'; -import './src/mixins/animation.mixin'; // optional animation +import './src/mixins/canvas_animation.mixin'; // optional animation + import './src/shapes/line.class'; import './src/shapes/circle.class'; import './src/shapes/triangle.class'; diff --git a/src/mixins/animation.mixin.ts b/src/mixins/animation.mixin.ts deleted file mode 100644 index da74ecbe3b7..00000000000 --- a/src/mixins/animation.mixin.ts +++ /dev/null @@ -1,258 +0,0 @@ -//@ts-nocheck -import { FabricObject } from '../shapes/fabricObject.class'; - -(function (global) { - var fabric = global.fabric; - fabric.util.object.extend( - fabric.StaticCanvas.prototype, - /** @lends fabric.StaticCanvas.prototype */ { - /** - * Animation duration (in ms) for fx* methods - * @type Number - * @default - */ - FX_DURATION: 500, - - /** - * Centers object horizontally with animation. - * @param {fabric.Object} object Object to center - * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - * @return {fabric.AnimationContext} context - */ - fxCenterObjectH: function (object, callbacks) { - callbacks = callbacks || {}; - - var empty = function () {}, - onComplete = callbacks.onComplete || empty, - onChange = callbacks.onChange || empty, - _this = this; - - return fabric.util.animate({ - target: this, - startValue: object.getX(), - endValue: this.getCenterPoint().x, - duration: this.FX_DURATION, - onChange: function (value) { - object.setX(value); - _this.requestRenderAll(); - onChange(); - }, - onComplete: function () { - object.setCoords(); - onComplete(); - }, - }); - }, - - /** - * Centers object vertically with animation. - * @param {fabric.Object} object Object to center - * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - * @return {fabric.AnimationContext} context - */ - fxCenterObjectV: function (object, callbacks) { - callbacks = callbacks || {}; - - var empty = function () {}, - onComplete = callbacks.onComplete || empty, - onChange = callbacks.onChange || empty, - _this = this; - - return fabric.util.animate({ - target: this, - startValue: object.getY(), - endValue: this.getCenterPoint().y, - duration: this.FX_DURATION, - onChange: function (value) { - object.setY(value); - _this.requestRenderAll(); - onChange(); - }, - onComplete: function () { - object.setCoords(); - onComplete(); - }, - }); - }, - - /** - * Same as `fabric.Canvas#remove` but animated - * @param {fabric.Object} object Object to remove - * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - * @return {fabric.AnimationContext} context - */ - fxRemove: function (object, callbacks) { - callbacks = callbacks || {}; - - var empty = function () {}, - onComplete = callbacks.onComplete || empty, - onChange = callbacks.onChange || empty, - _this = this; - - return fabric.util.animate({ - target: this, - startValue: object.opacity, - endValue: 0, - duration: this.FX_DURATION, - onChange: function (value) { - object.set('opacity', value); - _this.requestRenderAll(); - onChange(); - }, - onComplete: function () { - _this.remove(object); - onComplete(); - }, - }); - }, - } - ); - - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * Animates object's properties - * @param {String|Object} property Property to animate (if string) or properties to animate (if object) - * @param {Number|Object} value Value to animate property to (if string was given first) or options object - * @return {fabric.Object} thisArg - * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation} - * @return {fabric.AnimationContext | fabric.AnimationContext[]} animation context (or an array if passed multiple properties) - * - * As object — multiple properties - * - * object.animate({ left: ..., top: ... }); - * object.animate({ left: ..., top: ... }, { duration: ... }); - * - * As string — one property - * - * object.animate('left', ...); - * object.animate('left', { duration: ... }); - * - */ - animate: function () { - if (arguments[0] && typeof arguments[0] === 'object') { - var propsToAnimate = [], - prop, - skipCallbacks, - out = []; - for (prop in arguments[0]) { - propsToAnimate.push(prop); - } - for (var i = 0, len = propsToAnimate.length; i < len; i++) { - prop = propsToAnimate[i]; - skipCallbacks = i !== len - 1; - out.push( - this._animate( - prop, - arguments[0][prop], - arguments[1], - skipCallbacks - ) - ); - } - return out; - } else { - return this._animate.apply(this, arguments); - } - }, - - /** - * @private - * @param {String} property Property to animate - * @param {String} to Value to animate to - * @param {Object} [options] Options object - * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked - */ - _animate: function (property, to, options, skipCallbacks) { - var _this = this, - propPair; - - to = to.toString(); - - options = Object.assign({}, options); - - if (~property.indexOf('.')) { - propPair = property.split('.'); - } - - var propIsColor = - _this.colorProperties.indexOf(property) > -1 || - (propPair && _this.colorProperties.indexOf(propPair[1]) > -1); - - var currentValue = propPair - ? this.get(propPair[0])[propPair[1]] - : this.get(property); - - if (!('from' in options)) { - options.from = currentValue; - } - - if (!propIsColor) { - if (~to.indexOf('=')) { - to = currentValue + parseFloat(to.replace('=', '')); - } else { - to = parseFloat(to); - } - } - - var _options = { - target: this, - startValue: options.from, - endValue: to, - byValue: options.by, - easing: options.easing, - duration: options.duration, - abort: - options.abort && - function (value, valueProgress, timeProgress) { - return options.abort.call( - _this, - value, - valueProgress, - timeProgress - ); - }, - onChange: function (value, valueProgress, timeProgress) { - if (propPair) { - _this[propPair[0]][propPair[1]] = value; - } else { - _this.set(property, value); - } - if (skipCallbacks) { - return; - } - options.onChange && - options.onChange(value, valueProgress, timeProgress); - }, - onComplete: function (value, valueProgress, timeProgress) { - if (skipCallbacks) { - return; - } - - _this.setCoords(); - options.onComplete && - options.onComplete(value, valueProgress, timeProgress); - }, - }; - - if (propIsColor) { - return fabric.util.animateColor( - _options.startValue, - _options.endValue, - _options.duration, - _options - ); - } else { - return fabric.util.animate(_options); - } - }, - } - ); -})(typeof exports !== 'undefined' ? exports : window); diff --git a/src/mixins/canvas_animation.mixin.ts b/src/mixins/canvas_animation.mixin.ts new file mode 100644 index 00000000000..305e2c0f0b4 --- /dev/null +++ b/src/mixins/canvas_animation.mixin.ts @@ -0,0 +1,114 @@ +// @ts-nocheck + +import { fabric } from '../../HEADER'; + +fabric.util.object.extend( + fabric.StaticCanvas.prototype, + /** @lends fabric.StaticCanvas.prototype */ { + /** + * Animation duration (in ms) for fx* methods + * @type Number + * @default + */ + FX_DURATION: 500, + + /** + * Centers object horizontally with animation. + * @param {fabric.Object} object Object to center + * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + * @return {fabric.AnimationContext} context + */ + fxCenterObjectH: function (object, callbacks) { + callbacks = callbacks || {}; + + var empty = function () {}, + onComplete = callbacks.onComplete || empty, + onChange = callbacks.onChange || empty, + _this = this; + + return fabric.util.animate({ + target: this, + startValue: object.getX(), + endValue: this.getCenterPoint().x, + duration: this.FX_DURATION, + onChange: function (value) { + object.setX(value); + _this.requestRenderAll(); + onChange(); + }, + onComplete: function () { + object.setCoords(); + onComplete(); + }, + }); + }, + + /** + * Centers object vertically with animation. + * @param {fabric.Object} object Object to center + * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + * @return {fabric.AnimationContext} context + */ + fxCenterObjectV: function (object, callbacks) { + callbacks = callbacks || {}; + + var empty = function () {}, + onComplete = callbacks.onComplete || empty, + onChange = callbacks.onChange || empty, + _this = this; + + return fabric.util.animate({ + target: this, + startValue: object.getY(), + endValue: this.getCenterPoint().y, + duration: this.FX_DURATION, + onChange: function (value) { + object.setY(value); + _this.requestRenderAll(); + onChange(); + }, + onComplete: function () { + object.setCoords(); + onComplete(); + }, + }); + }, + + /** + * Same as `fabric.Canvas#remove` but animated + * @param {fabric.Object} object Object to remove + * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + * @return {fabric.AnimationContext} context + */ + fxRemove: function (object, callbacks) { + callbacks = callbacks || {}; + + var empty = function () {}, + onComplete = callbacks.onComplete || empty, + onChange = callbacks.onChange || empty, + _this = this; + + return fabric.util.animate({ + target: this, + startValue: object.opacity, + endValue: 0, + duration: this.FX_DURATION, + onChange: function (value) { + object.set('opacity', value); + _this.requestRenderAll(); + onChange(); + }, + onComplete: function () { + _this.remove(object); + onComplete(); + }, + }); + }, + } +); diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts new file mode 100644 index 00000000000..38610c3ee9a --- /dev/null +++ b/src/mixins/object_animation.mixin.ts @@ -0,0 +1,130 @@ +//@ts-nocheck +import { FabricObject } from '../shapes/fabricObject.class'; + +export class FabricObjectObjectAnimationMixin { + /** + * Animates object's properties + * @param {String|Object} property Property to animate (if string) or properties to animate (if object) + * @param {Number|Object} value Value to animate property to (if string was given first) or options object + * @return {FabricObject} thisArg + * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation} + * @return {AnimationContext | AnimationContext[]} animation context (or an array if passed multiple properties) + * + * As object — multiple properties + * + * object.animate({ left: ..., top: ... }); + * object.animate({ left: ..., top: ... }, { duration: ... }); + * + * As string — one property + * + * object.animate('left', ...); + * object.animate('left', { duration: ... }); + * + */ + animate() { + if (arguments[0] && typeof arguments[0] === 'object') { + let propsToAnimate = [], + prop, + skipCallbacks, + out = []; + for (prop in arguments[0]) { + propsToAnimate.push(prop); + } + for (let i = 0, len = propsToAnimate.length; i < len; i++) { + prop = propsToAnimate[i]; + skipCallbacks = i !== len - 1; + out.push( + this._animate(prop, arguments[0][prop], arguments[1], skipCallbacks) + ); + } + return out; + } else { + return this._animate.apply(this, arguments); + } + } + + /** + * @private + * @param {String} property Property to animate + * @param {String} to Value to animate to + * @param {Object} [options] Options object + * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked + */ + _animate(property, to, options, skipCallbacks) { + let propPair; + + to = to.toString(); + + options = Object.assign({}, options); + + if (~property.indexOf('.')) { + propPair = property.split('.'); + } + + const propIsColor = + this.colorProperties.indexOf(property) > -1 || + (propPair && this.colorProperties.indexOf(propPair[1]) > -1); + + const currentValue = propPair + ? this.get(propPair[0])[propPair[1]] + : this.get(property); + + if (!('from' in options)) { + options.from = currentValue; + } + + if (!propIsColor) { + if (~to.indexOf('=')) { + to = currentValue + parseFloat(to.replace('=', '')); + } else { + to = parseFloat(to); + } + } + + const _options = { + target: this, + startValue: options.from, + endValue: to, + byValue: options.by, + easing: options.easing, + duration: options.duration, + abort: + options.abort && + ((value, valueProgress, timeProgress) => { + return options.abort(value, valueProgress, timeProgress); + }), + onChange: (value, valueProgress, timeProgress) => { + if (propPair) { + this[propPair[0]][propPair[1]] = value; + } else { + this.set(property, value); + } + if (skipCallbacks) { + return; + } + options.onChange && + options.onChange(value, valueProgress, timeProgress); + }, + onComplete: (value, valueProgress, timeProgress) => { + if (skipCallbacks) { + return; + } + + this.setCoords(); + options.onComplete && + options.onComplete(value, valueProgress, timeProgress); + }, + }; + + if (propIsColor) { + return animateColor( + _options.startValue, + _options.endValue, + _options.duration, + _options + ); + } else { + return animate(_options); + } + } +} diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index fef9bf2a997..3fad35e5f4f 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -6,6 +6,7 @@ import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { applyMixins } from '../util/applyMixins'; import { fabric } from '../../HEADER'; import { FabricObject } from './object.class'; +import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; // TODO somehow we have to make a tree-shakeable import @@ -14,13 +15,14 @@ applyMixins(InteractiveFabricObject, [ FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, + FabricObjectObjectAnimationMixin, ]); // export interface InteractiveFabricObject // extends FabricObjectAncestryMixin, // FabricObjectObjectStackingMixin, // FabricObjectObjectStraighteningMixin, -// FabricObjectSVGExportMixin {} +// FabricObjectSVGExportMixin,FabricObjectObjectAnimationMixin {} export { InteractiveFabricObject as FabricObject }; From a01a55d3fe8a60f3c708bdfcf98f25118e645234 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:33:27 +0200 Subject: [PATCH 16/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 3fad35e5f4f..dee134ff785 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -26,4 +26,4 @@ applyMixins(InteractiveFabricObject, [ export { InteractiveFabricObject as FabricObject }; -fabric.Object = FabricObject; +fabric.Object = InteractiveFabricObject; From c03ba7c45372c36c5551921019ff5c42ce5bdb97 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:35:34 +0200 Subject: [PATCH 17/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index dee134ff785..5a13cdd5b30 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,12 +1,11 @@ +import { fabric } from '../../HEADER'; +import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; -import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; +import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; +import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; -import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { applyMixins } from '../util/applyMixins'; -import { fabric } from '../../HEADER'; -import { FabricObject } from './object.class'; -import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; // TODO somehow we have to make a tree-shakeable import From d23c73cd3ec234ef06a77a82711e7ade8a720cd8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:36:51 +0200 Subject: [PATCH 18/58] Update object.svg_export.ts --- src/mixins/object.svg_export.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index 07000667cc7..9d01a915c63 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -1,8 +1,8 @@ -//@ts-nocheck - import { Color } from '../color'; import { config } from '../config'; import { FabricObject } from '../shapes/fabricObject.class'; +import { matrixToSVG } from '../util/misc/svgParsing'; +import { toFixed } from '../util/misc/toFixed'; /* _TO_SVG_START_ */ @@ -171,8 +171,8 @@ export class FabricObjectSVGExportMixin { */ getSvgTransform(full, additionalTransform) { const transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(), - svgTransform = 'transform="' + matrixToSVG(transform); - return svgTransform + (additionalTransform || '') + '" '; + svgTransform = `transform="${matrixToSVG(transform)}`; + return `${svgTransform + (additionalTransform || '')}" `; } _setSVGBg(textBgRects) { From 1ccd7a53fc6c5f96997ab3ece8e0b57b1de88351 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:37:04 +0200 Subject: [PATCH 19/58] Update object.svg_export.ts --- src/mixins/object.svg_export.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index 9d01a915c63..3829566ee8e 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -1,3 +1,5 @@ +// @ts-nocheck + import { Color } from '../color'; import { config } from '../config'; import { FabricObject } from '../shapes/fabricObject.class'; From fc1b615251c52f143a90a739f3e9507273cd461b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:38:58 +0200 Subject: [PATCH 20/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 38610c3ee9a..de48fb90a11 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,5 +1,6 @@ -//@ts-nocheck -import { FabricObject } from '../shapes/fabricObject.class'; +import type { FabricObject } from '../shapes/fabricObject.class'; +import { animate } from '../util/animate'; +import { animateColor } from '../util/animate_color'; export class FabricObjectObjectAnimationMixin { /** From 7ef6efa65d9bba6715f37f305730b84f1c497510 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:40:38 +0200 Subject: [PATCH 21/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index de48fb90a11..225032b4daf 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,3 +1,5 @@ +// @ts-nocheck + import type { FabricObject } from '../shapes/fabricObject.class'; import { animate } from '../util/animate'; import { animateColor } from '../util/animate_color'; From 3ab5f74893612a862be4744876450739d7f45e6c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 00:42:06 +0200 Subject: [PATCH 22/58] revert --- src/mixins/object_animation.mixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 225032b4daf..d8936f00d69 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -94,7 +94,7 @@ export class FabricObjectObjectAnimationMixin { abort: options.abort && ((value, valueProgress, timeProgress) => { - return options.abort(value, valueProgress, timeProgress); + return options.abort.call(this, value, valueProgress, timeProgress); }), onChange: (value, valueProgress, timeProgress) => { if (propPair) { From 2622152f63244aedce0f57cde25f5ad816103e75 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 07:16:17 +0200 Subject: [PATCH 23/58] type --- src/mixins/object_ancestry.mixin.ts | 84 +++++++++++++++++------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index e3810b2605b..6b200a94abe 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,9 +1,28 @@ -// @ts-nocheck - import { fabric } from '../../HEADER'; import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; -import type { Canvas, StaticCanvas } from '../__types__'; +import type { StaticCanvas } from '../__types__'; + +type TAncestor = FabricObject | StaticCanvas; + +type TStrictAncestor = Group | StaticCanvas; + +export type Ancestors = FabricObject[] | [...FabricObject[], StaticCanvas]; + +export type AncestryComparison = { + /** + * common ancestors of `this` and`other`(may include`this` | `other`) + */ + common: Ancestors; + /** + * ancestors that are of `this` only + */ + fork: Ancestors; + /** + * ancestors that are of `other` only + */ + otherFork: Ancestors; +}; export class FabricObjectAncestryMixin { group?: Group; @@ -11,11 +30,11 @@ export class FabricObjectAncestryMixin { /** * Checks if object is descendant of target - * Should be used instead of @link {fabric.Collection.contains} for performance reasons - * @param {FabricObject|fabric.StaticCanvas} target + * Should be used instead of @link {Collection.contains} for performance reasons + * @param {TAncestor} target * @returns {boolean} */ - isDescendantOf(target: FabricObject | StaticCanvas) { + isDescendantOf(target: TAncestor): boolean { let parent = this.group || this.canvas; while (parent) { if (target === parent) { @@ -30,50 +49,38 @@ export class FabricObjectAncestryMixin { } /** - * - * @typedef {FabricObject[] | [...FabricObject[], fabric.StaticCanvas]} Ancestors * * @param {boolean} [strict] returns only ancestors that are objects (without canvas) * @returns {Ancestors} ancestors from bottom to top */ - getAncestors(strict?: boolean) { - const ancestors: (Group | StaticCanvas | Canvas)[] = []; + getAncestors(strict?: boolean): Ancestors { + const ancestors: TAncestor[] = []; let parent = this.group || (strict ? undefined : this.canvas); while (parent) { ancestors.push(parent); parent = parent.group || (strict ? undefined : parent.canvas); } - return ancestors; + return ancestors as Ancestors; } /** - * Returns an object that represent the ancestry situation. - * - * @typedef {object} AncestryComparison - * @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`) - * @property {Ancestors} fork ancestors that are of `this` only - * @property {Ancestors} otherFork ancestors that are of `other` only + * Compare ancestors * * @param {FabricObject} other * @param {boolean} [strict] finds only ancestors that are objects (without canvas) - * @returns {AncestryComparison | undefined} - * + * @returns {AncestryComparison} an object that represent the ancestry situation. */ findCommonAncestors( this: FabricObject & this, other: FabricObject & this, strict?: boolean - ) { + ): AncestryComparison { if (this === other) { return { fork: [] as FabricObject[], otherFork: [] as FabricObject[], common: [this, ...this.getAncestors(strict)], }; - } else if (!other) { - // meh, warn and inform, and not my issue. - // the argument is NOT optional, we can't end up here. - return undefined; } const ancestors = this.getAncestors(strict); const otherAncestors = other.getAncestors(strict); @@ -90,7 +97,7 @@ export class FabricObjectAncestryMixin { ...otherAncestors.slice(0, otherAncestors.length - 1), ], common: [this], - }; + } as AncestryComparison; } // compare ancestors for (let i = 0, ancestor; i < ancestors.length; i++) { @@ -100,7 +107,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors.slice(0, i)], otherFork: [], common: ancestors.slice(i), - }; + } as AncestryComparison; } for (let j = 0; j < otherAncestors.length; j++) { if (this === otherAncestors[j]) { @@ -108,14 +115,14 @@ export class FabricObjectAncestryMixin { fork: [], otherFork: [other, ...otherAncestors.slice(0, j)], common: [this, ...ancestors], - }; + } as AncestryComparison; } if (ancestor === otherAncestors[j]) { return { fork: [this, ...ancestors.slice(0, i)], otherFork: [other, ...otherAncestors.slice(0, j)], common: ancestors.slice(i), - }; + } as AncestryComparison; } } } @@ -124,7 +131,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors], otherFork: [other, ...otherAncestors], common: [], - }; + } as AncestryComparison; } /** @@ -137,7 +144,7 @@ export class FabricObjectAncestryMixin { this: FabricObject & this, other: FabricObject & this, strict?: boolean - ) { + ): boolean { const commonAncestors = this.findCommonAncestors(other, strict); return commonAncestors && !!commonAncestors.common.length; } @@ -147,7 +154,10 @@ export class FabricObjectAncestryMixin { * @param {FabricObject} other object to compare against * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` */ - isInFrontOf(this: FabricObject & this, other: FabricObject & this) { + isInFrontOf( + this: FabricObject & this, + other: FabricObject & this + ): boolean | undefined { if (this === other) { return undefined; } @@ -165,10 +175,14 @@ export class FabricObjectAncestryMixin { if (!firstCommonAncestor) { return undefined; } - const headOfFork = ancestorData.fork.pop(), - headOfOtherFork = ancestorData.otherFork.pop(), - thisIndex = firstCommonAncestor._objects.indexOf(headOfFork), - otherIndex = firstCommonAncestor._objects.indexOf(headOfOtherFork); + const headOfFork = ancestorData.fork.pop() as FabricObject, + headOfOtherFork = ancestorData.otherFork.pop() as FabricObject, + thisIndex = (firstCommonAncestor as TStrictAncestor)._objects.indexOf( + headOfFork + ), + otherIndex = (firstCommonAncestor as TStrictAncestor)._objects.indexOf( + headOfOtherFork + ); return thisIndex > -1 && thisIndex > otherIndex; } } From 038e3e15e4fd3db6185f9e4933ae1972b124780b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 07:40:09 +0200 Subject: [PATCH 24/58] better types --- src/mixins/object_ancestry.mixin.ts | 47 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 6b200a94abe..400c1837104 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -7,21 +7,28 @@ type TAncestor = FabricObject | StaticCanvas; type TStrictAncestor = Group | StaticCanvas; -export type Ancestors = FabricObject[] | [...FabricObject[], StaticCanvas]; +export type Ancestors = Strict extends true + ? [FabricObject | Group] | [FabricObject | Group, ...Group[]] | Group[] + : + | [FabricObject | Group | StaticCanvas] + | [FabricObject | Group, StaticCanvas] + | [FabricObject, ...Group[]] + | Group[] + | [FabricObject | Group, ...Group[], StaticCanvas]; -export type AncestryComparison = { +export type AncestryComparison = { /** * common ancestors of `this` and`other`(may include`this` | `other`) */ - common: Ancestors; + common: Ancestors; /** * ancestors that are of `this` only */ - fork: Ancestors; + fork: Ancestors; /** * ancestors that are of `other` only */ - otherFork: Ancestors; + otherFork: Ancestors; }; export class FabricObjectAncestryMixin { @@ -53,14 +60,14 @@ export class FabricObjectAncestryMixin { * @param {boolean} [strict] returns only ancestors that are objects (without canvas) * @returns {Ancestors} ancestors from bottom to top */ - getAncestors(strict?: boolean): Ancestors { + getAncestors(strict?: T): Ancestors { const ancestors: TAncestor[] = []; let parent = this.group || (strict ? undefined : this.canvas); while (parent) { ancestors.push(parent); parent = parent.group || (strict ? undefined : parent.canvas); } - return ancestors as Ancestors; + return ancestors as Ancestors; } /** @@ -70,17 +77,17 @@ export class FabricObjectAncestryMixin { * @param {boolean} [strict] finds only ancestors that are objects (without canvas) * @returns {AncestryComparison} an object that represent the ancestry situation. */ - findCommonAncestors( + findCommonAncestors( this: FabricObject & this, other: FabricObject & this, - strict?: boolean - ): AncestryComparison { + strict?: T + ): AncestryComparison { if (this === other) { return { - fork: [] as FabricObject[], - otherFork: [] as FabricObject[], + fork: [], + otherFork: [], common: [this, ...this.getAncestors(strict)], - }; + } as AncestryComparison; } const ancestors = this.getAncestors(strict); const otherAncestors = other.getAncestors(strict); @@ -97,7 +104,7 @@ export class FabricObjectAncestryMixin { ...otherAncestors.slice(0, otherAncestors.length - 1), ], common: [this], - } as AncestryComparison; + } as AncestryComparison; } // compare ancestors for (let i = 0, ancestor; i < ancestors.length; i++) { @@ -107,7 +114,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors.slice(0, i)], otherFork: [], common: ancestors.slice(i), - } as AncestryComparison; + } as AncestryComparison; } for (let j = 0; j < otherAncestors.length; j++) { if (this === otherAncestors[j]) { @@ -115,14 +122,14 @@ export class FabricObjectAncestryMixin { fork: [], otherFork: [other, ...otherAncestors.slice(0, j)], common: [this, ...ancestors], - } as AncestryComparison; + } as AncestryComparison; } if (ancestor === otherAncestors[j]) { return { fork: [this, ...ancestors.slice(0, i)], otherFork: [other, ...otherAncestors.slice(0, j)], common: ancestors.slice(i), - } as AncestryComparison; + } as AncestryComparison; } } } @@ -131,7 +138,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors], otherFork: [other, ...otherAncestors], common: [], - } as AncestryComparison; + } as AncestryComparison; } /** @@ -165,10 +172,10 @@ export class FabricObjectAncestryMixin { if (!ancestorData) { return undefined; } - if (ancestorData.fork.includes(other)) { + if (ancestorData.fork.includes(other as any)) { return true; } - if (ancestorData.otherFork.includes(this)) { + if (ancestorData.otherFork.includes(this as any)) { return false; } const firstCommonAncestor = ancestorData.common[0]; From 4c0c130c629e9128de771cde224945728cd479e3 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 07:40:51 +0200 Subject: [PATCH 25/58] Update object_ancestry.mixin.ts --- src/mixins/object_ancestry.mixin.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 400c1837104..6e82c1b39fc 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -7,6 +7,9 @@ type TAncestor = FabricObject | StaticCanvas; type TStrictAncestor = Group | StaticCanvas; +/** + * Strict: only ancestors that are objects (without canvas) + */ export type Ancestors = Strict extends true ? [FabricObject | Group] | [FabricObject | Group, ...Group[]] | Group[] : From 3803c0705918157ed5a39d921212d3f374287353 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Tue, 1 Nov 2022 07:41:49 +0200 Subject: [PATCH 26/58] naming --- src/mixins/object_ancestry.mixin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 6e82c1b39fc..10a9f0ccca7 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -5,7 +5,7 @@ import type { StaticCanvas } from '../__types__'; type TAncestor = FabricObject | StaticCanvas; -type TStrictAncestor = Group | StaticCanvas; +type TContainer = Group | StaticCanvas; /** * Strict: only ancestors that are objects (without canvas) @@ -187,10 +187,10 @@ export class FabricObjectAncestryMixin { } const headOfFork = ancestorData.fork.pop() as FabricObject, headOfOtherFork = ancestorData.otherFork.pop() as FabricObject, - thisIndex = (firstCommonAncestor as TStrictAncestor)._objects.indexOf( + thisIndex = (firstCommonAncestor as TContainer)._objects.indexOf( headOfFork ), - otherIndex = (firstCommonAncestor as TStrictAncestor)._objects.indexOf( + otherIndex = (firstCommonAncestor as TContainer)._objects.indexOf( headOfOtherFork ); return thisIndex > -1 && thisIndex > otherIndex; From c64bcd097f5ed9be7e5cf1f45eec6f43bf2fa98b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 23 Nov 2022 09:42:58 +0200 Subject: [PATCH 27/58] fix proto chain --- src/mixins/object_ancestry.mixin.ts | 39 ------------------------- src/mixins/object_stacking.mixin.ts | 45 +++++++++++++++++++++++++++-- src/shapes/fabricObject.class.ts | 2 -- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 10a9f0ccca7..c03844cc31b 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -5,8 +5,6 @@ import type { StaticCanvas } from '../__types__'; type TAncestor = FabricObject | StaticCanvas; -type TContainer = Group | StaticCanvas; - /** * Strict: only ancestors that are objects (without canvas) */ @@ -158,41 +156,4 @@ export class FabricObjectAncestryMixin { const commonAncestors = this.findCommonAncestors(other, strict); return commonAncestors && !!commonAncestors.common.length; } - - /** - * - * @param {FabricObject} other object to compare against - * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` - */ - isInFrontOf( - this: FabricObject & this, - other: FabricObject & this - ): boolean | undefined { - if (this === other) { - return undefined; - } - const ancestorData = this.findCommonAncestors(other); - if (!ancestorData) { - return undefined; - } - if (ancestorData.fork.includes(other as any)) { - return true; - } - if (ancestorData.otherFork.includes(this as any)) { - return false; - } - const firstCommonAncestor = ancestorData.common[0]; - if (!firstCommonAncestor) { - return undefined; - } - const headOfFork = ancestorData.fork.pop() as FabricObject, - headOfOtherFork = ancestorData.otherFork.pop() as FabricObject, - thisIndex = (firstCommonAncestor as TContainer)._objects.indexOf( - headOfFork - ), - otherIndex = (firstCommonAncestor as TContainer)._objects.indexOf( - headOfOtherFork - ); - return thisIndex > -1 && thisIndex > otherIndex; - } } diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts index ff99a3aa2d9..d2c2770ea25 100644 --- a/src/mixins/object_stacking.mixin.ts +++ b/src/mixins/object_stacking.mixin.ts @@ -1,9 +1,13 @@ // @ts-nocheck - import { fabric } from '../../HEADER'; import type { FabricObject } from '../shapes/fabricObject.class'; +import type { Group } from '../shapes/group.class'; +import type { StaticCanvas } from '../__types__'; +import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; + +type TContainer = Group | StaticCanvas; -export class FabricObjectObjectStackingMixin { +export class FabricObjectObjectStackingMixin extends FabricObjectAncestryMixin { /** * Moves an object to the bottom of the stack of drawn objects * @return {FabricObject} thisArg @@ -84,4 +88,41 @@ export class FabricObjectObjectStackingMixin { } return this; } + + /** + * + * @param {FabricObject} other object to compare against + * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` + */ + isInFrontOf( + this: FabricObject & this, + other: FabricObject & this + ): boolean | undefined { + if (this === other) { + return undefined; + } + const ancestorData = this.findCommonAncestors(other); + if (!ancestorData) { + return undefined; + } + if (ancestorData.fork.includes(other as any)) { + return true; + } + if (ancestorData.otherFork.includes(this as any)) { + return false; + } + const firstCommonAncestor = ancestorData.common[0]; + if (!firstCommonAncestor) { + return undefined; + } + const headOfFork = ancestorData.fork.pop() as FabricObject, + headOfOtherFork = ancestorData.otherFork.pop() as FabricObject, + thisIndex = (firstCommonAncestor as TContainer)._objects.indexOf( + headOfFork + ), + otherIndex = (firstCommonAncestor as TContainer)._objects.indexOf( + headOfOtherFork + ); + return thisIndex > -1 && thisIndex > otherIndex; + } } diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 5a13cdd5b30..774a27ccd89 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,6 +1,5 @@ import { fabric } from '../../HEADER'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; -import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; @@ -10,7 +9,6 @@ import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import applyMixins(InteractiveFabricObject, [ - FabricObjectAncestryMixin, FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, From cb83bc16e4879b7bf3b4536e5a163fd8bc927e54 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 08:05:36 +0200 Subject: [PATCH 28/58] Update __types__.ts --- src/__types__.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/__types__.ts b/src/__types__.ts index 28323dc80ab..58eb316b9ed 100644 --- a/src/__types__.ts +++ b/src/__types__.ts @@ -1,6 +1,7 @@ -import { CanvasEvents, ModifierKey } from './EventTypeDefs'; +import { CanvasEvents, ModifierKey, StaticCanvasEvents } from './EventTypeDefs'; import type { Observable } from './mixins/observable.mixin'; import type { Point } from './point.class'; +import type { FabricObject } from './shapes/fabricObject.class'; import { TMat2D } from './typedefs'; /** @@ -10,7 +11,8 @@ export type Canvas = StaticCanvas & { altActionKey: ModifierKey; uniScaleKey: ModifierKey; uniformScaling: boolean; -} & Record; +} & Record & + Observable; export type StaticCanvas = Record & { getZoom(): number; viewportTransform: TMat2D; @@ -19,4 +21,5 @@ export type StaticCanvas = Record & { br: Point; }; getRetinaScaling(): number; -} & Observable; + _objects: FabricObject; +} & Observable; From ddf366c28d91ab2bcbccda83b34ccaeecfe3a3d8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:07:39 +0200 Subject: [PATCH 29/58] Update object_straightening.mixin.ts --- src/mixins/object_straightening.mixin.ts | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts index 7cf47a3b889..8cf408b9fb7 100644 --- a/src/mixins/object_straightening.mixin.ts +++ b/src/mixins/object_straightening.mixin.ts @@ -1,10 +1,16 @@ -// @ts-nocheck - +import { noop } from '../constants'; import type { FabricObject } from '../shapes/fabricObject.class'; import { TDegree } from '../typedefs'; import { animate } from '../util/animate'; export class FabricObjectObjectStraighteningMixin { + /** + * Animation duration (in ms) for fx* methods + * @type Number + * @default + */ + FX_DURATION = 500; + /** * @private * @return {Number} angle value @@ -36,19 +42,16 @@ export class FabricObjectObjectStraighteningMixin { fxStraighten( this: FabricObject & this, callbacks: { - onChange(value: TDegree): any; - onComplete(): any; - } + onChange?(value: TDegree): any; + onComplete?(): any; + } = {} ) { - callbacks = callbacks || {}; - - const empty = function () {}, - onComplete = callbacks.onComplete || empty, - onChange = callbacks.onChange || empty; + const onComplete = callbacks.onComplete || noop, + onChange = callbacks.onChange || noop; return animate({ target: this, - startValue: this.get('angle'), + startValue: this.angle, endValue: this._getAngleValueForStraighten(), duration: this.FX_DURATION, onChange: (value: TDegree) => { From 805394453df3373ddc2f9d255e4f9f43b0e662da Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:08:01 +0200 Subject: [PATCH 30/58] imports --- index.js | 10 ++++------ src/shapes/object.class.ts | 4 +--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 113583b4193..c8807d5a5e5 100644 --- a/index.js +++ b/index.js @@ -17,12 +17,10 @@ import './src/mixins/canvas_grouping.mixin'; // optional interaction import './src/mixins/canvas_dataurl_exporter.mixin'; import './src/mixins/canvas_serialization.mixin'; // optiona serialization import './src/mixins/canvas_gestures.mixin'; // optional gesturesv -import './src/mixins/object_interactivity.mixin'; // optional interaction -import './src/mixins/object_ancestry.mixin'; -import './src/mixins/object_stacking.mixin'; -import './src/mixins/object.svg_export'; -import './src/mixins/stateful.mixin'; -import './src/mixins/animation.mixin'; // optional animation +import './src/mixins/stateful.mixin'; // will die soon +import './src/mixins/canvas_animation.mixin'; // optional animation +import './src/mixins/canvas_straightening.mixin'; // optional animation +import './src/shapes/fabricObject.class'; import './src/shapes/line.class'; import './src/shapes/circle.class'; import './src/shapes/triangle.class'; diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index 12b8fe6ddb7..8f339fcdf31 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -3,6 +3,7 @@ import { fabric } from '../../HEADER'; import { cache } from '../cache'; import { config } from '../config'; import { ALIASING_LIMIT, iMatrix, VERSION } from '../constants'; +import { ObjectEvents } from '../EventTypeDefs'; import { ObjectGeometry } from '../mixins/object_geometry.mixin'; import { Point } from '../point.class'; import { Shadow } from '../shadow.class'; @@ -16,9 +17,6 @@ import { qrDecompose, transformPoint } from '../util/misc/matrix'; import { enlivenObjectEnlivables } from '../util/misc/objectEnlive'; import { pick } from '../util/misc/pick'; import { toFixed } from '../util/misc/toFixed'; -import { Shadow } from '../__types__'; -import { Canvas, StaticCanvas } from '../__types__'; -import { ObjectEvents } from '../EventTypeDefs'; import type { Group } from './group.class'; // temporary hack for unfinished migration From c6a9d56498dcbbc05e9635db8aa73952c7cc78f8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:08:48 +0200 Subject: [PATCH 31/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index bc043ce5c0c..bf88c5ea789 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -21,7 +21,7 @@ applyMixins(InteractiveFabricObject, [ // FabricObjectObjectStraighteningMixin, // FabricObjectSVGExportMixin,FabricObjectObjectAnimationMixin {} -export { InteractiveFabricObject as FabricObject }; export { fabricObjectDefaultValues } from './object.class'; +export { InteractiveFabricObject as FabricObject }; fabric.Object = InteractiveFabricObject; From 9bb922a2e4f83dc0af4edbf29b7b1243b8cad7a3 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:26:56 +0200 Subject: [PATCH 32/58] types --- src/mixins/object_ancestry.mixin.ts | 4 ++-- src/mixins/object_straightening.mixin.ts | 2 +- src/shapes/fabricObject.class.ts | 18 ++++++++++-------- src/shapes/object.class.ts | 2 -- src/util/misc/planeChange.ts | 3 ++- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index c03844cc31b..96e8afe1937 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,7 +1,7 @@ import { fabric } from '../../HEADER'; import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; -import type { StaticCanvas } from '../__types__'; +import type { Canvas, StaticCanvas } from '../__types__'; type TAncestor = FabricObject | StaticCanvas; @@ -34,7 +34,7 @@ export type AncestryComparison = { export class FabricObjectAncestryMixin { group?: Group; - canvas?: StaticCanvas; + canvas?: StaticCanvas | Canvas; /** * Checks if object is descendant of target diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts index 8cf408b9fb7..11837fb2935 100644 --- a/src/mixins/object_straightening.mixin.ts +++ b/src/mixins/object_straightening.mixin.ts @@ -29,7 +29,7 @@ export class FabricObjectObjectStraighteningMixin { * @chainable */ straighten(this: FabricObject & this) { - return this.rotate(this._getAngleValueForStraighten()); + this.rotate(this._getAngleValueForStraighten()); } /** diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index bf88c5ea789..018b46ede8e 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -8,20 +8,22 @@ import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import -applyMixins(InteractiveFabricObject, [ +const FabricObject = applyMixins(InteractiveFabricObject, [ FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, FabricObjectObjectAnimationMixin, ]); -// export interface InteractiveFabricObject -// extends FabricObjectAncestryMixin, -// FabricObjectObjectStackingMixin, -// FabricObjectObjectStraighteningMixin, -// FabricObjectSVGExportMixin,FabricObjectObjectAnimationMixin {} +// TS should merge all these types on top of FabricObject but it doesn't +export interface FabricObject + extends InteractiveFabricObject, + FabricObjectObjectStackingMixin, + FabricObjectObjectStraighteningMixin, + FabricObjectSVGExportMixin, + FabricObjectObjectAnimationMixin {} export { fabricObjectDefaultValues } from './object.class'; -export { InteractiveFabricObject as FabricObject }; +export { FabricObject }; -fabric.Object = InteractiveFabricObject; +fabric.Object = FabricObject; diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index 8f339fcdf31..79309e0dc58 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -1861,8 +1861,6 @@ export class FabricObject< if (shouldCenterOrigin) { this._resetOrigin(); } - - return this; } /** diff --git a/src/util/misc/planeChange.ts b/src/util/misc/planeChange.ts index d47cedd5708..90d886a71b2 100644 --- a/src/util/misc/planeChange.ts +++ b/src/util/misc/planeChange.ts @@ -2,6 +2,7 @@ import { iMatrix } from '../../constants'; import type { Point } from '../../point.class'; import type { FabricObject } from '../../shapes/fabricObject.class'; import type { TMat2D } from '../../typedefs'; +import { StaticCanvas } from '../../__types__'; import { invertTransform, multiplyTransformMatrices } from './matrix'; import { applyTransformToObject } from './objectTransforms'; @@ -71,7 +72,7 @@ export const sendPointToPlane = ( */ export const transformPointRelativeToCanvas = ( point: Point, - canvas: any, + canvas: StaticCanvas, relationBefore: ObjectRelation, relationAfter: ObjectRelation ): Point => { From 8fd4b8098b6eb7433b6504746d1b76a19c48f363 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:28:55 +0200 Subject: [PATCH 33/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 018b46ede8e..5bc1da34bd1 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -15,7 +15,6 @@ const FabricObject = applyMixins(InteractiveFabricObject, [ FabricObjectObjectAnimationMixin, ]); -// TS should merge all these types on top of FabricObject but it doesn't export interface FabricObject extends InteractiveFabricObject, FabricObjectObjectStackingMixin, From ac5fdf40755e545b8c76e2f3e3499b3d80719539 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 11:36:13 +0200 Subject: [PATCH 34/58] types --- src/mixins/object_ancestry.mixin.ts | 8 ++++---- src/mixins/object_animation.mixin.ts | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 96e8afe1937..bc3e736ae85 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -3,7 +3,7 @@ import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; import type { Canvas, StaticCanvas } from '../__types__'; -type TAncestor = FabricObject | StaticCanvas; +type TAncestor = FabricObject | Canvas | StaticCanvas; /** * Strict: only ancestors that are objects (without canvas) @@ -11,11 +11,11 @@ type TAncestor = FabricObject | StaticCanvas; export type Ancestors = Strict extends true ? [FabricObject | Group] | [FabricObject | Group, ...Group[]] | Group[] : - | [FabricObject | Group | StaticCanvas] - | [FabricObject | Group, StaticCanvas] + | [FabricObject | Group | Canvas | StaticCanvas] + | [FabricObject | Group, Canvas | StaticCanvas] | [FabricObject, ...Group[]] | Group[] - | [FabricObject | Group, ...Group[], StaticCanvas]; + | [FabricObject | Group, ...Group[], Canvas | StaticCanvas]; export type AncestryComparison = { /** diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index d8936f00d69..c82bdd0e142 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,6 +1,5 @@ // @ts-nocheck -import type { FabricObject } from '../shapes/fabricObject.class'; import { animate } from '../util/animate'; import { animateColor } from '../util/animate_color'; @@ -9,7 +8,6 @@ export class FabricObjectObjectAnimationMixin { * Animates object's properties * @param {String|Object} property Property to animate (if string) or properties to animate (if object) * @param {Number|Object} value Value to animate property to (if string was given first) or options object - * @return {FabricObject} thisArg * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation} * @return {AnimationContext | AnimationContext[]} animation context (or an array if passed multiple properties) * From 05f8a327635b2eff32d33bc6914d93a5ce9d925e Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 12:10:29 +0200 Subject: [PATCH 35/58] TS/es6 --- src/mixins/object.svg_export.ts | 142 ++++++++++++++++---------------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index 3829566ee8e..ee1765d96f1 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -1,18 +1,18 @@ // @ts-nocheck - import { Color } from '../color'; import { config } from '../config'; -import { FabricObject } from '../shapes/fabricObject.class'; import { matrixToSVG } from '../util/misc/svgParsing'; import { toFixed } from '../util/misc/toFixed'; +type SVGReviver = (markup: string) => string; + /* _TO_SVG_START_ */ -function getSvgColorString(prop, value) { +function getSvgColorString(prop: string, value?: any) { if (!value) { - return prop + ': none; '; + return `${prop}: none; `; } else if (value.toLive) { - return prop + ': url(#SVGID_' + value.id + '); '; + return `${prop}: url(#SVGID_${value.id}); `; } else { const color = new Color(value), opacity = color.getAlpha(); @@ -33,7 +33,7 @@ export class FabricObjectSVGExportMixin { * @param {Boolean} skipShadow a boolean to skip shadow filter output * @return {String} */ - getSvgStyles(skipShadow) { + getSvgStyles(skipShadow?: boolean) { const fillRule = this.fillRule ? this.fillRule : 'nonzero', strokeWidth = this.strokeWidth ? this.strokeWidth : '0', strokeDashArray = this.strokeDashArray @@ -87,36 +87,30 @@ export class FabricObjectSVGExportMixin { * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the style. * @return {String} */ - getSvgSpanStyles(style, useWhiteSpace) { - const term = '; '; - var fontFamily = style.fontFamily - ? 'font-family: ' + - (style.fontFamily.indexOf("'") === -1 && - style.fontFamily.indexOf('"') === -1 - ? "'" + style.fontFamily + "'" - : style.fontFamily) + - term - : ''; - var strokeWidth = style.strokeWidth - ? 'stroke-width: ' + style.strokeWidth + term + getSvgSpanStyles(style, useWhiteSpace?: boolean) { + const term = '; ', + fontFamily = style.fontFamily + ? `font-family: ${ + style.fontFamily.indexOf("'") === -1 && + style.fontFamily.indexOf('"') === -1 + ? `'${style.fontFamily}'` + : style.fontFamily + }${term}` : '', - fontFamily = fontFamily, - fontSize = style.fontSize - ? 'font-size: ' + style.fontSize + 'px' + term + strokeWidth = style.strokeWidth + ? `stroke-width: ${style.strokeWidth}${term}` : '', + fontSize = style.fontSize ? `font-size: ${style.fontSize}px${term}` : '', fontStyle = style.fontStyle - ? 'font-style: ' + style.fontStyle + term + ? `font-style: ${style.fontStyle}${term}` : '', fontWeight = style.fontWeight - ? 'font-weight: ' + style.fontWeight + term + ? `font-weight: ${style.fontWeight}${term}` : '', fill = style.fill ? getSvgColorString('fill', style.fill) : '', stroke = style.stroke ? getSvgColorString('stroke', style.stroke) : '', textDecoration = this.getSvgTextDecoration(style), - deltaY = style.deltaY ? 'baseline-shift: ' + -style.deltaY + '; ' : ''; - if (textDecoration) { - textDecoration = 'text-decoration: ' + textDecoration + term; - } + deltaY = style.deltaY ? `baseline-shift: ${-style.deltaY}; ` : ''; return [ stroke, @@ -125,7 +119,9 @@ export class FabricObjectSVGExportMixin { fontSize, fontStyle, fontWeight, - textDecoration, + textDecoration + ? `text-decoration: ${textDecoration}${term}` + : textDecoration, fill, deltaY, useWhiteSpace ? 'white-space: pre; ' : '', @@ -139,9 +135,7 @@ export class FabricObjectSVGExportMixin { */ getSvgTextDecoration(style) { return ['overline', 'underline', 'line-through'] - .filter(function (decoration) { - return style[decoration.replace('-', '')]; - }) + .filter((decoration) => style[decoration.replace('-', '')]) .join(' '); } @@ -150,7 +144,7 @@ export class FabricObjectSVGExportMixin { * @return {String} */ getSvgFilter() { - return this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : ''; + return this.shadow ? `filter: url(#SVGID_${this.shadow.id});` : ''; } /** @@ -159,10 +153,8 @@ export class FabricObjectSVGExportMixin { */ getSvgCommons() { return [ - this.id ? 'id="' + this.id + '" ' : '', - this.clipPath - ? 'clip-path="url(#' + this.clipPath.clipPathId + ')" ' - : '', + this.id ? `id="${this.id}" ` : '', + this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : '', ].join(''); } @@ -171,13 +163,13 @@ export class FabricObjectSVGExportMixin { * @param {Boolean} use the full transform or the single object one. * @return {String} */ - getSvgTransform(full, additionalTransform) { + getSvgTransform(full?: boolean, additionalTransform = '') { const transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(), svgTransform = `transform="${matrixToSVG(transform)}`; - return `${svgTransform + (additionalTransform || '')}" `; + return `${svgTransform}${additionalTransform}" `; } - _setSVGBg(textBgRects) { + _setSVGBg(textBgRects: string[]) { if (this.backgroundColor) { const NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; textBgRects.push( @@ -198,25 +190,25 @@ export class FabricObjectSVGExportMixin { /** * Returns svg representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. + * @param {SVGReviver} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ - toSVG(reviver) { + toSVG(reviver?: SVGReviver) { return this._createBaseSVGMarkup(this._toSVG(reviver), { - reviver: reviver, + reviver, }); } /** * Returns svg clipPath representation of an instance - * @param {Function} [reviver] Method for further parsing of svg representation. + * @param {SVGReviver} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ - toClipPathSVG(reviver) { + toClipPathSVG(reviver?: SVGReviver) { return ( '\t' + this._createBaseClipPathSVGMarkup(this._toSVG(reviver), { - reviver: reviver, + reviver, }) ); } @@ -224,11 +216,14 @@ export class FabricObjectSVGExportMixin { /** * @private */ - _createBaseClipPathSVGMarkup(objectMarkup, options) { - options = options || {}; - const reviver = options.reviver, - additionalTransform = options.additionalTransform || '', - commonPieces = [ + _createBaseClipPathSVGMarkup( + objectMarkup: string[], + { + reviver, + additionalTransform = '', + }: { reviver?: SVGReviver; additionalTransform?: string } = {} + ) { + const commonPieces = [ this.getSvgTransform(true, additionalTransform), this.getSvgCommons(), ].join(''), @@ -241,14 +236,22 @@ export class FabricObjectSVGExportMixin { /** * @private */ - _createBaseSVGMarkup(objectMarkup, options) { - options = options || {}; - let noStyle = options.noStyle, - reviver = options.reviver, - styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ', - shadowInfo = options.withShadow - ? 'style="' + this.getSvgFilter() + '" ' - : '', + _createBaseSVGMarkup( + objectMarkup: string[], + { + noStyle, + reviver, + withShadow, + additionalTransform, + }: { + noStyle?: boolean; + reviver?: SVGReviver; + withShadow?: boolean; + additionalTransform?: string; + } = {} + ) { + const styleInfo = noStyle ? '' : `style="${this.getSvgStyles()}" `, + shadowInfo = withShadow ? `style="${this.getSvgFilter()}" ` : '', clipPath = this.clipPath, vectorEffect = this.strokeUniform ? 'vector-effect="non-scaling-stroke" ' @@ -257,20 +260,15 @@ export class FabricObjectSVGExportMixin { stroke = this.stroke, fill = this.fill, shadow = this.shadow, - commonPieces, markup = [], - clipPathMarkup, // insert commons in the markup, style and svgCommons - index = objectMarkup.indexOf('COMMON_PARTS'), - additionalTransform = options.additionalTransform; + index = objectMarkup.indexOf('COMMON_PARTS'); + let clipPathMarkup; if (clipPath) { - clipPath.clipPathId = 'CLIPPATH_' + FabricObject.__uid++; - clipPathMarkup = - '\n' + - clipPath.toClipPathSVG(reviver) + - '\n'; + clipPath.clipPathId = `CLIPPATH_${FabricObject.__uid++}`; + clipPathMarkup = `\n${clipPath.toClipPathSVG(reviver)}\n`; } if (absoluteClipPath) { markup.push('\n'); @@ -281,12 +279,12 @@ export class FabricObjectSVGExportMixin { !absoluteClipPath ? shadowInfo + this.getSvgCommons() : '', ' >\n' ); - commonPieces = [ + const commonPieces = [ styleInfo, vectorEffect, noStyle ? '' : this.addPaintOrder(), ' ', - additionalTransform ? 'transform="' + additionalTransform + '" ' : '', + additionalTransform ? `transform="${additionalTransform}" ` : '', ].join(''); objectMarkup[index] = commonPieces; if (fill && fill.toLive) { @@ -309,7 +307,7 @@ export class FabricObjectSVGExportMixin { addPaintOrder() { return this.paintFirst !== 'fill' - ? ' paint-order="' + this.paintFirst + '" ' + ? ` paint-order="${this.paintFirst}" ` : ''; } } From 184e36096544beabf105275845c745370ff2216b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 12:41:13 +0200 Subject: [PATCH 36/58] Canvas type --- src/util/fireEvent.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util/fireEvent.ts b/src/util/fireEvent.ts index 1b204254494..90c662beb64 100644 --- a/src/util/fireEvent.ts +++ b/src/util/fireEvent.ts @@ -1,4 +1,5 @@ import { TModificationEvents, BasicTransformEvent } from '../EventTypeDefs'; +import { Canvas } from '../__types__'; export const fireEvent = ( eventName: TModificationEvents, @@ -7,6 +8,9 @@ export const fireEvent = ( const { transform: { target }, } = options; - target.canvas?.fire(`object:${eventName}`, { ...options, target }); + (target.canvas as Canvas)?.fire(`object:${eventName}`, { + ...options, + target, + }); target.fire(eventName, options); }; From ef6c29dfcaa7a9ed5b01139044ac73883387e047 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 12:41:27 +0200 Subject: [PATCH 37/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 5bc1da34bd1..4d7604200c7 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,4 +1,5 @@ import { fabric } from '../../HEADER'; +import { ObjectEvents } from '../EventTypeDefs'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; @@ -8,21 +9,21 @@ import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import -const FabricObject = applyMixins(InteractiveFabricObject, [ +export class FabricObject extends applyMixins(InteractiveFabricObject, [ FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, FabricObjectObjectAnimationMixin, -]); +]) {} -export interface FabricObject - extends InteractiveFabricObject, +// @ts-expect-error applyMixins is not generic so EventSpec is different +export interface FabricObject + extends InteractiveFabricObject, FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, FabricObjectObjectAnimationMixin {} export { fabricObjectDefaultValues } from './object.class'; -export { FabricObject }; fabric.Object = FabricObject; From a972e95250373a68ce075623cb421e1d58ab6481 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 12:46:23 +0200 Subject: [PATCH 38/58] Update applyMixins.ts --- src/util/applyMixins.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/applyMixins.ts b/src/util/applyMixins.ts index d7d2d122ff1..ecae0dc7aab 100644 --- a/src/util/applyMixins.ts +++ b/src/util/applyMixins.ts @@ -18,5 +18,5 @@ export function applyMixins( ); }); }); - return derivedCtor as T & S; + return derivedCtor; } From d1789309cdbffbd7b1250c78c3c6ea2c43c8f218 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 13:06:16 +0200 Subject: [PATCH 39/58] try fix non applied mixin --- src/mixins/object_ancestry.mixin.ts | 26 +++++++++++++------------- src/shapes/fabricObject.class.ts | 2 ++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index bc3e736ae85..42655592b7d 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,21 +1,23 @@ import { fabric } from '../../HEADER'; -import type { FabricObject } from '../shapes/fabricObject.class'; import type { Group } from '../shapes/group.class'; import type { Canvas, StaticCanvas } from '../__types__'; -type TAncestor = FabricObject | Canvas | StaticCanvas; +type TAncestor = FabricObjectAncestryMixin | Canvas | StaticCanvas; /** * Strict: only ancestors that are objects (without canvas) */ export type Ancestors = Strict extends true - ? [FabricObject | Group] | [FabricObject | Group, ...Group[]] | Group[] + ? + | [FabricObjectAncestryMixin | Group] + | [FabricObjectAncestryMixin | Group, ...Group[]] + | Group[] : - | [FabricObject | Group | Canvas | StaticCanvas] - | [FabricObject | Group, Canvas | StaticCanvas] - | [FabricObject, ...Group[]] + | [FabricObjectAncestryMixin | Group | Canvas | StaticCanvas] + | [FabricObjectAncestryMixin | Group, Canvas | StaticCanvas] + | [FabricObjectAncestryMixin, ...Group[]] | Group[] - | [FabricObject | Group, ...Group[], Canvas | StaticCanvas]; + | [FabricObjectAncestryMixin | Group, ...Group[], Canvas | StaticCanvas]; export type AncestryComparison = { /** @@ -74,13 +76,12 @@ export class FabricObjectAncestryMixin { /** * Compare ancestors * - * @param {FabricObject} other + * @param {FabricObjectAncestryMixin} other * @param {boolean} [strict] finds only ancestors that are objects (without canvas) * @returns {AncestryComparison} an object that represent the ancestry situation. */ findCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, + other: FabricObjectAncestryMixin, strict?: T ): AncestryComparison { if (this === other) { @@ -144,13 +145,12 @@ export class FabricObjectAncestryMixin { /** * - * @param {FabricObject} other + * @param {FabricObjectAncestryMixin} other * @param {boolean} [strict] checks only ancestors that are objects (without canvas) * @returns {boolean} */ hasCommonAncestors( - this: FabricObject & this, - other: FabricObject & this, + other: FabricObjectAncestryMixin, strict?: boolean ): boolean { const commonAncestors = this.findCommonAncestors(other, strict); diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 4d7604200c7..c92d457555c 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,6 +1,7 @@ import { fabric } from '../../HEADER'; import { ObjectEvents } from '../EventTypeDefs'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; +import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; @@ -10,6 +11,7 @@ import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import export class FabricObject extends applyMixins(InteractiveFabricObject, [ + FabricObjectAncestryMixin, FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, From 39c056ba21fd06b7bd6c9c75886815a50bbcf09a Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 13:12:19 +0200 Subject: [PATCH 40/58] Update index.js --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index c8807d5a5e5..cc7ac36bcce 100644 --- a/index.js +++ b/index.js @@ -15,12 +15,12 @@ import './src/canvas.class'; // optional interaction import './src/mixins/canvas_events.mixin'; // optional interaction import './src/mixins/canvas_grouping.mixin'; // optional interaction import './src/mixins/canvas_dataurl_exporter.mixin'; -import './src/mixins/canvas_serialization.mixin'; // optiona serialization -import './src/mixins/canvas_gestures.mixin'; // optional gesturesv -import './src/mixins/stateful.mixin'; // will die soon +import './src/mixins/canvas_serialization.mixin'; // optional serialization +import './src/mixins/canvas_gestures.mixin'; // optional gestures import './src/mixins/canvas_animation.mixin'; // optional animation import './src/mixins/canvas_straightening.mixin'; // optional animation import './src/shapes/fabricObject.class'; +import './src/mixins/stateful.mixin'; // will die soon import './src/shapes/line.class'; import './src/shapes/circle.class'; import './src/shapes/triangle.class'; @@ -32,7 +32,7 @@ import './src/shapes/path.class'; import './src/shapes/group.class'; import './src/shapes/active_selection.class'; // optional interaction import './src/shapes/image.class'; -import './src/mixins/object_straightening.mixin'; // optional objectstraightening +import './src/mixins/object_straightening.mixin'; // optional object straightening import './src/filters/WebGLProbe'; // optional image_filters import './src/filters/base_filter.class'; // optional image_filters import './src/filters/colormatrix_filter.class'; // optional image_filters From e11045756af5ab71e77aa03e6829be8531a856a7 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 13:17:50 +0200 Subject: [PATCH 41/58] fix imports --- src/mixins/object.svg_export.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index ee1765d96f1..539e18b60d1 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -1,6 +1,7 @@ // @ts-nocheck import { Color } from '../color'; import { config } from '../config'; +import { FabricObject } from '../shapes/object.class'; import { matrixToSVG } from '../util/misc/svgParsing'; import { toFixed } from '../util/misc/toFixed'; From c8dc7fec0b725cf6493677dd8327daa441b9f990 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 13:34:01 +0200 Subject: [PATCH 42/58] Update object.js --- test/unit/object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/object.js b/test/unit/object.js index 1025b07089a..84bf94ed4ac 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -305,7 +305,7 @@ var cObj = new fabric.Object(); assert.ok(typeof cObj.rotate === 'function'); assert.equal(cObj.get('angle'), 0); - assert.equal(cObj.rotate(45), cObj, 'chainable'); + cObj.rotate(45); assert.equal(cObj.get('angle'), 45); }); From 3f4e2a80263e2c12086b27d5b0f38f0eb3244192 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 13:37:35 +0200 Subject: [PATCH 43/58] fix import --- src/mixins/object.svg_export.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index 539e18b60d1..92b90195250 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { Color } from '../color'; import { config } from '../config'; -import { FabricObject } from '../shapes/object.class'; +import { FabricObject } from '../shapes/fabricObject.class'; import { matrixToSVG } from '../util/misc/svgParsing'; import { toFixed } from '../util/misc/toFixed'; From c80efe1ba6c546b2aa383c66f8beb424144b4351 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 17:48:50 +0200 Subject: [PATCH 44/58] BREAKING: rm object stacking methods --- src/__types__.ts | 2 +- src/mixins/object_ancestry.mixin.ts | 69 +++++++++++---- src/mixins/object_stacking.mixin.ts | 128 ---------------------------- src/shapes/fabricObject.class.ts | 5 -- src/shapes/object.class.ts | 4 +- 5 files changed, 54 insertions(+), 154 deletions(-) delete mode 100644 src/mixins/object_stacking.mixin.ts diff --git a/src/__types__.ts b/src/__types__.ts index 58eb316b9ed..19ac0df3293 100644 --- a/src/__types__.ts +++ b/src/__types__.ts @@ -21,5 +21,5 @@ export type StaticCanvas = Record & { br: Point; }; getRetinaScaling(): number; - _objects: FabricObject; + _objects: FabricObject[]; } & Observable; diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 42655592b7d..23042f7aaba 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -1,8 +1,11 @@ import { fabric } from '../../HEADER'; +import { ObjectEvents } from '../EventTypeDefs'; import type { Group } from '../shapes/group.class'; import type { Canvas, StaticCanvas } from '../__types__'; +import { ObjectGeometry } from './object_geometry.mixin'; type TAncestor = FabricObjectAncestryMixin | Canvas | StaticCanvas; +type TCollection = Group | Canvas | StaticCanvas; /** * Strict: only ancestors that are objects (without canvas) @@ -34,10 +37,9 @@ export type AncestryComparison = { otherFork: Ancestors; }; -export class FabricObjectAncestryMixin { - group?: Group; - canvas?: StaticCanvas | Canvas; - +export class FabricObjectAncestryMixin< + EventSpec extends ObjectEvents = ObjectEvents +> extends ObjectGeometry { /** * Checks if object is descendant of target * Should be used instead of @link {Collection.contains} for performance reasons @@ -80,16 +82,16 @@ export class FabricObjectAncestryMixin { * @param {boolean} [strict] finds only ancestors that are objects (without canvas) * @returns {AncestryComparison} an object that represent the ancestry situation. */ - findCommonAncestors( - other: FabricObjectAncestryMixin, - strict?: T - ): AncestryComparison { + findCommonAncestors( + other: T, + strict?: S + ): AncestryComparison { if (this === other) { return { fork: [], otherFork: [], common: [this, ...this.getAncestors(strict)], - } as AncestryComparison; + } as AncestryComparison; } const ancestors = this.getAncestors(strict); const otherAncestors = other.getAncestors(strict); @@ -106,7 +108,7 @@ export class FabricObjectAncestryMixin { ...otherAncestors.slice(0, otherAncestors.length - 1), ], common: [this], - } as AncestryComparison; + } as AncestryComparison; } // compare ancestors for (let i = 0, ancestor; i < ancestors.length; i++) { @@ -116,7 +118,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors.slice(0, i)], otherFork: [], common: ancestors.slice(i), - } as AncestryComparison; + } as AncestryComparison; } for (let j = 0; j < otherAncestors.length; j++) { if (this === otherAncestors[j]) { @@ -124,14 +126,14 @@ export class FabricObjectAncestryMixin { fork: [], otherFork: [other, ...otherAncestors.slice(0, j)], common: [this, ...ancestors], - } as AncestryComparison; + } as AncestryComparison; } if (ancestor === otherAncestors[j]) { return { fork: [this, ...ancestors.slice(0, i)], otherFork: [other, ...otherAncestors.slice(0, j)], common: ancestors.slice(i), - } as AncestryComparison; + } as AncestryComparison; } } } @@ -140,7 +142,7 @@ export class FabricObjectAncestryMixin { fork: [this, ...ancestors], otherFork: [other, ...otherAncestors], common: [], - } as AncestryComparison; + } as AncestryComparison; } /** @@ -149,11 +151,42 @@ export class FabricObjectAncestryMixin { * @param {boolean} [strict] checks only ancestors that are objects (without canvas) * @returns {boolean} */ - hasCommonAncestors( - other: FabricObjectAncestryMixin, - strict?: boolean - ): boolean { + hasCommonAncestors(other: T, strict?: boolean): boolean { const commonAncestors = this.findCommonAncestors(other, strict); return commonAncestors && !!commonAncestors.common.length; } + + /** + * + * @param {FabricObject} other object to compare against + * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` + */ + isInFrontOf(other: T): boolean | undefined { + if (this === other) { + return undefined; + } + const ancestorData = this.findCommonAncestors(other); + if (!ancestorData) { + return undefined; + } + if (ancestorData.fork.includes(other as any)) { + return true; + } + if (ancestorData.otherFork.includes(this as any)) { + return false; + } + const firstCommonAncestor = ancestorData.common[0]; + if (!firstCommonAncestor) { + return undefined; + } + const headOfFork = ancestorData.fork.pop(), + headOfOtherFork = ancestorData.otherFork.pop(), + thisIndex = (firstCommonAncestor as TCollection)._objects.indexOf( + headOfFork as any + ), + otherIndex = (firstCommonAncestor as TCollection)._objects.indexOf( + headOfOtherFork as any + ); + return thisIndex > -1 && thisIndex > otherIndex; + } } diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts deleted file mode 100644 index d2c2770ea25..00000000000 --- a/src/mixins/object_stacking.mixin.ts +++ /dev/null @@ -1,128 +0,0 @@ -// @ts-nocheck -import { fabric } from '../../HEADER'; -import type { FabricObject } from '../shapes/fabricObject.class'; -import type { Group } from '../shapes/group.class'; -import type { StaticCanvas } from '../__types__'; -import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; - -type TContainer = Group | StaticCanvas; - -export class FabricObjectObjectStackingMixin extends FabricObjectAncestryMixin { - /** - * Moves an object to the bottom of the stack of drawn objects - * @return {FabricObject} thisArg - * @chainable - */ - sendToBack() { - if (this.group) { - fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); - } else if (this.canvas) { - this.canvas.sendToBack(this); - } - return this; - } - - /** - * Moves an object to the top of the stack of drawn objects - * @return {FabricObject} thisArg - * @chainable - */ - bringToFront() { - if (this.group) { - fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); - } else if (this.canvas) { - this.canvas.bringToFront(this); - } - return this; - } - - /** - * Moves an object down in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object - * @return {FabricObject} thisArg - * @chainable - */ - sendBackwards(intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.sendBackwards.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.sendBackwards(this, intersecting); - } - return this; - } - - /** - * Moves an object up in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object - * @return {FabricObject} thisArg - * @chainable - */ - bringForward(intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.bringForward.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.bringForward(this, intersecting); - } - return this; - } - - /** - * Moves an object to specified level in stack of drawn objects - * @param {Number} index New position of object - * @return {FabricObject} thisArg - * @chainable - */ - moveTo(index) { - if (this.group && this.group.type !== 'activeSelection') { - fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); - } else if (this.canvas) { - this.canvas.moveTo(this, index); - } - return this; - } - - /** - * - * @param {FabricObject} other object to compare against - * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` - */ - isInFrontOf( - this: FabricObject & this, - other: FabricObject & this - ): boolean | undefined { - if (this === other) { - return undefined; - } - const ancestorData = this.findCommonAncestors(other); - if (!ancestorData) { - return undefined; - } - if (ancestorData.fork.includes(other as any)) { - return true; - } - if (ancestorData.otherFork.includes(this as any)) { - return false; - } - const firstCommonAncestor = ancestorData.common[0]; - if (!firstCommonAncestor) { - return undefined; - } - const headOfFork = ancestorData.fork.pop() as FabricObject, - headOfOtherFork = ancestorData.otherFork.pop() as FabricObject, - thisIndex = (firstCommonAncestor as TContainer)._objects.indexOf( - headOfFork - ), - otherIndex = (firstCommonAncestor as TContainer)._objects.indexOf( - headOfOtherFork - ); - return thisIndex > -1 && thisIndex > otherIndex; - } -} diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index c92d457555c..7ff6e055419 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,18 +1,14 @@ import { fabric } from '../../HEADER'; import { ObjectEvents } from '../EventTypeDefs'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; -import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; -import { FabricObjectObjectStackingMixin } from '../mixins/object_stacking.mixin'; import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import export class FabricObject extends applyMixins(InteractiveFabricObject, [ - FabricObjectAncestryMixin, - FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, FabricObjectObjectAnimationMixin, @@ -21,7 +17,6 @@ export class FabricObject extends applyMixins(InteractiveFabricObject, [ // @ts-expect-error applyMixins is not generic so EventSpec is different export interface FabricObject extends InteractiveFabricObject, - FabricObjectObjectStackingMixin, FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, FabricObjectObjectAnimationMixin {} diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index 79309e0dc58..d88554e21d3 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -4,7 +4,7 @@ import { cache } from '../cache'; import { config } from '../config'; import { ALIASING_LIMIT, iMatrix, VERSION } from '../constants'; import { ObjectEvents } from '../EventTypeDefs'; -import { ObjectGeometry } from '../mixins/object_geometry.mixin'; +import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; import { Point } from '../point.class'; import { Shadow } from '../shadow.class'; import type { TClassProperties, TDegree, TFiller, TSize } from '../typedefs'; @@ -54,7 +54,7 @@ type TCallSuper = (arg0: string, ...moreArgs: any[]) => any; */ export class FabricObject< EventSpec extends ObjectEvents = ObjectEvents -> extends ObjectGeometry { +> extends FabricObjectAncestryMixin { type: string; /** From efe92f7effc2f698b1135e53d0a29475708b4ea6 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 18:08:55 +0200 Subject: [PATCH 45/58] merge straightening into animation mixin --- src/mixins/object_animation.mixin.ts | 81 +++++++++++++++++++++--- src/mixins/object_straightening.mixin.ts | 67 -------------------- src/shapes/fabricObject.class.ts | 8 +-- src/shapes/object.class.ts | 5 +- 4 files changed, 76 insertions(+), 85 deletions(-) delete mode 100644 src/mixins/object_straightening.mixin.ts diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index c82bdd0e142..1c9e8e5803c 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,9 +1,22 @@ -// @ts-nocheck - +import { noop } from 'lodash'; +import { ObjectEvents } from '../EventTypeDefs'; +import { TDegree } from '../typedefs'; import { animate } from '../util/animate'; import { animateColor } from '../util/animate_color'; +import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; + +export abstract class AnimatableObject< + EventSpec extends ObjectEvents = ObjectEvents +> extends FabricObjectAncestryMixin { + /** + * Animation duration (in ms) for fx* methods + * @type Number + * @default + */ + FX_DURATION: number; + + abstract rotate(deg: TDegree): void; -export class FabricObjectObjectAnimationMixin { /** * Animates object's properties * @param {String|Object} property Property to animate (if string) or properties to animate (if object) @@ -82,7 +95,7 @@ export class FabricObjectObjectAnimationMixin { } } - const _options = { + const animationOptions = { target: this, startValue: options.from, endValue: to, @@ -119,13 +132,63 @@ export class FabricObjectObjectAnimationMixin { if (propIsColor) { return animateColor( - _options.startValue, - _options.endValue, - _options.duration, - _options + animationOptions.startValue, + animationOptions.endValue, + animationOptions.duration, + animationOptions ); } else { - return animate(_options); + return animate(animationOptions); + } + } + + /** + * @private + * @return {Number} angle value + */ + _getAngleValueForStraighten() { + const angle = this.angle % 360; + if (angle > 0) { + return Math.round((angle - 1) / 90) * 90; } + return Math.round(angle / 90) * 90; + } + + /** + * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) + */ + straighten() { + this.rotate(this._getAngleValueForStraighten()); + } + + /** + * Same as {@link straighten} but with animation + * @param {Object} callbacks Object with callback functions + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + */ + fxStraighten( + callbacks: { + onChange?(value: TDegree): any; + onComplete?(): any; + } = {} + ) { + const onComplete = callbacks.onComplete || noop, + onChange = callbacks.onChange || noop; + + return animate({ + target: this, + startValue: this.angle, + endValue: this._getAngleValueForStraighten(), + duration: this.FX_DURATION, + onChange: (value: TDegree) => { + this.rotate(value); + onChange(value); + }, + onComplete: () => { + this.setCoords(); + onComplete(); + }, + }); } } diff --git a/src/mixins/object_straightening.mixin.ts b/src/mixins/object_straightening.mixin.ts deleted file mode 100644 index 11837fb2935..00000000000 --- a/src/mixins/object_straightening.mixin.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { noop } from '../constants'; -import type { FabricObject } from '../shapes/fabricObject.class'; -import { TDegree } from '../typedefs'; -import { animate } from '../util/animate'; - -export class FabricObjectObjectStraighteningMixin { - /** - * Animation duration (in ms) for fx* methods - * @type Number - * @default - */ - FX_DURATION = 500; - - /** - * @private - * @return {Number} angle value - */ - _getAngleValueForStraighten(this: FabricObject) { - const angle = this.angle % 360; - if (angle > 0) { - return Math.round((angle - 1) / 90) * 90; - } - return Math.round(angle / 90) * 90; - } - - /** - * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) - * @return {FabricObject} thisArg - * @chainable - */ - straighten(this: FabricObject & this) { - this.rotate(this._getAngleValueForStraighten()); - } - - /** - * Same as {@link FabricObject.prototype.straighten} but with animation - * @param {Object} callbacks Object with callback functions - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - * @return {FabricObject} thisArg - */ - fxStraighten( - this: FabricObject & this, - callbacks: { - onChange?(value: TDegree): any; - onComplete?(): any; - } = {} - ) { - const onComplete = callbacks.onComplete || noop, - onChange = callbacks.onChange || noop; - - return animate({ - target: this, - startValue: this.angle, - endValue: this._getAngleValueForStraighten(), - duration: this.FX_DURATION, - onChange: (value: TDegree) => { - this.rotate(value); - onChange(value); - }, - onComplete: () => { - this.setCoords(); - onComplete(); - }, - }); - } -} diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 7ff6e055419..8407a838a1e 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -1,25 +1,19 @@ import { fabric } from '../../HEADER'; import { ObjectEvents } from '../EventTypeDefs'; import { FabricObjectSVGExportMixin } from '../mixins/object.svg_export'; -import { FabricObjectObjectAnimationMixin } from '../mixins/object_animation.mixin'; import { InteractiveFabricObject } from '../mixins/object_interactivity.mixin'; -import { FabricObjectObjectStraighteningMixin } from '../mixins/object_straightening.mixin'; import { applyMixins } from '../util/applyMixins'; // TODO somehow we have to make a tree-shakeable import export class FabricObject extends applyMixins(InteractiveFabricObject, [ - FabricObjectObjectStraighteningMixin, FabricObjectSVGExportMixin, - FabricObjectObjectAnimationMixin, ]) {} // @ts-expect-error applyMixins is not generic so EventSpec is different export interface FabricObject extends InteractiveFabricObject, - FabricObjectObjectStraighteningMixin, - FabricObjectSVGExportMixin, - FabricObjectObjectAnimationMixin {} + FabricObjectSVGExportMixin {} export { fabricObjectDefaultValues } from './object.class'; diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index d88554e21d3..7abc085fe58 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -4,7 +4,7 @@ import { cache } from '../cache'; import { config } from '../config'; import { ALIASING_LIMIT, iMatrix, VERSION } from '../constants'; import { ObjectEvents } from '../EventTypeDefs'; -import { FabricObjectAncestryMixin } from '../mixins/object_ancestry.mixin'; +import { AnimatableObject } from '../mixins/object_animation.mixin'; import { Point } from '../point.class'; import { Shadow } from '../shadow.class'; import type { TClassProperties, TDegree, TFiller, TSize } from '../typedefs'; @@ -54,7 +54,7 @@ type TCallSuper = (arg0: string, ...moreArgs: any[]) => any; */ export class FabricObject< EventSpec extends ObjectEvents = ObjectEvents -> extends FabricObjectAncestryMixin { +> extends AnimatableObject { type: string; /** @@ -2133,6 +2133,7 @@ export const fabricObjectDefaultValues = { clipPath: undefined, inverted: false, absolutePositioned: false, + FX_DURATION: 500, }; Object.assign(FabricObject.prototype, fabricObjectDefaultValues); From cc075ca90be1915c1edb762ca59bf5238dd3bb41 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 18:09:29 +0200 Subject: [PATCH 46/58] rename --- src/mixins/object_ancestry.mixin.ts | 21 +++++++++------------ src/mixins/object_animation.mixin.ts | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/mixins/object_ancestry.mixin.ts b/src/mixins/object_ancestry.mixin.ts index 23042f7aaba..b8abcc25074 100644 --- a/src/mixins/object_ancestry.mixin.ts +++ b/src/mixins/object_ancestry.mixin.ts @@ -4,23 +4,20 @@ import type { Group } from '../shapes/group.class'; import type { Canvas, StaticCanvas } from '../__types__'; import { ObjectGeometry } from './object_geometry.mixin'; -type TAncestor = FabricObjectAncestryMixin | Canvas | StaticCanvas; +type TAncestor = StackedObject | Canvas | StaticCanvas; type TCollection = Group | Canvas | StaticCanvas; /** * Strict: only ancestors that are objects (without canvas) */ export type Ancestors = Strict extends true - ? - | [FabricObjectAncestryMixin | Group] - | [FabricObjectAncestryMixin | Group, ...Group[]] - | Group[] + ? [StackedObject | Group] | [StackedObject | Group, ...Group[]] | Group[] : - | [FabricObjectAncestryMixin | Group | Canvas | StaticCanvas] - | [FabricObjectAncestryMixin | Group, Canvas | StaticCanvas] - | [FabricObjectAncestryMixin, ...Group[]] + | [StackedObject | Group | Canvas | StaticCanvas] + | [StackedObject | Group, Canvas | StaticCanvas] + | [StackedObject, ...Group[]] | Group[] - | [FabricObjectAncestryMixin | Group, ...Group[], Canvas | StaticCanvas]; + | [StackedObject | Group, ...Group[], Canvas | StaticCanvas]; export type AncestryComparison = { /** @@ -37,7 +34,7 @@ export type AncestryComparison = { otherFork: Ancestors; }; -export class FabricObjectAncestryMixin< +export class StackedObject< EventSpec extends ObjectEvents = ObjectEvents > extends ObjectGeometry { /** @@ -78,7 +75,7 @@ export class FabricObjectAncestryMixin< /** * Compare ancestors * - * @param {FabricObjectAncestryMixin} other + * @param {StackedObject} other * @param {boolean} [strict] finds only ancestors that are objects (without canvas) * @returns {AncestryComparison} an object that represent the ancestry situation. */ @@ -147,7 +144,7 @@ export class FabricObjectAncestryMixin< /** * - * @param {FabricObjectAncestryMixin} other + * @param {StackedObject} other * @param {boolean} [strict] checks only ancestors that are objects (without canvas) * @returns {boolean} */ diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 1c9e8e5803c..ed42d265318 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -3,11 +3,11 @@ import { ObjectEvents } from '../EventTypeDefs'; import { TDegree } from '../typedefs'; import { animate } from '../util/animate'; import { animateColor } from '../util/animate_color'; -import { FabricObjectAncestryMixin } from './object_ancestry.mixin'; +import { StackedObject } from './object_ancestry.mixin'; export abstract class AnimatableObject< EventSpec extends ObjectEvents = ObjectEvents -> extends FabricObjectAncestryMixin { +> extends StackedObject { /** * Animation duration (in ms) for fx* methods * @type Number From 90b0dc5f6522d43cb8b26f21cdf81143072b8f4d Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 18:12:19 +0200 Subject: [PATCH 47/58] Update index.js --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index cc7ac36bcce..56683016248 100644 --- a/index.js +++ b/index.js @@ -32,7 +32,6 @@ import './src/shapes/path.class'; import './src/shapes/group.class'; import './src/shapes/active_selection.class'; // optional interaction import './src/shapes/image.class'; -import './src/mixins/object_straightening.mixin'; // optional object straightening import './src/filters/WebGLProbe'; // optional image_filters import './src/filters/base_filter.class'; // optional image_filters import './src/filters/colormatrix_filter.class'; // optional image_filters From a031d4c9a564c9256b8ef2492d6681a103bbc1a9 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 18:14:43 +0200 Subject: [PATCH 48/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index ed42d265318..43a11af816c 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import { noop } from 'lodash'; import { ObjectEvents } from '../EventTypeDefs'; import { TDegree } from '../typedefs'; From 4ce9226193a64978b6df12a32ffd36a491798ef4 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 18:15:27 +0200 Subject: [PATCH 49/58] Update object.class.ts --- src/shapes/object.class.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index 7abc085fe58..ded93017d53 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -1843,9 +1843,7 @@ export class FabricObject< /** * Sets "angle" of an instance with centered rotation - * @param {Number} angle Angle value (in degrees) - * @return {fabric.Object} thisArg - * @chainable + * @param {TDegree} angle Angle value (in degrees) */ rotate(angle: TDegree) { const shouldCenterOrigin = From e03cfedef2bb8a95e435157847029a4171bec740 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:07:10 +0200 Subject: [PATCH 50/58] a bit of cleanup --- src/mixins/object_animation.mixin.ts | 105 ++++++++++++--------------- src/shapes/object.class.ts | 6 -- 2 files changed, 47 insertions(+), 64 deletions(-) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 43a11af816c..25643bf1eaf 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -6,6 +6,11 @@ import { animate } from '../util/animate'; import { animateColor } from '../util/animate_color'; import { StackedObject } from './object_ancestry.mixin'; +/** + * TODO remove transient + */ +type TAnimationOptions = Record; + export abstract class AnimatableObject< EventSpec extends ObjectEvents = ObjectEvents > extends StackedObject { @@ -16,6 +21,12 @@ export abstract class AnimatableObject< */ FX_DURATION: number; + /** + * List of properties to consider for animating colors. + * @type String[] + */ + colorProperties: string[]; + abstract rotate(deg: TDegree): void; /** @@ -33,61 +44,45 @@ export abstract class AnimatableObject< * As string — one property * * object.animate('left', ...); - * object.animate('left', { duration: ... }); * */ - animate() { - if (arguments[0] && typeof arguments[0] === 'object') { - let propsToAnimate = [], - prop, - skipCallbacks, - out = []; - for (prop in arguments[0]) { - propsToAnimate.push(prop); - } - for (let i = 0, len = propsToAnimate.length; i < len; i++) { - prop = propsToAnimate[i]; - skipCallbacks = i !== len - 1; - out.push( - this._animate(prop, arguments[0][prop], arguments[1], skipCallbacks) - ); - } - return out; - } else { - return this._animate.apply(this, arguments); - } + animate(key: string, toValue: T, options?: TAnimationOptions): void; + animate(animatable: Record, options?: TAnimationOptions): void; + animate>( + arg0: S, + arg1: S extends string ? T : TAnimationOptions, + arg2?: TAnimationOptions + ) { + const animatable = ( + typeof arg0 === 'string' ? { [arg0]: arg1 } : arg0 + ) as Record; + const keys = Object.keys(animatable); + const options = ( + typeof arg0 === 'string' ? arg2 : arg1 + ) as TAnimationOptions; + keys.map((key, index) => + this._animate( + key, + animatable[key], + index === keys.length - 1 + ? options + : { ...options, onChange: undefined, onComplete: undefined } + ) + ); } /** * @private - * @param {String} property Property to animate + * @param {String} key Property to animate * @param {String} to Value to animate to * @param {Object} [options] Options object - * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked */ - _animate(property, to, options, skipCallbacks) { - let propPair; + _animate(key: string, to: T, options: TAnimationOptions = {}) { + const path = key.split('.'); + const propIsColor = this.colorProperties.includes(path[path.length - 1]); + const currentValue = path.reduce((deep: any, key) => deep[key], this); to = to.toString(); - - options = Object.assign({}, options); - - if (~property.indexOf('.')) { - propPair = property.split('.'); - } - - const propIsColor = - this.colorProperties.indexOf(property) > -1 || - (propPair && this.colorProperties.indexOf(propPair[1]) > -1); - - const currentValue = propPair - ? this.get(propPair[0])[propPair[1]] - : this.get(property); - - if (!('from' in options)) { - options.from = currentValue; - } - if (!propIsColor) { if (~to.indexOf('=')) { to = currentValue + parseFloat(to.replace('=', '')); @@ -98,7 +93,7 @@ export abstract class AnimatableObject< const animationOptions = { target: this, - startValue: options.from, + startValue: options.from ?? currentValue, endValue: to, byValue: options.by, easing: options.easing, @@ -109,22 +104,16 @@ export abstract class AnimatableObject< return options.abort.call(this, value, valueProgress, timeProgress); }), onChange: (value, valueProgress, timeProgress) => { - if (propPair) { - this[propPair[0]][propPair[1]] = value; - } else { - this.set(property, value); - } - if (skipCallbacks) { - return; - } + path.reduce((deep: any, key, index) => { + if (index === path.length - 1) { + deep[key] = value; + } + return deep[key]; + }, this); options.onChange && options.onChange(value, valueProgress, timeProgress); }, onComplete: (value, valueProgress, timeProgress) => { - if (skipCallbacks) { - return; - } - this.setCoords(); options.onComplete && options.onComplete(value, valueProgress, timeProgress); @@ -147,7 +136,7 @@ export abstract class AnimatableObject< * @private * @return {Number} angle value */ - _getAngleValueForStraighten() { + protected _getAngleValueForStraighten() { const angle = this.angle % 360; if (angle > 0) { return Math.round((angle - 1) / 90) * 90; diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index ded93017d53..f0d112734a8 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -468,12 +468,6 @@ export class FabricObject< */ cacheProperties: string[]; - /** - * List of properties to consider for animating colors. - * @type String[] - */ - colorProperties: string[]; - /** * a fabricObject that, without stroke define a clipping area with their shape. filled in black * the clipPath object gets used when the object has rendered, and the context is placed in the center From 52cf10c3560ebf8ea56f21c119faf45a7e1b9426 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:13:35 +0200 Subject: [PATCH 51/58] checkout --- src/mixins/object_stacking.mixin.ts | 121 ++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/mixins/object_stacking.mixin.ts diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts new file mode 100644 index 00000000000..ef6e9ef8ce3 --- /dev/null +++ b/src/mixins/object_stacking.mixin.ts @@ -0,0 +1,121 @@ +//@ts-nocheck +import { FabricObject } from '../shapes/fabricObject.class'; + +(function (global) { + var fabric = global.fabric; + fabric.util.object.extend( + FabricObject.prototype, + /** @lends FabricObject.prototype */ { + /** + * Moves an object to the bottom of the stack of drawn objects + * @return {fabric.Object} thisArg + * @chainable + */ + sendToBack: function () { + if (this.group) { + fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); + } else if (this.canvas) { + this.canvas.sendToBack(this); + } + return this; + }, + + /** + * Moves an object to the top of the stack of drawn objects + * @return {fabric.Object} thisArg + * @chainable + */ + bringToFront: function () { + if (this.group) { + fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); + } else if (this.canvas) { + this.canvas.bringToFront(this); + } + return this; + }, + + /** + * Moves an object down in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object + * @return {fabric.Object} thisArg + * @chainable + */ + sendBackwards: function (intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.sendBackwards.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.sendBackwards(this, intersecting); + } + return this; + }, + + /** + * Moves an object up in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object + * @return {fabric.Object} thisArg + * @chainable + */ + bringForward: function (intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.bringForward.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.bringForward(this, intersecting); + } + return this; + }, + + /** + * Moves an object to specified level in stack of drawn objects + * @param {Number} index New position of object + * @return {fabric.Object} thisArg + * @chainable + */ + moveTo: function (index) { + if (this.group && this.group.type !== 'activeSelection') { + fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); + } else if (this.canvas) { + this.canvas.moveTo(this, index); + } + return this; + }, + + /** + * + * @param {fabric.Object} other object to compare against + * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` + */ + isInFrontOf: function (other) { + if (this === other) { + return undefined; + } + var ancestorData = this.findCommonAncestors(other); + if (!ancestorData) { + return undefined; + } + if (ancestorData.fork.includes(other)) { + return true; + } + if (ancestorData.otherFork.includes(this)) { + return false; + } + var firstCommonAncestor = ancestorData.common[0]; + if (!firstCommonAncestor) { + return undefined; + } + var headOfFork = ancestorData.fork.pop(), + headOfOtherFork = ancestorData.otherFork.pop(), + thisIndex = firstCommonAncestor._objects.indexOf(headOfFork), + otherIndex = firstCommonAncestor._objects.indexOf(headOfOtherFork); + return thisIndex > -1 && thisIndex > otherIndex; + }, + } + ); +})(typeof exports !== 'undefined' ? exports : window); From 42c826ed878b73f427d6349383b255f3b722903c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:23:44 +0200 Subject: [PATCH 52/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 25643bf1eaf..2f8a5ece58a 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -44,6 +44,7 @@ export abstract class AnimatableObject< * As string — one property * * object.animate('left', ...); + * object.animate('left', ..., { duration: ... }); * */ animate(key: string, toValue: T, options?: TAnimationOptions): void; From 7b93359c4479f01dd9b45c63f8e05b42dc26d68a Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:28:01 +0200 Subject: [PATCH 53/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 8407a838a1e..9e912e3dabe 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -10,7 +10,6 @@ export class FabricObject extends applyMixins(InteractiveFabricObject, [ FabricObjectSVGExportMixin, ]) {} -// @ts-expect-error applyMixins is not generic so EventSpec is different export interface FabricObject extends InteractiveFabricObject, FabricObjectSVGExportMixin {} From 8af3477f07ebc5dc78b2fc11bbc4adcd93d1098c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:28:06 +0200 Subject: [PATCH 54/58] revert --- index.js | 1 + src/mixins/object_stacking.mixin.ts | 30 ----------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/index.js b/index.js index 56683016248..75a173672f4 100644 --- a/index.js +++ b/index.js @@ -20,6 +20,7 @@ import './src/mixins/canvas_gestures.mixin'; // optional gestures import './src/mixins/canvas_animation.mixin'; // optional animation import './src/mixins/canvas_straightening.mixin'; // optional animation import './src/shapes/fabricObject.class'; +import './src/mixins/object_stacking.mixin'; // removed in #8461 import './src/mixins/stateful.mixin'; // will die soon import './src/shapes/line.class'; import './src/shapes/circle.class'; diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts index ef6e9ef8ce3..06912837bf7 100644 --- a/src/mixins/object_stacking.mixin.ts +++ b/src/mixins/object_stacking.mixin.ts @@ -86,36 +86,6 @@ import { FabricObject } from '../shapes/fabricObject.class'; } return this; }, - - /** - * - * @param {fabric.Object} other object to compare against - * @returns {boolean | undefined} if objects do not share a common ancestor or they are strictly equal it is impossible to determine which is in front of the other; in such cases the function returns `undefined` - */ - isInFrontOf: function (other) { - if (this === other) { - return undefined; - } - var ancestorData = this.findCommonAncestors(other); - if (!ancestorData) { - return undefined; - } - if (ancestorData.fork.includes(other)) { - return true; - } - if (ancestorData.otherFork.includes(this)) { - return false; - } - var firstCommonAncestor = ancestorData.common[0]; - if (!firstCommonAncestor) { - return undefined; - } - var headOfFork = ancestorData.fork.pop(), - headOfOtherFork = ancestorData.otherFork.pop(), - thisIndex = firstCommonAncestor._objects.indexOf(headOfFork), - otherIndex = firstCommonAncestor._objects.indexOf(headOfOtherFork); - return thisIndex > -1 && thisIndex > otherIndex; - }, } ); })(typeof exports !== 'undefined' ? exports : window); From 02c360d545b07d85131a78db967bfaa271824d8d Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:35:01 +0200 Subject: [PATCH 55/58] Update fabricObject.class.ts --- src/shapes/fabricObject.class.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shapes/fabricObject.class.ts b/src/shapes/fabricObject.class.ts index 9e912e3dabe..b1b268ad712 100644 --- a/src/shapes/fabricObject.class.ts +++ b/src/shapes/fabricObject.class.ts @@ -10,6 +10,7 @@ export class FabricObject extends applyMixins(InteractiveFabricObject, [ FabricObjectSVGExportMixin, ]) {} +// @ts-expect-error type conflict of generic EventSpec export interface FabricObject extends InteractiveFabricObject, FabricObjectSVGExportMixin {} From d7433c1aba42914b46a940033ecc117f4b067f7f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:38:15 +0200 Subject: [PATCH 56/58] Update object_stacking.mixin.ts --- src/mixins/object_stacking.mixin.ts | 169 ++++++++++++++-------------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/src/mixins/object_stacking.mixin.ts b/src/mixins/object_stacking.mixin.ts index 06912837bf7..a89649c34a4 100644 --- a/src/mixins/object_stacking.mixin.ts +++ b/src/mixins/object_stacking.mixin.ts @@ -1,91 +1,90 @@ -//@ts-nocheck +//@ts-nocheck this file is trash kept only for tests + +import { fabric } from '../../HEADER'; import { FabricObject } from '../shapes/fabricObject.class'; -(function (global) { - var fabric = global.fabric; - fabric.util.object.extend( - FabricObject.prototype, - /** @lends FabricObject.prototype */ { - /** - * Moves an object to the bottom of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - sendToBack: function () { - if (this.group) { - fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); - } else if (this.canvas) { - this.canvas.sendToBack(this); - } - return this; - }, +fabric.util.object.extend( + FabricObject.prototype, + /** @lends FabricObject.prototype */ { + /** + * Moves an object to the bottom of the stack of drawn objects + * @return {fabric.Object} thisArg + * @chainable + */ + sendToBack: function () { + if (this.group) { + fabric.StaticCanvas.prototype.sendToBack.call(this.group, this); + } else if (this.canvas) { + this.canvas.sendToBack(this); + } + return this; + }, - /** - * Moves an object to the top of the stack of drawn objects - * @return {fabric.Object} thisArg - * @chainable - */ - bringToFront: function () { - if (this.group) { - fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); - } else if (this.canvas) { - this.canvas.bringToFront(this); - } - return this; - }, + /** + * Moves an object to the top of the stack of drawn objects + * @return {fabric.Object} thisArg + * @chainable + */ + bringToFront: function () { + if (this.group) { + fabric.StaticCanvas.prototype.bringToFront.call(this.group, this); + } else if (this.canvas) { + this.canvas.bringToFront(this); + } + return this; + }, - /** - * Moves an object down in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - sendBackwards: function (intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.sendBackwards.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.sendBackwards(this, intersecting); - } - return this; - }, + /** + * Moves an object down in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object + * @return {fabric.Object} thisArg + * @chainable + */ + sendBackwards: function (intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.sendBackwards.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.sendBackwards(this, intersecting); + } + return this; + }, - /** - * Moves an object up in stack of drawn objects - * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object - * @return {fabric.Object} thisArg - * @chainable - */ - bringForward: function (intersecting) { - if (this.group) { - fabric.StaticCanvas.prototype.bringForward.call( - this.group, - this, - intersecting - ); - } else if (this.canvas) { - this.canvas.bringForward(this, intersecting); - } - return this; - }, + /** + * Moves an object up in stack of drawn objects + * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object + * @return {fabric.Object} thisArg + * @chainable + */ + bringForward: function (intersecting) { + if (this.group) { + fabric.StaticCanvas.prototype.bringForward.call( + this.group, + this, + intersecting + ); + } else if (this.canvas) { + this.canvas.bringForward(this, intersecting); + } + return this; + }, - /** - * Moves an object to specified level in stack of drawn objects - * @param {Number} index New position of object - * @return {fabric.Object} thisArg - * @chainable - */ - moveTo: function (index) { - if (this.group && this.group.type !== 'activeSelection') { - fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); - } else if (this.canvas) { - this.canvas.moveTo(this, index); - } - return this; - }, - } - ); -})(typeof exports !== 'undefined' ? exports : window); + /** + * Moves an object to specified level in stack of drawn objects + * @param {Number} index New position of object + * @return {fabric.Object} thisArg + * @chainable + */ + moveTo: function (index) { + if (this.group && this.group.type !== 'activeSelection') { + fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index); + } else if (this.canvas) { + this.canvas.moveTo(this, index); + } + return this; + }, + } +); From 27157bc71a476fc143cf02b40f10925abb6dba4f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:42:11 +0200 Subject: [PATCH 57/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 2f8a5ece58a..44376b75d51 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import { noop } from 'lodash'; +import { noop } from '../constants'; import { ObjectEvents } from '../EventTypeDefs'; import { TDegree } from '../typedefs'; import { animate } from '../util/animate'; From e6ef7a1911ce9928b3ad5ca5922d187db1715ae7 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 27 Nov 2022 19:44:10 +0200 Subject: [PATCH 58/58] Update object_animation.mixin.ts --- src/mixins/object_animation.mixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/object_animation.mixin.ts b/src/mixins/object_animation.mixin.ts index 44376b75d51..d84da45470e 100644 --- a/src/mixins/object_animation.mixin.ts +++ b/src/mixins/object_animation.mixin.ts @@ -52,7 +52,7 @@ export abstract class AnimatableObject< animate>( arg0: S, arg1: S extends string ? T : TAnimationOptions, - arg2?: TAnimationOptions + arg2?: S extends string ? TAnimationOptions : never ) { const animatable = ( typeof arg0 === 'string' ? { [arg0]: arg1 } : arg0