您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
MyDealz Enstyler enhanced features incl. Amazon Mobile Redirect
当前为
// ==UserScript== // @name EnstylerJS // @namespace Enstyler // @description MyDealz Enstyler enhanced features incl. Amazon Mobile Redirect // @include https://www.preisjaeger.at/* // @include https://www.mydealz.de/* // @include https://userstyles.org/styles/128262/* // @include https://www.amazon.*/gp/aw/* // @version 2.12.133 // @grant GM_getValue // @grant GM_setValue // @require http://code.jquery.com/jquery-3.1.1.min.js // @require http://openuserjs.org/src/libs/sizzle/GM_config.js // ==/UserScript== // ========== INIT EnstylerJS ===================================== // init Enstyler environment var enUserLogin = false; var enUserName = ''; var enSection = ''; const EnstylerStartTime=Date.now(); // Parse location for later use const enLocParser=location; const DEBUG=false; // Basic Initialisation ========================== function EnstylerInit () { // hide Enstyler2 CSS (c) text because we have a button now addStyleString('.threadWidget-footer::after {display: none !important};'); // get LoginStatus and Username if (enUserLogin = $('.avatar--type-nav').length) { enUserName = $('.navDropDown a').attr('href'); enUserName = enUserName.replace(/.*\/profile\/([^\/]+).*/,'$1'); } // get Section (first element in path) enSection= enLocParser.pathname.replace(/\/([^\/]+\/*).*/,'/$1'); } // add actions @ some places ================================== // additional Deal Actions ======================= // code used for MyDealz Dealz actions, thanks to mydealz :-) const enDealAction = [ '<a title="Sag was dazu" class="link ico ico--pos-l ico--type-comment-blue space--mr-3"'+ // comment 0+1+3 'href="<ENSTYLER-HREF-HERE>#comment-form" data-handler="track" data-track="{"action":"scroll_to_comment_add_form","label":"engagement"}">', '<span class="hide--toW3">Sag was dazu</span><span class="hide--fromW3 hide--toW2">Sag</span>', '', '</a>', '<a title="Von Liste entfernen" class="link text--color-blue ico ico--type-bookmark-blue ico--pos-l space--mr-3"' + //un-bookmark 4+5+7 'data-handler="track replace" data-replace="{"endpoint":"https:\/\/www.mydealz.de\/threads\/<ENSTYLER-THREADID-HERE>/remove","method":"post"}" data-track="{"action":"save_thread","label":"engagement"}">', '<span class="hide--toW3">Von Liste entfernen</span><span class="hide--fromW3 hide--toW2">Enft</span>', '', '</a>', '<a title="Bearbeiten" class="link text--color-blue ico ico--type-pencil-blue ico--pos-l space--mr-3"'+ // edit 8+9+11 'href="<ENSTYLER-HREF-HERE>/edit" data-handler="track" data-track="{"action":"goto_thread_edit_form","beacon":true}">', '<span class="hide--toW3">Bearbeiten</span><span class="hide--fromW3 hide--toW2">Bearb</span>', '', '<span></a>', '<a title="Als Mail versenden" class="link text--color-blue ico ico--type-code-blue ico--pos-l space--mr-3"'+ // mail 12+13+15 'href="mailto:?subject=<ENSTYLER-TEXT-HERE>" <span class="hide--toW3">', '<span class="hide--toW3">Mail versenden</span><span class="hide--fromW3 hide--toW2">Mail</span>', '', '<span></a>', ]; const enDealMarker='thread_' var enDealAdd=''; var enDealActionMailFooter=''; function EnstylerDealActions(){ // if logged in and enabled ... if (enUserLogin && GM_config.get('enConfMoreDeal')) { // compose Footer enDealActionMailFooter = ('%0D%0A%0D%0A-- %0D%0A'+ enInternationalName + ':%0D%0A'+ $('footer ul li:first p').html().replace(/.*<br>/g,'')); // use parsed location var pathname = enLocParser.pathname; var myText=0; // no username ?? if (enUserName != "") { pathname = pathname.replace(enUserName + '/',''); // remove username if path is longer } // display short/no text? if ($('.ico--type-grid-subNavActive').length) { myText=1; } // default for all Dealz: first comment enDealAdd = enDealAction[0]+ enDealAction[1+myText] + enDealAction[3]; // Action for special locations only =========== switch(true) { case (pathname.endsWith('profile/saved-deals')): // add for user saved-dealz: un-bookmark enDealAdd += enDealAction[4]+ enDealAction[5+myText]; + enDealAction[7]; break; case (pathname.endsWith('profile/diskussion')): case (pathname.endsWith(enUserName)): // add user dealz and discussions: comment edit enDealAdd += enDealAction[8]+ enDealAction[9+myText] + enDealAction[11]; break; } // default last: add mail to enDealAdd += enDealAction[12]+ enDealAction[13+myText] + enDealAction[15]; EnstylerDealActionsDo(); } } // surrounding myDealz HTML const enDealActionOuterHtml = [ '<span class="js-options bg--em bRad--a space--h-3 space--v-3 space--mt-3 text--b">', '</span>']; function EnstylerDealActionsDo() { // if logged in and enabled ... if (enUserLogin && GM_config.get('enConfMoreDeal')) { // every thread on thread page ... $('article:not('+enClassHidden+')').each(function () { // get ThreadID, Link to Deal and DealID var myThread = $(this).attr('id'); if (!myThread.startsWith(enDealMarker)) {return;} var myDeal=$('#' + myThread + ' .thread-title a'); var myDealHref = myDeal.attr('href'); var myDealID = myThread.replace(enDealMarker,''); var myMailTo= encodeURIComponent(enInternationalName+': '+myDeal.text()) +'&body=' +myDealHref +enDealActionMailFooter; // compose final HTML var newHtml= enDealActionOuterHtml[0] + enDealAdd + enDealActionOuterHtml[1]; newHtml = newHtml.replace(enPATTERN[0], myDealHref); newHtml = newHtml.replace(enPATTERN[1], myDealID); newHtml = newHtml.replace(enPATTERN[2], myMailTo); // append HTML to Deal $('#' + myThread +' .thread-infoRow').each(function () { $(this).append(newHtml); $(this).removeClass('thread-infoRow'); }); }); // actions for somewhere =========== // remove unwanted HTML from deal description $('.thread--type-detail .userHtml').each(function () { // userhtml code from mydalz, need to find jafascript to save automatically :-( var myUserhtml = ['<div class="userHtml overflow--wrap-break space--t-3" data-handler="lightbox-xhr" data-lightbox-xhr="{"name":"threads"}">', '</div>', // sourround deal description ]; // get inner html var myHtml = $(this).html(); // remove unwanted Stuff: combined <div><br><br> stuff, created by cut'npaste html // not elegant, but works ... var newHtml = myHtml.replace(/<div>|<\/br>|<\/div>/gi,''); newHtml = myHtml.replace(/<p>/gi,'<br><br>'); newHtml = newHtml.replace(/<br>(<br>)*<br>/gi,'<br><br>'); // replace original with modifyed html $(this).replaceWith(myUserhtml[0] + newHtml + myUserhtml[1]); }); } // END enabled } // show popup user info while click on avatar ... ====================== function EnstylerAvatarPopup(){ if (enUserLogin && GM_config.get('enConfUser')) { // remove second image from cardview addStyleString('.thread-footer-cell a img.avatar.vAlign--all-m.space--mr-1.thread-avatar {display: none;}'); EnstylerAvatarPopupDo(); } } // code used for MyDealz avatar popup, thanks to mydealz :-) const enPopupUser = ['<button data-handler="track popover" data-track="{"action":"show_short_user_profile","label":"engagement"}" data-popover="{"endpoint":"', '/short","target":"#template-popoverLoader","layout":[{"preset":"e","y":"50%","left":{"offset":0},"width":300,"maxWidth":"100%"}]}">', '</button>', ]; function EnstylerAvatarPopupDo() { // login needed ... (Error in Popup without login ...) // replace every avatar link without popup if (enUserLogin && GM_config.get('enConfUser')) { $('.thread-footer-cell a.user.linkPlain, .user.linkPlain.thread-user').each(function () { // get inner html and link to user profile var myHtml = $(this).html(); var mysrc = $(this).attr('href'); // seperate user name from image and add class user var myAvatar1 = myHtml.replace(/<span.*/,''); var myAvatar2 = myHtml.replace(/.*<span class=".* space--mr-1">/,'<span class=" space--mr-1 user link-plain">'); // show small / medium sized Avatar myAvatar1 = myAvatar1.replace('avatar--type-s','avatar--type-m'); //in Dealz if (GM_config.get('enConfAvatar')) { myAvatar1 = myAvatar1.replace('thread-avatar','avatar--type-m'); } // compose popup var myPopup = enPopupUser[0] + mysrc + enPopupUser[1] + myAvatar1 + enPopupUser[2] + '<a href="' + mysrc + '">'+ myAvatar2 + '</a>'; $(this).replaceWith(myPopup); }); } } // create select page or scrollwheel for page navigation ============= const EnstylerPageEnum='enPageEnum'; const selectList = document.createElement("select"); selectList.id = EnstylerPageEnum; selectList.setAttribute('class', EnstylerPageEnum); selectList.onchange = EnstylerPageAction; //const selectListDown = document.createElement("select"); //selectListDown.id = EnstylerPageEnum+'Down'; //selectList.setAttribute('class', EnstylerPageEnum); //selectListDown.onchange = EnstylerPageAction; function EnstylerPagePickerCreate() { // revome existing picker EnstylerPagePickerRemove(); // if enabled if (GM_config.get('enConfPagePicker')) { // init values and clear select list var page=1, max=1; $(selectList).empty(); //$(selectListDown).empty(); // get page and max from pagenav if ( $('.text--color-charcoalTint').length ) { // remove linebreaks var pageHtml = $('.text--color-charcoalTint').html().replace(/\r?\n|\r/g); //locate actual page and last page if( NaN == (page = parseInt(pageHtml.replace( /.*>Seite /i ,'')) )) { page=1;} if( NaN == (max = parseInt(pageHtml.replace( /.*page=/ ,'')) )) { max=page;} } // create page select element function addOption(select,text) { var option = document.createElement("option"); option.text = text; select.add(option); } for (var x = 1; x <= max; ) { addOption(selectList, x); //addOption(selectListDown,x); var last = x; // non linear increment var diff = Math.abs(x-page); if ( x < 10 || diff < 5) { x++; } else if ( x < 1000 && diff > 600) { x += Math.floor(diff/100); } else { x += Math.floor(diff/2); } } // add last page if (page > max) { max=page;} if (last < max ) { addOption(selectList,max); //addOption(selectListDown,max); } // set default value selectList.value = page; //selectListDown.value = page; // placement of MAIN Picker MainPicker= ['.js-navDropDown-messages', //Element EnstylerPageEnum+' js-navDropDown-messages vAlign--all-m' //class ]; // login button present in Mainnav if ($('.test-loginButton').length) { MainPicker[0]='.test-loginButton'; //Element } // in deal always in sticky votebar (was in subnav) if ($('.voteBar').length) { MainPicker= [ '.voteBar--sticky-off--hide:last', // Element EnstylerPageEnum +' subNavMenu-link subNavMenu-btn voteBar--sticky-off--hide' //class ]; } // Main Picker add class and palce before element selectList.setAttribute('class',MainPicker[1]); $(MainPicker[0]).before(selectList); // Footer Picker //$('.text--color-brandPrimary button').replaceWith(selectListDown); } } function EnstylerPagePickerDo() { // get page and max from pagenav if ( $('.js-sticky .text--color-charcoalTint').length ) { // remove linebreaks var pageHtml = $('.js-sticky .text--color-charcoalTint').html().replace(/\r?\n|\r/g); //locate actual page and last page var page = pageHtml.replace( /.*>Seite /i ,''); page = page.replace( /<.*/i , ''); if (page == '') {page=1;} // set default value selectList.value = page; //selectListDown.value = page; } } // goto selected Page function EnstylerPageAction() { var enPage = 'page=' + $(this).val(); // remove page= and everthing behind var enUrl = enLocParser.toString().replace( /page=.*|#.*/ ,''); // add new page parameter if ( enUrl.endsWith('?') || enUrl.endsWith('&')) { enUrl += enPage; } else { enUrl += '?'+enPage; } // add #thread-comments for deal if (enSection == '/deals/') { enUrl += '#thread-comments';} window.location = enUrl; } function EnstylerPagePickerRemove() { // Removes pagepicker from the document $('.'+ EnstylerPageEnum).remove(); } // blacklist do not show dealz containing blacklistet words ========================== // search in kategorie, dealtitle, and username const enClassHidden = 'enClassHidden'; const enClassBlackDone = 'enClassBlackDone'; var enBlacklisted=0; const unwantedRegex = [ /[\[\]\(\)\{\}\?\.\:\;\!\"\*\+\ ]/g, // in White/Backlist /[\[\]\(\)\{\}\?\.\:\;\!\"\*\+\,]/g // in Dealtext ]; var enBlack; var enWhite; var enBlackTemp; function EnstylerBlacklist() { // if logged in and user is not in whitelist if (enUserLogin && ! GM_config.get('enConfWhitelist').includes(enUserName)) { // add actual user to whitelist GM_config.set('enConfWhitelist', '@'+enUserName +',' + GM_config.get('enConfWhitelist')); //GM_config.setValue('enConfWhitelist', GM_config.get('enConfWhitelist')); } // convert Black/Whitelist to RegEx var myTemp=GM_config.get('enConfBlacklist'); myTemp = myTemp.replace(unwantedRegex[0], ''); myTemp = myTemp.replace(/^,|,$/g,''); enBlack = myTemp.replace(/(.),(.)/g,'$1|$2'); myTemp=GM_config.get('enConfWhitelist'); myTemp = myTemp.replace(/^,|,$/g,''); enWhite = myTemp.replace(/(.),(.)/g,'$1|$2'); enBlackTemp= GM_config.get('enConfHideColder'); EnstylerBlacklistRemove(); EnstylerBlacklistDo(); } function EnstylerBlacklistDo() { if (!GM_config.get('enConfBlackEnable')) { return;} // process every article $('article:not(' +enClassBlackDone+')').each(function () { var myThread = '#'+$(this).attr('id'); // return if already done or return no deal if (!myThread.startsWith('#'+enDealMarker)) { return;} // mark as already seen $(this).addClass(enClassBlackDone); // get title, categorie, user var myDealText = $(myThread + ' .thread-category').text(); myDealText += ' ' +$(myThread + ' .thread-title a').text(); myDealText += ' @' +$(myThread + ' .user').text(); myDealText = myDealText.replace(unwantedRegex[1] ,' '); // Whitelist first // whitelist Regex, exit if found if (enWhite !='' && myDealText.match(new RegExp(enWhite,'i'))) { return; } //get temp var myVoteTemp = $(myThread + ' .vote-temp').text(); // blacklist vote temp if (parseInt(myVoteTemp) <= enBlackTemp) { $(this).addClass(enClassHidden); enBlacklisted++; EnstylerLastSeenSkip(myThread) return; } // blacklist last // blacklist Regex, rxit if found if (enBlack !='' && myDealText.match(new RegExp(enBlack,'i'))) { $(this).addClass(enClassHidden); enBlacklisted++; EnstylerLastSeenSkip(myThread) return; } }); // END Article // set label for unBlacklist button EnstylerBlacklistShow() } // blacklist support functions .... const enUnblackText = 'unBlacklist <ENSTYLER-TEXT-HERE> Dealz'; function EnstylerBlacklistShow() { enJSfieldDefs.enConfUnblacklist.label=enUnblackText.replace(enPATTERN[2],enBlacklisted) } function EnstylerBlacklistRemove() { enBlacklisted=0; EnstylerBlacklistShow() $('.'+enClassHidden).removeClass(enClassHidden); $('.'+enClassBlackDone).removeClass(enClassBlackDone); } function EnstylerBlacklistUnhide() { enBlacklisted=0; EnstylerBlacklistShow() $('.'+enClassHidden).removeClass(enClassHidden); } // Main Nav will stay on TOP of the screen ========================= const myFixedCSS = [ /* 0 everywhere */'.enFixedNav { display: block; position: fixed; width: 100%; z-index: 120;} .subNav, .userProfile, .tabbedInterface {margin-top: <ENSTYLER-TEXT-HERE>px;}', /* 1 subnav */ '.subNav {margin-top: 0 !important;} .nav-subheadline {margin-top: <ENSTYLER-TEXT-HERE>px;}', /* 2 diskussion */'.tGrid.page2-center.height--all-full {margin-top: calc(<ENSTYLER-TEXT-HERE>px + 10px);}' ]; function EnstylerFixedNav() { if (GM_config.get('enConfNavFixed')) { // everywhere but in Deal detail, I like it like it is ... if (enSection != '/deals/' && enSection != '/gutscheine/' ){ // delete header element with active stuff, but keep inside HTML var mySavedHtml = $('header').html(); $('header').replaceWith('<header class="enFixedNav">'+mySavedHtml+'</header>'); // fixed NAV for everywhere var myFixedStyle=myFixedCSS[0]; // additionla CSS for different sections if (enSection == '/diskussion/') { myFixedStyle+=myFixedCSS[2]; } if ($('.nav-subheadline').length || enSection=='/profile/') { // additional CSS for categories myFixedStyle+=myFixedCSS[1]; } myFixedStyle= myFixedStyle.replace(enPATTERN[2], enMainHeigth) addStyleString(myFixedStyle) } } } // the return of "gestern xx:xx Uhr" ============== var enNow; var DealDate; var TodayStart; var ShowTime; var EnstylerTimeSeen='enTimeSeen'; function EnstylerDealTime() { enNow = new Date(); enNow.setTime(EnstylerStartTime) DealDate=new Date(); TodayStart = new Date(enNow.getFullYear(), enNow.getMonth(), enNow.getDate()); ShowTime= GM_config.get('enConfDealMinTime')*3600*1000; EnstylerDealTimeDo(); } function EnstylerDealTimeDo() { if (GM_config.get('enConfDealTime')) { enNow.setTime(Date.now()) // process every article, optimization: not if class TiemSeen $('time:not(.'+EnstylerTimeSeen+')').each(function () { // get Deal time Diff, return if no diff or already seen var myTime= $(this).text(); if ( !myTime.startsWith('v')) {return;} // get Deal Time difference, optimzed: no s, no NaN var h = parseInt(myTime.replace(/.* ([0-9].*) h.*/, '$1')); //if (isNaN(h)) h=0; var m = parseInt(myTime.replace(/.* ([0-9].*) m.*/, '$1')); //if (isNaN(m)) m=0; // compose real deal time var myDealDiff = (h*60+m)*60000; // Offset deal, seconds ignored DealDate.setTime( enNow.getTime() - myDealDiff ); // last midnigth if (DealDate < TodayStart) { $(this).text('gestern '+ DealDate.toString().slice(16, 21) +' Uhr'); // more than 6 hours ago } else if (myDealDiff > ShowTime){ $(this).text(myTime + ' (heute '+ DealDate.toString().slice(16, 21) +' Uhr)'); } else { return; } $(this).addClass(EnstylerTimeSeen); }); } } // mark last seen Deal in Highligth, Hot and New ============================ var enSec='off'; var enSeenArticle=''; // GM variables used here // store newest loaded deal // 'enNewestDeal...new' // 'enNewestDeal...hot' // 'enNewestDeal...' // international support added const enNewestBase='enNewest'+enLocParser.hostname.replace('www',''); function EnstylerLastSeen(){ // only in main categories if(enSection.match(enMainSectionMatch)) { // get section and save enSec= enNewestBase + enSection.replace(/\//, ''); GM_setValue(enNewestBase+'LastSec', enSec) // mark last seen article enSeenArticle=GM_getValue(enSec); EnstylerLastSeenDo(); // save actual last seen $('article:not(.threadWidget-item)').each(function () { // pinned ? if ($(this).find('.cept-pinned-flag').length) {return;} // thread ID var myThread = $(this).attr('id'); // when in base of section ... if(enLocParser.search == '') { //store actual seen GM_setValue(enSec, myThread); //store last seen GM_setValue(enSec+'Last', enSeenArticle); } // exit loop return false; }); } else { // if we are not on base of a categorie restore last value enSec=GM_getValue(enNewestBase+'LastSec'); GM_setValue(enSec, GM_getValue(enSec+'Last')); } } function EnstylerLastSeenDo(){ // only in main categories if(enSec != 'off') { // mark last seen article if ( typeof enSeenArticle != 'undefined' && enSeenArticle != '') { //store last marked GM_setValue(enSec+'Last', enSeenArticle); $('#'+enSeenArticle).addClass('enClassMarkArticle'); $('#'+enSeenArticle).prev().addClass('comments-marker'); } else { // first time GM_setValue(enSec, enDealMarker+'1'); } } } // article is not availible i.e. blacklisted function EnstylerLastSeenSkip(DealID) { // if article last seen one, skip to next if (DealID == '#'+enSeenArticle) { // magic, get ID of next article enSeenArticle=$(DealID).next().attr('id'); EnstylerLastSeenDo(); } } // AMAZON mobile redirect ================================== // workaround to not intercept myDealz redirects with GM_xmlhttp // stolen from amazon redirect mobile: https://greasyfork.org/de/scripts/19700 function enAmazonMobileRedirect() { var enMyLocation=enLocParser.toString(); // do we run on amazon? if (enMyLocation.startsWith("https://www.amazon")) { // redirect enabled? if (GM_config.get('enConfAmazonRedirect')) { // do it if (enMyLocation.includes("/gp/aw/d/")) { window.location.assign(enMyLocation.replace("/gp/aw/d/", "/dp/")) } else { window.location.assign(enMyLocation.replace("/gp/aw/ol/", "/gp/offer-listing/")); } } // Amazon but no redirect enabled return false; } return true; } // compose Nav Menu items ======================================= // i.e. create button for display Config ====================== // define pattern actions here, incl. international support const enMainSectionMatch=/^\/$|^\/hot$|^\/new$|^\/settings$|^\/discussed$|^\/hei%C3%9F$|^\/diskutiert$/; const enPATTERN = [ /<ENSTYLER-HREF-HERE>/g, // 0 pattern to insert link ... /<ENSTYLER-THREADID-HERE>/g, // 1 pattern to insert ID /<ENSTYLER-TEXT-HERE>/g, // 2 pattern to insert Text ]; const enNavEntry='enNavEntry'; const enMenuItemCode = [ /*0 MainNav*/ '<a class="enNavEntry navMenu-link" id="<ENSTYLER-THREADID-HERE>" href="<ENSTYLER-HREF-HERE>" data-handler="track" data-track="{"action":"goto_main_target","beacon":true}"><span class="navMenu-link-ico ico ico--type-discussion-navMenuLayerItem navMenu-ico--selected--type-discussion navMenu-ico--hover--type-discussion"></span><ENSTYLER-TEXT-HERE></a>', /*1 SubNav */ '<li class="enNavEntry subNavMenu-item--separator test-tablink-discussed"><a href="<ENSTYLER-HREF-HERE>" class="subNavMenu-item subNavMenu-link space--h-4 vAlign--all-m" id="<ENSTYLER-THREADID-HERE>" data-handler="track" data-track="{"action":"goto_menu_target sort","label":"diskutiert","beacon":true}"><span class="box--all-i size--all-xl vAlign--all-m"><ENSTYLER-TEXT-HERE></span><span class="js-vue-container--threadcount" data-handler="vue" data-vue="{"count":null}"></span></a></li>', /*2 MainBut */ '<a class="enNavEntry navMenu-link" id="<ENSTYLER-THREADID-HERE>"><span class="navMenu-link-ico ico ico--type-page-navMenuLayerText"></span><ENSTYLER-TEXT-HERE></a>', /*3 SubBut */ '<li class="enNavEntry subNavMenu-item--separator test-tablink-discussed"><a class="subNavMenu-item subNavMenu-link space--h-4 vAlign--all-m" id="<ENSTYLER-THREADID-HERE>"><span class="box--all-i size--all-xl vAlign--all-m"><ENSTYLER-TEXT-HERE></span></a></li>' ]; const enMenuMain=0; const enMenuSub=1; const enMenuMainButton=2; const enMenuSubButton=3; const enMenuItemLength= enMenuItemCode.length; // Enstyler Button const EnstylerButton = 'EnstylerButton'; // compose default Enstyler Menu function EnstylerMenuActions(){ EnstylerNavRemove() // add entrys to Main Nav, now Menu only! //if(GM_config.get('enConfMenuItem')) { if (enLocParser.host.endsWith('mydealz.de')) { // the return of Diskussionen for Mydealz EnstylerAddNav(enMenuMain,'Alle Diskussionen', 'https://www.mydealz.de/diskussion','enMainDiscussion') } // Enstyler Homepage on MyDealz EnstylerAddNav(enMenuMain, 'Enstyler Homepage', 'https://www.mydealz.de/diskussion/enstyler-856062" target="_blank','enMainHomepage'); EnstylerAddNav(enMenuMainButton, 'Enstyler Einstellungen', showEnstylerConfig, EnstylerButton); //} // add to Sub Nav //EnstylerAddNav(enMenuSubButton, 'Enstyler' , showEnstylerConfig, EnstylerButton) } // add to Nav ====================== // nav = menu action // text = menu text // target = URL to show, in case of Button function to call function EnstylerAddNav(nav,text,target,ID) { // exit if no defined Menu action if (nav >= enMenuItemLength) {return;} var isFunc=false; // compose menu entry var myEntry = enMenuItemCode[nav].replace(enPATTERN[1],ID); myEntry = myEntry.replace(enPATTERN[2],text); // target can be a function if (typeof target === "function") { isFunc=true; } else { myEntry = myEntry.replace(enPATTERN[0],target); } switch(nav) { case enMenuMain: // Main Nav add delayed case enMenuMainButton: // first Main menu entry, start listen to klick if(enAddMain == '') { $('.navMenu-page').click(EnstylerMainDo); } enAddMain += myEntry; if(isFunc) { enAddMainFunc[enAddMainCount++]= { ID: ID , target: target}; } break; case enMenuSub: // Sub Nav, add now case enMenuSubButton: // ad to Subnav, click if visible $('.subNavMenu-list').append(myEntry); if(isFunc) { $('#'+ID).click(target); } // handler if dropdown, start listen to klick if(enAddSub == '') { $('.subNavMenu-trigger').click(EnstylerSubDo); enAddSub='done'; } if(isFunc) { enAddSubFunc[enAddSubCount++]= { ID: ID , target: target}; } break; } } // Show items in Sub / Main Menu ===== // store ID and function to call on click var enAddMain=''; var enAddMainFunc= [ ]; var enAddMainCount=0; var enAddSub=''; var enAddSubFunc= [ ]; var enAddSubCount=0; function EnstylerMainDo() { // klick event handler, wait until menu is created sleepAsync(300).then(() => { //add items $(enAddMain).insertBefore('.popover-content nav .navMenu-div:first'); // create space for new entrys var myMenu=$('.popover--mainNav'); // 35px per items + old heigth var myHeigth=35*(enAddMain.split(enNavEntry).length -1) + parseInt(myMenu.attr('style').split('height: ')[1]); myMenu.attr('style',myMenu.attr('style').replace(/height: [0-9.]*px/,'height: '+myHeigth+'px')); // add button callbacks for (var i=0; i<enAddMainCount; i++ ) { $('section #' + enAddMainFunc[i].ID).click(enAddMainFunc[i].target); } }); } function EnstylerSubDo() { // klick event handler, wait until menu is created sleepAsync(300).then(() => { //add items for (var i=0; i<enAddSubCount; i++ ) { $('section #' + enAddSubFunc[i].ID).click(enAddSubFunc[i].target); } }); } function EnstylerNavRemove() { // Clear Menu Items stored enAddMain=''; enAddMainFunc= [ ]; enAddMainCount=0; $('.navMenu-page').unbind('click'); // remove visible items $('.'+enNavEntry).remove(); } // ============= GM_config functions ======================================= const enJSAutoUpdate=GM_info.scriptWillUpdate; var enUpdateWindow; const enInternationalSite=enLocParser.hostname.replace('www',''); const enInternationalName=capitalizeFirstLetter(enInternationalSite.replace(/^\.|\..*/g,'')); const enInternational=(enInternationalName != 'Mydealz'); // define EnstylerJS GM_config elements const enJSfieldDefs = { // Part one: load external content -------- 'enstyler': { 'section': ['additonal features for Enstyler', ''], 'label': 'Update CSS', // Appears on the button 'type': 'button', // Makes this setting a button input 'click': function() { // Function to call when button is clicked enUpdateWindow=window.open('https://userstyles.org/styles/128262#style-info', 'UserCSS', 'width=600,height=950,left=0,top=0'); } }, 'enstylerJS': { 'label': 'Update UserScript', // Appears on the button 'type': 'button', // Makes this setting a button input 'click': function() { // Function to call when button is clicked enUpdateWindow=window.open(!DEBUG ? 'https://greasyfork.org/scripts/24243-enstylerjs/code/EnstylerJS.user.js' : ' https://greasyfork.org/scripts/24244-enstylerjs-develop/code/EnstylerJS Develop.user.js', 'UserScript', 'width=110,height=110,left=0,top=0'); // give 5s to start update, then close sleepAsync(5000).then(() => { enUpdateWindow.close(); }); } }, 'dontCookies': { 'label': 'Mozilla no cookies', // Appears on the button 'type': 'button', // Makes this setting a button input 'click': function() { // Function to call when button is clicked window.open('https://addons.mozilla.org/de/addon/i-dont-care-about-cookies/'); } }, // part two: EnstylerJS internal configuration options ------ 'Section': { // display next section, dont kow why ... 'section': ['Configuration', ''], 'type': 'hidden', // Makes this setting a hidden input }, 'enConfNavFixed': { 'label': 'Display FIXED MainNav', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, // ehanced USerInfo 'enConfUser': { 'label': 'Show Popuop Userinfo', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'enConfAvatar': { 'label': 'bigger Avatar for Popuop', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, // enable filtering of external links 'enConfAmazonRedirect': { 'label': 'Amazon mobile redirect', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, // */ // more Deal actions 'enConfMoreDeal': { 'label': 'additional Deal actions', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, // more Deal actions // Page picker 'enConfPagePicker': { 'label': 'Enable Page Picker', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, // show real Dealtime 'enConfDealTime': { 'label': 'Show real Deal Time', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'enConfDealMinTime': { 'label': 'if older than Hours', // Appears next to field 'type': 'int', // Makes this setting a text input 'min': 1, // Optional lower range limit 'max': 24, // Optional upper range limit 'size': 4, // Limit length of input (default is 25) 'default': 6 // Default value if user doesn't change it }, // Black/Whitelist input 'enConfBlackEnable': { 'label': 'Enable Black- / Whitelist', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'enConfHideColder': { 'label': 'Blacklist if colder then', // Appears next to field 'type': 'int', // Makes this setting a text input 'min': -9999, // Optional lower range limit 'max': -9, // Optional upper range limit 'size': 4, // Limit length of input (default is 25) 'default': -999 // Default value if user doesn't change it }, 'enConfBlacklist': { 'label': 'Blacklist - deals, categories, @users', // Appears next to field 'type': 'text', // Makes this setting a text input 'size': 70, // Limit length of input (default is 25) 'default': 'Nutella, Bangood, @Admin' // Default value if user doesn't change it }, 'enConfWhitelist': { 'label': 'Whitelist', // Appears next to field 'type': 'text', // Makes this setting a text input 'size': 70, // Limit length of input (default is 25) 'default': '' // Default value if user doesn't change it }, 'enConfUnblacklist': { 'label': 'UnBlacklist', // Appears on the button 'type': 'button', // Makes this setting a button input 'click': function() { // Function to call when button is clicked EnstylerBlacklistUnhide(); } }, // used to not destroy saved Enstyler2 Options 'saveOpt': { 'type': 'hidden', // Makes this setting a button input }, }; // define EnstylerJS GM_config elements const enHomefieldDefs = { // Part one: load external content -------- 'saveOpt': { 'section': ['save your CSS options for next visit', ''], 'label': 'Select your CSS on main page then come back and klick "Save" ', // Appears near textarea 'type': 'textarea', // Makes this setting a button input 'size': 70, }, // display copy message at end of section ... 'copy': { 'section': ['', '(c) Gnadelwartz - <a target="blank" href="https://www.mydealz.de/diskussion/enstyler2-style-your-mydealz-incl-pepper-sites-736219">Enstyler2 - Style your MyDealz</a>'], 'type': 'hidden', // Makes this setting a hidden input 'value': 'Some hidden value' // Value stored }, }; // display GM_config as div, so we can apply CSS easy!! var enGMOptChange = false; const enGMFrame = document.createElement('div'); enGMFrame.setAttribute('class','GM_config'); document.body.appendChild(enGMFrame); // basic config panel formatting, everything else is formatted by enstyler const enCSS = ['.GM_config {left: 5% !iportant; top: 8% !important; height: auto !important; max-width: 35em !important; background-color: white;}', '.GM_config input, .GM_config button, .GM_config textarea { border: 1px solid; margin: 0.5em 0em 0.2em 1em; padding: 0.1em;}', '.GM_config .reset { font-size: 9pt; padding-right: 1em; }', '.enClassHidden, #EnPopup_closeBtn {display: none;}', ].join(" "); // contains hight of navigation ... var enMainHeigth; // set colors of GM_Config panel to integrate better in international pages if ($('.nav').length){ // pepper sites // calc colors and topx var myBgColor=$('.nav').css('background-color'); var enGmCss = ' .GM_config {background-color: '+ shadeRGBColor(myBgColor, -0.30) + ' !important; color: '+ shadeRGBColor(myBgColor, 0.50); enMainHeigth = parseInt($('header').outerHeight()); enGmCss += '; top: '+ enMainHeigth +'px !important;}' // calc navlink hover color enGmCss += ' .nav-link-text:hover, .js-navDropDown-messages:hover, .js-navDropDown-activities:hover { background-color: ' +shadeRGBColor(myBgColor, 0.11)+ ' !important;}'; } addStyleString(enCSS+enGmCss); var enGMConfigOpen=false; // EnstylerJS Config Panel anzeigen function showEnstylerConfig () { if(!enGMConfigOpen) { enGetHome(); GM_config.open(); // hide menu $('.popover--mainNav').remove(); enGMConfigOpen=true; } else { //$('section.popover--mainNav').removeClass(enClassHidden); GM_config.close(); } } // export to window context //unsafeWindow.showEnstylerConfig = showEnstylerConfig; // EnstylerJS START ======================== if (!window.location.hostname.endsWith('userstyles.org')) { var enFixedNavLast=false; var enButtonLast=false; GM_config.init( { // international sites support id: enInternational ? 'GM_config' + enInternationalSite : 'GM_config', title: !DEBUG ? 'EnstylerJS - Settings' : ' EnstylerJS - >> Debug <<', fields: enJSfieldDefs, 'events': // Callback functions object { //'init': function() { alert('onInit()'); }, // remove elements ich switch is checked or not 'open': function() { var enRemoveConfig = [ { check: false, switch: 'enConfDealTime', remove: 'enConfDealMinTime'}, { check: false, switch: 'enConfBlackEnable', remove: 'enConfWhitelist'}, { check: false, switch: 'enConfBlackEnable', remove: 'enConfBlacklist'}, { check: false, switch: 'enConfBlackEnable', remove: 'enConfHideColder'}, { check: false, switch: 'enConfBlackEnable', remove: 'enConfUnblacklist'} ]; enFixedNavLast=GM_config.get('enConfNavFixed'); // remove unneeded controls $(enRemoveConfig).each(function() { if (GM_config.get(this.switch) == this.check) { GM_config.fields[this.remove].remove(); } }); // remove / display update dialog if (enJSAutoUpdate) {GM_config.fields['enstylerJS'].remove();} }, //'reset': function() { alert('reset') }, // relaod page on close after save 'save': function() { // disabeling FixedNav can only done with reload if (!GM_config.get('enConfNavFixed') && GM_config.get('enConfNavFixed')!=enFixedNavLast) {window.location.reload(false);} // restart Enstyler magic GM_config.close(); EnstylerStart(); EnstylerMenuActions(); EnstylerPagePickerCreate(); // show changes in config after processing magic GM_config.open(); }, 'close': function() { enGMConfigOpen=false;}, }, 'frame': enGMFrame // Element used for the panel } ); // Enstyler internal Startup functions ====================== // HACK: we are NOT on Amazon if (enAmazonMobileRedirect()) { // dummy, do not delete function enGetHome() {;} // Start Enstyler Magic function EnstylerStart() { EnstylerFixedNav(); EnstylerDealTime(); EnstylerLastSeen(); EnstylerBlacklist(); EnstylerAvatarPopup(); EnstylerDealActions(); } // delayed actions after finishing everything else function EnstylerDelayedInit() { // don't know why, but works only if called with delay ... EnstylerMenuActions(); EnstylerPagePickerCreate(); // track DOM change Events, debounce: wait 1000ms after mutiple events // then re-apply (somse) changes to dynamic loaded content, // $('.cept-event-deals, .thread-list--type-card').bind("DOMSubtreeModified",$.debounce( 400, function(){ $('.js-pagi-bottom').bind("DOMSubtreeModified", debounce( 100, function(){ EnstylerLastSeenDo(); EnstylerDealTimeDo(); EnstylerBlacklistDo(); EnstylerAvatarPopupDo(); EnstylerDealActionsDo(); EnstylerPagePickerDo(); })); } // =============== MAIN: START EnstyerJS =================== EnstylerInit(); EnstylerStart(); // wait until page is loaded completely if (document.readyState == 'loading' || document.readyState == 'interactive'){ // Greasemonkey and Tampermonky -> runs script on DOM ready -> wait for load $(window).bind("load", function() { EnstylerDelayedInit(); }); } else { // if script run on page loaded -> give some time to finish rendering sleepAsync(Date.now()-EnstylerStartTime).then(() => { EnstylerDelayedInit(); }); } } // END Enstyler MAIN // ============= EnStyler UserScript Homepage functions ======= // support for EnStyler2 export / import } else { // we are on ujserstyle var input = document.createElement('input'); input.type = 'button'; input.setAttribute('id', EnstylerButton); input.onclick = showEnstylerConfig; input.value = 'Save Options'; function EnstylerHomeButton() { $('#'+EnstylerButton).remove() input.setAttribute('style', 'font-size: 1.1em; padding: 0.8em;'); $('#style-settings').after(input); } function enGetHome() { var myOptions=''; $('#style-settings select').each(function() { var myID = $(this).attr('id'); var myValue = $('#'+myID).val(); var myText = $('option[value='+ myValue +']').text(); myOptions +='#' + myID + ':' + myValue +':' + myText +';\n'; }); $('#style-settings input:checked').each(function() { var myID = $(this).attr('id'); var myValue = $('#'+myID).val(); var myText = $('label[for='+ myID +']').text(); myOptions +='#' + myID + ':' + myValue +':' + myText +';\n'; }); GM_config.set('saveOpt', myOptions); } function enSetHome() { input.value = 'Save Options'; // get saved options,remove newlines and split to settings array var myOptions=GM_config.get('saveOpt'); myOptions=myOptions.replace(/\n/g,''); var mySettings = myOptions.split(';'); // abort if no options found if (myOptions=='' || !myOptions.startsWith('#')) {return;} for (var i=0; i< mySettings.length; i++) { // each Setting has 3 fields seperated by :, but only 2 used var myField=mySettings[i].split(':'); if (myField[0].match(/^#setting/i)) { // select $(myField[0]).val(''); $(myField[0]).val(myField[1]); } else if (myField[0].startsWith('#option')) { // option $(myField[0]).prop('checked', true); } else { if (myField[0] != '') {alert('unkown option: "' + myField +'"');} } } // update shown otions HideShowLogoSelect(); HideShowEnstyler(); } // close Window after Click on Update function closeOnClick () { sleepAsync(10000).then(() => { window.close(); }); } $(".install").click(closeOnClick); // show if options not saved function showNotSaved() { input.value = 'Options not saved!'; EnstylerHomeButton(); } $("#style-settings").click(showNotSaved); function ShowHideItem(selectID, hideVal, jqSelektor) { if ($(selectID).val() == hideVal) { $('#style-settings '+jqSelektor).parent().addClass(enClassHidden); } else { $('#style-settings '+jqSelektor).parent().removeClass(enClassHidden); } } // remove / display logo selection function HideShowLogoSelect() { ShowHideItem('#setting-455195', 'ik-logo1', 'label:contains(MyDealz Logo)' ); } $('#setting-455195').change(HideShowLogoSelect); // remove / display enstyler options function HideShowEnstyler() { ShowHideItem('#setting-451668', 'ik-compact1', 'label:contains(|---)' ); } $('#setting-451668').change(HideShowEnstyler); // activate config for Enstyler Homepage GM_config.init( { id: 'GM_config', title: 'Enstyler2 - Settings', fields: enHomefieldDefs, 'events': // Callback functions object { //'init': function() { alert('onInit()'); }, // remove elements ich switch is checked or not //'open': function() { enGetHome(); }, //'reset': function() { enGMOptChange = true; }, // relaod page on close after save 'save': function() { enSetHome(); EnstylerHomeButton(); GM_config.close();}, 'close': function() { enGMConfigOpen=false; }, }, 'frame': enGMFrame // Element used for the panel } ); // START Enstyler 2 Homepage EnstylerHomeButton(); // set saved options enSetHome(); } //=========== Support functions for actual use ====== // add CSS in to document function addStyleString(str) { var node = document.createElement('style'); node.innerHTML = str; document.body.appendChild(node); } function capitalizeFirstLetter(string) { return string[0].toUpperCase() + string.slice(1); } // sleep time expects milliseconds, then execute code // NOTE: code runs in parallel (asnyc)! // Usage! // sleepAsync(500).then(() => { // Do something after the sleep! // }); function sleepAsync (time) { return new Promise((resolve) => setTimeout(resolve, time)); } // make colors ligther or darker // http://stackoverflow.com/questions/5560248 //color = "rbg(63,131,163)"; //lighterColor = shadeRGBColor(color, 0.5); // rgb(159,193,209) //darkerColor = shadeRGBColor(color, -0.25); // rgb(47,98,122) function shadeRGBColor(color, percent) { var f=color.split(","),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=parseInt(f[0].slice(4)),G=parseInt(f[1]),B=parseInt(f[2]); return "rgb("+(Math.round((t-R)*p)+R)+","+(Math.round((t-G)*p)+G)+","+(Math.round((t-B)*p)+B)+")"; } // https://remysharp.com/2010/07/21/throttling-function-calls // $('input.username').keypress(debounce(250, function)); // Ensytler debounce Funtionen, modified: parameter swapped, no args passed // todo: dynamic delay? function debounce(delay, fn) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function () { fn.call(this); }, delay); }; }