EnstylerJS

MyDealz Enstyler Frontend and enhanced features

目前為 2016-11-03 提交的版本,檢視 最新版本

// ==UserScript==
// @name        EnstylerJS
// @namespace   Enstyler
// @description MyDealz Enstyler Frontend and enhanced features
// @include     http://www.mydealz.de/*
// @include     https://www.mydealz.de/*
// @version     2.11.031
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_log
// @grant       GM_xmlhttpRequest
// @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==

// ========== RUN EnstylerJS =====================================
// create object for enstyler "Menu Button"
var input = document.createElement('input');
    input.type = 'button';
    input.value = 'Enstyler';
    input.onclick = showEnstylerConfig;


function EnstylerInit () {
    // hide Enstyler2 CSS (c) text because we have a button now
    addStyleString('.threadWidget-footer::after {display: none !important};');

    // basic config panel formatting, everything else is formatted by enstyler
    var enCSS = ['#GM_config {left: 5% !important; top: 9% !important; height: auto !important; max-width: 35em !important;}',
             '#GM_config input, #GM_config button { border: 1px solid; margin: 0.5em 0em 0.2em 1em; padding: 0.1em;}',
             '#GM_config .reset { font-size: 9pt; padding-right: 1em; }',
            ].join(" ");
    addStyleString(enCSS);
}

function EnstylerJS () {
    // add Enstyler Button to ...
    var myElement;
    if (GM_config.get('enConfBtn') && $(window).width() > GM_config.get('enConfBtnMinWidth')) { 
       // add button to MainNav
       var Elements = document.getElementsByClassName("navMenu-trigger");
       myElement = Elements[0]; 
       input.className = 'nav-link-text vAlign--all-m';
    } else {
       // add button to SubNav
       myElement = document.getElementById('tour-filter');
       input.className = '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);
    }

    // ============ functions to modify page content =============================

    // REGEX to detect external URL
    var REGEX_THREAD     = /^https?:\/\/www\.mydealz\.de\/visit\/thread(image)?\/\d+$/;
    var REGEX_DESC       = /^https?:\/\/www\.mydealz\.de\/visit\/threaddesc\/\d+\/\d+$/;
    var REGEX_COMMENT    = /^https?:\/\/www\.mydealz\.de\/visit\/comment\/\d+\/\d+$/;
    var REGEX_AMAZONMOB  = /^https?:\/\/www\.amazon\..*\/gp\/aw\/.*$/;
    //var REGEX_AMAZON     = /^https?:\/\/www\.amazon\..*$/;

    //* GM_xmlhhtpREquest not supported by firefox mobile :-((( 
    // abfangen aller links mit jQuery
    $('a').bind('click', function(){
         var url = this.href;

         // externer Link mit Redirect ...
         // Match REDIRECT URLs and open new Window with finalUrl
         if (GM_config.get('enConfFilterLink') && url.match(REGEX_THREAD) || url.match(REGEX_DESC) || url.match(REGEX_COMMENT)) {
             // Workaround: pre open window because of popup blockers
             var asyncWindow = window.open(url, '_blank');
         
             // now lookup redirecd external URL ...
             // alert("external URL detected");
             GM_xmlhttpRequest({
                 method: 'GET',
                 url: url,
                 // here we get the final URL from redirect
                 onload: function (response) {
                   // process final URL
                   var newUrl = response.finalUrl;
                   //alert(newUrl);
                   // lets see ...
                   if (newUrl.match(REGEX_AMAZONMOB)) {
                       newUrl = newUrl.replace("/gp/aw/d/", "/dp/");
                       newUrl = newUrl.replace("/gp/aw/ol/", "/gp/offer-listing/");
                       //alert("Amazon rewritten URL: " + newUrl);
                   }
                   // load processed URL in preopened window
                   asyncWindow.location = newUrl;
                 }    
             });
             // return without link processing by Browser
             return false;
         }
  
         // return with link processing in Browser
         return true;  
    }); //  - END GM_xmlhttpRequest */
} // END Enstyler

function EnstylerDealActions() {
    var enDealAction = ['<ENSTYLER-HREF-HERE>', // pattern to replace by deal link ...
                        '<a class="link text--color-blue ico ico--type-pencil-blue ico--pos-l space--mr-3"', // edit
                        '<a class="link text--color-blue ico ico--type-clock-blue ico--pos-l space--mr-3"',  // expiried
                        '<a class="link text--color-blue ico ico--type-bookmark-blue ico--pos-l space--mr-3"', //un-bookmark
                        'href=<ENSTYLER-HREF-HERE>/edit" data-handler="track" data-track="{&quot;action&quot;:&quot;goto_thread_edit_form&quot;,&quot;beacon&quot;:true}">Bearbeiten</a>',
                        'href=<ENSTYLER-HREF-HERE>/expire" data-handler="track" data-track="{&quot;action&quot;:&quot;expire_thread&quot;,&quot;label&quot;:&quot;contribution&quot;}">Abgelaufen</a>',
                        'data-handler="track replace" data-replace="{&quot;endpoint&quot;:&quot;<ENSTYLER-HREF-HERE>/remove&quot;,&quot;method&quot;:&quot;post&quot;}" data-track="{&quot;action&quot;:&quot;save_thread&quot;,&quot;label&quot;:&quot;engagement&quot;}"> Von Liste entfernen</a>',
                       ];
}

function EnstylerAvatarPopup() {
        // show popup user info while click on avatar ...
    // code used for MyDealz avatar popup, thanks to mydealz :-)
    var 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>',
                   ];
    // remove second image from cardview
    if (GM_config.get('enConfUser')) { addStyleString('.thread-footer-cell a img.avatar.vAlign--all-m.space--mr-1.thread-avatar {display: none;}'); }

    // replace every avatar link without popup
    $('.thread-footer-cell a.user.linkPlain, .user.linkPlain.thread-user').each(function () {
        if (GM_config.get('enConfUser')) {
            var Iam = $(this);
            // get inner html and link to user profile
            var myHtml = $(this).html();
            var mysrc = $(this).attr('href');
            
            //GM_log(myHtml);
            // 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] + Iam.attr('href') + enPopupUser[1] + myAvatar1 +  enPopupUser[2] + '<a href="' + Iam.attr('href') + '">'+ myAvatar2 + '</a>';
            //GM_log(myPopup);
            Iam.replaceWith(myPopup);
        }
    });    
}


// ============= GM_config functions =======================================

// define GM_config elements
var fieldDefs = {
    // Part one: load external content --------
    'enstyler': { 
      'section': ['additonal features for Enstyler', ''],
      'label': 'Install / Update CSS...', // Appears on the button
      'type': 'button', // Makes this setting a button input
      'click': function() { // Function to call when button is clicked
               showUrl('https://userstyles.org/styles/128262#style-info'); }
    },

   '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
               showUrl('https://addons.mozilla.org/de/addon/i-dont-care-about-cookies/'); }
    },

    //* thhis has to be the last one,display only if internal version is disabled 
    'externalMobileRedirect': {
      'label': 'Amazon mobile redirect...', // Appears on the button
      'type': 'button', // Makes this setting a button input
      'click': function() { // Function to call when button is clicked
               showUrl('https://greasyfork.org/de/scripts/19700'); }
    }, // */

   // part two: EnstylerJS internal configuration options ------ 
   'hidden1': { // display next section, dont kow why ...
      'section': ['Configuration', ''],
        'type': 'hidden', // Makes this setting a hidden input
        'value': 'Some hidden value' // Value stored
   },

   // postion of enstyler "button"
   'enConfBtn': {
      'label': 'Show 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': 'only 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
    },

   // 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
  'enConfFilterLink': { 
      '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
    },

  
   // 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_copnfig as div, so we cam apply CSS!!
var enGMOptChange = false;
var enGMFrame = document.createElement('div');
document.body.appendChild(enGMFrame);

GM_config.init(
  {
    id: 'GM_config',
    title: 'EnstylerJS - Settings', 
    fields: fieldDefs, 
   'events': // Callback functions object
     {
      //'init': function() { alert('onInit()'); },
      // do not diplay external mobile redirect is internal is activated
      'open': function() { if (GM_config.get('enConfFilterLink')) {GM_config.fields['externalMobileRedirect'].remove();} },
      //'reset': function() { enGMOptChange = true; },
      // relaod page on close after save
      'save':  function() { enGMOptChange = true; if (GM_config.get('enConfFilterLink')) {GM_config.fields['externalMobileRedirect'].remove();} },
      'close': function() { if (enGMOptChange) { location.reload(); enGMOptChange = false;} },
     },
   'frame': enGMFrame // Element used for the panel
  }
);


// EnstylerJS Config Panel anzeigen
function showEnstylerConfig() {
    GM_config.open(); 
}

//=========== Support functions for actual use =======

// display website in external window

function showUrl(str) {
    var myDeco = "innerheight=800,innerwidth=600";
    var myName = "enstyler";
    
    // workaround for not working window.focus(): close an existing window first
    var myWindowShow = window.open('', myName, "width=100,height=100").close();
    myWindowShow = window.open(str, myName, myDeco);
}

// add CSS in to document
function addStyleString(str) {
    var node = document.createElement('style');
    node.innerHTML = str;
    document.body.appendChild(node);
}
// 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));
}

// ============== START EnstyerJS =============
EnstylerInit();
EnstylerJS();
EnstylerAvatarPopup();

// track DOM change Events, debounce: wait 500ms after mutiple events
// then re-apply (somse) changes to dynamic loaded content, 
$('.fGrid-last2, .thread-list--type-card').bind("DOMSubtreeModified",$.debounce( 500, function(){
   //GM_log('DOMSubtreeModified detected!!!');
    EnstylerAvatarPopup();

}));