@@ -18,7 +18,7 @@ function i18nOrdinal(n) {
1818 return `${ n } ` ;
1919}
2020
21- function alertErrorMsgElement ( text ) {
21+ function alertErrorMsg ( text ) {
2222 const msg = createErrorMsgElement ( text ) ;
2323 $ ( '#alertMessages' ) . appendChild ( msg ) ;
2424 setTimeout ( ( ) => {
@@ -37,7 +37,7 @@ function createErrorMsgElement(text) {
3737function validateSpName ( name , index ) {
3838 if ( ! / ^ \S { 2 , 15 } $ / . test ( name ) ) {
3939 const msg = chrome . i18n . getMessage ( 'providerNamePlaceholderError' , i18nOrdinal ( index ) ) ;
40- alertErrorMsgElement ( msg ) ;
40+ alertErrorMsg ( msg ) ;
4141 return false ;
4242 }
4343 return true ;
@@ -46,7 +46,7 @@ function validateSpName(name, index) {
4646function validateSpUrl ( url , index ) {
4747 if ( ! / ^ h t t p s ? : \/ \/ .* % s .* $ / . test ( url ) ) {
4848 const msg = chrome . i18n . getMessage ( 'providerURLPlaceholderError' , i18nOrdinal ( index ) ) ;
49- alertErrorMsgElement ( msg ) ;
49+ alertErrorMsg ( msg ) ;
5050 return false ;
5151 }
5252 return true ;
@@ -102,8 +102,76 @@ function createSpIconElement(src) {
102102 // <img src=""/>
103103 // </span>
104104 const span = $el ( 'span' ) ;
105- span . innerHTML = `<img src="${ src } "/>` ;
106105 span . classList . add ( 'sp-icon' , 'input-group-addon' ) ;
106+
107+ const iconImg = new Image ( ) ;
108+ iconImg . src = src ;
109+
110+ span . appendChild ( iconImg ) ;
111+
112+ const msgIconUploadNotImage = chrome . i18n . getMessage ( 'msgIconUploadNotImage' ) ;
113+ const msgIconUploadNotSquareImage = chrome . i18n . getMessage ( 'msgIconUploadNotSquareImage' ) ;
114+
115+ /* custom icon by click icon image */
116+ iconImg . onclick = ( ) => {
117+ const fileInput = document . createElement ( 'input' ) ;
118+ fileInput . type = 'file' ;
119+ fileInput . onchange = ( ) => {
120+ const file = fileInput . files [ 0 ] ;
121+ if ( file . type . includes ( 'image' ) ) {
122+ const tmpImg = new Image ( ) ;
123+
124+ /* input img load failed */
125+ tmpImg . onerror = ( ) => {
126+ alertErrorMsg ( msgIconUploadNotImage ) ;
127+ } ;
128+
129+ /* input img load successfully */
130+ tmpImg . onload = ( ) => {
131+ if ( tmpImg . naturalHeight !== tmpImg . naturalWidth ) {
132+ alertErrorMsg ( msgIconUploadNotSquareImage ) ;
133+ return ;
134+ }
135+
136+ const canvas = document . createElement ( 'canvas' ) ;
137+ /* we use 24 * 24 resolution in options.html and less resolution in contextMenu */
138+ canvas . width = 24 ;
139+ canvas . height = 24 ;
140+
141+ const ctx = canvas . getContext ( '2d' ) ;
142+ ctx . drawImage ( tmpImg , 0 , 0 , 24 , 24 ) ;
143+
144+ /* Do some strategy to minimize base64 */
145+ function iconEncodeURL ( ctx ) {
146+ const pixels = ctx . getImageData ( 0 , 0 , 24 , 24 ) . data ;
147+ for ( const i in pixels ) {
148+ if ( i % 4 === 3 && pixels [ i ] !== 255 ) {
149+ /* using alpha channel */
150+ return ctx . canvas . toDataURL ( ) ;
151+ }
152+ }
153+
154+ const pngBase64 = ctx . canvas . toDataURL ( ) ;
155+ /* jpeg quality 0.8 is magic number 😅 */
156+ const jpegBase64 = ctx . canvas . toDataURL ( 'image/jpeg' , 0.8 ) ;
157+ if ( pngBase64 . length < jpegBase64 . length ) {
158+ return pngBase64 ;
159+ } else {
160+ return jpegBase64 ;
161+ }
162+ }
163+
164+ iconImg . src = iconEncodeURL ( ctx ) ;
165+ } ;
166+
167+ tmpImg . src = URL . createObjectURL ( file ) ;
168+ } else {
169+ alertErrorMsg ( msgIconUploadNotImage ) ;
170+ }
171+ } ;
172+ fileInput . click ( ) ;
173+ } ;
174+
107175 return span ;
108176}
109177
@@ -237,7 +305,7 @@ saveOptions.onclick = () => {
237305 }
238306
239307 if ( li . children [ 2 ] . classList . contains ( 'sp-edit' ) ) {
240- alertErrorMsgElement ( chrome . i18n . getMessage ( 'msgExistEdittingSearchProviders' ) ) ;
308+ alertErrorMsg ( chrome . i18n . getMessage ( 'msgExistEdittingSearchProviders' ) ) ;
241309 return ;
242310 }
243311
@@ -246,12 +314,12 @@ saveOptions.onclick = () => {
246314 }
247315
248316 if ( selectedCount < 1 ) {
249- alertErrorMsgElement ( chrome . i18n . getMessage ( 'msgAtLeastOneSearchProvider' ) ) ;
317+ alertErrorMsg ( chrome . i18n . getMessage ( 'msgAtLeastOneSearchProvider' ) ) ;
250318 return ;
251319 }
252320
253321 if ( nameSet . size < storedSettings . storageProviders . length ) {
254- alertErrorMsgElement ( chrome . i18n . getMessage ( 'msgDuplicatedProviderName' ) ) ;
322+ alertErrorMsg ( chrome . i18n . getMessage ( 'msgDuplicatedProviderName' ) ) ;
255323 return ;
256324 }
257325
0 commit comments