Skip to content

Commit b386ca1

Browse files
authored
Merge branch 'develop' into fix/16-add-svg-support
2 parents 2826f2e + edb4aab commit b386ca1

10 files changed

Lines changed: 194 additions & 96 deletions

File tree

cimo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Author URI: http://gambit.ph
88
* License: GPLv2 or later
99
* Text Domain: cimo-image-optimizer
10-
* Version: 1.2.1
10+
* Version: 1.2.2
1111
*
1212
* @fs_premium_only /freemius.php, /freemius/, /lib/freemius-php-sdk
1313
*/

package-lock.json

Lines changed: 10 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cimo",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"description": "Convert images to WebP on upload",
55
"private": true,
66
"license": "GPL-2.0-or-later",
@@ -39,5 +39,6 @@
3939
"@mediabunny/mp3-encoder": "^1.25.0",
4040
"mediabunny": "^1.25.0",
4141
"svgo": "^4.0.0"
42+
"heic2any": "^0.0.4"
4243
}
4344
}

readme.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
=== Cimo - Free Instant Image Optimizer & WebP Converter ===
22
Contributors: bfintal, gambitph
33
Tags: image, optimization, compress, convert, webp
4-
Requires at least: 6.6.4
4+
Requires at least: 6.7.4
55
Tested up to: 6.9
66
Requires PHP: 8.0
7-
Stable tag: 1.2.1
7+
Stable tag: 1.2.2
88
License: GPLv2 or later
99
License URI: https://www.gnu.org/licenses/gpl-2.0.html
1010

@@ -204,6 +204,14 @@ Yes! The settings page includes a statistics dashboard showing:
204204

205205
== Changelog ==
206206

207+
= 1.2.2 =
208+
209+
* Fixed: Uploading an image via Media > Add Media File doesn't show stats #4
210+
* Fixed: Dragging multiple images in quick succession sometimes doesn't optimize the image #3
211+
* Fixed: LQIP doesn't work correctly when the image has a srcset attribute #11
212+
* Fixed: Closing the upload button on the block toolbar stops uploading of media #9
213+
* Fixed: Uploading images with the same name in the same session doesn't add the metadata correctly #8
214+
207215
= 1.2.1 =
208216

209217
* New: If the resulting optimized image is bigger than the original image, the image is now skipped and the original image is kept.

scripts/package.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ function copyDir( src, dest ) {
8787
let hasCopiedFiles = false
8888

8989
for ( const item of items ) {
90+
// Skip hidden files/folders (start with '.')
91+
if ( item.startsWith( '.' ) ) {
92+
continue
93+
}
9094
const srcPath = path.join( src, item )
9195
const destPath = path.join( dest, item )
9296
const stat = fs.statSync( srcPath )

src/admin/js/media-manager/drop-zone.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function addDropZoneListenerToMediaManager( targetDocument ) {
4646
// is triggered. We might break other funcitonality that we don't have
4747
// the conversion to happen.
4848
if ( ! event.target.closest( '.media-frame-uploader' ) && // Allowed to drop in the Media Manager
49+
! event.target.closest( '.supports-drag-drop' ).querySelector( '.media-frame-uploader' ) && // Allowed a fallback to drop in the Media Manager
4950
! event.target.closest( '.media-upload-form' ) && // Allowed to drop in the admin Media > Add Media File.
5051
! event.target.closest( '.editor-post-featured-image' ) && // Allowed to drop in the featured image drop zone.
5152
! event.target.closest( '.editor-styles-wrapper' ) && // Allowed to drop in the block editor when adding new image blocks

src/admin/js/media-manager/progress-modal.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ class ProgressModal {
6868
z-index: 9999999;
6969
`
7070

71+
this.modal.addEventListener( 'mousedown', event => {
72+
// Prevent clicks from removing the upload popover
73+
event.preventDefault()
74+
event.stopPropagation()
75+
event.stopImmediatePropagation()
76+
} )
77+
7178
const wrapper = document.createElement( 'div' )
7279
wrapper.className = 'cimo-progress-wrapper'
7380
wrapper.style.cssText = `

src/admin/js/media-manager/sidebar-info.js

Lines changed: 150 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -62,87 +62,159 @@ function getMediaTypeLabel( mimetype ) {
6262
}
6363
}
6464

65+
function injectCimoMetadata( {
66+
model,
67+
container,
68+
} ) {
69+
if ( ! model || ! container ) {
70+
return
71+
}
72+
73+
// Prevent duplicate inserts
74+
if ( container.querySelector( '.cimo-media-manager-metadata' ) ) {
75+
return
76+
}
77+
78+
let customMetadata = model.get( 'cimo' ) || null
79+
80+
if ( ! customMetadata || Object.keys( customMetadata ).length === 0 ) {
81+
customMetadata = getCachedMetadata(
82+
model.get( 'originalImageName' ) || model.get( 'filename' )
83+
)
84+
}
85+
86+
if ( ! customMetadata ) {
87+
return
88+
}
89+
90+
const customContent = document.createElement( 'div' )
91+
customContent.className = 'cimo-media-manager-metadata'
92+
93+
const convertedFormatRaw = customMetadata.convertedFormat || model.get( 'mime' ) || ''
94+
const mediaTypeLabel = getMediaTypeLabel( convertedFormatRaw )
95+
96+
let html = `
97+
<div class="cimo-media-manager-metadata-title-container">
98+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 672" height="20" width="20">
99+
<path d="M132.5 132.5C182.4 82.5 253 56 336 56C419 56 489.6 82.5 539.5 132.5C589.4 182.5 616 253 616 336C616 419 589.5 489.6 539.5 539.5C489.5 589.4 419 616 336 616C253 616 182.4 589.5 132.5 539.5C82.6 489.5 56 419 56 336C56 253 82.5 182.4 132.5 132.5z"/>
100+
</svg>
101+
<h3 class="cimo-media-manager-metadata-title">
102+
${ escape( sprintf( __( '%s Optimized by Cimo', 'cimo-image-optimizer' ), mediaTypeLabel ) ) }
103+
</h3>
104+
</div>
105+
<ul>
106+
`
107+
108+
const optimizationSavings = customMetadata.compressionSavings
109+
? ( 100 - ( customMetadata.compressionSavings * 100 ) ).toFixed( 2 )
110+
: null
111+
112+
const kbSaved = formatFilesize(
113+
customMetadata.originalFilesize - customMetadata.convertedFilesize,
114+
1,
115+
true
116+
)
117+
118+
const optimizationSavingsClass =
119+
optimizationSavings > 0
120+
? 'cimo-optimization-savings-up'
121+
: 'cimo-optimization-savings-down'
122+
123+
html += `
124+
<li class="cimo-compression-savings ${ escape( optimizationSavingsClass ) }">
125+
Saved ${ escape( optimizationSavings ) }%
126+
<span class="cimo-compression-savings-bytes">(${ escape( kbSaved ) })</span>
127+
</li>
128+
`
129+
130+
html += `
131+
<li class="cimo-filesize-original">
132+
Original: <span class="cimo-value">${ escape( formatFilesize( parseInt( customMetadata.originalFilesize ) || 0 ) ) }</span>
133+
</li>
134+
<li class="cimo-filesize-optimized">
135+
Optimized: <span class="cimo-value">${ escape( formatFilesize( parseInt( customMetadata.convertedFilesize ) || 0 ) ) }</span>
136+
</li>
137+
`
138+
139+
html += `
140+
<li class="cimo-converted">
141+
🏞️ Converted to <span class="cimo-value">${ escape( convertMimetypeToFormat( customMetadata.convertedFormat ) ) }</span>
142+
</li>
143+
`
144+
145+
let conversionTimeDisplay = 'N/A'
146+
if ( customMetadata.conversionTime ) {
147+
const timeMs = parseFloat( customMetadata.conversionTime )
148+
conversionTimeDisplay =
149+
timeMs < 1000
150+
? `${ timeMs.toFixed( 0 ) } ms`
151+
: timeMs < 60000
152+
? `${ ( timeMs / 1000 ).toFixed( 1 ) } sec`
153+
: `${ ( timeMs / 60000 ).toFixed( 1 ) } min`
154+
}
155+
156+
html += `
157+
<li class="cimo-time">
158+
⚡️ Done in <span class="cimo-value">${ escape( conversionTimeDisplay ) }</span>
159+
</li>
160+
</ul>
161+
`
162+
163+
customContent.innerHTML = html
164+
container.appendChild( customContent )
165+
}
166+
65167
domReady( () => {
66168
// Only proceed if wp.media is available (media library is loaded)
67-
if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.view || ! wp.media.view.Attachment || ! wp.media.view.Attachment.Details ) {
169+
if (
170+
typeof wp === 'undefined' ||
171+
! wp.media ||
172+
! wp.media.view ||
173+
! wp.media.view.Attachment ||
174+
! wp.media.view.Attachment.Details
175+
) {
68176
return
69177
}
70178

71-
wp.media.view.Attachment.Details = wp.media.view.Attachment.Details.extend( {
72-
template: function template( view ) {
73-
const html = wp.media.template( 'attachment-details' )( view )
74-
const dom = document.createElement( 'div' )
75-
dom.innerHTML = html
76-
77-
// Get the metadata from the model or the cache
78-
let customMetadata = view.model.get( 'cimo' ) || null
79-
80-
// If the attachment was just uploaded, the model data won't be available, fetch it from the cache
81-
if ( ! customMetadata || Object.keys( customMetadata ).length === 0 ) {
82-
customMetadata = getCachedMetadata( view.model.get( 'originalImageName' ) || view.model.get( 'filename' ) )
83-
}
84-
85-
// TODO: Translate this
86-
const details = dom.querySelector( '.attachment-info' )
87-
if ( customMetadata && details ) {
88-
const customContent = document.createElement( 'div' )
89-
customContent.className = 'cimo-media-manager-metadata'
90-
91-
const convertedFormatRaw = customMetadata.convertedFormat || view.model.get( 'mime' ) || ''
92-
const mediaTypeLabel = getMediaTypeLabel( convertedFormatRaw )
93-
94-
let html = `<div class="cimo-media-manager-metadata-title-container">
95-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 672" height="20" width="20"><path d="M132.5 132.5C182.4 82.5 253 56 336 56C419 56 489.6 82.5 539.5 132.5C589.4 182.5 616 253 616 336C616 419 589.5 489.6 539.5 539.5C489.5 589.4 419 616 336 616C253 616 182.4 589.5 132.5 539.5C82.6 489.5 56 419 56 336C56 253 82.5 182.4 132.5 132.5zM465.5 273.9C477.6 264.2 479.5 246.6 469.9 234.5C460.3 222.4 442.6 220.5 430.5 230.1C378 272.1 330.3 341.9 306.7 379.4C291.4 359.3 267.2 331.1 239.5 312.6C226.6 304 209.2 307.5 200.7 320.4C192.2 333.3 195.6 350.7 208.5 359.2C237.4 378.5 264.1 415.1 274.1 429.9C281.5 440.9 294 447.9 307.9 447.9C322.3 447.9 335.5 440.3 342.8 428C357.2 403.5 410 318.3 465.6 273.8z"/></svg>
96-
<h3 class="cimo-media-manager-metadata-title">${ escape( sprintf( __( '%s Optimized by Cimo', 'cimo-image-optimizer' ), mediaTypeLabel ) ) }</h3>
97-
</div>
98-
<ul>`
99-
100-
/**
101-
* Format optimization savings, display an arrow up or down and color it green or red.
102-
*/
103-
const optimizationSavings = customMetadata.compressionSavings
104-
? ( 100 - ( customMetadata.compressionSavings * 100 ) ).toFixed( 2 )
105-
: null
106-
const kbSaved = formatFilesize( customMetadata.originalFilesize - customMetadata.convertedFilesize, 1, true )
107-
const optimizationSavingsClass = optimizationSavings > 0 ? 'cimo-optimization-savings-up' : 'cimo-optimization-savings-down'
108-
109-
html += `<li class="cimo-compression-savings ${ escape( optimizationSavingsClass ) }">Saved ${ escape( optimizationSavings ) }% <span class="cimo-compression-savings-bytes">(${ escape( kbSaved ) })</span></li>`
110-
111-
/**
112-
* Filesize
113-
*/
114-
const originalSize = formatFilesize( parseInt( customMetadata.originalFilesize ) || 0 )
115-
const convertedSize = formatFilesize( parseInt( customMetadata.convertedFilesize ) || 0 )
116-
html += `<li class="cimo-filesize-original">Original: <span class="cimo-value">${ escape( originalSize ) }</span></li>`
117-
html += `<li class="cimo-filesize-optimized">Optimized: <span class="cimo-value">${ escape( convertedSize ) }</span></li>`
118-
119-
/**
120-
* Original format
121-
*/
122-
html += `<li class="cimo-converted">🏞️ Converted to <span class="cimo-value">${ escape( convertMimetypeToFormat( customMetadata.convertedFormat ) ) }</span></li>`
123-
124-
/**
125-
* Conversion time
126-
*/
127-
// This is number string.
128-
let conversionTimeDisplay = 'N/A'
129-
if ( customMetadata.conversionTime ) {
130-
const timeMs = parseFloat( customMetadata.conversionTime )
131-
if ( timeMs < 1000 ) {
132-
conversionTimeDisplay = `${ timeMs.toFixed( 0 ) } ms`
133-
} else if ( timeMs < 60000 ) {
134-
conversionTimeDisplay = `${ ( timeMs / 1000 ).toFixed( 1 ) } sec`
135-
} else {
136-
conversionTimeDisplay = `${ ( timeMs / 60000 ).toFixed( 1 ) } min`
137-
}
138-
}
139-
html += `<li class="cimo-time">⚡️ Done in <span class="cimo-value">${ escape( conversionTimeDisplay ) }</span></li>`
140-
141-
customContent.innerHTML = html
142-
details.appendChild( customContent )
143-
}
144-
145-
return dom.innerHTML
146-
},
147-
} )
179+
// Editor media library modal (Attachment.Details)
180+
wp.media.view.Attachment.Details =
181+
wp.media.view.Attachment.Details.extend( {
182+
template( view ) {
183+
const html = wp.media.template( 'attachment-details' )( view )
184+
const dom = document.createElement( 'div' )
185+
dom.innerHTML = html
186+
187+
const container = dom.querySelector( '.attachment-info' )
188+
189+
injectCimoMetadata( {
190+
model: view.model,
191+
container,
192+
} )
193+
194+
return dom.innerHTML
195+
},
196+
} )
197+
198+
// Admin Media in Grid View (Attachment.Details.TwoColumn)
199+
if ( wp.media.view.Attachment.Details.TwoColumn ) {
200+
const TwoColumn = wp.media.view.Attachment.Details.TwoColumn
201+
202+
wp.media.view.Attachment.Details.TwoColumn =
203+
TwoColumn.extend( {
204+
render() {
205+
TwoColumn.prototype.render.apply( this, arguments )
206+
207+
const container = this.el.querySelector(
208+
'.attachment-info > .details'
209+
)
210+
211+
injectCimoMetadata( {
212+
model: this.model,
213+
container,
214+
} )
215+
216+
return this
217+
},
218+
} )
219+
}
148220
} )

src/shared/converters/image-converter.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Converter } from './converter-abstract'
2+
import { applyFilters, applyFiltersAsync } from '@wordpress/hooks'
23

34
// Supported output formats
45
const supportedFormats = [
@@ -14,12 +15,12 @@ const supportedFormats = [
1415
class ImageConverter extends Converter {
1516
static get mimeTypes() {
1617
// Accept all images supported by most browsers for conversion.
17-
return [
18+
return applyFilters( 'cimo.imageConverter.mimeTypes', [
1819
'image/jpeg',
1920
'image/png',
2021
'image/webp',
2122
'image/jpg',
22-
]
23+
] )
2324
}
2425

2526
static get showProgress() {
@@ -128,6 +129,12 @@ class ImageConverter extends Converter {
128129
maxDimension = parseFloat( maxDimension )
129130
}
130131

132+
// Allow preprocessing of the image before the conversion
133+
const updatedFile = await applyFiltersAsync( 'cimo.convertImage.prepare', fileItem.file, outputFormat, options )
134+
if ( updatedFile ) {
135+
fileItem.file = updatedFile
136+
}
137+
131138
return new Promise( ( resolve, reject ) => {
132139
const img = new Image()
133140
let objectUrl = null

0 commit comments

Comments
 (0)