Permalink
Cannot retrieve contributors at this time
355 lines (307 sloc)
14.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* jshint esversion: 6 */ | |
| // TODO: info on namespacing | |
| // You can listen to the following custom events from this module: | |
| // | |
| // wk_subject_ready args: (curPage) | |
| // | |
| // The "subject" of a page is ready and can be accessed via `getSubject()`. | |
| // For content pages like the kanji and radical pages this will be triggered | |
| // instantly to get the radical and kanji as a string. For the lessons and | |
| // reviews we have to wait for the information folds to appear. We wait for | |
| // these elements using a `MutationObserver`. The callback will get the pageType | |
| // as an additional argument. | |
| // | |
| // | |
| // wk_new_review_item_ready | |
| // | |
| // During a review a new question is visible, i.e., a review session started | |
| // with the first review or another question was answered previously. | |
| // | |
| // wk_review_answered | |
| // | |
| // A review question was answered and not ignored. | |
| // | |
| // wk_review_session_finished | |
| // | |
| // ############################################################################# | |
| function WKInteraction(namespace) | |
| { | |
| this.namespace = namespace; | |
| this.PageEnum = Object.freeze({ | |
| "unknown": 0, | |
| "radicals": 1, | |
| "kanji": 2, | |
| "vocabulary": 3, | |
| "reviews": 4, | |
| "reviews_summary": 5, | |
| "lessons": 6, | |
| "lessons_reviews": 7 | |
| }); | |
| this.curPage = this.PageEnum.unknown; | |
| this.lastQuestionCount = 0; | |
| this.lastWrongCount = 0; | |
| this.lastQuestionAnswered = false; | |
| this.lastItem = null; | |
| this.lastQType = null; // meaning, reading | |
| } | |
| // ############################################################################# | |
| // ############################################################################# | |
| (function() { | |
| "use strict"; | |
| // ######################################################################### | |
| WKInteraction.prototype = { | |
| constructor: WKInteraction, | |
| init: function() | |
| { | |
| this.lessonInfoObserver = new MutationObserver(this.lessonInfoCallback.bind(this)); | |
| this.reviewInfoObserver = new MutationObserver(this.reviewInfoCallback.bind(this)); | |
| this.reviewStartObserver = new MutationObserver(this.reviewStartCallback.bind(this)); | |
| // TODO: removal didn't work, store the functions for now ... | |
| this.bound_currentItem = this.newReviewItemCallback.bind(this); | |
| this.bound_questionCount = this.questionCountCallback.bind(this); | |
| $.jStorage.listenKeyChange(`currentItem`, this.bound_currentItem); | |
| $.jStorage.listenKeyChange(`questionCount`, this.bound_questionCount); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| detectCurPage: function() | |
| { | |
| if (/\/radicals\/./.test(document.URL)) /* Radical Pages */ | |
| this.curPage = this.PageEnum.radicals; | |
| else if (/\/kanji\/./.test(document.URL)) /* Kanji Pages */ | |
| this.curPage = this.PageEnum.kanji; | |
| else if (/\/vocabulary\/./.test(document.URL)) /* Vocab Pages */ | |
| this.curPage = this.PageEnum.vocabulary; | |
| else if (/\/review\/session/.test(document.URL)) /* Reviews Pages */ | |
| this.curPage = this.PageEnum.reviews; | |
| else if (/\/review/.test(document.URL)) /* Reviews Summary Page then? */ | |
| this.curPage = this.PageEnum.reviews_summary; | |
| else if (/\/lesson\/./.test(document.URL)) /* Lessons Pages */ | |
| this.curPage = this.PageEnum.lessons; | |
| else | |
| this.curPage = this.PageEnum.unknown; | |
| return this.curPage; | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| startInteraction: function() | |
| { | |
| this.detectCurPage(); | |
| switch(this.curPage) | |
| { | |
| case this.PageEnum.radicals: | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.radicals]); | |
| break; | |
| case this.PageEnum.kanji: | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.kanji]); | |
| break; | |
| case this.PageEnum.vocabulary: | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.vocabulary]); | |
| break; | |
| case this.PageEnum.reviews: | |
| this.reviewInfoObserver.observe(document.getElementById(`item-info-col2`), | |
| {childList: true}); | |
| // reviews are not ready after load, wait until the WK animation finishes. | |
| this.reviewStartObserver.observe(document.getElementById(`loading`), | |
| {attributes: true}); | |
| $(window).on(`beforeunload`, this.reviewEndCallback.bind(this)); | |
| break; | |
| case this.PageEnum.reviews_summary: | |
| break; | |
| case this.PageEnum.lessons: | |
| this.lessonInfoObserver.observe(document.getElementById(`supplement-rad`), {attributes: true}); | |
| this.lessonInfoObserver.observe(document.getElementById(`supplement-kan`), {attributes: true}); | |
| this.lessonInfoObserver.observe(document.getElementById(`supplement-voc`), {attributes: true}); | |
| // Observer for the reviews after lessons, thankfully equal to normal reviews! | |
| this.lessonInfoObserver.observe(document.getElementById(`item-info-col2`), {childList: true}); | |
| break; | |
| default: | |
| return; | |
| } | |
| if (this.curPage !== this.PageEnum.unknown && | |
| this.curPage !== this.PageEnum.reviews) | |
| $(document).trigger(`${this.namespace}_wk_page_ready`, [this.curPage]); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| reviewStartCallback: function(mutations) | |
| { | |
| // The last change has three elements, use that for now | |
| if (mutations.length !== 3) | |
| return; | |
| $(document).trigger(`${this.namespace}_wk_page_ready`, [this.curPage]); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| reviewEndCallback: function(event) | |
| { | |
| const wasWrongAnswer = this.lastWrongCount < $.jStorage.get(`wrongCount`); | |
| if (this.lastQuestionAnswered) | |
| $(document).trigger(`${this.namespace}_wk_review_answered`, | |
| [wasWrongAnswer, this.lastQType, this.lastItem]); | |
| $(document).trigger(`${this.namespace}_wk_review_session_finished`); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| reviewInfoCallback: function(mutations) | |
| { | |
| let doTrigger = false; | |
| mutations.forEach( | |
| function(mutation) { | |
| // Length 5 for radical page, 9 for kanji page (vocab is 11) | |
| if ([5, 9, 11].includes(mutation.addedNodes.length)) | |
| doTrigger = true; | |
| }, | |
| this | |
| ); | |
| if (doTrigger) | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.reviews]); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| lessonInfoCallback: function(mutations) | |
| { | |
| if (mutations[0].type === `attributes`) | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.lessons]); | |
| else if (mutations[0].type === `childList`) | |
| { | |
| let doTrigger = false; | |
| mutations.forEach( | |
| function(mutation) { | |
| // Length 5 for radical page, 9 for kanji page (vocab is 11) | |
| if ([5, 9, 11].includes(mutation.addedNodes.length)) | |
| doTrigger = true; | |
| }, | |
| this | |
| ); | |
| if (doTrigger) | |
| $(document).triggerHandler(`${this.namespace}_wk_subject_ready`, | |
| [this.PageEnum.lessons_reviews]); | |
| } | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| newReviewItemCallback: function(key, action) | |
| { | |
| const wasWrongAnswer = this.lastWrongCount < $.jStorage.get(`wrongCount`); | |
| this.lastWrongCount = $.jStorage.get(`wrongCount`); | |
| if (this.lastQuestionAnswered) | |
| { | |
| $(document).trigger(`${this.namespace}_wk_review_answered`, | |
| [wasWrongAnswer, this.lastQType, this.lastItem]); | |
| this.lastQuestionAnswered = false; | |
| } | |
| $(document).trigger(`${this.namespace}_wk_new_review_item_ready`); | |
| }, | |
| // ##################################################################### | |
| // The questionCount is changed once a question is answered. However, | |
| // this value might also me decreased by the ignore script, so we | |
| // keep track if the last change was an increase. | |
| // ##################################################################### | |
| questionCountCallback: function(key, action) | |
| { | |
| if ($.jStorage.get(`questionCount`) > this.lastQuestionCount) | |
| { | |
| this.lastQuestionAnswered = true; | |
| this.lastItem = $.jStorage.get(`currentItem`); | |
| this.lastQType = $.jStorage.get(`questionType`); | |
| } | |
| else | |
| this.lastQuestionAnswered = false; | |
| this.lastQuestionCount = $.jStorage.get(`questionCount`); | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| getItemType: function(item) | |
| { | |
| if (`rad` in item) | |
| return `rad`; | |
| else if (`kan` in item) | |
| return `kan`; | |
| else if (`voc` in item) | |
| return `voc`; | |
| else | |
| return null; | |
| }, | |
| // ##################################################################### | |
| // ##################################################################### | |
| getSubject: function() | |
| { | |
| let result = {"rad": null, "kan": null, "voc": null}; | |
| switch(this.curPage) | |
| { | |
| case this.PageEnum.radicals: | |
| result.rad = document.URL.split(`/`).slice(-1)[0]; | |
| result.rad = result.rad.replace("#", ""); | |
| break; | |
| case this.PageEnum.kanji: | |
| result.kan = document.title[document.title.length - 1]; | |
| break; | |
| case this.PageEnum.vocabulary: | |
| result.voc = decodeURIComponent(document.URL.split(`/`).slice(-1)[0]); | |
| break; | |
| case this.PageEnum.reviews: | |
| const curItem = $.jStorage.get(`currentItem`); | |
| const curType = this.getItemType(curItem); | |
| // GM_log(`Getting the subject of this page, from storage:`, curItem); | |
| if (curType === `kan`) | |
| result.kan = curItem.kan.trim(); | |
| else if (curType === `rad`) | |
| { | |
| if (curItem.custom_font_name) | |
| result.rad = curItem.custom_font_name.trim(); | |
| else | |
| result.rad = curItem.rad.trim(); | |
| } | |
| else if (curType === `voc`) | |
| result.voc = curItem.voc.trim(); | |
| break; | |
| case this.PageEnum.lessons: | |
| const $character = $(`#character`); | |
| if ($(`#main-info`).hasClass(`radical`)) | |
| { | |
| // Fixed for elf radical: grab the meaning instead | |
| if (!$(`#character > i`).length) | |
| if ($character.text().length) | |
| result.rad = $character.text().trim(); | |
| else | |
| result.rad = $(`#main-info > #meaning`) | |
| .text().trim().replace(" ", "-").toLowerCase(); | |
| else | |
| result.rad = $(`#character > i`).attr(`class`).slice(8); | |
| } | |
| else if ($(`#main-info`).hasClass(`kanji`)) | |
| result.kan = $character.text().trim(); | |
| else if ($(`#main-info`).hasClass(`vocabulary`)) | |
| result.voc = $character.text().trim(); | |
| break; | |
| default: | |
| result = null; | |
| break; | |
| } | |
| return result; | |
| }, | |
| // ##################################################################### | |
| // Check if at least one data element in subject is set | |
| // ##################################################################### | |
| checkSubject: function(subject, white_list) | |
| { | |
| let result = false; | |
| Object.keys(subject).forEach( | |
| function(key) { | |
| if (white_list.includes(key) && !!subject[key]) | |
| result = true; | |
| } | |
| ); | |
| return result; | |
| } | |
| // ##################################################################### | |
| }; | |
| // ######################################################################### | |
| }()); | |
| // ############################################################################# |