diff --git a/build_zip.sh b/build_zip.sh index be34255d..fd4e494a 100755 --- a/build_zip.sh +++ b/build_zip.sh @@ -1,4 +1,4 @@ #!/bin/sh ZIP=yomichan.zip rm -f $ZIP -7z a $ZIP ./ext/* +7za a $ZIP ./ext/* diff --git a/ext/bg/legal.html b/ext/bg/legal.html index f3ca4e01..4b0a3649 100644 --- a/ext/bg/legal.html +++ b/ext/bg/legal.html @@ -24,39 +24,6 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. - -

VLGothic License

-
-Copyright (c) 1990-2003 Wada Laboratory, the University of Tokyo.
-Copyright (c) 2003-2004 Electronic Font Open Laboratory (/efont/).
-Copyright (C) 2002-2013 M+ FONTS PROJECT
-Copyright (C) 2006-2013 Daisuke SUZUKI .
-Copyright (C) 2006-2013 Project Vine .
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-3. Neither the name of the Wada Laboratory, the University of Tokyo nor
-the names of its contributors may be used to endorse or promote products
-derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY WADA LABORATORY, THE UNIVERSITY OF TOKYO AND
-CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
-NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE LABORATORY OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

EDRDG License

diff --git a/ext/manifest.json b/ext/manifest.json
index 3357e661..215397d0 100644
--- a/ext/manifest.json
+++ b/ext/manifest.json
@@ -1,7 +1,7 @@
 {
     "manifest_version": 2,
     "name": "Yomichan",
-    "version": "1.1.6",
+    "version": "1.1.7",
 
     "description": "Japanese dictionary with Anki integration",
     "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 25a095e0..45c1e08c 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -23,6 +23,7 @@ class Display {
         this.container = container;
         this.definitions = [];
         this.audioCache = {};
+        this.responseCache = {};
         this.sequence = 0;
         this.index = 0;
 
@@ -261,91 +262,118 @@ class Display {
 
     noteAdd(index, mode) {
         this.spinner.show();
-
         const definition = this.definitions[index];
+
+        let promise = Promise.resolve();
         if (mode !== 'kanji') {
-            const url = Display.audioBuildUrl(definition);
             const filename = Display.audioBuildFilename(definition);
-            if (url && filename) {
-                definition.audio = {url, filename};
+            if (filename) {
+                promise = this.audioBuildUrl(definition).then(url => definition.audio = {url, filename}).catch(() => {});
             }
         }
 
-        this.definitionAdd(definition, mode).then(success => {
-            if (success) {
-                Display.adderButtonFind(index, mode).addClass('disabled');
-            } else {
-                this.handleError('note could not be added');
-            }
+        promise.then(() => {
+            return this.definitionAdd(definition, mode).then(success => {
+                if (success) {
+                    Display.adderButtonFind(index, mode).addClass('disabled');
+                } else {
+                    this.handleError('note could not be added');
+                }
+            });
         }).catch(this.handleError.bind(this)).then(() => this.spinner.hide());
     }
 
     audioPlay(index) {
-        for (const key in this.audioCache) {
-            const audio = this.audioCache[key];
-            if (audio !== null) {
-                audio.pause();
-            }
-        }
-
+        this.spinner.show();
         const definition = this.definitions[index];
-        const url = Display.audioBuildUrl(definition);
-        if (!url) {
-            return;
+
+        for (const key in this.audioCache) {
+            this.audioCache[key].pause();
         }
 
-        let audio = this.audioCache[url];
-        if (audio) {
-            audio.currentTime = 0;
-            audio.play();
-        } else {
-            audio = new Audio(url);
-            audio.onloadeddata = () => {
-                if (audio.duration === 5.694694 || audio.duration === 5.720718) {
-                    audio = new Audio('/mixed/mp3/button.mp3');
-                }
+        this.audioBuildUrl(definition).then(url => {
+            if (!url) {
+                url = '/mixed/mp3/button.mp3';
+            }
 
-                this.audioCache[url] = audio;
+            let audio = this.audioCache[url];
+            if (audio) {
+                audio.currentTime = 0;
                 audio.play();
+            } else {
+                audio = new Audio(url);
+                audio.onloadeddata = () => {
+                    if (audio.duration === 5.694694 || audio.duration === 5.720718) {
+                        audio = new Audio('/mixed/mp3/button.mp3');
+                    }
+
+                    this.audioCache[url] = audio;
+                    audio.play();
+                };
+            }
+        }).catch(this.handleError.bind(this)).then(() => this.spinner.hide());
+    }
+
+    audioBuildUrl(definition) {
+        return new Promise((resolve, reject) => {
+            const response = this.responseCache[definition.expression];
+            if (response) {
+                resolve(response);
+                return;
+            }
+
+            const data = {
+                post: 'dictionary_reference',
+                match_type: 'exact',
+                search_query: definition.expression
             };
-        }
-    }
 
-    static entryIndexFind(element) {
-        return $('.entry').index(element.closest('.entry'));
-    }
+            const params = [];
+            for (const key in data) {
+                params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
+            }
 
-    static adderButtonFind(index, mode) {
-        return $('.entry').eq(index).find(`.action-add-note[data-mode="${mode}"]`);
-    }
+            const xhr = new XMLHttpRequest();
+            xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post');
+            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+            xhr.addEventListener('error', () => reject('failed to scrape audio data'));
+            xhr.addEventListener('load', () => {
+                this.responseCache[definition.expression] = xhr.responseText;
+                resolve(xhr.responseText);
+            });
 
-    static audioBuildUrl(definition) {
-        let kana = definition.reading;
-        let kanji = definition.expression;
+            xhr.send(params.join('&'));
+        }).then(response => {
+            const dom = new DOMParser().parseFromString(response, 'text/html');
+            const entries = [];
 
-        if (!kana && !kanji) {
-            return null;
-        }
+            for (const row of dom.getElementsByClassName('dc-result-row')) {
+                try {
+                    const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url');
+                    const expression = dom.getElementsByClassName('dc-vocab').item(0).innerText;
+                    const reading = dom.getElementsByClassName('dc-vocab_kana').item(0).innerText;
 
-        if (!kana && wanakana.isHiragana(kanji)) {
-            kana = kanji;
-            kanji = null;
-        }
+                    if (url && expression && reading) {
+                        entries.push({url, expression, reading});
+                    }
+                } catch (e) {
+                    // NOP
+                }
+            }
 
-        const params = [];
-        if (kanji) {
-            params.push(`kanji=${encodeURIComponent(kanji)}`);
-        }
-        if (kana) {
-            params.push(`kana=${encodeURIComponent(kana)}`);
-        }
-
-        return `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`;
+            return entries;
+        }).then(entries => {
+            for (const entry of entries) {
+                if (!definition.reading || definition.reading === entry.reading) {
+                    return entry.url;
+                }
+            }
+        });
     }
 
     static audioBuildFilename(definition) {
         if (!definition.reading && !definition.expression) {
-            return null;
+            return;
         }
 
         let filename = 'yomichan';
@@ -358,4 +386,12 @@ class Display {
 
         return filename += '.mp3';
     }
+
+    static entryIndexFind(element) {
+        return $('.entry').index(element.closest('.entry'));
+    }
+
+    static adderButtonFind(index, mode) {
+        return $('.entry').eq(index).find(`.action-add-note[data-mode="${mode}"]`);
+    }
 }