Greasy Fork

Duolingo LevelJumper

Provides jump buttons to the next lesson for leveling up (based on minirock / oltodosel autoScroller).

目前为 2021-04-29 提交的版本。查看 最新版本

// ==UserScript==
// @name        Duolingo LevelJumper
// @description Provides jump buttons to the next lesson for leveling up (based on minirock / oltodosel autoScroller).
// @version     2.4
// @namespace   esh
// @match       https://*.duolingo.com/*
// ==/UserScript==

// 2.4: take into concern if there are different levels in one row
// TODO: fix bug clickable before jumpMark ready
// 2.3: find a better solution to move to the first lesson of this level
// 2.4: better jumpMark more satisfying
// TODO: fix if jumpMark is the very first lesson

new MutationObserver(checkJumpMark).observe(document.body, {
	childList: true,
	subtree: true
});

window.onload = function () {
    //let lessons = document.querySelectorAll('img[src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg"]');
    //lessons[lessons.length-1].scrollIntoView();
    //document.body.addEventListener('pageshow', addJumpMarks());
    //addJumpMarks();
};

function checkJumpMark() {
     if (document.querySelectorAll('.GkDDe').length!=0) {
         if (document.querySelector('#jumpMark')===null) {
             if (document.querySelector('._3yqw1')!=null) addJumpMarks();
         }
     }
}

function getAnchorElement(elem) {
    // whole lesson element
    let lesson = elem[elem.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
    let row;
    // if it is not the last lesson in the row, anchor has to be on the previous row
    // it it is the last lesson, anchor has to be in the same row to focus on the following row
    if(lesson.nextSibling) {
        row = lesson.parentNode.previousSibling;
    } else {
        row = lesson.parentNode;
    }
    return row;
}

function addJumpMarks() {
    // let notDone = document.querySelectorAll('img[src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg"]');
    // notDone[notDone.length-1].id = 'notDone';
    let level = document.querySelectorAll('.GkDDe');
    let level1 = new Array();
    let level2 = new Array();
    let level3 = new Array();
    let level4 = new Array();
    let level5 = new Array();
    for (let i=0;i<level.length;i++) {
        switch(level[i].innerHTML) {
            case '1':
                level1.push(level[i]);
                // if there is no level 2 the first level 1 should be selected for becoming 2
                //if(level2[0]===null) level2[0] = level[i];
                break;
            case '2':
                level2.push(level[i]);
                //if(level3[0]===null) level3[0] = level[i];
                break;
            case '3':
                level3.push(level[i]);
                //if(level4[0]===null) level4[0] = level[i];
                break;
            case '4':
                level4.push(level[i]);
                //if(level5[0]===null) level5[0] = level[i];
                break;
            case '5':
                //level5.push(level[i]);
                //if(level5[0]===null) level5[0] = level[i];
                break;
        }
    }
    // have a lesson for the highest level, too
    if(level5.length == 0 && level4.length != 0) level5[0] = level4[0];
    if(level4.length == 0 && level3.length != 0) level4[0] = level3[0];
    if(level3.length == 0 && level2.length != 0) level3[0] = level2[0];
    if(level2.length == 0 && level1.length != 0) level2[0] = level1[0];
    // freaky selecting the previous row
    if(level1.length!=0) level1 = [getAnchorElement(level1)];//[level1[level1.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
    if(level2.length!=0) level2 = [getAnchorElement(level2)];//[level2[level2.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
    if(level3.length!=0) level3 = [getAnchorElement(level3)];//[level3[level3.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
    if(level4.length!=0) level4 = [getAnchorElement(level4)];//[level4[level4.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
    if(level5.length!=0) level5 = [getAnchorElement(level5)];//[level5[level5.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
    let insertElement = document.querySelector('._3yqw1');
    let jumpMark = document.createElement('div');
    jumpMark.setAttribute('class','_3yqw1 np6Tv');
    jumpMark.setAttribute('style','padding-top: 0.6rem; padding-right: 0.6rem; top: 148px;');
    jumpMark.innerHTML = '<div id="jumpMark"/>';
    if(level1.length!=0) {
        level1[level1.length-1].id = 'notDone';
    jumpMark.innerHTML += /* `<a href="#notDone"><img src="//d35aaqx5ub95lt.cloudfront.net/images/fafe27c9c1efa486f49f87a3d691a66e.svg"/></a> */
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#notDone">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/fafe27c9c1efa486f49f87a3d691a66e.svg">
  </a>
</div>`;
    }
    if(level2.length!=0) {
        level2[level2.length-1].id = 'level1';
        jumpMark.innerHTML +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#level1">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
    <div class="GkDDe" data-test="level-crown">1</div>
  </a>
</div>`;
    }
    if(level3.length!=0) {
        level3[level3.length-1].id = 'level2';
        jumpMark.innerHTML +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#level2">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
    <div class="GkDDe" data-test="level-crown">2</div>
  </a>
</div>`;
    }
    if(level4.length!=0) {
        level4[level4.length-1].id = 'level3';
        jumpMark.innerHTML +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#level3">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
    <div class="GkDDe" data-test="level-crown">3</div>
  </a>
</div>`;
    }
    if(level5.length!=0) {
        level5[level5.length-1].id = 'level4';
        jumpMark.innerHTML +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#level4">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
    <div class="GkDDe" data-test="level-crown">4</div>
  </a>
</div>`;
    }
/*    if(level5.length!=0) {
        level5[level5.length-1].id = 'level5';
        jumpMark.innerHTML +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
  <a href="#level5">
    <img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
    <div class="GkDDe" data-test="level-crown">5</div>
  </a>
</div>`;
    } */
            insertElement.insertAdjacentElement('afterend',jumpMark);
}