// ==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.222 // @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 // @downloadURL none // ==/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; var 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'); } else { //restore old last seen if user logs in // use this variant for dynamic loaded content click ... $(document).on("click",'.test-loginButton', EnstylerLastSeenLast); } // 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 = [ '', 'Sag was dazuSag', '', '', '', 'Von Liste entfernenEnft', '', '', '', 'BearbeitenBearb', '', '', '', 'Mail versendenMail', '', '', ]; const enDealMarker='#thread_'; const enDealNoAction='.ico--type-clock-grey, .vote-temp--colder'; var enDealAdd='', enDealUnbook=false; 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(/.*
/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]; enDealUnbook=true; 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]; // Compose final Deal Actions enDealAdd= enDealActionOuterHtml[0] + enDealAdd + enDealActionOuterHtml[1]; // do it ... EnstylerDealActionsDo(); } } // surrounding myDealz HTML const enDealActionOuterHtml = [ '', '']; function EnstylerDealActionsDo() { // if logged in and enabled ... if (enUserLogin && GM_config.get('enConfMoreDeal')) { // every thread on thread page ... $('article:not(.'+enClassHidden+')').each(function () { // get infoRow var myInfoRow=$('.thread-infoRow', $(this)); if (!myInfoRow.length || $(enDealNoAction, $(this)).length) {return;} // get Titel, Link, DealID num var myDeal =$('.thread-title a', $(this)); var myDealHref = myDeal.attr('href'); // compose mail subject var mySub=encodeURIComponent(enInternationalName+': '+myDeal.text()); //if (mySub.length < 100 && $('.thread-price', $(this)).length) { // mySub += encodeURIComponent(' ->_' +$('.thread-price', $(this)).text().replace(/\t/g,'')); //} // compose final HTML var newHtml = enDealAdd.replace(enPATTERN[0], myDealHref) .replace(enPATTERN[2], truncStringWord(mySub, 160, '%20') +'&body=' +mySub +'%0D%0A%0D%0A' +myDealHref +enDealActionMailFooter); if (enDealUnbook) {newHtml = newHtml.replace(enPATTERN[1], ('#'+$(this).attr('id')).replace(enDealMarker,''));} // append HTML to Deal myInfoRow.append(newHtml); myInfoRow.removeClass('thread-infoRow'); }); // actions for somewhere =========== // remove unwanted HTML from deal description $('.thread--type-detail .userHtml').each(function () { // get inner html var myHtml = $(this).html(); // remove unwanted Stuff: combined


stuff, created by cut'npaste html // not elegant, but works ... var newHtml = myHtml.replace(/<\/p>|<\/div>/gi,'').replace(/
|

/gi,'

').replace(/
(
)*
/gi,'

'); $(this).html(newHtml); }); } // 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 = ['', ]; const enClassAvatarDone = 'enClassAvatarDone'; function EnstylerAvatarPopupDo() { // login needed ... (Error in Popup without login ...) // replace every avatar link without popup if (enUserLogin && GM_config.get('enConfUser')) { var myBigAvatar=GM_config.get('enConfAvatar'); // each avatar $('.thread-footer-cell a.user.linkPlain, .user.linkPlain.thread-user').each(function () { if( $(this).hasClass(enClassAvatarDone)|| $(this).hasClass(enClassBlackDone)) return; $(this).addClass(enClassAvatarDone); // 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 myAvatar = myHtml.replace(//,''); if (myBigAvatar) { myAvatar = myAvatar.replace(/thread-avatar/,'avatar--type-m'); } // compose popup $(this).html(enPopupUser[0] + mysrc + enPopupUser[1] + myAvatar + enPopupUser[2] + ''+ myHtml.replace(/.*/,'') + ''); }); } } // 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; 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(); // 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( isNaN(page = parseInt(pageHtml.replace( /.*>Seite /i ,'')) )) { page=1;} if( isNaN(max = parseInt(pageHtml.replace( /.*page=/ ,'')) )) { max=page;} } // create page select element for (var x = 1; x <= max; ) { var option = document.createElement("option"); option.text = x; selectList.add(option); 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 ) { var option = document.createElement("option"); option.text = max; selectList.add(option); } // set default value selectList.value = page; // placement of MAIN Picker var 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); } } function EnstylerPagePickerDo() { // get page and max from pagenav if ( $('.js-sticky .text--color-charcoalTint').length ) { //locate actual page incl remove line breaks var page = parseInt($('.js-sticky .text--color-charcoalTint').html().replace(/\r?\n|\r/g).split( '--toW2">Seite ')[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, enBlackTrue; var enWhite, enWhiteTrue; 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 enBlack=RegExp(GM_config.get('enConfBlacklist').replace(unwantedRegex[0], '').replace(/^,|,$/g,'').replace(/(.),(.)/g,'$1|$2'),'i'); ''.match(enBlack) ? enBlackTrue=false : enBlackTrue=true ; enWhite=RegExp(GM_config.get('enConfWhitelist').replace(/^,|,$/g,'').replace(/(.),(.)/g,'$1|$2'),'i'); ''.match(enWhite) ? enWhiteTrue=false : enWhiteTrue=true ; enBlackTemp= GM_config.get('enConfHideColder'); EnstylerBlacklistRemove() EnstylerBlacklistDo(); } function EnstylerBlacklistDo() { if (!GM_config.get('enConfBlackEnable') || ( !enBlackTrue && enBlackTemp < -900)) { return;} // process every article $('article:not(.'+enClassBlackDone+', .threadWidget-item)').each( function () { // mark as already seen $(this).addClass(enClassBlackDone); // get title, categorie, user, remove unwanted chars var myDealText = ($('.thread-category',$(this)).text() +' ' +$('.thread-title a',$(this)).text() +' @' +$('.user',$(this)).text()).replace(unwantedRegex[1] ,' '); // whitelist Regex, exit if found if ( enWhiteTrue && myDealText.match(enWhite)) { return; } // vote temp & blacklist if (parseInt($('.vote-temp', $(this)).text()) <= enBlackTemp || enBlackTrue && myDealText.match(enBlack)) { $(this).addClass(enClassHidden); enBlacklisted++; EnstylerLastSeenSkip('#'+$(this).attr('id')); } }); // END Article // set label for unBlacklist button EnstylerBlacklistShow() } // blacklist support functions .... const enUnblackText = 'unBlacklist 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, .splitPage-wrapper {margin-top: px;}', /* 1 subnav */ '.subNav {margin-top: 0 !important;} .nav-subheadline {margin-top: px;}', /* 2 diskussion */'.tGrid.page2-center.height--all-full {margin-top: calc(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('

'+mySavedHtml+'
'); // 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, var myTime= $(this).text(); // next article if less than an h or older than 24h if ( myTime.length <9 || !myTime.startsWith('v')) {return;} // compose deal offset var myDealDiff = (parseInt(myTime.replace(/.* ([0-9].*) h.*/, '$1'))*60+parseInt(myTime.replace(/.* ([0-9].*) m.*/, '$1')))*60000; DealDate.setTime( enNow.getTime() - myDealDiff ); // last midnigth if (DealDate < TodayStart) { $(this).text('gestern '+ DealDate.toString().slice(16, 21) +' Uhr'); // more than x hours ago } else if (myDealDiff > ShowTime){ $(this).html(myTime + ' (heute '+ DealDate.toString().slice(16, 21) +' Uhr)'); } else { return; } $(this).addClass(EnstylerTimeSeen); }); } } // mark last seen Deal in Highligth, Hot and New ============================ var enSec=''; 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',''); var LastSeenOnce=true; function EnstylerLastSeen(){ // only once and in main categories if(LastSeenOnce) { LastSeenOnce=false; // store last seen for Main catergories if(enSection.match(enMainSectionMatch) && enLocParser.search == '') { // get section and save enSec= enNewestBase + enSection.replace(/\//, ''); GM_setValue(enNewestBase+'LastSec', enSec) // get last seen article enSeenArticle=GM_getValue(enSec); if ( typeof enSeenArticle == 'undefined') {enSeenArticle='';} EnstylerLastSeenDo(); // save actual last seen $('article:not(.threadWidget-item)').each(function () { // pinned ? if (!('.cept-pinned-flag',$(this)).length) {return;} //store actual seen GM_setValue(enSec, $(this).attr('id')); //store last seen GM_setValue(enSec+'Last', enSeenArticle); // exit loop return false; }); } else { // if we are not in main categorie => restore last value EnstylerLastSeenLast() } } } function EnstylerLastSeenDo(){ // only in main categories if(enSec != '') { // mark last seen article if (enSeenArticle != '') { //store last marked GM_setValue(enSec+'Last', enSeenArticle); $('#'+enSeenArticle).addClass('enClassMarkArticle'); } else { // first time GM_setValue(enSec, 'thread_1'); } } } // restore last seen from last last seen function EnstylerLastSeenLast(){ // restore last value enSec=GM_getValue(enNewestBase+'LastSec'); GM_setValue(enSec, GM_getValue(enSec+'Last')); } // 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(); } } // 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 = [ //g, // 0 pattern to insert link ... //g, // 1 pattern to insert ID //g, // 2 pattern to insert Text ]; const enNavEntry='enNavEntry'; const enMenuItemCode = [ /*0 MainNav*/ '', /*1 SubNav */ '', /*2 MainBut */ '', /*3 SubBut */ '' ]; const enMenuMain=0; const enMenuMainButton=2; //const const enMenuSub=1; const enMenuSubButton=3; const enMenuItemLength= enMenuItemCode.length; // Enstyler Button const EnstylerButton = 'EnstylerButton'; // compose default Enstyler Menu function EnstylerMenuActions(){ EnstylerNavRemove() if (!enInternational) { // MyDealz only: alle Diskussionen EnstylerAddNav(enMenuMain,'Alle Diskussionen', 'https://www.mydealz.de/diskussion','enMainDiscussion') } // add Enstyler Homepage EnstylerAddNav(enMenuMain, 'Enstyler Homepage', 'https://www.mydealz.de/diskussion/enstyler-856062" target="_blank','enMainHomepage', 'building'); // add EnstylerJS config EnstylerAddNav(enMenuMainButton, 'Enstyler Einstellungen', showEnstylerConfig, EnstylerButton, 'page'); // 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 // Icon can be home, tag, scissors, free, discussion (default), building, star, snowflake, page (button), star (button) var enNavIconPat='--type-discussion'; function EnstylerAddNav(nav,text,target,ID, Icon) { // exit if no defined Menu action if (nav >= enMenuItemLength) {return;} if (typeof Icon == 'undefined' || Icon == '') Icon=enNavIconPat; var isFunc=false; // compose menu entry var myEntry = enMenuItemCode[nav].replace(enPATTERN[1],ID).replace(enPATTERN[2],text); if(Icon !=enNavIconPat) { myEntry = myEntry.split(enNavIconPat).join('--type-'+Icon)} // 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 == '') { $('.nav-link.navMenu-trigger').click(debounce( 300, 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(debounce( 300, 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; function EnstylerMainDo() { // klick event handler, call with debounce( 300) to wait until menu is created and avoid double klicks //add items $(enAddMain).insertBefore('.popover-content nav .navMenu-div:first'); // create space for new entrys var myMenu=$('.popover--mainNav'); // +35px per new items 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