// ==UserScript== // @name Duolingo LevelJumper // @description Provides jump buttons to the next lesson for leveling up (based on minirock / oltodosel autoScroller). // @version 2.6 // @namespace esh // @match https://*.duolingo.com/* // @downloadURL none // ==/UserScript== // 2.3: find a better solution to move to the first lesson of this level // 2.4: take into concern if there are different levels in one row // 2.4: better jumpMark more satisfying // 2.5: set jumpMark to the crown-div, so it moves the old lessons out of view // 2.5: jumpMark for Crown 3 does not work???? // 2.6: fix if jumpMark is the very first lesson // ok, I had a flash of genius ... I just could match the very first lesson of a level and go up one row for the jump target. // don't know for sure, well maybe because level 0 is not matchable with the given class. // Well if nothing works any more I do a rewrite, but for now, it works good enough not to spend any more time with it ;) // TODO: what happens, if some crowns do not exist, E.g. lessons level 4 and lessons level 2, but no lessons level 3 available // I suppose it's just set to the lower level, in this example level 2. // TODO: fix bug clickable before jumpMark ready // I don't know why it happens and I don't know what to do against // great, it happens, when you reload a page with a target in the url, so it's mostly a developer problem nothing more // TODO: highlight lesson by mouseover / mousein / pointerin / focus event // elem.dispatchEvent(event) // this is the 20 % which take me 80 % of the time // could be silly thinking as I got a proper positioning in 2.5 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 // means the lesson you want to go to is in this row // it it is the last lesson, anchor has to be in the same row to focus on the following row // means the lesson you want to go to is in the next row if(lesson.nextSibling) { row = lesson.parentNode.previousSibling; } else { row = lesson.parentNode; } // select the crown image as anchor element return row.querySelector('[alt="crown"]'); } function addJumpMarks() { // let notDone = document.querySelectorAll('img[src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg"]'); // notDone[notDone.length-1].id = 'notDone'; let firstLesson = document.querySelector('[data-test="skill-tree"]').querySelector('[alt="crown"]'); 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 */ `
crown
`; } if(level2.length!=0) { // show the first level 1 lesson let id = 'level1'; // if there have two level the same jump mark, it uses the given one instead of overriding it if(level2[level2.length-1].id !== '') { console.info('Id already exists'); console.info(level2[level2.length-1].id); id = level2[level2.length-1].id; } else { level2[level2.length-1].id = id; } level2[level2.length-1] === firstLesson? id = 'javascript:scroll(0,0);' : id = '#'+id; jumpMark.innerHTML += `
crown
1
`; } if(level3.length!=0) { // show the first level 2 lesson let id = 'level2'; if(level3[level3.length-1].id !== '') { console.info('Id already exists'); console.info(level3[level3.length-1].id); id = level3[level3.length-1].id; } else { level3[level3.length-1].id = id; } //level3[level3.length-1].id = 'level2'; level3[level3.length-1] === firstLesson? id = 'javascript:scroll(0,0);' : id = '#'+id; jumpMark.innerHTML += `
crown
2
`; } if(level4.length!=0) { // show the first level 3 lesson let id = 'level3'; if(level4[level4.length-1].id !== '') { console.info('Id already exists'); console.info(level4[level4.length-1].id); id = level4[level4.length-1].id; } else { level4[level4.length-1].id = id; } //level4[level4.length-1].id = 'level3'; level4[level4.length-1] === firstLesson? id = 'scroll(0,0)' : id = '#'+id; jumpMark.innerHTML += `
crown
3
`; } if(level5.length!=0) { // show the first level 4 lesson let id = 'level4'; if(level5[level5.length-1].id !== '') { console.info('Id already exists'); console.info(level5[level5.length-1].id); id = level5[level5.length-1].id; } else { level5[level5.length-1].id = id; } //level5[level5.length-1].id = 'level4'; level5[level5.length-1] === firstLesson? id = 'javascript:scroll(0,0);' : id = '#'+id; jumpMark.innerHTML += `
crown
4
`; } console.groupEnd(); insertElement.insertAdjacentElement('afterend',jumpMark); }