UNPKG

@wordpress/upload-media

Version:
8 lines (7 loc) 6.84 kB
{ "version": 3, "sources": ["../src/utils.ts"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { getFilename } from '@wordpress/url';\nimport { _x } from '@wordpress/i18n';\n\n/**\n * Converts a Blob to a File with a default name like \"image.png\".\n *\n * If it is already a File object, it is returned unchanged.\n *\n * Handles cross-realm File objects (e.g., from iframes) that have a `name`\n * property but fail `instanceof File` checks because the File constructor\n * differs between browsing contexts.\n *\n * @param fileOrBlob Blob object.\n * @return File object.\n */\nexport function convertBlobToFile( fileOrBlob: Blob | File ): File {\n\tif ( fileOrBlob instanceof File ) {\n\t\treturn fileOrBlob;\n\t}\n\n\t// Handle cross-realm File objects (e.g., from iframes where the block\n\t// editor canvas renders). These objects have a `name` property but fail\n\t// the `instanceof File` check because each browsing context has its own\n\t// File constructor.\n\tif (\n\t\t'name' in fileOrBlob &&\n\t\ttypeof ( fileOrBlob as File ).name === 'string'\n\t) {\n\t\treturn new File( [ fileOrBlob ], ( fileOrBlob as File ).name, {\n\t\t\ttype: fileOrBlob.type,\n\t\t\tlastModified: ( fileOrBlob as File ).lastModified,\n\t\t} );\n\t}\n\n\t// Extension is only an approximation.\n\t// The server will override it if incorrect.\n\tconst ext = fileOrBlob.type.split( '/' )[ 1 ];\n\tconst mediaType =\n\t\t'application/pdf' === fileOrBlob.type\n\t\t\t? 'document'\n\t\t\t: fileOrBlob.type.split( '/' )[ 0 ];\n\treturn new File( [ fileOrBlob ], `${ mediaType }.${ ext }`, {\n\t\ttype: fileOrBlob.type,\n\t} );\n}\n\n/**\n * Renames a given file and returns a new file.\n *\n * Copies over the last modified time.\n *\n * @param file File object.\n * @param name File name.\n * @return Renamed file object.\n */\nexport function renameFile( file: File, name: string ): File {\n\treturn new File( [ file ], name, {\n\t\ttype: file.type,\n\t\tlastModified: file.lastModified,\n\t} );\n}\n\n/**\n * Clones a given file object.\n *\n * @param file File object.\n * @return New file object.\n */\nexport function cloneFile( file: File ): File {\n\treturn renameFile( file, file.name );\n}\n\n/**\n * Returns the file extension from a given file name or URL.\n *\n * @param file File URL.\n * @return File extension or null if it does not have one.\n */\nexport function getFileExtension( file: string ): string | null {\n\treturn file.includes( '.' ) ? file.split( '.' ).pop() || null : null;\n}\n\n/**\n * Returns file basename without extension.\n *\n * For example, turns \"my-awesome-file.jpeg\" into \"my-awesome-file\".\n *\n * @param name File name.\n * @return File basename.\n */\nexport function getFileBasename( name: string ): string {\n\treturn name.includes( '.' )\n\t\t? name.split( '.' ).slice( 0, -1 ).join( '.' )\n\t\t: name;\n}\n\n/**\n * Returns the file name including extension from a URL.\n *\n * @param url File URL.\n * @return File name.\n */\nexport function getFileNameFromUrl( url: string ) {\n\treturn getFilename( url ) || _x( 'unnamed', 'file name' );\n}\n\n/**\n * Detects whether a file buffer contains an animated GIF.\n *\n * Performs binary analysis of the GIF file structure:\n * 1. Checks for the GIF magic bytes (\"GIF8\")\n * 2. Counts frame blocks by scanning for Graphic Control Extension headers\n * (Block Terminator 0x00 + Extension Introducer 0x21 + Graphic Control Label 0xF9)\n * 3. Returns true if more than 1 frame is found\n *\n * This is a deliberately cheap heuristic, not a full GIF parser. It scans\n * the raw bytes for the Graphic Control Extension marker rather than walking\n * the block structure, which has two known limitations:\n *\n * - False positives: the 0x00 0x21 0xF9 byte sequence can occur coincidentally\n * inside LZW-compressed image data, so a single-frame GIF may be reported as\n * animated.\n * - False negatives: Graphic Control Extension blocks are optional per the GIF\n * spec, so an animated GIF that omits them is reported as static.\n *\n * Both outcomes are non-destructive: the worker's ImageDecoder frame count is\n * the authoritative source, so a misdetected static GIF still encodes to an\n * accurate (1-frame) video, and a misdetected animated GIF simply falls back\n * to the normal image upload pipeline. A full structural parse is intentionally\n * avoided here to keep this off the hot path of every GIF upload.\n *\n * Based on the GIF specification:\n *\n * @see http://www.matthewflickinger.com/lab/whatsinagif/\n *\n * @param buffer File ArrayBuffer.\n * @return Whether the buffer contains an animated GIF.\n */\nexport function isAnimatedGif( buffer: ArrayBuffer ): boolean {\n\tconst view = new Uint8Array( buffer );\n\n\t// Check GIF magic bytes: \"GIF8\" (0x47 0x49 0x46 0x38).\n\tif (\n\t\tview.length < 4 ||\n\t\tview[ 0 ] !== 0x47 ||\n\t\tview[ 1 ] !== 0x49 ||\n\t\tview[ 2 ] !== 0x46 ||\n\t\tview[ 3 ] !== 0x38\n\t) {\n\t\treturn false;\n\t}\n\n\t// Count frames by looking for Graphic Control Extension headers.\n\t// Pattern: Block Terminator (0x00) + Extension Introducer (0x21) + Graphic Control Label (0xF9).\n\tlet frameCount = 0;\n\tfor ( let i = 0; i < view.length - 2; i++ ) {\n\t\tif (\n\t\t\tview[ i ] === 0x00 &&\n\t\t\tview[ i + 1 ] === 0x21 &&\n\t\t\tview[ i + 2 ] === 0xf9\n\t\t) {\n\t\t\tframeCount++;\n\t\t\tif ( frameCount > 1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"], "mappings": ";AAGA,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAcZ,SAAS,kBAAmB,YAAgC;AAClE,MAAK,sBAAsB,MAAO;AACjC,WAAO;AAAA,EACR;AAMA,MACC,UAAU,cACV,OAAS,WAAqB,SAAS,UACtC;AACD,WAAO,IAAI,KAAM,CAAE,UAAW,GAAK,WAAqB,MAAM;AAAA,MAC7D,MAAM,WAAW;AAAA,MACjB,cAAgB,WAAqB;AAAA,IACtC,CAAE;AAAA,EACH;AAIA,QAAM,MAAM,WAAW,KAAK,MAAO,GAAI,EAAG,CAAE;AAC5C,QAAM,YACL,sBAAsB,WAAW,OAC9B,aACA,WAAW,KAAK,MAAO,GAAI,EAAG,CAAE;AACpC,SAAO,IAAI,KAAM,CAAE,UAAW,GAAG,GAAI,SAAU,IAAK,GAAI,IAAI;AAAA,IAC3D,MAAM,WAAW;AAAA,EAClB,CAAE;AACH;AAWO,SAAS,WAAY,MAAY,MAAqB;AAC5D,SAAO,IAAI,KAAM,CAAE,IAAK,GAAG,MAAM;AAAA,IAChC,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,EACpB,CAAE;AACH;AAQO,SAAS,UAAW,MAAmB;AAC7C,SAAO,WAAY,MAAM,KAAK,IAAK;AACpC;AAQO,SAAS,iBAAkB,MAA8B;AAC/D,SAAO,KAAK,SAAU,GAAI,IAAI,KAAK,MAAO,GAAI,EAAE,IAAI,KAAK,OAAO;AACjE;AAUO,SAAS,gBAAiB,MAAuB;AACvD,SAAO,KAAK,SAAU,GAAI,IACvB,KAAK,MAAO,GAAI,EAAE,MAAO,GAAG,EAAG,EAAE,KAAM,GAAI,IAC3C;AACJ;AAQO,SAAS,mBAAoB,KAAc;AACjD,SAAO,YAAa,GAAI,KAAK,GAAI,WAAW,WAAY;AACzD;AAkCO,SAAS,cAAe,QAA+B;AAC7D,QAAM,OAAO,IAAI,WAAY,MAAO;AAGpC,MACC,KAAK,SAAS,KACd,KAAM,CAAE,MAAM,MACd,KAAM,CAAE,MAAM,MACd,KAAM,CAAE,MAAM,MACd,KAAM,CAAE,MAAM,IACb;AACD,WAAO;AAAA,EACR;AAIA,MAAI,aAAa;AACjB,WAAU,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAM;AAC3C,QACC,KAAM,CAAE,MAAM,KACd,KAAM,IAAI,CAAE,MAAM,MAClB,KAAM,IAAI,CAAE,MAAM,KACjB;AACD;AACA,UAAK,aAAa,GAAI;AACrB,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;", "names": [] }