Skip to content

Commit 814ef73

Browse files
authored
Merge pull request #24 from FlandreDaisuki/custom-icon
Custom icon
2 parents 932e72d + 81902be commit 814ef73

File tree

4 files changed

+96
-11
lines changed

4 files changed

+96
-11
lines changed

_locales/en/messages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@
9191
"message": "Some search provider name duplicated!"
9292
},
9393

94+
"msgIconUploadNotImage": {
95+
"message": "Please upload available image format! (png, jpg, ico)"
96+
},
97+
98+
"msgIconUploadNotSquareImage": {
99+
"message": "Please upload square image!"
100+
},
101+
94102
"msgSuccessSaveOptions": {
95103
"message": "Saved!"
96104
}

_locales/zh_TW/messages.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,15 @@
8888
},
8989

9090
"msgDuplicatedProviderName": {
91-
"message": "搜尋引擎名字重複"
91+
"message": "搜尋引擎名字重複!"
92+
},
93+
94+
"msgIconUploadNotImage": {
95+
"message": "請上傳合法的圖片檔案! (png, jpg, ico)"
96+
},
97+
98+
"msgIconUploadNotSquareImage": {
99+
"message": "請上傳正方形圖片!"
92100
},
93101

94102
"msgSuccessSaveOptions": {

options/options.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
body {
2-
padding-top: 60px;
1+
body {
2+
padding-top: 60px;
33
}
44

55
.jumbotron {
6-
padding: 2rem 2rem;
6+
padding: 2rem 2rem;
77
}
88

99
.hidden {
@@ -27,6 +27,7 @@
2727

2828
#searchProviderList .sp-icon img {
2929
max-width: 24px;
30+
cursor: pointer;
3031
}
3132

3233
#searchProviderList .sp-name {

options/options.js

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {
3737
function 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) {
4646
function validateSpUrl(url, index) {
4747
if (!/^https?:\/\/.*%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

Comments
 (0)