Greasy Fork is available in English.
Provides jump buttons to the next lesson for leveling up (based on minirock / oltodosel autoScroller).
当前为
// ==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/*
// ==/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<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
// level1 is the anchor element for new lessons
if(level1.length!=0) level1 = [getAnchorElement(level1)];//[level1[level1.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
// level2 is the anchor element for level 1 lessons
if(level2.length!=0) level2 = [getAnchorElement(level2)];//[level2[level2.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
// level3 is the anchor element for level 2 lessons
if(level3.length!=0) level3 = [getAnchorElement(level3)];//[level3[level3.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
// level4 is the anchor element for level 3 lessons
if(level4.length!=0) level4 = [getAnchorElement(level4)];//[level4[level4.length-1].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling];
// level5 is the anchor element for level 4 lessons
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"/>';
console.group('Not overriding double id tags');
if(level1.length!=0) {
// last level 1 element get the id = notDone for jumping to new lessons
let id = 'notDone';
level1[level1.length-1].id = id;
level1[level1.length-1] === firstLesson? id = 'javascript:scroll(0,0);' : id = '#'+id;
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="${id}">
<img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/fafe27c9c1efa486f49f87a3d691a66e.svg">
</a>
</div>`;
}
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 +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
<a href="${id}">
<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) {
// 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 +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
<a href="${id}">
<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) {
// 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 +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
<a href="${id}">
<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) {
// 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 +=
`<div class="_2-dXY _1swBH" style="font-size: 14.84px;">
<a href="${id}">
<img alt="crown" class="_18sNN" src="//d35aaqx5ub95lt.cloudfront.net/images/b3ede3d53c932ee30d981064671c8032.svg">
<div class="GkDDe" data-test="level-crown">4</div>
</a>
</div>`;
}
console.groupEnd();
insertElement.insertAdjacentElement('afterend',jumpMark);
}