// ==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.055
// @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 EnstylerButton = 'EnstylerButton';
//var input = document.createElement('a');
// input.setAttribute('href', 'showEnstylerConfig()');
// input.setAttribute('id', EnstylerButton);
var input = document.createElement('input');
input.type = 'button';
input.value = 'Enstyler';
input.setAttribute('id', EnstylerButton)
input.onclick = showEnstylerConfig;
function EnstylerInit () {
// hide Enstyler2 CSS (c) text because we have a button now
addStyleString('.threadWidget-footer::after {display: none !important};');
EnstylerButtonCreate();
// 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);
}
// blacklist do not show dealz containing blacklistet words
// search in kategorie, dealtitle, and username
function EnstylerBlacklist() {
;
}
function EnstylerStart () {
// ============ stuff 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 EnstylerStart
// add actions to tread overview @ some places ==================================
function EnstylerDealActions() {
// code used for MyDealz Dealz actions, thanks to mydealz :-)
var myOuterHtml = [ '<span class="js-options bg--em bRad--a space--h-3 space--v-3 space--mt-3 text--b">', '</span>'];
var enDealAction = [ '<a class="link ico ico--pos-l ico--type-comment-blue space--mr-3"', // comment 0+1
'href="<ENSTYLER-HREF-HERE>#comment-form" data-handler="track" data-track="{"action":"scroll_to_comment_add_form","label":"engagement"}">Sag was dazu</a>',
'<a class="link text--color-blue ico ico--type-bookmark-blue ico--pos-l space--mr-3"', //un-bookmark 2+3
'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"}"> Von Liste entfernen</a>',
'<a class="link text--color-blue ico ico--type-pencil-blue ico--pos-l space--mr-3"', // edit 4+5
'href="<ENSTYLER-HREF-HERE>/edit" data-handler="track" data-track="{"action":"goto_thread_edit_form","beacon":true}">Bearbeiten</a>',
'<a class="thread-expire link ico ico--type-clock-blue ico--pos-l space--mr-3"', // expiried not working :-(
'href="<ENSTYLER-HREF-HERE>/expire/report" rel="nofollow" data-handler="track replace" data-track="{"action":"report_expired_thread","label":"contribution"}" data-replace="{"endpoint":"https:\/\/www.mydealz.de\/<ENSTYLER-HREF-HERE>/expire\/report"}">Abgelaufen?</a>',
];
var PATTERN = [ /<ENSTYLER-HREF-HERE>/g, // pattern to replace by deal link ...
/<ENSTYLER-THREADID-HERE>/g, // pattern to replace ID
]
if (GM_config.get('enConfMoreDeal')) {
var parser = location; // parse location
var pathname = parser.pathname;
var username = pathname.replace(/.*profile\//,'');
username = username.replace(/\/.*/,'');
// no username ??
if (username == "") {
username = "unknown";
} else {
pathname = pathname.replace(username + '/',''); // remove username if path is longer
}
//alert("username:" + username + " pfadname:" +pathname)
// default for all Dealz: comment
var myAction = enDealAction[0]+ enDealAction[1];
// Action for special locations only ====================
// saved Dealz panel
if (pathname.endsWith('profile/saved-deals') ){
// add for user saved-dealz: un-bookmark
myAction += enDealAction[2]+ enDealAction[3];
}
if (pathname.endsWith('profile/diskussion') || pathname.endsWith(username)){
// add user dealz and discussions: comment edit
myAction += enDealAction[4]+ enDealAction[5];
}
// we have an Action to add to an Deal!
if (myAction != "") {
//GM_log('Action:' + myAction);
// every thread on page ...
$('article').each(function () {
// get ThreadID, Link to Deal and DealID
var myThread = $(this).attr('id');
var myDealHref = $('#' + myThread + ' .thread-title a').attr('href');
var myDealID = myThread.replace('thread_','')
// compose final HTML
var newHtml= myOuterHtml[0] + myAction + myOuterHtml[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 everywhere ==============
// remove unwanted HTML from deal description
$('.thread--type-detail .userHtml').each(function () {
// userhtml code from mydalz, need to find jafascript to save automatically :-(
var enUserhtml = ['<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>|<div><br>|<\/br>|<\/div>/gi,'');
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(enUserhtml[0] + newHtml + enUserhtml[1]);
});
} // END enabled
}
// show popup user info while click on avatar ... ==============
function EnstylerAvatarPopup() {
// code used for MyDealz avatar popup, thanks to mydealz :-)
var 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>',
];
// 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
if (GM_config.get('enConfUser')) {
$('.thread-footer-cell a.user.linkPlain, .user.linkPlain.thread-user').each(function () {
var Iam = $(this);
// get inner html and link to user profile
var myHtml = Iam.html();
var mysrc = Iam.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] + Iam.attr('href') + enPopupUser[1] + myAvatar1 + enPopupUser[2] + '<a href="' + Iam.attr('href') + '">'+ myAvatar2 + '</a>';
Iam.replaceWith(myPopup);
});
}
}
function EnstylerButtonCreate() {
// 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("nav-link");
myElement = Elements[3];
input.setAttribute('class', 'nvAlign--all-m nav-link-text');
} 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 an element from the document
var element = document.getElementById(EnstylerButton);
element.parentNode.removeChild(element);
}
// ============= 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();
}
if (!GM_config.get('enConfBtn')) {
GM_config.fields['enConfBtnMinWidth'].remove();
}
if (!GM_config.get('enConfUser')) {
GM_config.fields['enConfAvatar'].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();
}
EnstylerButtonRemove();
EnstylerButtonCreate();
},
//'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));
}
// like .insertBefore but as function
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
// ============== START EnstyerJS =============
EnstylerInit();
EnstylerStart();
EnstylerBlacklist();
EnstylerAvatarPopup();
EnstylerDealActions();
// 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!!!');
EnstylerBlacklist();
EnstylerAvatarPopup();
}));