From 10214b54bbc8196683c86c6337ce79e121c3780f Mon Sep 17 00:00:00 2001
From: Georgebuk <george.boulton@hotmail.co.uk>
Date: Wed, 26 Jul 2023 13:06:57 +0900
Subject: [PATCH 1/2] Added English to Japanese Cambridge dictionary

---
 src/dict/enjp_Cambridge.js | 140 +++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 src/dict/enjp_Cambridge.js

diff --git a/src/dict/enjp_Cambridge.js b/src/dict/enjp_Cambridge.js
new file mode 100644
index 0000000..cc335b0
--- /dev/null
+++ b/src/dict/enjp_Cambridge.js
@@ -0,0 +1,140 @@
+/* global api */
+class enjp_Cambridge {
+    constructor(options) {
+        this.options = options;
+        this.maxexample = 2;
+        this.word = '';
+    }
+
+    async displayName() {
+        let locale = await api.locale();
+        if (locale.indexOf('CN') != -1) return '剑桥英日词典';
+        if (locale.indexOf('TW') != -1) return '剑桥英日词典';
+        return 'Cambridge EN->JP Dictionary';
+    }
+
+    setOptions(options) {
+        this.options = options;
+        this.maxexample = options.maxexample;
+    }
+
+    async findTerm(word) {
+        this.word = word;
+        return this.findCambridge(word);
+    }
+
+    async findCambridge(word) {
+        let notes = [];
+        if (!word) return notes; // return empty notes
+
+        function T(node) {
+            if (!node)
+                return '';
+            else
+                return node.innerText.trim();
+        }
+
+        let base = 'https://dictionary.cambridge.org/search/english-japanese/direct/?q=';
+        let url = base + encodeURIComponent(word);
+        let doc = '';
+        try {
+            let data = await api.fetch(url);
+            let parser = new DOMParser();
+            doc = parser.parseFromString(data, 'text/html');
+        } catch (err) {
+            return [];
+        }
+
+        let entries = doc.querySelectorAll('.pr .entry-body__el') || [];
+        for (const entry of entries) {
+            let definitions = [];
+            let audios = [];
+
+            let expression = T(entry.querySelector('.headword'));
+            let reading = '';
+            let readings = entry.querySelectorAll('.pron .ipa');
+            if (readings) {
+                let reading_uk = T(readings[0]);
+                let reading_us = T(readings[1]);
+                reading = (reading_uk || reading_us) ? `UK[${reading_uk}] US[${reading_us}] ` : '';
+            }
+            let pos = T(entry.querySelector('.posgram'));
+            pos = pos ? `<span class='pos'>${pos}</span>` : '';
+            audios[0] = entry.querySelector(".uk.dloc source");
+            audios[0] = audios[0] ? 'https://dictionary.cambridge.org' + audios[0].getAttribute('src') : '';
+
+            audios[1] = entry.querySelector(".us.dloc source");
+            audios[1] = audios[1] ? 'https://dictionary.cambridge.org' + audios[1].getAttribute('src') : '';
+
+            let sensbodys = entry.querySelectorAll('.sense-body') || [];
+            for (const sensbody of sensbodys) {
+                let sensblocks = sensbody.childNodes || [];
+                for (const sensblock of sensblocks) {
+                    let phrasehead = '';
+                    let defblocks = [];
+                    if (sensblock.classList && sensblock.classList.contains('phrase-block')) {
+                        phrasehead = T(sensblock.querySelector('.phrase-title'));
+                        phrasehead = phrasehead ? `<div class="phrasehead">${phrasehead}</div>` : '';
+                        defblocks = sensblock.querySelectorAll('.def-block') || [];
+                    }
+                    if (sensblock.classList && sensblock.classList.contains('def-block')) {
+                        defblocks = [sensblock];
+                    }
+                    if (defblocks.length <= 0) continue;
+
+                    // make definition segement
+                    for (const defblock of defblocks) {
+                        let eng_tran = T(defblock.querySelector('.ddef_h .def'));
+                        let chn_tran = T(defblock.querySelector('.def-body .trans'));
+                        if (!eng_tran) continue;
+                        let definition = '';
+                        eng_tran = `<span class='eng_tran'>${eng_tran.replace(RegExp(expression, 'gi'),`<b>${expression}</b>`)}</span>`;
+                        chn_tran = `<span class='chn_tran'>${chn_tran}</span>`;
+                        let tran = `<span class='tran'>${eng_tran}${chn_tran}</span>`;
+                        definition += phrasehead ? `${phrasehead}${tran}` : `${pos}${tran}`;
+
+                        // make exmaple segement
+                        let examps = defblock.querySelectorAll('.def-body .examp') || [];
+                        if (examps.length > 0 && this.maxexample > 0) {
+                            definition += '<ul class="sents">';
+                            for (const [index, examp] of examps.entries()) {
+                                if (index > this.maxexample - 1) break; // to control only 2 example sentence.
+                                let eng_examp = T(examp.querySelector('.eg'));
+                                let chn_examp = T(examp.querySelector('.trans'));
+                                definition += `<li class='sent'><span class='eng_sent'>${eng_examp.replace(RegExp(expression, 'gi'),`<b>${expression}</b>`)}</span><span class='chn_sent'>${chn_examp}</span></li>`;
+                            }
+                            definition += '</ul>';
+                        }
+                        definition && definitions.push(definition);
+                    }
+                }
+            }
+            let css = this.renderCSS();
+            notes.push({
+                css,
+                expression,
+                reading,
+                definitions,
+                audios
+            });
+        }
+        return notes;
+    }
+
+    renderCSS() {
+        let css = `
+            <style>
+                div.phrasehead{margin: 2px 0;font-weight: bold;}
+                span.star {color: #FFBB00;}
+                span.pos  {text-transform:lowercase; font-size:0.9em; margin-right:5px; padding:2px 4px; color:white; background-color:#0d47a1; border-radius:3px;}
+                span.tran {margin:0; padding:0;}
+                span.eng_tran {margin-right:3px; padding:0;}
+                span.chn_tran {color:#0d47a1;}
+                ul.sents {font-size:0.8em; list-style:square inside; margin:3px 0;padding:5px;background:rgba(13,71,161,0.1); border-radius:5px;}
+                li.sent  {margin:0; padding:0;}
+                span.eng_sent {margin-right:5px;}
+                span.chn_sent {color:#0d47a1;}
+            </style>`;
+        return css;
+    }
+}
\ No newline at end of file

From a3e8a612735d57f4f8f0ad78e3709c20afe0183f Mon Sep 17 00:00:00 2001
From: Georgebuk <george.boulton@hotmail.co.uk>
Date: Tue, 1 Aug 2023 10:03:14 +0900
Subject: [PATCH 2/2] Added dictionary to System Scripts list

---
 src/_locales/en/messages.json    | 3 ++-
 src/_locales/zh_CN/messages.json | 1 +
 src/_locales/zh_TW/messages.json | 1 +
 src/bg/js/options.js             | 2 +-
 4 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index c10f763..355a626 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -93,6 +93,7 @@
     "enen_UrbanDict": { "message": "Urban EN->EN Dictionary" },
     "enfr_Cambridge": { "message": "Cambridge EN->FR Dictionary" },
     "enfr_Collins": { "message": "Collins EN->FR Dictionary" },
+    "enjp_Cambridge": { "message": "Cambridge EN->JP Dictionary" },
     "esen_Spanishdict": { "message": "Spanishdict.com ES->CN Dictionary" },
     "decn_Eudict": { "message": "Eudict DE->CN Dictionary" },
     "escn_Eudict": { "message": "Eudict ES->CN Dictionary" },
@@ -101,6 +102,6 @@
     "fren_Cambridge": { "message": "Cambridge FR->EN Dictionary" },
     "fren_Collins": { "message": "Collins FR->EN Dictionary" },
     "rucn_Qianyi": { "message": "Qianyi RU->CN Dictionary" },
-
+   
     "labelAlignment": { "message": "initial" }
 }
diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json
index e6eee91..cf82f33 100644
--- a/src/_locales/zh_CN/messages.json
+++ b/src/_locales/zh_CN/messages.json
@@ -91,6 +91,7 @@
     "enen_UrbanDict": { "message": "Urban英英俚语词典" },
     "enfr_Cambridge": { "message": "剑桥英法词典" },
     "enfr_Collins": { "message": "柯林斯英法词典" },
+    "enjp_Cambridge": { "message": "剑桥英日词典" },
     "esen_Spanishdict": { "message": "Spanishdict.com西英词典" },
     "decn_Eudict": { "message": "欧路德语助手" },
     "escn_Eudict": { "message": "欧路西语助手" },
diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json
index 7ef4454..01bcf17 100644
--- a/src/_locales/zh_TW/messages.json
+++ b/src/_locales/zh_TW/messages.json
@@ -91,6 +91,7 @@
     "enen_UrbanDict": { "message": "Urban英英俚語詞典" },
     "enfr_Cambridge": { "message": "劍橋英法詞典" },
     "enfr_Collins": { "message": "柯林斯英法詞典" },
+    "enjp_Cambridge": { "message": "剑桥英日词典" },
     "esen_Spanishdict": { "message": "Spanishdict.com西英詞典" },
     "decn_Eudict": { "message": "歐路德語助手" },
     "escn_Eudict": { "message": "歐路西語助手" },
diff --git a/src/bg/js/options.js b/src/bg/js/options.js
index a2b21c3..fac90c7 100644
--- a/src/bg/js/options.js
+++ b/src/bg/js/options.js
@@ -72,7 +72,7 @@ function populateSysScriptsList(dictLibrary) {
         'enen_Collins', 'enen_LDOCE6MDX', 'enen_UrbanDict', //en-en dictionaries
         'enfr_Cambridge', 'enfr_Collins', //en-fr dictionaries
         'fren_Cambridge', 'fren_Collins', //fr-cn dictionaries
-        'esen_Spanishdict', 'decn_Eudict', 'escn_Eudict', 'frcn_Eudict', 'frcn_Youdao', 'rucn_Qianyi' //msci dictionaries
+        'esen_Spanishdict', 'decn_Eudict', 'escn_Eudict', 'frcn_Eudict', 'frcn_Youdao', 'rucn_Qianyi', 'enjp_Cambridge' //msci dictionaries
     ];
     $('#scriptslistbody').empty();
     systemscripts.forEach(script => {