fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 8.81 kB
Source Map (JSON)
{"version":3,"file":"objectEnlive.min.mjs","names":[],"sources":["../../../../src/util/misc/objectEnlive.ts"],"sourcesContent":["import { noop } from '../../constants';\nimport type { FabricObject } from '../../shapes/Object/FabricObject';\nimport type {\n Abortable,\n Constructor,\n TCrossOrigin,\n TFiller,\n} from '../../typedefs';\nimport { createImage } from './dom';\nimport { classRegistry } from '../../ClassRegistry';\nimport type { BaseFilter } from '../../filters/BaseFilter';\nimport type { FabricObject as BaseFabricObject } from '../../shapes/Object/Object';\nimport { FabricError, SignalAbortedError } from '../internals/console';\nimport type { Shadow } from '../../Shadow';\n\nexport type LoadImageOptions = Abortable & {\n /**\n * cors value for the image loading, default to anonymous\n */\n crossOrigin?: TCrossOrigin;\n};\n\n/**\n * Loads image element from given url and resolve it, or catch.\n * @param {String} url URL representing an image\n * @param {LoadImageOptions} [options] image loading options\n * @returns {Promise<HTMLImageElement>} the loaded image.\n */\nexport const loadImage = (\n url: string,\n { signal, crossOrigin = null }: LoadImageOptions = {},\n) =>\n new Promise<HTMLImageElement>(function (resolve, reject) {\n if (signal && signal.aborted) {\n return reject(new SignalAbortedError('loadImage'));\n }\n const img = createImage();\n let abort: EventListenerOrEventListenerObject;\n if (signal) {\n abort = function (err: Event) {\n img.src = '';\n reject(err);\n };\n signal.addEventListener('abort', abort, { once: true });\n }\n const done = function () {\n img.onload = img.onerror = null;\n abort && signal?.removeEventListener('abort', abort);\n resolve(img);\n };\n if (!url) {\n done();\n return;\n }\n img.onload = done;\n img.onerror = function () {\n abort && signal?.removeEventListener('abort', abort);\n reject(new FabricError(`Error loading ${img.src}`));\n };\n crossOrigin && (img.crossOrigin = crossOrigin);\n img.src = url;\n });\n\nexport type EnlivenObjectOptions = Abortable & {\n /**\n * Method for further parsing of object elements,\n * called after each fabric object created.\n */\n reviver?: <\n T extends\n | BaseFabricObject\n | FabricObject\n | BaseFilter<string>\n | Shadow\n | TFiller,\n >(\n serializedObj: Record<string, any>,\n instance: T | undefined,\n error?: FabricError,\n ) => void | Promise<T>;\n};\n\n/**\n * @TODO type this correctly.\n * Creates corresponding fabric instances from their object representations\n * @param {Object[]} objects Objects to enliven\n * @param {EnlivenObjectOptions} [options]\n * @param {(serializedObj: object, instance: FabricObject) => any} [options.reviver] Method for further parsing of object elements,\n * called after each fabric object created.\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @returns {Promise<FabricObject[]>}\n */\nexport const enlivenObjects = <\n T extends\n | BaseFabricObject\n | FabricObject\n | BaseFilter<string>\n | Shadow\n | TFiller,\n>(\n objects: any[],\n { signal, reviver = noop }: EnlivenObjectOptions = {},\n) =>\n new Promise<T[]>((resolve, reject) => {\n const instances: T[] = [];\n signal && signal.addEventListener('abort', reject, { once: true });\n Promise.allSettled(\n objects.map((obj) =>\n classRegistry\n .getClass<\n Constructor<T> & {\n fromObject(options: any, context: Abortable): Promise<T>;\n }\n >(obj.type)\n .fromObject(obj, { signal }),\n ),\n )\n .then(async (elementsResult) => {\n for (const [index, result] of elementsResult.entries()) {\n if (result.status === 'fulfilled') {\n await reviver(objects[index], result.value);\n instances.push(result.value);\n }\n if (result.status === 'rejected') {\n const fallback = await reviver(\n objects[index],\n undefined,\n result.reason,\n );\n if (fallback) {\n instances.push(fallback as T);\n }\n }\n }\n resolve(instances);\n })\n .catch((error) => {\n // cleanup\n instances.forEach((instance) => {\n (instance as FabricObject).dispose &&\n (instance as FabricObject).dispose();\n });\n reject(error);\n })\n .finally(() => {\n signal && signal.removeEventListener('abort', reject);\n });\n });\n\n/**\n * Creates corresponding fabric instances residing in an object, e.g. `clipPath`\n * @param {Object} object with properties to enlive ( fill, stroke, clipPath, path )\n * @param {object} [options]\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @returns {Promise<Record<string, FabricObject | TFiller | null>>} the input object with enlived values\n */\nexport const enlivenObjectEnlivables = <\n R = Record<string, FabricObject | TFiller | null>,\n>(\n serializedObject: any,\n { signal }: Abortable = {},\n) =>\n new Promise<R>((resolve, reject) => {\n const instances: (FabricObject | TFiller | Shadow)[] = [];\n signal && signal.addEventListener('abort', reject, { once: true });\n // enlive every possible property\n const promises = Object.values(serializedObject).map((value: any) => {\n if (!value) {\n return value;\n }\n /**\n * clipPath or shadow or gradient or text on a path or a pattern,\n * or the backgroundImage or overlayImage of canvas\n * If we have a type and there is a classe registered for it, we enlive it.\n * If there is no class registered for it we return the value as is\n * */\n if (value.type && classRegistry.has(value.type)) {\n return enlivenObjects<FabricObject | Shadow | TFiller>([value], {\n signal,\n }).then(([enlived]) => {\n instances.push(enlived);\n return enlived;\n });\n }\n return value;\n });\n const keys = Object.keys(serializedObject);\n Promise.all(promises)\n .then((enlived) => {\n return enlived.reduce((acc, instance, index) => {\n acc[keys[index]] = instance;\n return acc;\n }, {});\n })\n .then(resolve)\n .catch((error) => {\n // cleanup\n instances.forEach((instance: any) => {\n instance.dispose && instance.dispose();\n });\n reject(error);\n })\n .finally(() => {\n signal && signal.removeEventListener('abort', reject);\n });\n });\n"],"mappings":"0OA4BA,MAAa,GACX,EAAA,CACE,OAAA,EAAQ,YAAA,EAAc,MAA2B,EAAA,GAEnD,IAAI,QAA0B,SAAU,EAAS,EAAA,CAC/C,GAAI,GAAU,EAAO,QACnB,OAAO,EAAO,IAAI,EAAmB,YAAA,CAAA,CAEvC,IAAM,EAAM,GAAA,CACR,EACA,IACF,EAAQ,SAAU,EAAA,CAChB,EAAI,IAAM,GACV,EAAO,EAAA,EAET,EAAO,iBAAiB,QAAS,EAAO,CAAE,KAAA,CAAM,EAAA,CAAA,EAElD,IAAM,EAAO,UAAA,CACX,EAAI,OAAS,EAAI,QAAU,KAC3B,IAAA,GAAA,MAAS,EAAQ,oBAAoB,QAAS,EAAA,EAC9C,EAAQ,EAAA,EAEL,GAIL,EAAI,OAAS,EACb,EAAI,QAAU,UAAA,CACZ,IAAA,GAAA,MAAS,EAAQ,oBAAoB,QAAS,EAAA,EAC9C,EAAO,IAAI,EAAY,iBAAiB,EAAI,MAAA,CAAA,EAE9C,IAAgB,EAAI,YAAc,GAClC,EAAI,IAAM,GATR,GAAA,EAAA,CAyCO,GAQX,EAAA,CACE,OAAA,EAAQ,QAAA,EAAU,GAA+B,EAAA,GAEnD,IAAI,SAAc,EAAS,IAAA,CACzB,IAAM,EAAiB,EAAA,CACvB,GAAU,EAAO,iBAAiB,QAAS,EAAQ,CAAE,KAAA,CAAM,EAAA,CAAA,CAC3D,QAAQ,WACN,EAAQ,IAAK,GACX,EACG,SAIC,EAAI,KAAA,CACL,WAAW,EAAK,CAAE,OAAA,EAAA,CAAA,CAAA,CAAA,CAGtB,KAAK,KAAO,IAAA,CACX,IAAK,GAAA,CAAO,EAAO,KAAW,EAAe,SAAA,CAK3C,GAJI,EAAO,SAAW,cAAX,MACH,EAAQ,EAAQ,GAAQ,EAAO,MAAA,CACrC,EAAU,KAAK,EAAO,MAAA,EAEpB,EAAO,SAAW,WAAY,CAChC,IAAM,EAAA,MAAiB,EACrB,EAAQ,GAAA,IACR,GACA,EAAO,OAAA,CAEL,GACF,EAAU,KAAK,EAAA,CAIrB,EAAQ,EAAA,EAAA,CAET,MAAO,GAAA,CAEN,EAAU,QAAS,GAAA,CAChB,EAA0B,SACxB,EAA0B,SAAA,EAAA,CAE/B,EAAO,EAAA,EAAA,CAER,YAAA,CACC,GAAU,EAAO,oBAAoB,QAAS,EAAA,EAAA,EAAA,CAWzC,GAGX,EAAA,CACE,OAAA,GAAsB,EAAA,GAExB,IAAI,SAAY,EAAS,IAAA,CACvB,IAAM,EAAiD,EAAA,CACvD,GAAU,EAAO,iBAAiB,QAAS,EAAQ,CAAE,KAAA,CAAM,EAAA,CAAA,CAE3D,IAAM,EAAW,OAAO,OAAO,EAAA,CAAkB,IAAK,GAC/C,GASD,EAAM,MAAQ,EAAc,IAAI,EAAM,KAAA,CACjC,EAAgD,CAAC,EAAA,CAAQ,CAC9D,OAAA,EAAA,CAAA,CACC,MAAA,CAAO,MACR,EAAU,KAAK,EAAA,CACR,GAAA,CAbF,EAAA,CAkBL,EAAO,OAAO,KAAK,EAAA,CACzB,QAAQ,IAAI,EAAA,CACT,KAAM,GACE,EAAQ,QAAQ,EAAK,EAAU,KACpC,EAAI,EAAK,IAAU,EACZ,GACN,EAAA,CAAA,CAAA,CAEJ,KAAK,EAAA,CACL,MAAO,GAAA,CAEN,EAAU,QAAS,GAAA,CACjB,EAAS,SAAW,EAAS,SAAA,EAAA,CAE/B,EAAO,EAAA,EAAA,CAER,YAAA,CACC,GAAU,EAAO,oBAAoB,QAAS,EAAA,EAAA,EAAA,CAAA,OAAA,KAAA,wBAAA,KAAA,eAAA,KAAA"}