@giphy/js-util 0.0.0-canary-20230620165510

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,304 @@
1
+ import { setRenditionScaleUpMaxPixels } from '../bestfit'
2
+ import { getBestRenditionUrl, getBestVideo } from '../gif-utils'
3
+
4
+ const testGifLandscape: any = {
5
+ images: {
6
+ fixed_height_still: {
7
+ url: '/media/200_s.gif',
8
+ width: '355',
9
+ height: '200',
10
+ },
11
+ original_still: {
12
+ url: '/media/giphy_s.gif',
13
+ width: '600',
14
+ height: '338',
15
+ },
16
+ fixed_width: {
17
+ url: '/media/200x113.gif',
18
+ width: '200',
19
+ height: '113',
20
+ size: '77670',
21
+ mp4: '/media/200x113.mp4',
22
+ mp4_size: '11777',
23
+ webp: '/media/200x113.webp',
24
+ webp_size: '52404',
25
+ },
26
+ fixed_height_small_still: {
27
+ url: '/media/100_s.gif',
28
+ width: '178',
29
+ height: '100',
30
+ },
31
+ fixed_height_small: {
32
+ url: '/media/178x100.gif',
33
+ width: '178',
34
+ height: '100',
35
+ size: '169486',
36
+ mp4: '/media/100.mp4',
37
+ mp4_size: '68837',
38
+ webp: '/media/100.webp',
39
+ webp_size: '43392',
40
+ },
41
+ fixed_width_small_still: {
42
+ url: '/media/100w_s.gif',
43
+ width: '100',
44
+ height: '56',
45
+ },
46
+ fixed_width_still: {
47
+ url: '/media/200w_s.gif',
48
+ width: '200',
49
+ height: '113',
50
+ },
51
+ fixed_width_small: {
52
+ url: '/media/100x56.gif',
53
+ width: '100',
54
+ height: '56',
55
+ size: '77670',
56
+ mp4: '/media/100x56.mp4',
57
+ mp4_size: '31328',
58
+ webp: '/media/100x56.webp',
59
+ webp_size: '17910',
60
+ },
61
+ original: {
62
+ url: '/media/600x338.gif',
63
+ width: '600',
64
+ height: '338',
65
+ size: '1020165',
66
+ frames: '14',
67
+ mp4: '/media/giphy.mp4',
68
+ mp4_size: '41339',
69
+ webp: '/media/giphy.webp',
70
+ webp_size: '295984',
71
+ },
72
+ fixed_height: {
73
+ url: '/media/355x200.gif',
74
+ width: '355',
75
+ height: '200',
76
+ size: '169486',
77
+ mp4: '/media/200.mp4',
78
+ mp4_size: '11945',
79
+ webp: '/media/200.webp',
80
+ webp_size: '129066',
81
+ },
82
+ },
83
+ }
84
+
85
+ const testVideo = {
86
+ ...testGifLandscape,
87
+ video: {
88
+ assets: {
89
+ fixed_width: {
90
+ url: '/media/200x113.gif',
91
+ width: '200',
92
+ height: '113',
93
+ size: '77670',
94
+ mp4: '/media/200x113.mp4',
95
+ mp4_size: '11777',
96
+ webp: '/media/200x113.webp',
97
+ webp_size: '52404',
98
+ },
99
+ fixed_width_small: {
100
+ url: '/media/100x56.gif',
101
+ width: '100',
102
+ height: '56',
103
+ size: '77670',
104
+ mp4: '/media/100x56.mp4',
105
+ mp4_size: '31328',
106
+ webp: '/media/100x56.webp',
107
+ webp_size: '17910',
108
+ },
109
+ original: {
110
+ url: '/media/600x338.gif',
111
+ width: '600',
112
+ height: '338',
113
+ size: '1020165',
114
+ frames: '14',
115
+ mp4: '/media/giphy.mp4',
116
+ mp4_size: '41339',
117
+ webp: '/media/giphy.webp',
118
+ webp_size: '295984',
119
+ },
120
+ fixed_height: {
121
+ url: '/media/355x200.gif',
122
+ width: '355',
123
+ height: '200',
124
+ size: '169486',
125
+ mp4: '/media/200.mp4',
126
+ mp4_size: '11945',
127
+ webp: '/media/200.webp',
128
+ webp_size: '129066',
129
+ },
130
+ fixed_height_small: {
131
+ url: '/media/178x100.gif',
132
+ width: '178',
133
+ height: '100',
134
+ size: '169486',
135
+ mp4: '/media/100.mp4',
136
+ mp4_size: '68837',
137
+ webp: '/media/100.webp',
138
+ webp_size: '43392',
139
+ },
140
+ },
141
+ },
142
+ }
143
+
144
+ const testGifPortrait: any = {
145
+ images: {
146
+ fixed_height_still: {
147
+ url: '/media/200_s.gif',
148
+ width: '200',
149
+ height: '355',
150
+ },
151
+ original_still: {
152
+ url: '/media/giphy_s.gif',
153
+ width: '338',
154
+ height: '600',
155
+ },
156
+ fixed_width: {
157
+ url: '/media/113x200.gif',
158
+ width: '113',
159
+ height: '200',
160
+ size: '77670',
161
+ mp4: '/media/200x113.mp4',
162
+ mp4_size: '11777',
163
+ webp: '/media/200x113.webp',
164
+ webp_size: '52404',
165
+ },
166
+ fixed_height_small_still: {
167
+ url: '/media/100_s.gif',
168
+ width: '100',
169
+ height: '178',
170
+ },
171
+ fixed_height_small: {
172
+ url: '/media/100x178.gif',
173
+ width: '100',
174
+ height: '178',
175
+ size: '169486',
176
+ mp4: '/media/100.mp4',
177
+ mp4_size: '68837',
178
+ webp: '/media/100.webp',
179
+ webp_size: '43392',
180
+ },
181
+ fixed_width_small_still: {
182
+ url: '/media/100w_s.gif',
183
+ width: '56',
184
+ height: '100',
185
+ },
186
+ fixed_width_still: {
187
+ url: '/media/200w_s.gif',
188
+ width: '113',
189
+ height: '200',
190
+ },
191
+ fixed_width_small: {
192
+ url: '/media/56x100.gif',
193
+ width: '56',
194
+ height: '100',
195
+ size: '77670',
196
+ mp4: '/media/100x56.mp4',
197
+ mp4_size: '31328',
198
+ webp: '/media/100x56.webp',
199
+ webp_size: '17910',
200
+ },
201
+ original: {
202
+ url: '/media/338x600.gif',
203
+ width: '338',
204
+ height: '600',
205
+ size: '1020165',
206
+ frames: '14',
207
+ mp4: '/media/giphy.mp4',
208
+ mp4_size: '41339',
209
+ webp: '/media/giphy.webp',
210
+ webp_size: '295984',
211
+ },
212
+ fixed_height: {
213
+ url: '/media/200x355.gif',
214
+ width: '200',
215
+ height: '355',
216
+ size: '169486',
217
+ mp4: '/media/200.mp4',
218
+ mp4_size: '11945',
219
+ webp: '/media/200.webp',
220
+ webp_size: '129066',
221
+ },
222
+ },
223
+ }
224
+
225
+ const testGifVideo: any = {
226
+ type: 'video',
227
+ images: {},
228
+ video: { previews: testGifLandscape.images },
229
+ }
230
+
231
+ describe('gif util', () => {
232
+ beforeEach(() => {
233
+ setRenditionScaleUpMaxPixels(50)
234
+ })
235
+ test('getBestRenditionUrl ', () => {
236
+ expect(getBestRenditionUrl(testGifLandscape, 450, 350)).toEqual('/media/600x338.gif')
237
+ expect(getBestRenditionUrl(testGifLandscape, 400, 300)).toEqual('/media/600x338.gif')
238
+ expect(getBestRenditionUrl(testGifLandscape, 300, 200)).toEqual('/media/355x200.gif')
239
+ expect(getBestRenditionUrl(testGifLandscape, 200, 200)).toEqual('/media/355x200.gif')
240
+ expect(getBestRenditionUrl(testGifLandscape, 100, 100)).toEqual('/media/100x56.gif')
241
+ expect(getBestRenditionUrl(testGifLandscape, 300, 200)).toEqual('/media/355x200.gif')
242
+ // within the scaleup range
243
+ expect(getBestRenditionUrl(testGifLandscape, 200, 100)).toEqual('/media/178x100.gif')
244
+ expect(getBestRenditionUrl(testGifLandscape, 200, 100)).toEqual('/media/178x100.gif')
245
+ expect(getBestRenditionUrl(testGifLandscape, 178, 100)).toEqual('/media/178x100.gif')
246
+ expect(getBestRenditionUrl(testGifLandscape, 100, 100)).toEqual('/media/100x56.gif')
247
+ expect(getBestRenditionUrl(testGifLandscape, 1, 1)).toEqual('/media/100x56.gif')
248
+ expect(getBestRenditionUrl(testGifLandscape, 0, 1)).toEqual('')
249
+ })
250
+ test('getBestVideo ', () => {
251
+ expect(getBestVideo(testVideo.video, 450, 350)?.url).toEqual('/media/600x338.gif')
252
+ expect(getBestVideo(testVideo.video, 400, 300)?.url).toEqual('/media/600x338.gif')
253
+ expect(getBestVideo(testVideo.video, 300, 200)?.url).toEqual('/media/355x200.gif')
254
+ expect(getBestVideo(testVideo.video, 200, 200)?.url).toEqual('/media/355x200.gif')
255
+ expect(getBestVideo(testVideo.video, 100, 100)?.url).toEqual('/media/100x56.gif')
256
+ expect(getBestVideo(testVideo.video, 300, 200)?.url).toEqual('/media/355x200.gif')
257
+ // within the scaleup range
258
+ expect(getBestVideo(testVideo.video, 200, 100)?.url).toEqual('/media/178x100.gif')
259
+ expect(getBestVideo(testVideo.video, 200, 100)?.url).toEqual('/media/178x100.gif')
260
+ expect(getBestVideo(testVideo.video, 178, 100)?.url).toEqual('/media/178x100.gif')
261
+ expect(getBestVideo(testVideo.video, 100, 100)?.url).toEqual('/media/100x56.gif')
262
+ expect(getBestVideo(testVideo.video, 1, 1)?.url).toEqual('/media/100x56.gif')
263
+ // expect(getBestVideo(testVideo.video, 0, 1).u).toEqual('')
264
+ })
265
+ test('getBestRenditionUrl portrait target landscape gif ', () => {
266
+ expect(getBestRenditionUrl(testGifLandscape, 700, 450)).toEqual('/media/600x338.gif')
267
+ expect(getBestRenditionUrl(testGifLandscape, 350, 450)).toEqual('/media/600x338.gif')
268
+ expect(getBestRenditionUrl(testGifLandscape, 300, 400)).toEqual('/media/600x338.gif')
269
+ expect(getBestRenditionUrl(testGifLandscape, 200, 300)).toEqual('/media/600x338.gif')
270
+ expect(getBestRenditionUrl(testGifLandscape, 100, 200)).toEqual('/media/355x200.gif')
271
+ expect(getBestRenditionUrl(testGifLandscape, 50, 100)).toEqual('/media/100x56.gif')
272
+ expect(getBestRenditionUrl(testGifLandscape, 1, 1)).toEqual('/media/100x56.gif')
273
+ expect(getBestRenditionUrl(testGifLandscape, 0, 1)).toEqual('')
274
+ })
275
+ test('getBestRenditionUrl landscape target portrait gif ', () => {
276
+ expect(getBestRenditionUrl(testGifPortrait, 700, 700)).toEqual('/media/338x600.gif')
277
+ expect(getBestRenditionUrl(testGifPortrait, 338, 200)).toEqual('/media/338x600.gif')
278
+ expect(getBestRenditionUrl(testGifPortrait, 300, 200)).toEqual('/media/338x600.gif')
279
+ expect(getBestRenditionUrl(testGifPortrait, 200, 100)).toEqual('/media/200x355.gif')
280
+ expect(getBestRenditionUrl(testGifPortrait, 100, 50)).toEqual('/media/56x100.gif')
281
+ expect(getBestRenditionUrl(testGifPortrait, 1, 1)).toEqual('/media/56x100.gif')
282
+ expect(getBestRenditionUrl(testGifPortrait, 0, 1)).toEqual('')
283
+ })
284
+ test('getBestRenditionUrl type video ', () => {
285
+ expect(getBestRenditionUrl(testGifVideo, 450, 350)).toEqual('/media/600x338.gif')
286
+ expect(getBestRenditionUrl(testGifVideo, 400, 300)).toEqual('/media/600x338.gif')
287
+ })
288
+ test('getBestRenditionUrl higher quality: 20', () => {
289
+ setRenditionScaleUpMaxPixels(20)
290
+ expect(getBestRenditionUrl(testGifLandscape, 450, 350)).toEqual('/media/600x338.gif')
291
+ expect(getBestRenditionUrl(testGifLandscape, 400, 300)).toEqual('/media/600x338.gif')
292
+ expect(getBestRenditionUrl(testGifLandscape, 300, 200)).toEqual('/media/355x200.gif')
293
+ expect(getBestRenditionUrl(testGifLandscape, 200, 200)).toEqual('/media/355x200.gif')
294
+ expect(getBestRenditionUrl(testGifLandscape, 100, 100)).toEqual('/media/178x100.gif')
295
+ expect(getBestRenditionUrl(testGifLandscape, 300, 200)).toEqual('/media/355x200.gif')
296
+ // within the scaleup range
297
+ expect(getBestRenditionUrl(testGifLandscape, 200, 100)).toEqual('/media/200x113.gif')
298
+ expect(getBestRenditionUrl(testGifLandscape, 200, 100)).toEqual('/media/200x113.gif')
299
+ expect(getBestRenditionUrl(testGifLandscape, 178, 100)).toEqual('/media/178x100.gif')
300
+ expect(getBestRenditionUrl(testGifLandscape, 100, 100)).toEqual('/media/178x100.gif')
301
+ expect(getBestRenditionUrl(testGifLandscape, 1, 1)).toEqual('/media/100x56.gif')
302
+ expect(getBestRenditionUrl(testGifLandscape, 0, 1)).toEqual('')
303
+ })
304
+ })
package/src/bestfit.ts ADDED
@@ -0,0 +1,64 @@
1
+ import { IRendition } from '@giphy/js-types'
2
+ import { Logger } from './log'
3
+
4
+ const closestArea = (width: number, height: number, renditions: IRendition[]) => {
5
+ let currentBest = Infinity
6
+ let result: IRendition
7
+ // sort the renditions so we can avoid scaling up low resolutions
8
+ renditions.forEach((rendition: IRendition) => {
9
+ const widthPercentage = rendition.width / width
10
+ const heightPercentage = rendition.height / height
11
+ // a width percentage of 1 is exact, 2 is double, .5 half etc
12
+ const areaPercentage = widthPercentage * heightPercentage
13
+ // img could be bigger or smaller
14
+ const testBest = Math.abs(1 - areaPercentage) // the closer to 0 the better
15
+ if (testBest < currentBest) {
16
+ currentBest = testBest
17
+ result = rendition
18
+ }
19
+ })
20
+ return result!
21
+ }
22
+
23
+ let SCALE_UP_MAX_PIXELS = 50
24
+ export const setRenditionScaleUpMaxPixels = (pixels: number) => {
25
+ Logger.debug(`@giphy/js-util set rendition selection scale up max pixels to ${pixels}`)
26
+ SCALE_UP_MAX_PIXELS = pixels
27
+ }
28
+ /**
29
+ * Finds image rendition that best fits a given container preferring images
30
+ * ##### Note: all renditions are assumed to have the same aspect ratio
31
+ *
32
+ * When we have a portrait target and landscape gif, we choose a higher rendition to match
33
+ * the height of the portrait target, otherwise it's blurry (same applies for landscape to portrait)
34
+ *
35
+ * @name bestfit
36
+ * @function
37
+ * @param {Array.<Object>} renditions available image renditions each having a width and height property
38
+ * @param {Number} width
39
+ * @param {Number} height
40
+ * @param {Number} scaleUpMaxPixels the maximum pixels an asset should be scaled up
41
+ */
42
+ function bestfit(
43
+ renditions: Array<IRendition>,
44
+ width: number,
45
+ height: number,
46
+ scaleUpMaxPixels: number = SCALE_UP_MAX_PIXELS
47
+ ) {
48
+ let [largestRendition] = renditions
49
+ // filter out renditions that are smaller than the target width and height by scaleUpMaxPixels value
50
+ const testRenditions = renditions.filter(rendition => {
51
+ if (rendition.width * rendition.height > largestRendition.width * largestRendition.height) {
52
+ largestRendition = rendition
53
+ }
54
+ return width - rendition.width <= scaleUpMaxPixels && height - rendition.height <= scaleUpMaxPixels
55
+ })
56
+ // if all are too small, use the largest we have
57
+ if (testRenditions.length === 0) {
58
+ return largestRendition
59
+ }
60
+ // find the closest area of the filtered renditions
61
+ return closestArea(width, height, testRenditions)
62
+ }
63
+
64
+ export default bestfit
@@ -0,0 +1,36 @@
1
+ export function mapValues(object: any, mapFn: (val: any, key: string) => any): any {
2
+ if (Array.isArray(object)) {
3
+ throw `This map is just for objects, just use array.map for arrays`
4
+ }
5
+ return Object.keys(object).reduce((result: any, key: string) => {
6
+ result[key] = mapFn(object[key], key)
7
+ return result
8
+ }, {})
9
+ }
10
+
11
+ export function forEach(object: any, mapFn: (val: any, key: any) => void): void {
12
+ if (Array.isArray(object)) {
13
+ throw `This map is just for objects, just use array.forEach for arrays`
14
+ }
15
+ return Object.keys(object).forEach((key: string) => {
16
+ mapFn(object[key], key)
17
+ })
18
+ }
19
+
20
+ export function take<T>(arr: T[], count = 0): T[] {
21
+ return arr.slice(0, count)
22
+ }
23
+
24
+ export function without<T>(arr: T[], values: T[]): T[] {
25
+ return arr.filter(val => values.indexOf(val) === -1)
26
+ }
27
+
28
+ export function pick<T extends object, U extends keyof T>(object: T, pick: Array<U>): Pick<T, U> {
29
+ const res: Partial<T> = {}
30
+ pick.forEach((key: U) => {
31
+ if (object[key] !== undefined) {
32
+ res[key] = object[key]
33
+ }
34
+ })
35
+ return res as Pick<T, U>
36
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @param el HTMLElement
3
+ * @returns calculated properties of DOMReact
4
+ */
5
+ const getClientRect = (el: HTMLElement): DOMRectReadOnly => {
6
+ let left = 0
7
+ let top = 0
8
+ const width = el.offsetWidth
9
+ const height = el.offsetHeight
10
+ // no layout thrash
11
+ do {
12
+ left += el.offsetLeft
13
+ top += el.offsetTop
14
+ el = el.offsetParent as HTMLElement
15
+ } while (el)
16
+ const result = {
17
+ left,
18
+ top,
19
+ width,
20
+ height,
21
+ right: left + width,
22
+ bottom: top + height,
23
+ x: left,
24
+ y: top,
25
+ }
26
+ return { ...result, toJSON: () => JSON.stringify(result) }
27
+ }
28
+
29
+ export default getClientRect
@@ -0,0 +1,42 @@
1
+ import { v4 as uuid } from 'uuid'
2
+ let pingbackId = ''
3
+
4
+ const idLength = 16
5
+
6
+ /* istanbul ignore next */
7
+ const noUUIDRandom = () => {
8
+ let result = ''
9
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
10
+ const charactersLength = characters.length
11
+ for (let i = 0; i < idLength; i++) {
12
+ result += characters.charAt(Math.floor(Math.random() * charactersLength))
13
+ }
14
+ return result
15
+ }
16
+ const getPingbackId = () => {
17
+ // it exists in memory
18
+ if (!pingbackId) {
19
+ try {
20
+ // it exists in storage
21
+ pingbackId = sessionStorage.getItem('giphyPingbackId') as string
22
+ } catch (_) {}
23
+ if (!pingbackId) {
24
+ // we need to create it
25
+ const hexTime = new Date().getTime().toString(16) // was told to mimic what we had
26
+ try {
27
+ // React Native doesn't support uuid without a polyfill
28
+ pingbackId = `${hexTime}${uuid().replace(/-/g, '')}`.substring(0, idLength) // 16 character max
29
+ } catch (error) {
30
+ /* istanbul ignore next */
31
+ pingbackId = noUUIDRandom()
32
+ }
33
+ try {
34
+ // save in storage
35
+ sessionStorage.setItem('giphyPingbackId', pingbackId)
36
+ } catch (_) {}
37
+ }
38
+ }
39
+ return pingbackId
40
+ }
41
+
42
+ export default getPingbackId
@@ -0,0 +1,127 @@
1
+ import { IGif, IImage, IImages, ImageAllTypes, IRendition } from '@giphy/js-types'
2
+ import IVideo from '@giphy/js-types/dist/video'
3
+ import bestfit from './bestfit'
4
+ import { pick, take, without } from './collections'
5
+ import { SUPPORTS_WEBP } from './webp-check'
6
+
7
+ export const getSpecificRendition = (
8
+ { images, is_sticker: isSticker }: IGif,
9
+ renditionLabel: string,
10
+ isStill = false,
11
+ useVideo = false
12
+ ) => {
13
+ if (!images || !renditionLabel) return ''
14
+ isStill = isStill && !useVideo
15
+ // @ts-ignore come back to this
16
+ const rendition = images[`${renditionLabel}${isStill ? '_still' : ''}`] as ImageAllTypes
17
+
18
+ if (rendition) {
19
+ if (isSticker || isStill) {
20
+ return rendition.url
21
+ }
22
+ const webP = SUPPORTS_WEBP && rendition.webp
23
+ return useVideo ? rendition.mp4 : webP || rendition.url
24
+ }
25
+
26
+ return ''
27
+ }
28
+
29
+ interface IRenditionWithName extends IRendition {
30
+ renditionName: keyof IImages
31
+ }
32
+
33
+ export const getBestVideo = (video: IGif['video'], width: number, height: number) => {
34
+ let assets = video?.assets
35
+ if (assets) {
36
+ assets = { ...assets }
37
+ // @ts-ignore we don't show source according to the existing code
38
+ delete assets.source
39
+ const filteredAssets = Object.values(assets).sort((a: IRendition, b: IRendition) => a.width - b.width)
40
+ return bestfit(filteredAssets, width, height) as IImage
41
+ }
42
+ }
43
+
44
+ const getRenditions = (type: IGif['type'], images: IImages, video?: IVideo) =>
45
+ type === 'video' && video && video.previews && !Object.keys(images).length ? video.previews : images
46
+
47
+ export const getBestRendition = (
48
+ images: IImages,
49
+ gifWidth: number,
50
+ gifHeight: number,
51
+ scaleUpMaxPixels?: number
52
+ ): IRenditionWithName => {
53
+ const checkRenditions = pick(images, [
54
+ 'original',
55
+ 'fixed_width',
56
+ 'fixed_height',
57
+ 'fixed_width_small',
58
+ 'fixed_height_small',
59
+ ])
60
+ const testImages = Object.entries(checkRenditions).map(([renditionName, val]) => ({
61
+ renditionName,
62
+ ...val,
63
+ })) as IRenditionWithName[]
64
+ return bestfit(testImages, gifWidth, gifHeight, scaleUpMaxPixels) as IRenditionWithName
65
+ }
66
+
67
+ type Options = {
68
+ isStill?: boolean
69
+ useVideo?: boolean
70
+ scaleUpMaxPixels?: number
71
+ }
72
+ export const getBestRenditionUrl = (
73
+ { images, video, type }: IGif,
74
+ gifWidth: number,
75
+ gifHeight: number,
76
+ options: Options = { isStill: false, useVideo: false }
77
+ ): keyof IImages | '' => {
78
+ if (!gifWidth || !gifHeight || !images) return ''
79
+ const { useVideo, isStill, scaleUpMaxPixels } = options
80
+ const renditions = getRenditions(type, images, video)
81
+ const { renditionName } = getBestRendition(renditions, gifWidth, gifHeight, scaleUpMaxPixels)
82
+
83
+ // still, video, webp or gif
84
+ const key = `${renditionName}${isStill && !useVideo ? '_still' : ''}` as keyof IImages
85
+ const rendition = renditions[key] as ImageAllTypes
86
+
87
+ const match = useVideo ? rendition.mp4 : SUPPORTS_WEBP && rendition.webp ? rendition.webp : rendition.url
88
+ return (match || '') as keyof IImages
89
+ }
90
+
91
+ export const getGifHeight = ({ images }: IGif, gifWidth: number) => {
92
+ const { fixed_width } = images
93
+ if (fixed_width) {
94
+ const { width, height } = fixed_width
95
+ const aspectRatio = width / height
96
+ return Math.round(gifWidth / aspectRatio)
97
+ }
98
+ return 0
99
+ }
100
+
101
+ export const getGifWidth = ({ images }: IGif, gifHeight: number) => {
102
+ const { fixed_width } = images
103
+ if (fixed_width) {
104
+ const { width, height } = fixed_width
105
+ const aspectRatio = width / height
106
+ return Math.round(gifHeight * aspectRatio)
107
+ }
108
+ return 0
109
+ }
110
+
111
+ /**
112
+ * GIF Text - Alt Text: Generates alt text for
113
+ * GIF images based on username and tags.
114
+ * @prop {Gif}
115
+ * @return {String} GIF alt text.
116
+ */
117
+ export const getAltText = ({ alt_text, user, tags = [], is_sticker = false, title = '' }: IGif): string => {
118
+ if (alt_text) {
119
+ return alt_text
120
+ }
121
+ if (title) {
122
+ return title
123
+ }
124
+ const username = (user && user.username) || ''
125
+ const filteredTags = take(without(tags, ['transparent']), username ? 4 : 5)
126
+ return `${username ? `${username} ` : ``}${filteredTags.join(' ')} ${is_sticker ? 'Sticker' : 'GIF'}`
127
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export { default as bestfit, setRenditionScaleUpMaxPixels } from './bestfit'
2
+ export * from './collections'
3
+ export { default as getClientRect } from './get-client-rect-from-el'
4
+ export { default as getPingbackId } from './get-pingback-id'
5
+ export {
6
+ getAltText,
7
+ getBestRendition,
8
+ getBestRenditionUrl,
9
+ getBestVideo,
10
+ getGifHeight,
11
+ getGifWidth,
12
+ getSpecificRendition,
13
+ } from './gif-utils'
14
+ export * from './log'
15
+ export * from './sdk-headers'
16
+ export { checkIfWebP } from './webp-check'
package/src/log.ts ADDED
@@ -0,0 +1,36 @@
1
+ /* istanbul ignore next */
2
+ export enum LogLevel {
3
+ DEBUG = 0,
4
+ INFO = 1,
5
+ WARN = 2,
6
+ ERROR = 3,
7
+ }
8
+ /* istanbul ignore next */
9
+ export const Logger = {
10
+ ENABLED:
11
+ typeof window !== 'undefined' &&
12
+ typeof location !== 'undefined' &&
13
+ location.search.indexOf('giphy-debug') !== -1,
14
+ LEVEL: 0,
15
+ PREFIX: 'GiphyJS',
16
+ debug: (...msg: any) => {
17
+ if (Logger.ENABLED && Logger.LEVEL <= LogLevel.DEBUG) {
18
+ console.debug(Logger.PREFIX, ...msg) // eslint-disable-line no-console
19
+ }
20
+ },
21
+ info: (...msg: any) => {
22
+ if (Logger.ENABLED && Logger.LEVEL <= LogLevel.INFO) {
23
+ console.info(Logger.PREFIX, ...msg) // eslint-disable-line no-console
24
+ }
25
+ },
26
+ warn: (...msg: any) => {
27
+ if (Logger.ENABLED && Logger.LEVEL <= LogLevel.WARN) {
28
+ console.warn(Logger.PREFIX, ...msg) // eslint-disable-line no-console
29
+ }
30
+ },
31
+ error: (...msg: any) => {
32
+ if (Logger.ENABLED && Logger.LEVEL <= LogLevel.ERROR) {
33
+ console.error(Logger.PREFIX, ...msg) // eslint-disable-line no-console
34
+ }
35
+ },
36
+ }
@@ -0,0 +1,18 @@
1
+ type GiphySDKRequestHeader = 'X-GIPHY-SDK-NAME' | 'X-GIPHY-SDK-VERSION' | 'X-GIPHY-SDK-PLATFORM'
2
+ const gl = ((typeof window !== 'undefined' ? window : global) || {}) as any
3
+ // define _GIPHY_SDK_HEADERS_ if they don't exist
4
+ gl._GIPHY_SDK_HEADERS_ =
5
+ gl._GIPHY_SDK_HEADERS_ ||
6
+ (gl.Headers
7
+ ? new gl.Headers({
8
+ 'X-GIPHY-SDK-PLATFORM': 'web',
9
+ })
10
+ : undefined)
11
+
12
+ export const getGiphySDKRequestHeaders = (): Headers | undefined => gl._GIPHY_SDK_HEADERS_
13
+
14
+ export const appendGiphySDKRequestHeader = (key: GiphySDKRequestHeader, value: string) =>
15
+ getGiphySDKRequestHeaders()?.set(key, value)
16
+
17
+ export const appendGiphySDKRequestParam = (key: GiphySDKRequestHeader, value: string) =>
18
+ getGiphySDKRequestHeaders()?.set(key, value)