{"version":3,"file":"Collection.mjs","names":[],"sources":["../../src/Collection.ts"],"sourcesContent":["import type { Constructor, TBBox } from './typedefs';\nimport { removeFromArray } from './util/internals/removeFromArray';\nimport { Point } from './Point';\nimport type { ActiveSelection } from './shapes/ActiveSelection';\nimport type { Group } from './shapes/Group';\nimport type { InteractiveFabricObject } from './shapes/Object/InteractiveObject';\nimport type { FabricObject } from './shapes/Object/FabricObject';\n\nexport const isCollection = (\n  fabricObject?: FabricObject,\n): fabricObject is Group | ActiveSelection => {\n  return !!fabricObject && Array.isArray((fabricObject as Group)._objects);\n};\n\nexport function createCollectionMixin<TBase extends Constructor>(Base: TBase) {\n  class Collection extends Base {\n    /**\n     * @type {FabricObject[]}\n     * @TODO needs to end up in the constructor too\n     */\n    _objects: FabricObject[] = [];\n\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    _onObjectAdded(object: FabricObject) {\n      // subclasses should override this method\n    }\n\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    _onObjectRemoved(object: FabricObject) {\n      // subclasses should override this method\n    }\n\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    _onStackOrderChanged(object: FabricObject) {\n      // subclasses should override this method\n    }\n\n    /**\n     * Adds objects to collection\n     * Objects should be instances of (or inherit from) FabricObject\n     * @param {...FabricObject[]} objects to add\n     * @returns {number} new array length\n     */\n    add(...objects: FabricObject[]): number {\n      const size = this._objects.push(...objects);\n      objects.forEach((object) => this._onObjectAdded(object));\n      return size;\n    }\n\n    /**\n     * Inserts an object into collection at specified index\n     * @param {number} index Index to insert object at\n     * @param {...FabricObject[]} objects Object(s) to insert\n     * @returns {number} new array length\n     */\n    insertAt(index: number, ...objects: FabricObject[]) {\n      this._objects.splice(index, 0, ...objects);\n      objects.forEach((object) => this._onObjectAdded(object));\n      return this._objects.length;\n    }\n\n    /**\n     * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)\n     * @private\n     * @param {...FabricObject[]} objects objects to remove\n     * @returns {FabricObject[]} removed objects\n     */\n    remove(...objects: FabricObject[]) {\n      const array = this._objects,\n        removed: FabricObject[] = [];\n      objects.forEach((object) => {\n        const index = array.indexOf(object);\n        // only call onObjectRemoved if an object was actually removed\n        if (index !== -1) {\n          array.splice(index, 1);\n          removed.push(object);\n          this._onObjectRemoved(object);\n        }\n      });\n      return removed;\n    }\n\n    /**\n     * Executes given function for each object in this group\n     * A simple shortcut for getObjects().forEach, before es6 was more complicated,\n     * now is just a shortcut.\n     * @param {Function} callback\n     *                   Callback invoked with current object as first argument,\n     *                   index - as second and an array of all objects - as third.\n     */\n    forEachObject(\n      callback: (\n        object: FabricObject,\n        index: number,\n        array: FabricObject[],\n      ) => any,\n    ) {\n      this.getObjects().forEach((object, index, objects) =>\n        callback(object, index, objects),\n      );\n    }\n\n    /**\n     * Returns an array of children objects of this instance\n     * @param {...String} [types] When specified, only objects of these types are returned\n     * @return {Array}\n     */\n    getObjects(...types: string[]) {\n      if (types.length === 0) {\n        return [...this._objects];\n      }\n      return this._objects.filter((o) => o.isType(...types));\n    }\n\n    /**\n     * Returns object at specified index\n     * @param {Number} index\n     * @return {Object} object at index\n     */\n    item(index: number) {\n      return this._objects[index];\n    }\n\n    /**\n     * Returns true if collection contains no objects\n     * @return {Boolean} true if collection is empty\n     */\n    isEmpty() {\n      return this._objects.length === 0;\n    }\n\n    /**\n     * Returns a size of a collection (i.e: length of an array containing its objects)\n     * @return {Number} Collection size\n     */\n    size() {\n      return this._objects.length;\n    }\n\n    /**\n     * Returns true if collection contains an object.\\\n     * **Prefer using {@link FabricObject#isDescendantOf} for performance reasons**\n     * instead of `a.contains(b)` use `b.isDescendantOf(a)`\n     * @param {Object} object Object to check against\n     * @param {Boolean} [deep=false] `true` to check all descendants, `false` to check only `_objects`\n     * @return {Boolean} `true` if collection contains an object\n     */\n    contains(object: FabricObject, deep?: boolean): boolean {\n      if (this._objects.includes(object)) {\n        return true;\n      } else if (deep) {\n        return this._objects.some(\n          (obj) =>\n            obj instanceof Collection &&\n            (obj as unknown as Collection).contains(object, true),\n        );\n      }\n      return false;\n    }\n\n    /**\n     * Returns number representation of a collection complexity\n     * @return {Number} complexity\n     */\n    complexity() {\n      return this._objects.reduce((memo, current) => {\n        memo += current.complexity ? current.complexity() : 0;\n        return memo;\n      }, 0);\n    }\n\n    /**\n     * Moves an object or the objects of a multiple selection\n     * to the bottom of the stack of drawn objects\n     * @param {fabric.Object} object Object to send to back\n     * @returns {boolean} true if change occurred\n     */\n    sendObjectToBack(object: FabricObject) {\n      if (!object || object === this._objects[0]) {\n        return false;\n      }\n      removeFromArray(this._objects, object);\n      this._objects.unshift(object);\n      this._onStackOrderChanged(object);\n      return true;\n    }\n\n    /**\n     * Moves an object or the objects of a multiple selection\n     * to the top of the stack of drawn objects\n     * @param {fabric.Object} object Object to send\n     * @returns {boolean} true if change occurred\n     */\n    bringObjectToFront(object: FabricObject) {\n      if (!object || object === this._objects[this._objects.length - 1]) {\n        return false;\n      }\n      removeFromArray(this._objects, object);\n      this._objects.push(object);\n      this._onStackOrderChanged(object);\n      return true;\n    }\n\n    /**\n     * Moves an object or a selection down in stack of drawn objects\n     * An optional parameter, `intersecting` allows to move the object in behind\n     * the first intersecting object. Where intersection is calculated with\n     * bounding box. If no intersection is found, there will not be change in the\n     * stack.\n     * @param {fabric.Object} object Object to send\n     * @param {boolean} [intersecting] If `true`, send object behind next lower intersecting object\n     * @returns {boolean} true if change occurred\n     */\n    sendObjectBackwards(object: FabricObject, intersecting?: boolean) {\n      if (!object) {\n        return false;\n      }\n      const idx = this._objects.indexOf(object);\n      if (idx !== 0) {\n        // if object is not on the bottom of stack\n        const newIdx = this.findNewLowerIndex(object, idx, intersecting);\n        removeFromArray(this._objects, object);\n        this._objects.splice(newIdx, 0, object);\n        this._onStackOrderChanged(object);\n        return true;\n      }\n      return false;\n    }\n\n    /**\n     * Moves an object or a selection up in stack of drawn objects\n     * An optional parameter, intersecting allows to move the object in front\n     * of the first intersecting object. Where intersection is calculated with\n     * bounding box. If no intersection is found, there will not be change in the\n     * stack.\n     * @param {fabric.Object} object Object to send\n     * @param {boolean} [intersecting] If `true`, send object in front of next upper intersecting object\n     * @returns {boolean} true if change occurred\n     */\n    bringObjectForward(object: FabricObject, intersecting?: boolean) {\n      if (!object) {\n        return false;\n      }\n      const idx = this._objects.indexOf(object);\n      if (idx !== this._objects.length - 1) {\n        // if object is not on top of stack (last item in an array)\n        const newIdx = this.findNewUpperIndex(object, idx, intersecting);\n        removeFromArray(this._objects, object);\n        this._objects.splice(newIdx, 0, object);\n        this._onStackOrderChanged(object);\n        return true;\n      }\n      return false;\n    }\n\n    /**\n     * Moves an object to specified level in stack of drawn objects\n     * @param {fabric.Object} object Object to send\n     * @param {number} index Position to move to\n     * @returns {boolean} true if change occurred\n     */\n    moveObjectTo(object: FabricObject, index: number) {\n      if (object === this._objects[index]) {\n        return false;\n      }\n      removeFromArray(this._objects, object);\n      this._objects.splice(index, 0, object);\n      this._onStackOrderChanged(object);\n      return true;\n    }\n\n    findNewLowerIndex(\n      object: FabricObject,\n      idx: number,\n      intersecting?: boolean,\n    ) {\n      let newIdx;\n\n      if (intersecting) {\n        newIdx = idx;\n        // traverse down the stack looking for the nearest intersecting object\n        for (let i = idx - 1; i >= 0; --i) {\n          if (object.isOverlapping(this._objects[i])) {\n            newIdx = i;\n            break;\n          }\n        }\n      } else {\n        newIdx = idx - 1;\n      }\n\n      return newIdx;\n    }\n\n    findNewUpperIndex(\n      object: FabricObject,\n      idx: number,\n      intersecting?: boolean,\n    ) {\n      let newIdx;\n\n      if (intersecting) {\n        newIdx = idx;\n        // traverse up the stack looking for the nearest intersecting object\n        for (let i = idx + 1; i < this._objects.length; ++i) {\n          if (object.isOverlapping(this._objects[i])) {\n            newIdx = i;\n            break;\n          }\n        }\n      } else {\n        newIdx = idx + 1;\n      }\n\n      return newIdx;\n    }\n\n    /**\n     * Given a bounding box, return all the objects of the collection that are contained in the bounding box.\n     * If `includeIntersecting` is true, return also the objects that intersect the bounding box as well.\n     * This is meant to work with selection. Is not a generic method.\n     * @param {TBBox} bbox a bounding box in scene coordinates\n     * @param {{ includeIntersecting?: boolean }} options an object with includeIntersecting\n     * @returns array of objects contained in the bounding box, ordered from top to bottom stacking wise\n     */\n    collectObjects(\n      { left, top, width, height }: TBBox,\n      { includeIntersecting = true }: { includeIntersecting?: boolean } = {},\n    ) {\n      const objects: InteractiveFabricObject[] = [],\n        tl = new Point(left, top),\n        br = tl.add(new Point(width, height));\n\n      // we iterate reverse order to collect top first in case of click.\n      for (let i = this._objects.length - 1; i >= 0; i--) {\n        const object = this._objects[i] as unknown as InteractiveFabricObject;\n        if (\n          object.selectable &&\n          object.visible &&\n          ((includeIntersecting && object.intersectsWithRect(tl, br)) ||\n            object.isContainedWithinRect(tl, br) ||\n            (includeIntersecting && object.containsPoint(tl)) ||\n            (includeIntersecting && object.containsPoint(br)))\n        ) {\n          objects.push(object);\n        }\n      }\n\n      return objects;\n    }\n  }\n\n  // https://github.com/microsoft/TypeScript/issues/32080\n  return Collection;\n}\n"],"mappings":"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"}