Change format rule

This commit is contained in:
sienori 2018-07-31 20:35:03 +09:00
parent a3bdaeca75
commit d3bcce91b6
13 changed files with 1399 additions and 1353 deletions

View file

@ -10,278 +10,282 @@ browser.runtime.onInstalled.addListener(function(){
browser.runtime.openOptionsPage(); browser.runtime.openOptionsPage();
}); });
*/ */
function settingsObj() {}; function settingsObj() {}
(function () { (function() {
//オプションページを書き換え,設定の初期化
//Rewrite option page, initialize setting
settingsObj.prototype.initOptionsPage = function() {
return new Promise(function(resolve, reject) {
labelSet();
getSettingsByHtml();
overRideSettingsByStorage().then(function() {
overRideHtml();
saveSettings();
resolve();
});
});
};
//オプションページから設定を保存
//Save settings from options page
settingsObj.prototype.saveOptionsPage = function() {
return new Promise(function(resolve, reject) {
getSettingsByHtml();
saveSettings().then(function() {
resolve();
});
});
};
//設定を初期化
//Initialize setting
settingsObj.prototype.init = function() {
return new Promise(function(resolve, reject) {
getSettings().then(function() {
resolve(Settings);
});
});
};
//設定を返す
//return settings
settingsObj.prototype.get = function() {
return Settings;
};
//受け取ったオブジェクトを保存
//Save the received object
settingsObj.prototype.save = function(settings) {
return new Promise(function(resolve, reject) {
for (let i in settings) {
Settings[i] = settings[i];
}
saveSettings().then(function() {
resolve();
});
});
};
//設定を削除
//Delete settings
settingsObj.prototype.clear = function(setting) {
return new Promise(function(resolve, reject) {
delete Settings[setting];
saveSettings().then(function() {
resolve();
});
});
};
//全ての設定を削除
//Delete all settings
settingsObj.prototype.clearAll = function() {
return new Promise(function(resolve, reject) {
Settings = new settingsObj();
saveSettings().then(function() {
resolve();
});
});
};
settingsObj.prototype.labelSet = function() {
labelSet();
};
//オプションページを書き換え,設定の初期化 //let Settings = new settingsObj();
//Rewrite option page, initialize setting let Settings = {};
settingsObj.prototype.initOptionsPage = function () { //S = new settingsObj(); //外部から呼び出し Call from outside
return new Promise(function (resolve, reject) {
labelSet(); //spanやoptionのidbuttonのclassに"Label"が含まれるときi18nから値を取得して書き換え
getSettingsByHtml(); //When "label" is included in span and option id, button class Retrieve the value from i18n and rewrite it
overRideSettingsByStorage().then(function () { function labelSet() {
overRideHtml(); textLabelSet("p");
saveSettings(); textLabelSet("span");
resolve(); textLabelSet("option");
}); textLabelSet("input");
})
}; function textLabelSet(tagName) {
//オプションページから設定を保存 const items = document.getElementsByTagName(tagName);
//Save settings from options page for (let i of items) {
settingsObj.prototype.saveOptionsPage = function () { let label;
return new Promise(function (resolve, reject) { if (i.id != undefined && i.id.includes("Label")) {
getSettingsByHtml(); label = browser.i18n.getMessage(i.id);
saveSettings().then(function () { } else if (i.className != undefined && i.className.includes("Label")) {
resolve(); const labelList = i.className.split(" ").filter((element, index, array) => {
}); return element.includes("Label");
}) });
}; label = browser.i18n.getMessage(labelList[0]);
//設定を初期化 } else {
//Initialize setting continue;
settingsObj.prototype.init = function () { }
return new Promise(function (resolve, reject) {
getSettings().then(function () { if (!label == "") {
resolve(Settings); if (tagName == "input") {
}) switch (i.type) {
}) case "button":
} case "submit":
//設定を返す i.value = label;
//return settings break;
settingsObj.prototype.get = function () { case "text":
return Settings; i.placeholder = label;
}; break;
//受け取ったオブジェクトを保存
//Save the received object
settingsObj.prototype.save = function (settings) {
return new Promise(function (resolve, reject) {
for (let i in settings) {
Settings[i] = settings[i];
} }
saveSettings().then(function () { } else {
resolve(); i.innerHTML = label;
}); }
}
}
}
}
//storageからSettingsの項目を取得して存在しない物をSettingsに上書き
//Retrieve the Settings item from storage and overwrite Settings that do not exist
function overRideSettingsByStorage() {
return new Promise(function(resolve, reject) {
browser.storage.local.get("Settings", function(value) {
for (let i in Settings) {
if (value.Settings != undefined && value.Settings[i] != undefined) {
Settings[i] = value.Settings[i];
}
}
for (let i in value.Settings) {
if (Settings[i] == undefined) Settings[i] = value.Settings[i];
}
resolve();
});
});
}
//オプションページにSettingsを反映
//Reflect Settings on option page
function overRideHtml() {
let inputs = document.getElementsByTagName("input");
for (let i in inputs) {
if (inputs[i].id == undefined) continue;
if (inputs[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1)
continue;
switch (inputs[i].type) {
case "text":
case "number":
case "search":
case "tel":
case "url":
case "email":
case "password":
case "datetime":
case "month":
case "week":
case "time":
case "datetime-local":
case "range":
case "color":
inputs[i].value = Settings[inputs[i].id];
break;
case "checkbox":
inputs[i].checked = Settings[inputs[i].id];
break;
case "radio":
if (Settings[inputs[i].name] == inputs[i].value) {
inputs[i].checked = true;
}
break;
}
}
let textareas = document.getElementsByTagName("textarea");
for (let i in textareas) {
if (textareas[i].id == undefined) continue;
if (textareas[i].className != undefined && textareas[i].className.indexOf("noSetting") != -1)
continue;
textareas[i].value = Settings[textareas[i].id];
}
let selects = document.getElementsByTagName("select");
for (let i in selects) {
if (selects[i].id == undefined) continue;
if (selects[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1)
continue;
selects[i].value = Settings[selects[i].id];
}
}
//オプションページから設定の値を取得
//Get setting value from option page
function getSettingsByHtml() {
let inputs = document.getElementsByTagName("input");
for (let i in inputs) {
if (inputs[i].id == undefined) continue;
if (inputs[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1)
continue;
switch (inputs[i].type) {
case "text":
case "number":
case "search":
case "tel":
case "url":
case "email":
case "password":
case "datetime":
case "month":
case "week":
case "time":
case "datetime-local":
case "range":
case "color":
Settings[inputs[i].id] = inputs[i].value;
break;
case "checkbox":
Settings[inputs[i].id] = inputs[i].checked;
break;
case "radio":
if (inputs[i].checked == true) {
Settings[inputs[i].name] = inputs[i].value;
}
break;
}
}
let textareas = document.getElementsByTagName("textarea");
for (let i in textareas) {
if (textareas[i].id == undefined) continue;
if (textareas[i].className != undefined && textareas[i].className.indexOf("noSetting") != -1)
continue;
Settings[textareas[i].id] = textareas[i].value;
}
let selects = document.getElementsByTagName("select");
for (let i in selects) {
if (selects[i].id == undefined) continue;
if (selects[i].className != undefined && selects[i].className.indexOf("noSetting") != -1)
continue;
Settings[selects[i].id] = selects[i].value;
}
}
//ストレージが変更されたらget
browser.storage.onChanged.addListener(changedSettings);
function changedSettings(changes, area) {
if (Object.keys(changes).includes("Settings")) {
Settings = changes.Settings.newValue;
}
}
function getSettings() {
return new Promise(function(resolve, reject) {
browser.storage.local.get("Settings", function(value) {
Settings = value.Settings;
resolve(Settings);
});
});
}
function saveSettings() {
return new Promise(function(resolve, reject) {
browser.storage.local
.set({
Settings: Settings
}) })
}; .then(function() {
//設定を削除 resolve(Settings);
//Delete settings });
settingsObj.prototype.clear = function (setting) { });
return new Promise(function (resolve, reject) { }
delete Settings[setting]; })();
saveSettings().then(function () {
resolve();
})
})
}
//全ての設定を削除
//Delete all settings
settingsObj.prototype.clearAll = function () {
return new Promise(function (resolve, reject) {
Settings = new settingsObj();
saveSettings().then(function () {
resolve();
})
})
}
settingsObj.prototype.labelSet = function () {
labelSet();
}
//let Settings = new settingsObj();
let Settings = {};
//S = new settingsObj(); //外部から呼び出し Call from outside
//spanやoptionのidbuttonのclassに"Label"が含まれるときi18nから値を取得して書き換え
//When "label" is included in span and option id, button class Retrieve the value from i18n and rewrite it
function labelSet() {
textLabelSet("p");
textLabelSet("span");
textLabelSet("option");
textLabelSet("input");
function textLabelSet(tagName) {
const items = document.getElementsByTagName(tagName);
for (let i of items) {
let label;
if (i.id != undefined && i.id.includes("Label")) {
label = browser.i18n.getMessage(i.id);
} else if (i.className != undefined && i.className.includes("Label")) {
const labelList = i.className.split(' ').filter((element, index, array) => {
return element.includes("Label");
});
label = browser.i18n.getMessage(labelList[0]);
} else {
continue;
}
if (!label == "") {
if (tagName == "input") {
switch (i.type) {
case "button":
case "submit":
i.value = label;
break;
case "text":
i.placeholder = label;
break;
}
} else {
i.innerHTML = label;
}
}
}
}
}
//storageからSettingsの項目を取得して存在しない物をSettingsに上書き
//Retrieve the Settings item from storage and overwrite Settings that do not exist
function overRideSettingsByStorage() {
return new Promise(function (resolve, reject) {
browser.storage.local.get("Settings", function (value) {
for (let i in Settings) {
if (value.Settings != undefined && value.Settings[i] != undefined) {
Settings[i] = value.Settings[i];
}
}
for (let i in value.Settings) {
if (Settings[i] == undefined) Settings[i] = value.Settings[i];
}
resolve();
})
})
}
//オプションページにSettingsを反映
//Reflect Settings on option page
function overRideHtml() {
let inputs = document.getElementsByTagName("input");
for (let i in inputs) {
if (inputs[i].id == undefined) continue;
if (inputs[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1) continue;
switch (inputs[i].type) {
case "text":
case "number":
case "search":
case "tel":
case "url":
case "email":
case "password":
case "datetime":
case "month":
case "week":
case "time":
case "datetime-local":
case "range":
case "color":
inputs[i].value = Settings[inputs[i].id];
break;
case "checkbox":
inputs[i].checked = Settings[inputs[i].id];
break;
case "radio":
if (Settings[inputs[i].name] == inputs[i].value) {
inputs[i].checked = true;
}
break;
}
}
let textareas = document.getElementsByTagName("textarea");
for (let i in textareas) {
if (textareas[i].id == undefined) continue;
if (textareas[i].className != undefined && textareas[i].className.indexOf("noSetting") != -1) continue;
textareas[i].value = Settings[textareas[i].id];
}
let selects = document.getElementsByTagName("select");
for (let i in selects) {
if (selects[i].id == undefined) continue;
if (selects[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1) continue;
selects[i].value = Settings[selects[i].id];
}
}
//オプションページから設定の値を取得
//Get setting value from option page
function getSettingsByHtml() {
let inputs = document.getElementsByTagName("input");
for (let i in inputs) {
if (inputs[i].id == undefined) continue;
if (inputs[i].className != undefined && inputs[i].className.indexOf("noSetting") != -1) continue;
switch (inputs[i].type) {
case "text":
case "number":
case "search":
case "tel":
case "url":
case "email":
case "password":
case "datetime":
case "month":
case "week":
case "time":
case "datetime-local":
case "range":
case "color":
Settings[inputs[i].id] = inputs[i].value;
break;
case "checkbox":
Settings[inputs[i].id] = inputs[i].checked;
break;
case "radio":
if (inputs[i].checked == true) {
Settings[inputs[i].name] = inputs[i].value;
}
break;
}
}
let textareas = document.getElementsByTagName("textarea");
for (let i in textareas) {
if (textareas[i].id == undefined) continue;
if (textareas[i].className != undefined && textareas[i].className.indexOf("noSetting") != -1) continue;
Settings[textareas[i].id] = textareas[i].value;
}
let selects = document.getElementsByTagName("select");
for (let i in selects) {
if (selects[i].id == undefined) continue;
if (selects[i].className != undefined && selects[i].className.indexOf("noSetting") != -1) continue;
Settings[selects[i].id] = selects[i].value;
}
}
//ストレージが変更されたらget
browser.storage.onChanged.addListener(changedSettings);
function changedSettings(changes, area) {
if (Object.keys(changes).includes("Settings")) {
Settings = changes.Settings.newValue;
}
}
function getSettings() {
return new Promise(function (resolve, reject) {
browser.storage.local.get("Settings", function (value) {
Settings = value.Settings;
resolve(Settings);
});
})
}
function saveSettings() {
return new Promise(function (resolve, reject) {
browser.storage.local.set({
'Settings': Settings
}).then(function () {
resolve(Settings);
});
})
}
}());

View file

@ -1,178 +1,182 @@
{ {
"extName": { "extName": {
"message": "Simple Translate" "message": "Simple Translate"
}, },
"extDescription": { "extDescription": {
"message": "View translations easily as you browse the web." "message": "View translations easily as you browse the web."
}, },
"donateWithPaypalLabel": { "donateWithPaypalLabel": {
"message": "Donate with PayPal" "message": "Donate with PayPal"
}, },
"initialTextArea": { "initialTextArea": {
"message": "Enter text" "message": "Enter text"
}, },
"showLink": { "showLink": {
"message": "Translate this page" "message": "Translate this page"
}, },
"targetLangLabel": { "targetLangLabel": {
"message": "Target language" "message": "Target language"
}, },
"langList": { "langList": {
"message": "af:Afrikaans, sq:Albanian, am:Amharic, ar:Arabic, hy:Armenian, az:Azerbaijani, eu:Basque, be:Belarusian, bn:Bengali, bs:Bosnian, bg:Bulgarian, ca:Catalan, ceb:Cebuano, ny:Chewa, zh-CN:Chinese (PRC), zh-TW:Chinese (Taiwan), co:Corsican, hr:Croatian, cs:Czech, da:Danish, nl:Dutch, en:English, eo:Esperanto, et:Estonian, fi:Finnish, fr:French, fy:Frisian, gl:Galician, ka:Georgian, de:German, el:Greek, gu:Gujarati, ht:Haitian, ha:Hausa, haw:Hawaiian, he:Hebrew, hi:Hindi, hu:Hungarian, is:Icelandic, ig:Igbo, id:Indonesian, ga:Irish, it:Italian, ja:Japanese, jv:Javanese, kn:Kannada, kk:Kazakh, km:Khmer, ky:Kirghiz, ko:Korean, ku:Kurdish, lo:Laotian, la:Latin, lv:Latvian, lt:Lithuanian, lb:Luxembourgish, mk:Macedonian, mg:Malagasy, ms:Malay, ml:Malayalam, mt:Maltese, mi:Maori, mr:Marathi, mn:Mongolian, hmn:Monk, my:Myanmar, ne:Nepali, no:Norwegian, fa:Persian, pl:Polish, pt:Portuguese, pa:Punjabi, ps:Pushto, ro:Romanian, ru:Russian, sm:Samoan, gd:Scottish Gaelic, sr:Serbian, sn:Shona, sd:Sindhi, si:Sinhala, sk:Slovak, sl:Slovenian, so:Somali, sx:Sotho, es:Spanish, su:Sundanese, sw:Swahili, sv:Swedish, tl:Tagalog, tg:Tajiki, ta:Tamil, te:Telugu, th:Thai, tr:Turkish, uk:Ukrainian, ur:Urdu, uz:Uzbek, vi:Vietnamese, cy:Welsh, xh:Xosa, yi:Yiddish, yo:Yoruba, zu:Zulu" "message":
}, "af:Afrikaans, sq:Albanian, am:Amharic, ar:Arabic, hy:Armenian, az:Azerbaijani, eu:Basque, be:Belarusian, bn:Bengali, bs:Bosnian, bg:Bulgarian, ca:Catalan, ceb:Cebuano, ny:Chewa, zh-CN:Chinese (PRC), zh-TW:Chinese (Taiwan), co:Corsican, hr:Croatian, cs:Czech, da:Danish, nl:Dutch, en:English, eo:Esperanto, et:Estonian, fi:Finnish, fr:French, fy:Frisian, gl:Galician, ka:Georgian, de:German, el:Greek, gu:Gujarati, ht:Haitian, ha:Hausa, haw:Hawaiian, he:Hebrew, hi:Hindi, hu:Hungarian, is:Icelandic, ig:Igbo, id:Indonesian, ga:Irish, it:Italian, ja:Japanese, jv:Javanese, kn:Kannada, kk:Kazakh, km:Khmer, ky:Kirghiz, ko:Korean, ku:Kurdish, lo:Laotian, la:Latin, lv:Latvian, lt:Lithuanian, lb:Luxembourgish, mk:Macedonian, mg:Malagasy, ms:Malay, ml:Malayalam, mt:Maltese, mi:Maori, mr:Marathi, mn:Mongolian, hmn:Monk, my:Myanmar, ne:Nepali, no:Norwegian, fa:Persian, pl:Polish, pt:Portuguese, pa:Punjabi, ps:Pushto, ro:Romanian, ru:Russian, sm:Samoan, gd:Scottish Gaelic, sr:Serbian, sn:Shona, sd:Sindhi, si:Sinhala, sk:Slovak, sl:Slovenian, so:Somali, sx:Sotho, es:Spanish, su:Sundanese, sw:Swahili, sv:Swedish, tl:Tagalog, tg:Tajiki, ta:Tamil, te:Telugu, th:Thai, tr:Turkish, uk:Ukrainian, ur:Urdu, uz:Uzbek, vi:Vietnamese, cy:Welsh, xh:Xosa, yi:Yiddish, yo:Yoruba, zu:Zulu"
},
"settingsLabel": { "settingsLabel": {
"message": "Settings" "message": "Settings"
}, },
"generalLabel": { "generalLabel": {
"message": "General" "message": "General"
}, },
"targetLangCaptionLabel": { "targetLangCaptionLabel": {
"message": "Select the default target language." "message": "Select the default target language."
}, },
"webPageLabel": { "webPageLabel": {
"message": "Web page" "message": "Web page"
}, },
"ifShowCandidateLabel": { "ifShowCandidateLabel": {
"message": "Show translation candidates" "message": "Show translation candidates"
}, },
"ifShowCandidateCaptionLabel": { "ifShowCandidateCaptionLabel": {
"message": "Show multiple translation candidates when a single word is translated." "message": "Show multiple translation candidates when a single word is translated."
}, },
"whenSelectTextLabel": { "whenSelectTextLabel": {
"message": "Behavior when selecting text" "message": "Behavior when selecting text"
}, },
"ifAutoTranslateLabel": { "ifAutoTranslateLabel": {
"message": "Display translation panel" "message": "Display translation panel"
}, },
"ifAutoTranslateCaptionLabel": { "ifAutoTranslateCaptionLabel": {
"message": "Directly display the translation panel without displaying the button." "message": "Directly display the translation panel without displaying the button."
}, },
"ifShowButtonLabel": { "ifShowButtonLabel": {
"message": "Display translation button" "message": "Display translation button"
}, },
"ifShowButtonCaptionLabel": { "ifShowButtonCaptionLabel": {
"message": "Display the translation button to open the panel when clicked." "message": "Display the translation button to open the panel when clicked."
}, },
"dontshowbuttonlabel": { "dontshowbuttonlabel": {
"message": "Don't display button or panel" "message": "Don't display button or panel"
}, },
"dontshowbuttonCaptionlabel": { "dontshowbuttonCaptionlabel": {
"message": "Don't display the translation button or the translation panel." "message": "Don't display the translation button or the translation panel."
}, },
"ifCheckLangLabel": { "ifCheckLangLabel": {
"message": "Do not display the button if translation is not required" "message": "Do not display the button if translation is not required"
}, },
"ifCheckLangCaptionLabel": { "ifCheckLangCaptionLabel": {
"message": "Detects the language of the selected text, and if it is the same as the target language, the button is not displayed." "message":
}, "Detects the language of the selected text, and if it is the same as the target language, the button is not displayed."
},
"toolbarLabel": { "toolbarLabel": {
"message": "Toolbar popup" "message": "Toolbar popup"
}, },
"ifChangeSecondLangLabel": { "ifChangeSecondLangLabel": {
"message": "Automatically switch to the second language" "message": "Automatically switch to the second language"
}, },
"ifChangeSecondLangCaptionLabel": { "ifChangeSecondLangCaptionLabel": {
"message": "Detects the language of the input text, and if it is the same as the default target language, translate it into the second language." "message":
}, "Detects the language of the input text, and if it is the same as the default target language, translate it into the second language."
"secondTargetLangLabel": { },
"message": "Second language" "secondTargetLangLabel": {
}, "message": "Second language"
"secondTargetLangCaptionLabel": { },
"message": "Select the second target language." "secondTargetLangCaptionLabel": {
}, "message": "Select the second target language."
},
"menuLabel":{ "menuLabel": {
"message": "Context menu" "message": "Context menu"
}, },
"ifShowMenuLabel": { "ifShowMenuLabel": {
"message": "Display the context menu" "message": "Display the context menu"
}, },
"ifShowMenuCaptionLabel": { "ifShowMenuCaptionLabel": {
"message": "Add items to the context menu displayed when right clicking on the web page or the tab." "message":
}, "Add items to the context menu displayed when right clicking on the web page or the tab."
},
"styleLabel": { "styleLabel": {
"message": "Style" "message": "Style"
}, },
"buttonStyleLabel": { "buttonStyleLabel": {
"message": "Translation button" "message": "Translation button"
}, },
"buttonStyleCaptionLabel": { "buttonStyleCaptionLabel": {
"message": "Specify the style of the translation button displayed on the web page." "message": "Specify the style of the translation button displayed on the web page."
}, },
"buttonSizeLabel": { "buttonSizeLabel": {
"message": "Size" "message": "Size"
}, },
"buttonPositionLabel": { "buttonPositionLabel": {
"message": "Display position" "message": "Display position"
}, },
"rightUpLabel": { "rightUpLabel": {
"message": "Top right" "message": "Top right"
}, },
"rightDownLabel": { "rightDownLabel": {
"message": "Bottom right" "message": "Bottom right"
}, },
"leftUpLabel": { "leftUpLabel": {
"message": "Top left" "message": "Top left"
}, },
"leftDownLabel": { "leftDownLabel": {
"message": "Bottom left" "message": "Bottom left"
}, },
"panelStyleLabel": { "panelStyleLabel": {
"message": "Translation panel" "message": "Translation panel"
}, },
"panelStyleCaptionLabel": { "panelStyleCaptionLabel": {
"message": "Specify the style of the translation panel displayed on the web page." "message": "Specify the style of the translation panel displayed on the web page."
}, },
"widthLabel": { "widthLabel": {
"message": "Width" "message": "Width"
}, },
"heightLabel": { "heightLabel": {
"message": "Height" "message": "Height"
}, },
"fontSizeLabel": { "fontSizeLabel": {
"message": "Font size" "message": "Font size"
}, },
"bgColorLabel": { "bgColorLabel": {
"message": "Background color" "message": "Background color"
}, },
"informationLabel": { "informationLabel": {
"message": "Information" "message": "Information"
}, },
"licenseLabel": { "licenseLabel": {
"message": "License" "message": "License"
}, },
"donationLabel": { "donationLabel": {
"message": "Please make a donation" "message": "Please make a donation"
}, },
"donationCaptionLabel": { "donationCaptionLabel": {
"message": "Thank you for using Simple Translate.<br>Your support will be a big encouragement, as I continue to develop the add-on.<br>If you like Simple Translate, I would be pleased if you could consider donating." "message":
}, "Thank you for using Simple Translate.<br>Your support will be a big encouragement, as I continue to develop the add-on.<br>If you like Simple Translate, I would be pleased if you could consider donating."
"amazonTitleLabel": { },
"message": "amazon.co.jp eGift Cards" "amazonTitleLabel": {
}, "message": "amazon.co.jp eGift Cards"
"addonPageLabel": { },
"message": "Add-on page" "addonPageLabel": {
}, "message": "Add-on page"
"amazonUrl": { },
"message": "https://www.amazon.co.jp/dp/B004N3APGO?language=en_US" "amazonUrl": {
}, "message": "https://www.amazon.co.jp/dp/B004N3APGO?language=en_US"
"addonUrl": { },
"message": "https:\/\/addons.mozilla.org\/en-US\/firefox\/addon\/simple-translate\/?src=optionpage" "addonUrl": {
}, "message": "https://addons.mozilla.org/en-US/firefox/addon/simple-translate/?src=optionpage"
},
"translatePageMenu": {
"translatePageMenu": { "message": "Translate this page"
"message": "Translate this page" },
}, "translateTextMenu": {
"translateTextMenu": { "message": "Translate selected text"
"message": "Translate selected text" },
}, "translateLinkMenu": {
"translateLinkMenu": { "message": "Translate selected link"
"message": "Translate selected link" }
}
} }

View file

@ -4,121 +4,127 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//初回起動時にオプションページを表示して設定を初期化 //初回起動時にオプションページを表示して設定を初期化
browser.runtime.onInstalled.addListener((details) => { browser.runtime.onInstalled.addListener(details => {
if (details.reason != 'install' && details.reason != 'update') return; if (details.reason != "install" && details.reason != "update") return;
browser.tabs.create({ browser.tabs.create({
url: "options/options.html#information?action=updated", url: "options/options.html#information?action=updated",
active: false active: false
}); });
}); });
let S = new settingsObj(); let S = new settingsObj();
browser.storage.onChanged.addListener(showMenu); browser.storage.onChanged.addListener(showMenu);
if (typeof (browser.contextMenus.onShown) != 'undefined') browser.contextMenus.onShown.addListener(updateMenu); if (typeof browser.contextMenus.onShown != "undefined")
browser.contextMenus.onShown.addListener(updateMenu);
S.init().then(function () { S.init().then(function() {
showMenu(); showMenu();
}); });
function showMenu() { function showMenu() {
if (S.get().ifShowMenu) { if (S.get().ifShowMenu) {
menuRemove(); menuRemove();
menuCreate(); menuCreate();
} else menuRemove(); } else menuRemove();
} }
//テキストまたはリンクの選択時はページ翻訳を非表示にする //テキストまたはリンクの選択時はページ翻訳を非表示にする
function updateMenu(info, tab) { function updateMenu(info, tab) {
if (info.contexts.includes('selection') || info.contexts.includes('link')) { if (info.contexts.includes("selection") || info.contexts.includes("link")) {
browser.contextMenus.update('translatePage', { contexts: ['password'] }); //passwordにすることで事実上無効にする browser.contextMenus.update("translatePage", { contexts: ["password"] }); //passwordにすることで事実上無効にする
} else { } else {
browser.contextMenus.update('translatePage', { contexts: ['all'] }); browser.contextMenus.update("translatePage", { contexts: ["all"] });
} }
browser.contextMenus.refresh(); browser.contextMenus.refresh();
} }
//メニューを表示 //メニューを表示
function menuCreate() { function menuCreate() {
browser.contextMenus.create({ browser.contextMenus.create({
id: "translatePageOnTab", id: "translatePageOnTab",
title: browser.i18n.getMessage("translatePageMenu"), title: browser.i18n.getMessage("translatePageMenu"),
contexts: ["tab"], contexts: ["tab"]
}); });
browser.contextMenus.create({ browser.contextMenus.create({
id: "translatePage", id: "translatePage",
title: browser.i18n.getMessage("translatePageMenu"), title: browser.i18n.getMessage("translatePageMenu"),
contexts: ["all"], contexts: ["all"]
}); });
browser.contextMenus.create({ browser.contextMenus.create({
id: "translateText", id: "translateText",
title: browser.i18n.getMessage("translateTextMenu"), title: browser.i18n.getMessage("translateTextMenu"),
contexts: ["selection"], contexts: ["selection"]
}); });
browser.contextMenus.create({ browser.contextMenus.create({
id: "translateLink", id: "translateLink",
title: browser.i18n.getMessage("translateLinkMenu"), title: browser.i18n.getMessage("translateLinkMenu"),
contexts: ["link"], contexts: ["link"]
}); });
} }
//メニューを削除 //メニューを削除
function menuRemove() { function menuRemove() {
browser.contextMenus.removeAll(); browser.contextMenus.removeAll();
} }
//メニュークリック時 //メニュークリック時
browser.contextMenus.onClicked.addListener(function (info, tab) { browser.contextMenus.onClicked.addListener(function(info, tab) {
switch (info.menuItemId) { switch (info.menuItemId) {
case "translatePage": case "translatePage":
case "translatePageOnTab": case "translatePageOnTab":
translatePageMenu(info, tab); translatePageMenu(info, tab);
break; break;
case "translateText": case "translateText":
translateTextMenu(info, tab); translateTextMenu(info, tab);
break; break;
case "translateLink": case "translateLink":
translateLinkMenu(info, tab); translateLinkMenu(info, tab);
break; break;
} }
}); });
//テキストを翻訳 //テキストを翻訳
function translateTextMenu(info, tab) { function translateTextMenu(info, tab) {
browser.tabs.sendMessage( browser.tabs.sendMessage(tab.id, {
tab.id, { message: "showPanelFromMenu"
message: "showPanelFromMenu" });
}
)
} }
//ページ全体を翻訳 //ページ全体を翻訳
function translatePageMenu(info, tab) { function translatePageMenu(info, tab) {
browser.tabs.create({ browser.tabs.create({
'url': "https://translate.google.com/translate?hl=" + S.get().targetLang + "&sl=auto&u=" + encodeURIComponent(info.pageUrl), url:
'active': true, "https://translate.google.com/translate?hl=" +
'index': tab.index + 1 S.get().targetLang +
}); "&sl=auto&u=" +
encodeURIComponent(info.pageUrl),
active: true,
index: tab.index + 1
});
} }
//リンクを翻訳 //リンクを翻訳
function translateLinkMenu(info, tab) { function translateLinkMenu(info, tab) {
browser.tabs.create({ browser.tabs.create({
'url': "https://translate.google.com/translate?hl=" + S.get().targetLang + "&sl=auto&u=" + encodeURIComponent(info.linkUrl), url:
'active': true, "https://translate.google.com/translate?hl=" +
'index': tab.index + 1 S.get().targetLang +
}); "&sl=auto&u=" +
encodeURIComponent(info.linkUrl),
active: true,
index: tab.index + 1
});
} }
//スクリプトからのメッセージに返信 //スクリプトからのメッセージに返信
browser.runtime.onMessage.addListener(function (request) { browser.runtime.onMessage.addListener(function(request) {
switch (request.message) { switch (request.message) {
case "getSetting": case "getSetting":
break; break;
} }
}); });

View file

@ -1,57 +1,56 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"version": "1.8.2", "version": "1.8.2",
"name": "__MSG_extName__", "name": "__MSG_extName__",
"description": "__MSG_extDescription__", "description": "__MSG_extDescription__",
"default_locale": "en", "default_locale": "en",
"applications": { "applications": {
"gecko": { "gecko": {
"id": "simple-translate@sienori" "id": "simple-translate@sienori"
} }
},
"permissions": ["<all_urls>", "storage", "contextMenus"],
"options_ui": {
"page": "options/options.html",
"open_in_tab": true
},
"icons": {
"512": "icons/512.png",
"128": "icons/128.png",
"64": "icons/64.png",
"48": "icons/48.png",
"32": "icons/32.png"
},
"background": {
"scripts": ["Settings.js", "background.js"]
},
"browser_action": {
"default_icon": {
"512": "icons/512.png",
"128": "icons/128.png",
"64": "icons/64.png",
"48": "icons/48.png",
"38": "icons/38.png",
"32": "icons/32.png",
"19": "icons/19.png",
"16": "icons/16.png"
}, },
"default_popup": "popup/popup.html"
},
"permissions": ["<all_urls>", "storage", "contextMenus"], "content_scripts": [
{
"options_ui": { "all_frames": true,
"page": "options/options.html", "matches": ["http://*/*", "https://*/*", "<all_urls>"],
"open_in_tab": true "css": ["simple-translate.css"],
}, "js": ["Settings.js", "translate.js", "simple-translate.js"]
}
"icons": { ]
"512": "icons/512.png",
"128": "icons/128.png",
"64": "icons/64.png",
"48": "icons/48.png",
"32": "icons/32.png"
},
"background": {
"scripts": ["Settings.js", "background.js"]
},
"browser_action": {
"default_icon": {
"512": "icons/512.png",
"128": "icons/128.png",
"64": "icons/64.png",
"48": "icons/48.png",
"38": "icons/38.png",
"32": "icons/32.png",
"19": "icons/19.png",
"16": "icons/16.png"
},
"default_popup": "popup/popup.html"
},
"content_scripts": [
{
"all_frames":true,
"matches": ["http://*/*", "https://*/*", "<all_urls>"],
"css": ["simple-translate.css"],
"js": ["Settings.js", "translate.js", "simple-translate.js"]
}
]
} }

View file

@ -1,221 +1,218 @@
:root { :root {
--main-text: #0c0c0d; --main-text: #0c0c0d;
--sub-text: #737373; --sub-text: #737373;
--line: #ededf0; --line: #ededf0;
--button: #d7d7db; --button: #d7d7db;
--highlight: #5595ff; --highlight: #5595ff;
--main-bg: #ffffff; --main-bg: #ffffff;
--new: #ff4f4f; --new: #ff4f4f;
} }
body { body {
font-family: 'Segoe UI', 'San Francisco', 'Ubuntu', 'Fira Sans', 'Roboto', 'Arial', 'Helvetica', sans-serif; font-family: "Segoe UI", "San Francisco", "Ubuntu", "Fira Sans", "Roboto", "Arial", "Helvetica",
font-size: 15px; sans-serif;
font-weight: 400; font-size: 15px;
color: var(--main-text); font-weight: 400;
background-color: var(--main-bg); color: var(--main-text);
line-height: 1.5; background-color: var(--main-bg);
display: flex; line-height: 1.5;
flex-direction: row; display: flex;
flex-direction: row;
} }
p { p {
margin: 0px; margin: 0px;
} }
ul { ul {
padding: 0px; padding: 0px;
} }
li { li {
list-style-type: none; list-style-type: none;
} }
hr { hr {
width: 100%; width: 100%;
background-color: var(--line); background-color: var(--line);
height: 1px; height: 1px;
border: none; border: none;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
/*----sidebar----*/ /*----sidebar----*/
#sidebar { #sidebar {
font-size: 17px; font-size: 17px;
font-weight: 400; font-weight: 400;
text-align: right; text-align: right;
flex-shrink: 0; flex-shrink: 0;
-moz-user-select: none; -moz-user-select: none;
} }
.titleContainer { .titleContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
.logo { .logo {
height: 64px; height: 64px;
width: 64px; width: 64px;
} }
.logotitle { .logotitle {
text-align: left; text-align: left;
font-size: 14px; font-size: 14px;
font-weight: 300; font-weight: 300;
color: var(--sub-text); color: var(--sub-text);
/*margin: auto;*/ /*margin: auto;*/
} }
.sidebarItem:hover { .sidebarItem:hover {
text-decoration-line: underline; text-decoration-line: underline;
} }
#sidebar > ul { #sidebar > ul {
padding-left: 40px; padding-left: 40px;
} }
#sidebar > ul > li { #sidebar > ul > li {
padding: 10px 15px; padding: 10px 15px;
} }
#sidebar .selected { #sidebar .selected {
color: var(--highlight); color: var(--highlight);
} }
/*----contents----*/ /*----contents----*/
#contents { #contents {
padding-top: 20px; padding-top: 20px;
padding-left: 20px; padding-left: 20px;
padding-bottom: 50px; padding-bottom: 50px;
width: 650px; width: 650px;
} }
.contentTitle { .contentTitle {
font-size: 33px; font-size: 33px;
font-weight: 200; font-weight: 200;
color: var(--sub-text); color: var(--sub-text);
line-height: 2; line-height: 2;
} }
.caption { .caption {
font-size: 13px; font-size: 13px;
font-weight: 400; font-weight: 400;
color: var(--sub-text); color: var(--sub-text);
} }
#contents ul { #contents ul {
margin: 0px; margin: 0px;
} }
.childElements { .childElements {
padding-left: 20px; padding-left: 20px;
margin-bottom: 30px; margin-bottom: 30px;
border-left: solid 10px var(--line); border-left: solid 10px var(--line);
} }
.categoryContainer {} .categoryContainer {
}
.categoryElements { .categoryElements {
padding-left: 20px; padding-left: 20px;
margin-bottom: 30px; margin-bottom: 30px;
} }
.categoryTitle { .categoryTitle {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: var(--sub-text); color: var(--sub-text);
} }
.optionContainer { .optionContainer {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
padding: 10px 0px 10px 0px; padding: 10px 0px 10px 0px;
} }
.optionContainer.reverse { .optionContainer.reverse {
flex-direction: row-reverse; flex-direction: row-reverse;
} }
.buttonsContainer { .buttonsContainer {
justify-content: flex-start; justify-content: flex-start;
} }
.optionText { .optionText {
flex: 1; flex: 1;
} }
.new p:nth-child(1)::after { .new p:nth-child(1)::after {
content: "New"; content: "New";
color: var(--new); color: var(--new);
font-size: 14px; font-size: 14px;
border: 1px solid var(--new); border: 1px solid var(--new);
border-radius: 2px; border-radius: 2px;
padding: 0px 5px; padding: 0px 5px;
margin-left: 5px; margin-left: 5px;
} }
.updated p:nth-child(1)::after { .updated p:nth-child(1)::after {
content: "Updated"; content: "Updated";
color: var(--new); color: var(--new);
font-size: 14px; font-size: 14px;
border: 1px solid var(--new); border: 1px solid var(--new);
border-radius: 2px; border-radius: 2px;
padding: 0px 5px; padding: 0px 5px;
margin-left: 5px; margin-left: 5px;
} }
.optionForm { .optionForm {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
margin-left: 10px; margin-left: 10px;
} }
.reverse .optionForm { .reverse .optionForm {
flex-basis: 40px; flex-basis: 40px;
justify-content: flex-start; justify-content: flex-start;
} }
#importClear { #importClear {
position: relative; position: relative;
left: 10px; left: 10px;
} }
.favicon { .favicon {
width: 18px; width: 18px;
height: 18px; height: 18px;
padding: 1px; padding: 1px;
display: block; display: block;
float: left; float: left;
} }
/*----forms----*/ /*----forms----*/
input { input {
font-family: inherit; font-family: inherit;
font-size: 14px; font-size: 14px;
} }
input[type="number"], input[type="number"],
input[type="text"], input[type="text"],
input[type="color"] { input[type="color"] {
-moz-appearance: textfield; -moz-appearance: textfield;
width: 50px; width: 50px;
height: 30px; height: 30px;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
border: 1px solid var(--button); border: 1px solid var(--button);
border-radius: 2px; border-radius: 2px;
} }
input[type="number"]:hover, input[type="number"]:hover,
@ -224,197 +221,195 @@ input[type="color"]:hover,
input[type="number"]:focus, input[type="number"]:focus,
input[type="text"]:focus, input[type="text"]:focus,
input[type="color"]:focus { input[type="color"]:focus {
border-color: var(--highlight); border-color: var(--highlight);
} }
input[type="text"] { input[type="text"] {
width: 200px; width: 200px;
} }
input[type="color"] { input[type="color"] {
background-color: var(--main-bg); background-color: var(--main-bg);
padding: 5px; padding: 5px;
} }
.button { .button {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
min-width: 100px; min-width: 100px;
text-align: center; text-align: center;
padding: 5px; padding: 5px;
height: 30px; height: 30px;
font-size: 13px; font-size: 13px;
color: var(--main-text); color: var(--main-text);
border: 1px solid var(--button); border: 1px solid var(--button);
border-radius: 2px; border-radius: 2px;
background-color: #fbfbfb; background-color: #fbfbfb;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
} }
.includeSpan { .includeSpan {
padding: 0px; padding: 0px;
height: 28px; height: 28px;
} }
.button:hover { .button:hover {
background: #f5f5f5; background: #f5f5f5;
border-color: var(--highlight); border-color: var(--highlight);
} }
::-moz-selection { ::-moz-selection {
background: var(--line); background: var(--line);
} }
a:link { a:link {
color: var(--sub-text); color: var(--sub-text);
text-decoration-line: none; text-decoration-line: none;
} }
a:visited { a:visited {
color: var(--sub-text); color: var(--sub-text);
} }
.pageLink { .pageLink {
color: var(--highlight) !important; color: var(--highlight) !important;
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
} }
.pageLink:hover { .pageLink:hover {
color: var(--highlight); color: var(--highlight);
text-decoration-line: underline; text-decoration-line: underline;
} }
input[type="checkbox"] { input[type="checkbox"] {
display: none; display: none;
} }
.checkbox { .checkbox {
padding-left: 20px; padding-left: 20px;
position: relative; position: relative;
/*margin-right: 20px;*/ /*margin-right: 20px;*/
cursor: pointer; cursor: pointer;
} }
.checkbox::before { .checkbox::before {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 0; top: 0;
left: -2px; left: -2px;
width: 20px; width: 20px;
height: 20px; height: 20px;
border: 1px solid var(--button); border: 1px solid var(--button);
border-radius: 2px; border-radius: 2px;
} }
.checkbox:hover::before { .checkbox:hover::before {
border-color: var(--highlight); border-color: var(--highlight);
} }
input[type="checkbox"]:checked + .checkbox { input[type="checkbox"]:checked + .checkbox {
color: var(--highlight); color: var(--highlight);
} }
input[type="checkbox"]:checked + .checkbox::after { input[type="checkbox"]:checked + .checkbox::after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 1px; top: 1px;
left: 4px; left: 4px;
width: 6px; width: 6px;
height: 14px; height: 14px;
transform: rotate(40deg); transform: rotate(40deg);
border-bottom: 3px solid var(--highlight); border-bottom: 3px solid var(--highlight);
border-right: 3px solid var(--highlight); border-right: 3px solid var(--highlight);
} }
input[type="radio"] { input[type="radio"] {
display: none; display: none;
} }
.radio { .radio {
padding-left: 20px; padding-left: 20px;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
} }
.radio::before { .radio::before {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 0; top: 0;
left: -2px; left: -2px;
width: 20px; width: 20px;
height: 20px; height: 20px;
border: 1px solid var(--button); border: 1px solid var(--button);
border-radius: 50%; border-radius: 50%;
} }
.radio:hover::before { .radio:hover::before {
border-color: var(--highlight); border-color: var(--highlight);
} }
input[type="radio"]:checked + .radio { input[type="radio"]:checked + .radio {
color: var(--highlight); color: var(--highlight);
} }
input[type="radio"]:checked + .radio::after { input[type="radio"]:checked + .radio::after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 6px; top: 6px;
left: 4px; left: 4px;
width: 10px; width: 10px;
height: 10px; height: 10px;
border-radius: 50%; border-radius: 50%;
background-color: var(--highlight); background-color: var(--highlight);
} }
select { select {
-moz-appearance: none; -moz-appearance: none;
text-overflow: ellipsis; text-overflow: ellipsis;
border: var(--button) solid 1px; border: var(--button) solid 1px;
border-radius: 2px; border-radius: 2px;
padding: 3px 5px; padding: 3px 5px;
padding-right: 20px; padding-right: 20px;
width: 100%; width: 100%;
} }
select:hover { select:hover {
border: var(--highlight) solid 1px; border: var(--highlight) solid 1px;
} }
.selectWrap { .selectWrap {
position: relative; position: relative;
} }
.selectWrap:before { .selectWrap:before {
pointer-events: none; pointer-events: none;
content: ""; content: "";
z-index: 1; z-index: 1;
position: absolute; position: absolute;
top: 40%; top: 40%;
right: 7px; right: 7px;
width: 5px; width: 5px;
height: 5px; height: 5px;
transform: rotate(45deg); transform: rotate(45deg);
border-bottom: 2px solid var(--sub-text); border-bottom: 2px solid var(--sub-text);
border-right: 2px solid var(--sub-text); border-right: 2px solid var(--sub-text);
} }
.selectWrap:hover::before { .selectWrap:hover::before {
border-bottom: 2px solid var(--highlight); border-bottom: 2px solid var(--highlight);
border-right: 2px solid var(--highlight); border-right: 2px solid var(--highlight);
} }
option { option {
font-family: inherit; font-family: inherit;
font-size: 14px; font-size: 14px;
} }

View file

@ -2,66 +2,68 @@
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const S = new settingsObj() const S = new settingsObj();
let targetLang = document.getElementById("targetLang"); let targetLang = document.getElementById("targetLang");
let secondTargetLang = document.getElementById("secondTargetLang"); let secondTargetLang = document.getElementById("secondTargetLang");
setLangList(); setLangList();
function setLangList() { function setLangList() {
let langList = browser.i18n.getMessage("langList"); let langList = browser.i18n.getMessage("langList");
langList = langList.split(", "); langList = langList.split(", ");
for (let i in langList) { for (let i in langList) {
langList[i] = langList[i].split(":"); langList[i] = langList[i].split(":");
} }
langList = langList.sort(alphabeticallySort); langList = langList.sort(alphabeticallySort);
let langListHtml = ""; let langListHtml = "";
for (let i of langList) { for (let i of langList) {
langListHtml += `<option value=${i[0]}>${i[1]}</option>` langListHtml += `<option value=${i[0]}>${i[1]}</option>`;
} }
targetLang.innerHTML = langListHtml; targetLang.innerHTML = langListHtml;
secondTargetLang.innerHTML = langListHtml; secondTargetLang.innerHTML = langListHtml;
initialSetting(); initialSetting();
} }
function alphabeticallySort(a, b) { function alphabeticallySort(a, b) {
if (a[1].toString() > b[1].toString()) { if (a[1].toString() > b[1].toString()) {
return 1; return 1;
} else { } else {
return -1; return -1;
} }
} }
function initialSetting() { function initialSetting() {
switch (browser.i18n.getUILanguage()) { //一部の言語はブラウザの設定に合わせる switch (
case "ja": browser.i18n.getUILanguage() //一部の言語はブラウザの設定に合わせる
case "zh-CN": ) {
case "zh-TW": case "ja":
case "ko": case "zh-CN":
case "ru": case "zh-TW":
case "de": case "ko":
case "fr": case "ru":
case "it": case "de":
targetLang.value = browser.i18n.getUILanguage(); case "fr":
secondTargetLang.value = "en"; case "it":
break; targetLang.value = browser.i18n.getUILanguage();
default: secondTargetLang.value = "en";
targetLang.value = "en"; break;
secondTargetLang.value = "ja"; default:
break; targetLang.value = "en";
} secondTargetLang.value = "ja";
break;
}
} }
S.initOptionsPage().then(function () { S.initOptionsPage().then(function() {
const saveByChangeItems = document.getElementsByClassName("saveByChange"); const saveByChangeItems = document.getElementsByClassName("saveByChange");
for (let item of saveByChangeItems) { for (let item of saveByChangeItems) {
item.addEventListener("change", save) item.addEventListener("change", save);
} }
}) });
function save() { function save() {
S.saveOptionsPage(); S.saveOptionsPage();
} }

View file

@ -6,10 +6,10 @@
let e = {}; let e = {};
e.hash = location.href; e.hash = location.href;
if (e.hash.indexOf('#') != -1) { if (e.hash.indexOf("#") != -1) {
e.hash = '#' + e.hash.split('#')[1]; e.hash = "#" + e.hash.split("#")[1];
} else { } else {
e.hash = "#settings"; e.hash = "#settings";
} }
readHash(e); readHash(e);
@ -18,52 +18,55 @@ tm.HashObserver.enable();
document.addEventListener("changehash", readHash, false); document.addEventListener("changehash", readHash, false);
function readHash(e) { function readHash(e) {
const hash = e.hash.split('?')[0]; const hash = e.hash.split("?")[0];
let selected = document.getElementsByClassName("selected"); let selected = document.getElementsByClassName("selected");
selected[0].classList.remove("selected"); selected[0].classList.remove("selected");
document.getElementById("settings").style.display = "none"; document.getElementById("settings").style.display = "none";
document.getElementById("information").style.display = "none"; document.getElementById("information").style.display = "none";
switch (hash) { switch (hash) {
case "#settings": case "#settings":
document.getElementById("settings").style.display = "block"; document.getElementById("settings").style.display = "block";
document.getElementsByClassName("settingsLabel")[0].classList.add("selected"); document.getElementsByClassName("settingsLabel")[0].classList.add("selected");
break; break;
case "#information": case "#information":
document.getElementById("information").style.display = "block"; document.getElementById("information").style.display = "block";
document.getElementsByClassName("informationLabel")[0].classList.add("selected"); document.getElementsByClassName("informationLabel")[0].classList.add("selected");
break; break;
default: default:
document.getElementById("settings").style.display = "block"; document.getElementById("settings").style.display = "block";
document.getElementsByClassName("settingsLabel")[0].classList.add("selected"); document.getElementsByClassName("settingsLabel")[0].classList.add("selected");
break; break;
} }
const params = getParams(e.hash); const params = getParams(e.hash);
switch (params.action) { switch (params.action) {
case 'updated': case "updated":
showUpdated(); showUpdated();
break; break;
} }
} }
function getParams(hash) { function getParams(hash) {
let params = {}; let params = {};
if (hash.split('?')[1] == undefined) return params; if (hash.split("?")[1] == undefined) return params;
hash = hash.split('?')[1].split('&'); hash = hash.split("?")[1].split("&");
for (let i of hash) { for (let i of hash) {
params[i.split('=')[0]] = i.split('=')[1]; params[i.split("=")[0]] = i.split("=")[1];
} }
return params; return params;
} }
function showUpdated() { function showUpdated() {
const version = document.getElementsByClassName('addonVersion')[0]; const version = document.getElementsByClassName("addonVersion")[0];
version.classList.add('updated'); version.classList.add("updated");
} }
document.getElementsByClassName("addonUrl")[0].href = browser.i18n.getMessage("addonUrl"); document.getElementsByClassName("addonUrl")[0].href = browser.i18n.getMessage("addonUrl");
document.getElementsByClassName("amazonUrl")[0].href = browser.i18n.getMessage("amazonUrl"); document.getElementsByClassName("amazonUrl")[0].href = browser.i18n.getMessage("amazonUrl");
document.getElementsByClassName('addonVersion')[0].getElementsByClassName('caption')[0].getElementsByTagName('a')[0].innerText = `Version ${browser.runtime.getManifest().version}`; document
.getElementsByClassName("addonVersion")[0]
.getElementsByClassName("caption")[0]
.getElementsByTagName("a")[0].innerText = `Version ${browser.runtime.getManifest().version}`;

View file

@ -1,227 +1,227 @@
:root { :root {
--main-text: #0c0c0d; --main-text: #0c0c0d;
--sub-text: #737373; --sub-text: #737373;
--line: #ededf0; --line: #ededf0;
--button: #d7d7db; --button: #d7d7db;
--highlight: #5595ff; --highlight: #5595ff;
--main-bg: #ffffff; --main-bg: #ffffff;
--confirm: #ff4f4f; --confirm: #ff4f4f;
} }
body { body {
font-family: 'Segoe UI', 'San Francisco', 'Ubuntu', 'Fira Sans', 'Roboto', 'Arial', 'Helvetica', sans-serif; font-family: "Segoe UI", "San Francisco", "Ubuntu", "Fira Sans", "Roboto", "Arial", "Helvetica",
text-align: left; sans-serif;
font-size: 13px; text-align: left;
width: 348px; font-size: 13px;
overflow: hidden; width: 348px;
background-color: var(--main-bg); overflow: hidden;
background-color: var(--main-bg);
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.hidden { .hidden {
display: none; display: none;
} }
svg { svg {
pointer-events: none; pointer-events: none;
} }
#header { #header {
padding: 10px; padding: 10px;
background-color: var(--line); background-color: var(--line);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
-moz-user-select: none; -moz-user-select: none;
} }
#title { #title {
font-size: 15px; font-size: 15px;
font-weight: 400; font-weight: 400;
color: #666; color: #666;
cursor: default; cursor: default;
} }
#header .rightButtons { #header .rightButtons {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
} }
#donate { #donate {
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
margin-right: 8px; margin-right: 8px;
} }
#donate svg { #donate svg {
height: 18px; height: 18px;
width: 18px; width: 18px;
fill: var(--sub-text); fill: var(--sub-text);
transition: all 100ms; transition: all 100ms;
} }
#donate:hover svg { #donate:hover svg {
fill: var(--confirm); fill: var(--confirm);
} }
#setting { #setting {
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
} }
#setting svg { #setting svg {
flex-shrink: 0; flex-shrink: 0;
height: 18px; height: 18px;
width: 18px; width: 18px;
fill: var(--sub-text); fill: var(--sub-text);
transform: rotate(180deg); transform: rotate(180deg);
transition: fill 100ms, transform 300ms ease; transition: fill 100ms, transform 300ms ease;
} }
#setting:hover svg { #setting:hover svg {
fill: var(--highlight); fill: var(--highlight);
transform: rotate(270deg); transform: rotate(270deg);
} }
#main { #main {
padding: 10px; padding: 10px;
} }
textarea { textarea {
font: inherit; font: inherit;
resize: none; resize: none;
overflow: auto; overflow: auto;
background-color: var(--main-bg); background-color: var(--main-bg);
max-height: 215px; max-height: 215px;
height: 37px; height: 37px;
/* 100% - padding*2 - border*2 */ /* 100% - padding*2 - border*2 */
width: calc(100% - 22px); width: calc(100% - 22px);
padding: 10px; padding: 10px;
border: solid 1px var(--button); border: solid 1px var(--button);
border-radius: 2px; border-radius: 2px;
transition: border-color 100ms ease-out; transition: border-color 100ms ease-out;
} }
textarea:hover, textarea:hover,
textarea:focus { textarea:focus {
border-color: var(--highlight) border-color: var(--highlight);
} }
hr { hr {
border: none; border: none;
border-top: solid 1px var(--button); border-top: solid 1px var(--button);
height: 1px; height: 1px;
margin: 10px 0px; margin: 10px 0px;
} }
#target { #target {
max-height: 215px; max-height: 215px;
min-height: 30px; min-height: 30px;
overflow-y: auto; overflow-y: auto;
word-wrap: break-word; word-wrap: break-word;
padding: 0px 5px 0px; padding: 0px 5px 0px;
background-color: var(--main-bg); background-color: var(--main-bg);
} }
#target p { #target p {
margin: 0; margin: 0;
background-color: var(--main-bg); background-color: var(--main-bg);
} }
#target .result{ #target .result {
background-color: var(--main-bg); background-color: var(--main-bg);
} }
#target .candidate { #target .candidate {
color: var(--sub-text); color: var(--sub-text);
margin-top:1em; margin-top: 1em;
background-color: var(--main-bg); background-color: var(--main-bg);
} }
#target .candidate:empty { #target .candidate:empty {
margin-top:0; margin-top: 0;
} }
#footer { #footer {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0px 10px 10px; padding: 0px 10px 10px;
} }
#link { #link {
flex-shrink: 0; flex-shrink: 0;
} }
#link a { #link a {
font-style: normal; font-style: normal;
text-decoration: none; text-decoration: none;
color: var(--highlight); color: var(--highlight);
} }
#link a:hover { #link a:hover {
text-decoration: underline; text-decoration: underline;
} }
select { select {
-moz-appearance: none; -moz-appearance: none;
text-overflow: ellipsis; text-overflow: ellipsis;
border: var(--button) solid 1px; border: var(--button) solid 1px;
border-radius: 2px; border-radius: 2px;
padding: 3px 5px; padding: 3px 5px;
padding-right: 20px; padding-right: 20px;
width: 100%; width: 100%;
transition: border-color 100ms ease-out; transition: border-color 100ms ease-out;
} }
select:hover { select:hover {
border: var(--highlight) solid 1px; border: var(--highlight) solid 1px;
} }
.selectWrap { .selectWrap {
position: relative; position: relative;
margin-left: 5px; margin-left: 5px;
} }
.selectWrap:before { .selectWrap:before {
pointer-events: none; pointer-events: none;
content: ""; content: "";
z-index: 1; z-index: 1;
position: absolute; position: absolute;
top: 40%; top: 40%;
right: 7px; right: 7px;
width: 5px; width: 5px;
height: 5px; height: 5px;
transform: rotate(45deg); transform: rotate(45deg);
border-bottom: 2px solid var(--sub-text); border-bottom: 2px solid var(--sub-text);
border-right: 2px solid var(--sub-text); border-right: 2px solid var(--sub-text);
transition: border-color 100ms ease-out; transition: border-color 100ms ease-out;
} }
.selectWrap:hover::before { .selectWrap:hover::before {
border-bottom: 2px solid var(--highlight); border-bottom: 2px solid var(--highlight);
border-right: 2px solid var(--highlight); border-right: 2px solid var(--highlight);
} }
::-moz-selection { ::-moz-selection {
background: var(--line); background: var(--line);
} }

View file

@ -7,14 +7,14 @@ const S = new settingsObj();
const T = new Translate(); const T = new Translate();
//設定を読み出し //設定を読み出し
S.init().then(function (value) { S.init().then(function(value) {
defaultTargetLang = value.targetLang; defaultTargetLang = value.targetLang;
secondTargetLang = value.secondTargetLang; secondTargetLang = value.secondTargetLang;
langList.value = value.targetLang; //リスト初期値をセット langList.value = value.targetLang; //リスト初期値をセット
langList.addEventListener("change", changeLang); langList.addEventListener("change", changeLang);
document.body.style.fontSize = value.fontSize; document.body.style.fontSize = value.fontSize;
}) });
let target = document.getElementById("target"); let target = document.getElementById("target");
let langList = document.getElementById("langList"); let langList = document.getElementById("langList");
@ -30,156 +30,165 @@ let sourceWord = "";
setLangList(); setLangList();
function setLangList() { function setLangList() {
let langListStr = browser.i18n.getMessage("langList"); let langListStr = browser.i18n.getMessage("langList");
langListStr = langListStr.split(", "); langListStr = langListStr.split(", ");
for (let i in langListStr) { for (let i in langListStr) {
langListStr[i] = langListStr[i].split(":"); langListStr[i] = langListStr[i].split(":");
} }
langListStr = langListStr.sort(alphabeticallySort); langListStr = langListStr.sort(alphabeticallySort);
let langListHtml = ""; let langListHtml = "";
for (let i of langListStr) { for (let i of langListStr) {
langListHtml += `<option value=${i[0]}>${i[1]}</option>` langListHtml += `<option value=${i[0]}>${i[1]}</option>`;
} }
langList.innerHTML = langListHtml; langList.innerHTML = langListHtml;
} }
setTitles(); setTitles();
function setTitles(){ function setTitles() {
document.getElementById('donate').title = browser.i18n.getMessage('donateWithPaypalLabel'); document.getElementById("donate").title = browser.i18n.getMessage("donateWithPaypalLabel");
document.getElementById("setting").title = browser.i18n.getMessage('settingsLabel'); document.getElementById("setting").title = browser.i18n.getMessage("settingsLabel");
document.getElementById("langList").title = browser.i18n.getMessage('targetLangLabel'); document.getElementById("langList").title = browser.i18n.getMessage("targetLangLabel");
} }
function alphabeticallySort(a, b) { function alphabeticallySort(a, b) {
if (a[1].toString() > b[1].toString()) { if (a[1].toString() > b[1].toString()) {
return 1; return 1;
} else { } else {
return -1; return -1;
} }
} }
//翻訳先言語変更時に更新 //翻訳先言語変更時に更新
async function changeLang() { async function changeLang() {
if (typeof (url) != "undefined") showLink(); if (typeof url != "undefined") showLink();
if (sourceWord !== "") { if (sourceWord !== "") {
const resultData = await T.translate(sourceWord, undefined, langList.value); const resultData = await T.translate(sourceWord, undefined, langList.value);
showResult(resultData.resultText, resultData.candidateText); showResult(resultData.resultText, resultData.candidateText);
} }
} }
//アクティブなタブを取得して渡す //アクティブなタブを取得して渡す
browser.tabs.query({ browser.tabs
.query({
currentWindow: true, currentWindow: true,
active: true active: true
}).then(function (tabs) { })
.then(function(tabs) {
getSelectionWord(tabs); getSelectionWord(tabs);
}); });
//アクティブタブから選択文字列とurlを取得 //アクティブタブから選択文字列とurlを取得
function getSelectionWord(tabs) { function getSelectionWord(tabs) {
for (let tab of tabs) { for (let tab of tabs) {
browser.tabs.sendMessage( browser.tabs
tab.id, { .sendMessage(tab.id, {
message: "fromPopup" message: "fromPopup"
} })
).then(response => { .then(response => {
sourceWord = response.word; sourceWord = response.word;
url = response.url; url = response.url;
refleshSource(); refleshSource();
showLink(); showLink();
}).catch(()=>{}); })
} .catch(() => {});
}
} }
//ページ翻訳へのリンクを表示 //ページ翻訳へのリンクを表示
function showLink() { function showLink() {
document.getElementById("link").innerHTML = "<a href=https://translate.google.com/translate?hl=" + langList.value + "&sl=auto&u=" + encodeURIComponent(url) + ">" + browser.i18n.getMessage('showLink') + "</a>"; document.getElementById("link").innerHTML =
"<a href=https://translate.google.com/translate?hl=" +
langList.value +
"&sl=auto&u=" +
encodeURIComponent(url) +
">" +
browser.i18n.getMessage("showLink") +
"</a>";
} }
//翻訳元テキストを表示 //翻訳元テキストを表示
function refleshSource() { function refleshSource() {
if (sourceWord !== "") { if (sourceWord !== "") {
textarea.innerHTML = sourceWord; textarea.innerHTML = sourceWord;
resize(); resize();
inputText(); inputText();
} }
} }
textarea.addEventListener("paste", resize) textarea.addEventListener("paste", resize);
textarea.addEventListener("keydown", resize); textarea.addEventListener("keydown", resize);
textarea.addEventListener("keyup", function (event) { textarea.addEventListener("keyup", function(event) {
//if (event.keyCode == 13) resize(); //if (event.keyCode == 13) resize();
resize(); resize();
inputText(); inputText();
}); });
//テキストボックスをリサイズ //テキストボックスをリサイズ
function resize() { function resize() {
setTimeout(function () { setTimeout(function() {
textarea.style.height = "0px"; textarea.style.height = "0px";
textarea.style.height = parseInt(textarea.scrollHeight) + "px"; textarea.style.height = parseInt(textarea.scrollHeight) + "px";
}, 0); }, 0);
} }
textarea.addEventListener("click", textAreaClick, { textarea.addEventListener("click", textAreaClick, {
once: true once: true
}); });
//テキストエリアクリック時の処理 //テキストエリアクリック時の処理
function textAreaClick() { function textAreaClick() {
textarea.select(); textarea.select();
} }
//文字入力時の処理 //文字入力時の処理
async function inputText() { async function inputText() {
sourceWord = textarea.value; sourceWord = textarea.value;
const resultData = await T.translate(sourceWord, 'auto', langList.value); const resultData = await T.translate(sourceWord, "auto", langList.value);
changeSecondLang(defaultTargetLang, resultData.sourceLanguage, resultData.percentage); changeSecondLang(defaultTargetLang, resultData.sourceLanguage, resultData.percentage);
showResult(resultData.resultText, resultData.candidateText); showResult(resultData.resultText, resultData.candidateText);
} }
function showResult(resultText, candidateText) { function showResult(resultText, candidateText) {
const resultArea = target.getElementsByClassName("result")[0]; const resultArea = target.getElementsByClassName("result")[0];
const candidateArea = target.getElementsByClassName("candidate")[0]; const candidateArea = target.getElementsByClassName("candidate")[0];
resultArea.innerText = resultText; resultArea.innerText = resultText;
if (S.get().ifShowCandidate) candidateArea.innerText = candidateText; if (S.get().ifShowCandidate) candidateArea.innerText = candidateText;
} }
let changeLangFlag = false; let changeLangFlag = false;
function changeSecondLang(defaultTargetLang, sourceLang, percentage) { function changeSecondLang(defaultTargetLang, sourceLang, percentage) {
if(!S.get().ifChangeSecondLang) return; if (!S.get().ifChangeSecondLang) return;
//検出された翻訳元言語がターゲット言語と一致 //検出された翻訳元言語がターゲット言語と一致
const equalsSourceAndTarget = sourceLang == langList.value && percentage > 0; const equalsSourceAndTarget = sourceLang == langList.value && percentage > 0;
//検出された翻訳元言語がデフォルト言語と一致 //検出された翻訳元言語がデフォルト言語と一致
const equalsSourceAndDefault = sourceLang == defaultTargetLang && percentage > 0; const equalsSourceAndDefault = sourceLang == defaultTargetLang && percentage > 0;
if (!changeLangFlag) { if (!changeLangFlag) {
//通常時 //通常時
if (equalsSourceAndTarget && equalsSourceAndDefault) { if (equalsSourceAndTarget && equalsSourceAndDefault) {
//ソースとターゲットとデフォルトが一致する場合 //ソースとターゲットとデフォルトが一致する場合
//ターゲットを第2言語に変更 //ターゲットを第2言語に変更
changeLangFlag = true; changeLangFlag = true;
langList.value = secondTargetLang; langList.value = secondTargetLang;
changeLang(); changeLang();
}
} else {
//第2言語に切替した後
if (!equalsSourceAndDefault) {
//ソースとデフォルトが異なる場合
//ターゲットをデフォルトに戻す
changeLangFlag = false;
langList.value = defaultTargetLang;
changeLang();
}
} }
} else {
//第2言語に切替した後
if (!equalsSourceAndDefault) {
//ソースとデフォルトが異なる場合
//ターゲットをデフォルトに戻す
changeLangFlag = false;
langList.value = defaultTargetLang;
changeLang();
}
}
} }

View file

@ -1,94 +1,95 @@
:root { :root {
--simple-translate-main-text: #0c0c0d; --simple-translate-main-text: #0c0c0d;
--simple-translate-sub-text: #737373; --simple-translate-sub-text: #737373;
--simple-translate-line: #ededf0; --simple-translate-line: #ededf0;
--simple-translate-button: #d7d7db; --simple-translate-button: #d7d7db;
--simple-translate-highlight: #5595ff; --simple-translate-highlight: #5595ff;
--simple-translate-main-bg: #ffffff; --simple-translate-main-bg: #ffffff;
} }
#simple-translate-button { #simple-translate-button {
all: initial; all: initial;
background-color: #fff; background-color: #fff;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08);
border-radius: 10%; border-radius: 10%;
background-image: url("icons/512.png"); background-image: url("icons/512.png");
background-size: 75%; background-size: 75%;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
height: 22px; height: 22px;
width: 22px; width: 22px;
position: fixed; position: fixed;
z-index: 2147483647; z-index: 2147483647;
left: 0px; left: 0px;
top: 0px; top: 0px;
display: none; display: none;
cursor: pointer; cursor: pointer;
animation-duration: 200ms; animation-duration: 200ms;
animation-name: simple-translate-showButton; animation-name: simple-translate-showButton;
} }
#simple-translate-panel { #simple-translate-panel {
all: initial; all: initial;
background-color: var(--simple-translate-main-bg); background-color: var(--simple-translate-main-bg);
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08);
border-radius: 3px; border-radius: 3px;
min-height: 20px; min-height: 20px;
max-height: 200px; max-height: 200px;
line-height: 150%; line-height: 150%;
height: auto; height: auto;
min-width: 10px; min-width: 10px;
max-width: 300px; max-width: 300px;
position: fixed; position: fixed;
padding: 10px 18px; padding: 10px 18px;
z-index: 2147483646; z-index: 2147483646;
left: 0px; left: 0px;
top: 0px; top: 0px;
display: none; display: none;
overflow-y: auto; overflow-y: auto;
} }
#simple-translate-panel p { #simple-translate-panel p {
all: initial; all: initial;
font-family: 'Segoe UI', 'San Francisco', 'Ubuntu', 'Fira Sans', 'Roboto', 'Arial', 'Helvetica', sans-serif !important; font-family: "Segoe UI", "San Francisco", "Ubuntu", "Fira Sans", "Roboto", "Arial", "Helvetica",
text-align: left; sans-serif !important;
display: block; text-align: left;
font-size: inherit; display: block;
color: var(--simple-translate-main-text); font-size: inherit;
margin: 0; color: var(--simple-translate-main-text);
word-wrap: break-word; margin: 0;
word-wrap: break-word;
} }
#simple-translate-panel p::-moz-selection { #simple-translate-panel p::-moz-selection {
background: var(--simple-translate-line); background: var(--simple-translate-line);
} }
#simple-translate-panel .candidate { #simple-translate-panel .candidate {
color: var(--simple-translate-sub-text); color: var(--simple-translate-sub-text);
margin-top:1em; margin-top: 1em;
} }
#simple-translate-panel .candidate:empty { #simple-translate-panel .candidate:empty {
margin-top:0; margin-top: 0;
} }
@keyframes simple-translate-showButton { @keyframes simple-translate-showButton {
0% { 0% {
transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1);
} }
50% { 50% {
transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1);
} }
100% { 100% {
transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1);
} }
} }
@keyframes simple-translate-fadein { @keyframes simple-translate-fadein {
from { from {
opacity: 0; opacity: 0;
} }
to { to {
opacity: 1; opacity: 1;
} }
} }

View file

@ -3,7 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
document.body.insertAdjacentHTML("afterend", "<div id='simple-translate-button'></div><div id='simple-translate-panel'><p>...</p></div>"); //bodyの直後にボタン配置 document.body.insertAdjacentHTML(
"afterend",
"<div id='simple-translate-button'></div><div id='simple-translate-panel'><p>...</p></div>"
); //bodyの直後にボタン配置
var button = document.getElementById("simple-translate-button"); var button = document.getElementById("simple-translate-button");
var panel = document.getElementById("simple-translate-panel"); var panel = document.getElementById("simple-translate-panel");
var selectionWord; var selectionWord;
@ -15,180 +18,199 @@ S.init();
window.addEventListener("mouseup", Select, false); window.addEventListener("mouseup", Select, false);
//テキスト選択時の処理 ダブルクリックした時2回処理が走るのを何とかしたい //テキスト選択時の処理 ダブルクリックした時2回処理が走るのを何とかしたい
async function Select(e) { async function Select(e) {
hidePanel(e); hidePanel(e);
if (e.target.tagName == "INPUT" && e.target.type == "password") return; if (e.target.tagName == "INPUT" && e.target.type == "password") return;
setTimeout(async () => { //誤動作防止の為ディレイを設ける setTimeout(async () => {
//選択文の取得 テキストフィールド内を選択した場合にも対応 //誤動作防止の為ディレイを設ける
const isTextField = (e.target.tagName == "INPUT") || (e.target.tagName == "TEXTAREA"); //選択文の取得 テキストフィールド内を選択した場合にも対応
if (isTextField) selectionWord = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd); const isTextField = e.target.tagName == "INPUT" || e.target.tagName == "TEXTAREA";
else selectionWord = String(window.getSelection()); if (isTextField)
selectionWord = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
else selectionWord = String(window.getSelection());
//選択文が存在し,パネル外を左クリックした場合は翻訳する //選択文が存在し,パネル外を左クリックした場合は翻訳する
const existsSelectionWord = (selectionWord.length !== 0); const existsSelectionWord = selectionWord.length !== 0;
const isLeftClick = (e.button == 0); const isLeftClick = e.button == 0;
const isPanelOutside = (e.target.id !== 'simple-translate-panel') && (e.target.parentElement.id !== 'simple-translate-panel'); const isPanelOutside =
const shouldTranslate = existsSelectionWord && isLeftClick && isPanelOutside; e.target.id !== "simple-translate-panel" &&
if (!shouldTranslate) return; e.target.parentElement.id !== "simple-translate-panel";
const shouldTranslate = existsSelectionWord && isLeftClick && isPanelOutside;
if (!shouldTranslate) return;
//選択した言語が翻訳先言語と異なれば翻訳する //選択した言語が翻訳先言語と異なれば翻訳する
const needTranslate = await checkLang(selectionWord, 'auto', S.get().targetLang); const needTranslate = await checkLang(selectionWord, "auto", S.get().targetLang);
if (!needTranslate) return; if (!needTranslate) return;
clickPosition = e; clickPosition = e;
switch (S.get().whenSelectText) { switch (S.get().whenSelectText) {
case 'showButton': case "showButton":
if (selectionWord.length == 0) return; if (selectionWord.length == 0) return;
popupButton(e); popupButton(e);
break; break;
case 'showPanel': case "showPanel":
translate(selectionWord, 'auto', S.get().targetLang); translate(selectionWord, "auto", S.get().targetLang);
if (selectionWord.length == 0) return; if (selectionWord.length == 0) return;
showPanel(e); showPanel(e);
break; break;
case 'dontShowButton': case "dontShowButton":
break; break;
} }
}, 0); }, 0);
} }
//選択テキストの言語をチェックして返す //選択テキストの言語をチェックして返す
async function checkLang(sourceWord, sourceLang, targetLang) { async function checkLang(sourceWord, sourceLang, targetLang) {
if (!S.get().ifCheckLang) return true; //設定がオフならtrue if (!S.get().ifCheckLang) return true; //設定がオフならtrue
sourceWord = sourceWord.substr(0, 100); //先頭100字で言語を判定 sourceWord = sourceWord.substr(0, 100); //先頭100字で言語を判定
const resultData = await T.translate(sourceWord, sourceLang, targetLang); const resultData = await T.translate(sourceWord, sourceLang, targetLang);
const needTranslate = (S.get().targetLang != resultData.sourceLanguage) && (resultData.percentage > 0); const needTranslate =
return needTranslate; //ターゲットとソースの言語が不一致ならtrue S.get().targetLang != resultData.sourceLanguage && resultData.percentage > 0;
return needTranslate; //ターゲットとソースの言語が不一致ならtrue
} }
//ボタンを表示 //ボタンを表示
function popupButton(e) { function popupButton(e) {
let topPosition = 10; let topPosition = 10;
let leftPosition = 10; let leftPosition = 10;
let buttonSize = S.get().buttonSize; let buttonSize = S.get().buttonSize;
switch (S.get().buttonPosition) { switch (S.get().buttonPosition) {
case "rightUp": case "rightUp":
topPosition = (-1 * buttonSize) - 10; topPosition = -1 * buttonSize - 10;
break; break;
case "rightDown": case "rightDown":
break; break;
case "leftUp": case "leftUp":
topPosition = (-1 * buttonSize) - 10; topPosition = -1 * buttonSize - 10;
leftPosition = (-1 * buttonSize) - 10; leftPosition = -1 * buttonSize - 10;
break; break;
case "leftDown": case "leftDown":
leftPosition = (-1 * buttonSize) - 10; leftPosition = -1 * buttonSize - 10;
break; break;
} }
button.style.left = e.clientX + leftPosition + 'px'; button.style.left = e.clientX + leftPosition + "px";
button.style.top = e.clientY + topPosition + 'px'; button.style.top = e.clientY + topPosition + "px";
button.style.width = S.get().buttonSize + "px"; button.style.width = S.get().buttonSize + "px";
button.style.height = S.get().buttonSize + "px"; button.style.height = S.get().buttonSize + "px";
button.style.display = 'block'; button.style.display = "block";
} }
button.addEventListener("click", function (e) { button.addEventListener(
translate(selectionWord, 'auto', S.get().targetLang); "click",
function(e) {
translate(selectionWord, "auto", S.get().targetLang);
showPanel(e); showPanel(e);
}, false); },
false
);
async function translate(sourceWord, sourceLang, targetLang) { async function translate(sourceWord, sourceLang, targetLang) {
const resultData = await T.translate(sourceWord, sourceLang, targetLang); const resultData = await T.translate(sourceWord, sourceLang, targetLang);
showResult(resultData.resultText, resultData.candidateText); showResult(resultData.resultText, resultData.candidateText);
panelPosition(clickPosition); panelPosition(clickPosition);
} }
function showResult(resultText, candidateText) { function showResult(resultText, candidateText) {
panel.innerHTML = '<p class=result></p><p class=candidate>'; panel.innerHTML = "<p class=result></p><p class=candidate>";
const resultArea = panel.getElementsByClassName("result")[0]; const resultArea = panel.getElementsByClassName("result")[0];
const candidateArea = panel.getElementsByClassName("candidate")[0]; const candidateArea = panel.getElementsByClassName("candidate")[0];
resultArea.innerText = resultText; resultArea.innerText = resultText;
if (S.get().ifShowCandidate) candidateArea.innerText = candidateText; if (S.get().ifShowCandidate) candidateArea.innerText = candidateText;
} }
//パネル表示 //パネル表示
function showPanel(e) { function showPanel(e) {
clickPosition = e; clickPosition = e;
panel.style.display = 'block'; panel.style.display = "block";
panelPosition(e); panelPosition(e);
} }
//パネル非表示 //パネル非表示
function hidePanel(e) { function hidePanel(e) {
button.style.display = 'none'; //ボタンを非表示 button.style.display = "none"; //ボタンを非表示
if ((e.target.id !== "simple-translate-panel") && (e.target.parentElement.id !== "simple-translate-panel")) { //パネル以外の場所をクリックでパネルを非表示 if (
panel.style.display = 'none'; e.target.id !== "simple-translate-panel" &&
panel.innerHTML = "<p>...</p>"; e.target.parentElement.id !== "simple-translate-panel"
} ) {
//パネル以外の場所をクリックでパネルを非表示
panel.style.display = "none";
panel.innerHTML = "<p>...</p>";
}
} }
//Esc押下でパネルを閉じる //Esc押下でパネルを閉じる
document.addEventListener('keydown', (e) => { document.addEventListener("keydown", e => {
if (e.key == 'Escape') hidePanel(e); if (e.key == "Escape") hidePanel(e);
}) });
//パネルがウィンドウ外にはみ出る時に位置を調整 //パネルがウィンドウ外にはみ出る時に位置を調整
function panelPosition(e) { function panelPosition(e) {
var p = new Object(); var p = new Object();
panel.style.width = S.get().width + 'px'; //300px panel.style.width = S.get().width + "px"; //300px
var panelHeight = panel.clientHeight; var panelHeight = panel.clientHeight;
var panelWidth = parseInt(window.getComputedStyle(panel.getElementsByTagName("p")[0], null).width); var panelWidth = parseInt(
//一旦パネルの横幅を300にしてpの横幅を取得 window.getComputedStyle(panel.getElementsByTagName("p")[0], null).width
);
//一旦パネルの横幅を300にしてpの横幅を取得
if (e.clientX + panelWidth > window.innerWidth - 80) { if (e.clientX + panelWidth > window.innerWidth - 80) {
p.x = window.innerWidth - panelWidth - 80; p.x = window.innerWidth - panelWidth - 80;
} else { } else {
p.x = e.clientX; p.x = e.clientX;
} }
if (e.clientY + panelHeight > window.innerHeight - 30) { if (e.clientY + panelHeight > window.innerHeight - 30) {
p.y = window.innerHeight - panelHeight - 30; p.y = window.innerHeight - panelHeight - 30;
} else { } else {
p.y = e.clientY; p.y = e.clientY;
} }
panel.style.width = 'auto'; //panelWidth + 'px'; panel.style.width = "auto"; //panelWidth + 'px';
panel.style.top = p.y + 'px'; panel.style.top = p.y + "px";
panel.style.left = p.x + 'px'; panel.style.left = p.x + "px";
panel.style.maxWidth = S.get().width + "px"; panel.style.maxWidth = S.get().width + "px";
panel.style.maxHeight = S.get().height + "px"; panel.style.maxHeight = S.get().height + "px";
panel.style.fontSize = S.get().fontSize + "px"; panel.style.fontSize = S.get().fontSize + "px";
panel.style.backgroundColor = S.get().bgColor; panel.style.backgroundColor = S.get().bgColor;
} }
//スクリプトからのメッセージに返信 //スクリプトからのメッセージに返信
browser.runtime.onMessage.addListener(function (request) { browser.runtime.onMessage.addListener(function(request) {
switch (request.message) { switch (request.message) {
case "fromPopup": case "fromPopup":
return sendToPopup(); return sendToPopup();
break; break;
case "showPanelFromMenu": case "showPanelFromMenu":
showPanelFromMenu(); showPanelFromMenu();
break; break;
} }
}); });
//popupにテキストとurlを返す //popupにテキストとurlを返す
function sendToPopup() { function sendToPopup() {
return Promise.resolve({ return Promise.resolve({
word: String(window.getSelection()), word: String(window.getSelection()),
url: window.location.href url: window.location.href
}); });
} }
//コンテキストメニュークリックでパネルを表示 //コンテキストメニュークリックでパネルを表示
function showPanelFromMenu() { function showPanelFromMenu() {
button.style.display = "none"; button.style.display = "none";
//キャレットブラウズモードに対応 //キャレットブラウズモードに対応
const isTextField = (document.activeElement.tagName == "INPUT") || (document.activeElement.tagName == "TEXTAREA"); const isTextField =
if (isTextField) selectionWord = document.activeElement.value.substring(document.activeElement.selectionStart, document.activeElement.selectionEnd); document.activeElement.tagName == "INPUT" || document.activeElement.tagName == "TEXTAREA";
else selectionWord = String(window.getSelection()); if (isTextField)
if (typeof (clickPosition) == 'undefined') clickPosition = { 'clientX': 0, 'clientY': 0 } selectionWord = document.activeElement.value.substring(
document.activeElement.selectionStart,
document.activeElement.selectionEnd
);
else selectionWord = String(window.getSelection());
if (typeof clickPosition == "undefined") clickPosition = { clientX: 0, clientY: 0 };
translate(selectionWord, 'auto', S.get().targetLang); translate(selectionWord, "auto", S.get().targetLang);
showPanel(clickPosition); showPanel(clickPosition);
} }

View file

@ -4,83 +4,84 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
class Translate { class Translate {
constructor() { constructor() {}
set sourceWord(word) {
this.sourceWord = word;
}
translate(sourceWord, sourceLang = "auto", targetLang) {
//改行で分割
const sourceLines = sourceWord.trim().split("\n");
let promises = [];
for (let sourceLine of sourceLines) {
promises.push(this.sendRequest(sourceLine, sourceLang, targetLang));
} }
set sourceWord(word) { return new Promise(resolve => {
this.sourceWord = word; Promise.all(promises).then(results => {
} resolve(this.formatResult(results));
});
});
}
translate(sourceWord, sourceLang = 'auto', targetLang) { sendRequest(word, sourceLang, targetLang) {
//改行で分割 const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&dt=bd&q=${encodeURIComponent(
const sourceLines = sourceWord.trim().split("\n"); word
)}`;
const xhr = new XMLHttpRequest();
xhr.responseType = "json";
xhr.open("GET", url);
xhr.send();
let promises = []; return new Promise((resolve, reject) => {
for (let sourceLine of sourceLines) { xhr.onload = () => {
promises.push(this.sendRequest(sourceLine, sourceLang, targetLang)); resolve(xhr);
};
});
}
formatResult(results) {
const resultData = {
resultText: "",
candidateText: "",
sourceLanguage: "",
percentage: 0
};
//翻訳元言語を取得
resultData.sourceLanguage = results[0].response[2];
resultData.percentage = results[0].response[6];
let candidateText = "";
let wordCount = 0;
let lineCount = 0;
for (const result of results) {
lineCount++;
//翻訳文を取得
for (const response of result.response[0]) {
resultData.resultText += response[0];
}
resultData.resultText += "\n";
//訳候補を取得
if (result.response[1]) {
wordCount++;
for (let i = 0; i < result.response[1].length; i++) {
const partsOfSpeech = result.response[1][i][0];
const candidates = result.response[1][i][1];
candidateText += `${partsOfSpeech}${partsOfSpeech != "" ? ": " : ""}${candidates.join(
", "
)}\n`;
} }
}
return new Promise(resolve => {
Promise.all(promises)
.then((results) => {
resolve(this.formatResult(results));
});
});
} }
//訳候補が一つの単語のみに対して存在するとき返す
if (wordCount == 1 && lineCount == 1) resultData.candidateText = candidateText;
return resultData;
sendRequest(word, sourceLang, targetLang) { }
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&dt=bd&q=${encodeURIComponent(word)}`;
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open("GET", url);
xhr.send();
return new Promise((resolve, reject) => {
xhr.onload = () => {
resolve(xhr);
};
})
}
formatResult(results) {
const resultData = {
resultText: '',
candidateText: '',
sourceLanguage: '',
percentage: 0
}
//翻訳元言語を取得
resultData.sourceLanguage = results[0].response[2];
resultData.percentage = results[0].response[6];
let candidateText = '';
let wordCount = 0;
let lineCount = 0;
for (const result of results) {
lineCount++;
//翻訳文を取得
for (const response of result.response[0]) {
resultData.resultText += response[0];
}
resultData.resultText += '\n';
//訳候補を取得
if (result.response[1]) {
wordCount++;
for (let i = 0; i < result.response[1].length; i++) {
const partsOfSpeech = result.response[1][i][0];
const candidates = result.response[1][i][1];
candidateText += `${partsOfSpeech}${partsOfSpeech != '' ? ': ' : ''}${candidates.join(', ')}\n`;
}
}
}
//訳候補が一つの単語のみに対して存在するとき返す
if (wordCount == 1 && lineCount == 1) resultData.candidateText = candidateText;
return resultData;
}
} }