Enstyler

MyDealz Enstyler enhanced features incl. Amazon Mobile Redirect

当前为 2016-12-07 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

/// ==UserScript==
// @name        Enstyler
// @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.071
// @grant       GM_getValue
// @grant       GM_setValue
// @require     http://code.jquery.com/jquery-3.1.1.min.js
// @require     http://cdnjs.cloudflare.com/ajax/libs/jquery-throttle-debounce/1.1/jquery.ba-throttle-debounce.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 = '';
var EnstylerStartTime=Date.now();

// Parse location for later use
var 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 to tread overview @ some places ==================================

// 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="{&quot;action&quot;:&quot;scroll_to_comment_add_form&quot;,&quot;label&quot;:&quot;engagement&quot;}">',
                       '<span class="hide--toW3">Sag was dazu</span><span class="hide--fromW2">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="{&quot;endpoint&quot;:&quot;https:\/\/www.mydealz.de\/threads\/<ENSTYLER-THREADID-HERE>/remove&quot;,&quot;method&quot;:&quot;post&quot;}" data-track="{&quot;action&quot;:&quot;save_thread&quot;,&quot;label&quot;:&quot;engagement&quot;}">',
                       '<span class="hide--toW3">Von Liste entfernen</span><span class="hide--fromW2">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="{&quot;action&quot;:&quot;goto_thread_edit_form&quot;,&quot;beacon&quot;:true}"><span class="hide--toW3">',
                       '<span class="hide--toW3">Bearbeiten</span><span class="hide--fromW2">Bearb</span>', '', '<span></a>',
                     ];
  const PATTERN = [ /<ENSTYLER-HREF-HERE>/g,     // pattern to replace by deal link ...
                  /<ENSTYLER-THREADID-HERE>/g // pattern to replace ID
                ];
  const enDealMarker='thread_'
  
  var enDealAdd='';

function EnstylerDealActions(){
    // 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: comment
    enDealAdd = enDealAction[0]+ enDealAction[1+myText] + enDealAction[3];

    // Action for special locations only ===========
    // saved Dealz panel 
    if (pathname.endsWith('profile/saved-deals') ){
        // add for user saved-dealz: un-bookmark
        enDealAdd += enDealAction[4]+ enDealAction[5+myText]; + enDealAction[7]
    }
    if (pathname.endsWith('profile/diskussion') || pathname.endsWith(enUserName)){
        // add user dealz and discussions: comment edit
        enDealAdd +=  enDealAction[8]+ enDealAction[9+myText] + enDealAction[11];
    }
    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').each(function () {
            // get ThreadID, Link to Deal and DealID
            var myThread = $(this).attr('id');
            if (!myThread.startsWith(enDealMarker)) {return;}
            
            var myDealHref = $('#' + myThread + ' .thread-title a').attr('href');
            var myDealID = myThread.replace(enDealMarker,'');
            
            if (myThread.startsWith('To be')) {return;}
            
            // compose final HTML
            var newHtml= enDealActionOuterHtml[0] + enDealAdd + enDealActionOuterHtml[1];
            newHtml = newHtml.replace(PATTERN[0], myDealHref);
            newHtml = newHtml.replace(PATTERN[1], myDealID);
            
            // 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="{&quot;name&quot;:&quot;threads&quot;}">',
                          '</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>|<div><br>|<\/br>|<\/div>/gi,'');
        newHtml =  myHtml.replace(/<p>|<p><br>/gi,'<br><br>');
        newHtml =  newHtml.replace(/<br><br><br>|<br><br><br><br>|<br><br><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="{&quot;action&quot;:&quot;show_short_user_profile&quot;,&quot;label&quot;:&quot;engagement&quot;}" data-popover="{&quot;endpoint&quot;:&quot;',
                   '/short&quot;,&quot;target&quot;:&quot;#template-popoverLoader&quot;,&quot;layout&quot;:[{&quot;preset&quot;:&quot;e&quot;,&quot;y&quot;:&quot;50%&quot;,&quot;left&quot;:{&quot;offset&quot;:0},&quot;width&quot;:300,&quot;maxWidth&quot;:&quot;100%&quot;}]}">',
                   '</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;
   var min=1;
   var 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
       page = pageHtml.replace( /.*>Seite /i ,''); page = page.replace( /<.*/i , '');
       max =  pageHtml.replace( /.*page=/ ,'');  max = max.replace( /[^0-9].*/i , '');
       if (page == '') {page=1;}
       if (max  == '') {max=page;}
   }

   // create page select element
   var x; var last; var option;
   function addOption(select,text) {
          var option = document.createElement("option");
          option.text = x;
          select.add(option);
   }
     
   for (x = min; x <= max; ) {

       addOption(selectList,x);
       //addOption(selectListDown,x);
       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
   var MainPicker;
     
   // Where to place? Opposite of Enstyler Button!
   if (GM_config.get('enConfBtn')) {
            // place in Subnav
            MainPicker= [ '.subNav-label:last', // Element
                          EnstylerPageEnum+' box--all-i subNavMenu-link subNavMenu-btn' //class
                        ];          

   } else {
            //place in MainNav
            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').each(function () {
            if ($(this).hasClass(enClassBlackDone)) { return;}
            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 <NUM-BLACK> Dealz';
function EnstylerBlacklistShow() {
       enJSfieldDefs.enConfUnblacklist.label=enUnblackText.replace('<NUM-BLACK>',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 myFixedCssMain='.nav { display: block; position: fixed; width: 100%; z-index: 120;} .subNav, .userProfile, .tabbedInterface {margin-top: 4.4em;}';
const myFixedCssSub ='.subNav {margin-top: 0 !important;} .nav-subheadline {margin-top: 4.4em;}';

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);
                
                // CSS for everywhere
                addStyleString(myFixedCssMain);
                if (! enSection.match(enMainSectionMatch)) {
                    // additional CSS for categories
                    addStyleString(myFixedCssSub);
                }
            }
        }
}

// 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
         $('time').each(function () {
            // get Deal time Diff, return if no diff or already seen
            var myTime= $(this).text();
            // next article
            if ( $(this).hasClass(EnstylerTimeSeen) || !myTime.startsWith('vor ')) { return;}
   
            // get Deal Time difference 
            var h = myTime.replace(/.* ([0-9].*) h.*/, '$1'); if (h==myTime) h=0; else h=parseInt(h);
            var m = myTime.replace(/.* ([0-9].*) m.*/, '$1'); if (m==myTime) m=0; else m=parseInt(m);
            var s = myTime.replace(/.* ([0-9].*) s.*/, '$1'); if (s==myTime) s=0; else s=parseInt(s);

            // compose real deal time
            var myDealDiff = ((h*60+m)*60+s)*1000; // Offset deal
            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').each(function () {
          // get ThreadID an return if not enDealMarker
          var myThread = $(this).attr('id');
          if (!myThread.startsWith(enDealMarker)) {return;}

          // 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 ancategorie 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') {
          //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');
        if (DEBUG) console.error('Skip Deal: '+DealID.substr(1)+' -> '+enSeenArticle)
        
        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;
}

// create button for display Config ======================
const EnstylerButton = 'EnstylerButton';

//var input = document.createElement('a');
//    input.setAttribute('href', 'showEnstylerConfig()');
//    input.setAttribute('id', EnstylerButton);
var input = document.createElement('input');
    input.type = 'button';
    input.setAttribute('id', EnstylerButton);
    input.onclick = showEnstylerConfig;

function EnstylerButtonCreate() {
    // add Enstyler Button to ...
    var myElement;
    var myMain=false;
   
    input.value = 'Enstyler';
    EnstylerButtonRemove();
    // MainNav or Deal
    if (GM_config.get('enConfBtn') || enSection.match(/deals\/|gutscheine\//))
        { myMain=true; }
    
    // only if space left
    if ($(window).width() < GM_config.get('enConfBtnMinWidth'))
        { myMain=false; }
    
    if (myMain) {
       // add button to MainNav
       var Elements = document.getElementsByClassName("navMenu-trigger");
       myElement=Elements[0];
       input.setAttribute('class', 'vAlign--all-m nav-link-text');
       input.setAttribute('style', '');
    } else {
       // add button to SubNav
       myElement = document.getElementById('tour-filter');
       input.setAttribute('class', 'box--all-i subNavMenu-link');
       input.setAttribute('style', 'font-size: 1.28571em; font-weight: 700; top: 3px; left: -0.7em'); 
    }
  
    // only if myElement exist
    if (myElement !== null) {
        myElement.appendChild(input);
        //insertAfter(input, myElement);
    }
}

function EnstylerButtonRemove() {
        // Removes button from the document
        $('#'+ EnstylerButton).remove
}


// ============= GM_config functions =======================================
const enJSAutoUpdate=GM_info.scriptWillUpdate;
var enUpdateWindow;

// define pattern for main sections here, incl. international support
const enMainSectionMatch=/^\/$|^\/hot$|^\/new$|^\/settings$|^\/discussed$|^\/hei%C3%9F$|^\/diskutiert$/
var enInternationalSite='';
if(!enLocParser.hostname.match(/mydealz\.de/i)) {
    enInternationalSite=enLocParser.hostname.replace('www','');
    if(DEBUG) console.error('International Site detected: ' +enInternationalSite);
}

// 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/de/scripts/24243' :
                                          ' https://greasyfork.org/scripts/24244-enstylerjs-develop/code/EnstylerJS Develop.user.js',
                                          'UserScript', 'width=110,height=110,left=0,top=0');
               // give 2s to start update, then close
               sleepAsync(2000).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
   },

   // postion of enstyler "button"
   'enConfBtn': {
      'label': 'Enstyler in MainNav', // Appears next to field
      'type': 'checkbox', // Makes this setting a checkbox input
      'default': false // Default value if user doesn't change it
    },
    
    'enConfBtnMinWidth': {
      'label': 'if width is bigger than ', // Appears next to field
      'type': 'int', // Makes this setting a text input
      'min': 600, // Optional lower range limit
      'max': 1200, // Optional upper range limit
      'size': 4, // Limit length of input (default is 25)
      'default': 850 // Default value if user doesn't change it
    },
    
   '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

    // 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
   },    

    // 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
    },

    // 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(); }
    },
   
   // 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
   },
};

// 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,
      //'click': function() { // Function to call when button is clicked
      //         showUrl('https://userstyles.org/styles/128262#style-info'); }
    },
   
   // 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: 9% !important; height: auto !important; max-width: 35em !important;}',
             '.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(" ");
addStyleString(enCSS);

var En_Popup = new GM_configStruct(
   {
  'id': 'EnPopup', // You need to use a different id for each instance
  'title': 'EnstylerJS - Info', 
  'fields': // Fields object
    {
       'Text': // This is the id of the field
       {
         'label': '', // Appears next to field
         'type': 'textarea', // Makes this setting a text field
         'default': '' // Default value if user doesn't change it
       }
    },
  'events':
    {
      'open': function (doc) {
              // rename the buttons
              var config = this;
              doc.getElementById(config.id + '_saveBtn').textContent = ' OK ';
              //doc.getElementById(config.id + '_closeBtn').textContent = 'Cancel';
             doc.getElementById(config.id + '_resetLink').textContent = '';
          },
      'save':  function() {
              enUpdateChecked=false;
              enCheckUpdates();
              En_Popup.close();
            },
      'close': function() { enGMConfigOpen=false;},
    },
  'frame': enGMFrame // Element used for the panel
  }
);

function showPopup(text) {
    En_Popup.fields['Text'].value = text;
    En_Popup.fields['Text'].reload();
    En_Popup.open();
}


var enGMConfigOpen=false;
// EnstylerJS Config Panel anzeigen
function showEnstylerConfig() {
  if(!enGMConfigOpen) {
      enGetHome();
      GM_config.open();
      enGMConfigOpen=true;
  } else {
      GM_config.close();
  }    
}


// EnstylerJS START ========================
if (!window.location.hostname.endsWith('userstyles.org')) {
    var enFixedNav=false;
    GM_config.init(
      {
                       //  international sites support
        id: 'GM_config'+enInternationalSite,
        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: true,  switch: 'enConfFilterLink',  remove: 'externalMobileRedirect'},
               { check: false, switch: 'enConfBtn',         remove: 'enConfBtnMinWidth'},
               { check: false, switch: 'enConfUser',        remove: 'enConfAvatar'},
               { 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'}
              ];
             enFixedNav=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')!=enFixedNav) {window.location.reload(false);}
              
              // restart Enstyler magic
              EnstylerStart();
              EnstylerButtonCreate();
              EnstylerPagePickerCreate();

              // show changes in config after processing magic
              GM_config.close();
              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 ...
           EnstylerButtonCreate();
           EnstylerPagePickerCreate();
        
          // track DOM change Events, debounce: wait 1000ms after mutiple events
          // then re-apply (somse) changes to dynamic loaded content, 
         if(DEBUG) console.error('DOMSubtreeModified INIT');
          $('.cept-event-deals, .thread-list--type-card').bind("DOMSubtreeModified",$.debounce( 300, function(){
              EnstylerLastSeenDo();
              EnstylerDealTimeDo();
              EnstylerBlacklistDo();
              EnstylerAvatarPopupDo();
              EnstylerDealActionsDo(); 
              EnstylerPagePickerDo();
              if(DEBUG) console.error('DOMSubtreeModified executed');
          }));
    }
   


    // =============== 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
        if(DEBUG) console.error('Run on DOM ready');
        $(window).bind("load", function() { EnstylerDelayedInit(); });
    } 
    else { // if script run on page loaded -> give some time to finish rendering
         if(DEBUG) console.error('Run on Document loaded');
         sleepAsync(Date.now()-EnstylerStartTime).then(() => { EnstylerDelayedInit(); });
    }
   } // END Enstyler MAIN


// ============= EnStyler UserScript Homepage functions =======
// support for EnStyler2 export / import

} else {
    // we are on ujserstyle
    function EnstylerHomeButton() {
        EnstylerButtonRemove()
        
        // add Enstyler2 Button to ...
        input.value = 'Save Options';
        if (GM_config.get('saveOpt')=='') {input.value = 'no Options saved'}
        
        var  myElement = document.getElementById('style-settings');
        input.setAttribute('style', 'font-size: 1.1em; padding: 0.8em;'); 
  
        // only if myElement exist
        if (myElement !== null) {
          //myElement.appendChild(input);
          insertAfter(input, myElement);
        }
    }

    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() {
        // get saved options,remove newlines and split to settings array
        var myOptions=GM_config.get('saveOpt');
        // if(DEBUG) console.error('Saved Options: ' + myOptions);
        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++) {
           //if(DEBUG) console.error('process:' + mySettings[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 +'"');}
            }
        }
    } 
    
    // close Window after Click on Update
    function UpdateOnClick () {
       sleepAsync(10000).then(() => { window.close(); });
    }
    $(".install").click(UpdateOnClick);

    // remove / display logo selection based on option 
    function UpdateLogoSelection() {
        // selection is "Original"
        if ($('#setting-453429').val() == 'ik-flame1') {
            // hide logo selection
            $('#style-settings > li:last-child').addClass('enClassHidden');
        } else {
            // show logo selection
            $('#style-settings > li:last-child').removeClass('enClassHidden');
        }
    }
    $('#setting-453429').change(UpdateLogoSelection);
    
    // 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();
    // show/hide logo selection
    UpdateLogoSelection();
    
    

}