// ==UserScript==
// @name EnstylerJS
// @namespace Enstyler
// @description MyDealz Enstyler enhanced features incl. Amazon Mobile Redirect
// @include http://www.mydealz.de/*
// @include https://www.mydealz.de/*
// @include https://userstyles.org/styles/128262/*
// @include https://www.amazon.*/gp/aw/*
// @version 2.11.276
// @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==
// ========== INIT EnstylerJS =====================================
// init Enstyler environment
var enUserLogin = false;
var enUserName = '';
var enSection = '';
var EnstylerStartTime=Date.now();
// Parse location for later use
var enLocParser= location;
var DEBUG=false;
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');
if(DEBUG) console.error('User: ' +enUserName);
}
// 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 :-)
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 title="Sag was dazu" class="link ico ico--pos-l ico--type-comment-blue space--mr-3"'+ // comment 0+1+3
'href="<ENSTYLER-HREF-HERE>#comment-form" data-handler="track" data-track="{"action":"scroll_to_comment_add_form","label":"engagement"}">',
'Sag was dazu', '', '</a>',
'<a title="Von Liste entfernen" class="link text--color-blue ico ico--type-bookmark-blue ico--pos-l space--mr-3"' + //un-bookmark 4+5+7
'data-handler="track replace" data-replace="{"endpoint":"https:\/\/www.mydealz.de\/threads\/<ENSTYLER-THREADID-HERE>/remove","method":"post"}" data-track="{"action":"save_thread","label":"engagement"}">',
'Von Liste entfernen', '', '</a>',
'<a title="Bearbeiten" class="link text--color-blue ico ico--type-pencil-blue ico--pos-l space--mr-3"'+ // edit 8+9+11
'href="<ENSTYLER-HREF-HERE>/edit" data-handler="track" data-track="{"action":"goto_thread_edit_form","beacon":true}">',
'Bearbeiten', '', '</a>',
'<a title="Abgelaufen?" 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
];
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()
}
function EnstylerDealActionsDo() {
// if logged in ...
if (enUserLogin) {
if (GM_config.get('enConfMoreDeal')) {
// we have an Action to add to an Deal!
if (enDealAdd != "") {
//GM_log('Action:' + myAction);
// every thread on thread page ...
$('article').each(function () {
// get ThreadID, Link to Deal and DealID
var myThread = $(this).attr('id');
if (!myThread.startsWith('thread_')) {return;}
var myDealHref = $('#' + myThread + ' .thread-title a').attr('href');
var myDealID = myThread.replace('thread_','');
if (myThread.startsWith('To be')) {return;}
// compose final HTML
var newHtml= myOuterHtml[0] + enDealAdd + 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 somewhere ===========
// remove unwanted HTML from deal description
$('.thread--type-detail .userHtml').each(function () {
// userhtml code from mydalz, need to find jafascript to save automatically :-(
var myUserhtml = ['<div class="userHtml overflow--wrap-break space--t-3" data-handler="lightbox-xhr" data-lightbox-xhr="{"name":"threads"}">',
'</div>', // sourround deal description
];
// get inner html
var myHtml = $(this).html();
// remove unwanted Stuff: combined <div><br><br> stuff, created by cut'npaste html
// not elegant, but works ...
var newHtml = myHtml.replace(/<div>|<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(){
EnstylerAvatarPopupDo();
}
function EnstylerAvatarPopupDo() {
// login needed ... (Error in Popup without login ...)
if (enUserLogin) {
// 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);
});
}
}
}
// create select page or scrollwheel for page navigation =============
var EnstylerPageEnum='enPageEnum';
var selectList = document.createElement("select");
selectList.id = EnstylerPageEnum;
selectList.setAttribute('class', EnstylerPageEnum);
selectList.onchange = EnstylerPageAction;
var EnstylerPageEnumDown='enPageEnumDown';
var selectListDown = document.createElement("select");
selectListDown.id = EnstylerPageEnumDown;
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();
// 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;
for (x = min; x <= max; ) {
option = document.createElement("option");
option.text = x;
selectList.add(option); //selectListDown.add(option);
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 ) {
option = document.createElement("option");
option.text = max;
selectList.add(option); //selectListDown.add(option);
}
// set default value
selectList.value = page; //selectListDown.value = page;
// Deal Page
if ($('.voteBar').length) {
selectList.setAttribute('class', EnstylerPageEnum +' subNavMenu-link subNavMenu-btn voteBar--sticky-off--hide');
$('.voteBar--sticky-off--hide:last').before(selectList);
} else {
// overwiew page
if (GM_config.get('enConfBtn')) {
// Place Picker in subnav
selectList.setAttribute('class', EnstylerPageEnum+' box--all-i subNavMenu-link subNavMenu-btn');
$('.subNav-label:last').before(selectList);
} else {
//place picker in MainNav
selectList.setAttribute('class', EnstylerPageEnum+' js-navDropDown-messages vAlign--all-m');
if ($('.test-loginButton').length) {
$('.test-loginButton').before(selectList);
} else {
$('.js-navDropDown-messages').before(selectList);
}
}
}
}
}
function EnstylerPagePickerCreateDo() {
// 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
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();
var enUrl = window.location.href;
var path = window.location.pathname;
// remove page= and everthing behind
enUrl = enUrl.replace( /page=.*|#.*/ ,'');
// add new page parameter
if ( enUrl.endsWith('?') || enUrl.endsWith('&')) {
enUrl += enPage;
} else {
enUrl += '?'+enPage;
}
// add #thread-comments for deal
if (path.startsWith('/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
var enClassHidden = 'enClassHidden';
var enClassBlackDone = 'enClassBlackDone';
var enUnblackText = 'unBlacklist <NUM-BLACK> Dealz';
var enBlacklisted=0;
var 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'));
}
// convert Black/Whitelist to Array
var myTemp=GM_config.get('enConfBlacklist');
myTemp = myTemp.replace(unwantedRegex[0], '');
enBlack = myTemp.split(',');
myTemp=GM_config.get('enConfWhitelist');
myTemp = myTemp.replace(unwantedRegex[0], '');
enWhite = myTemp.split(',');
enBlackTemp= GM_config.get('enConfHideColder');
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('thread_')) { 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
for (var i=-1, length=enWhite.length; ++i < length;) {
// whitelist Regex, exit if found
if (enWhite[i] !='' && myDealText.match(new RegExp(enWhite[i],'i'))) {
return;
}
}
//get temp
var myVoteTemp = $('#' + myThread + ' .vote-temp').text();
myVoteTemp = myVoteTemp.replace(/°$/,'');
// blacklist vote temp
if (myVoteTemp <= enBlackTemp) {
$(this).addClass(enClassHidden);
enBlacklisted++;
return;
}
// blacklist
for (var i=-1, length=enBlack.length; ++i < length;) {
// whitelist Regex, unwanted characteres are already removed
if (enBlack[i] !='' && myDealText.match(new RegExp(enBlack[i],'i'))) {
$(this).addClass(enClassHidden);
enBlacklisted++;
return;
}
}
}); // END Article
// set label for unBlacklist button
enJSfieldDefs.enConfUnblacklist.label=enUnblackText.replace('<NUM-BLACK>',enBlacklisted)
}
function EnstylerBlacklistRemove() {
enBlacklisted=0;
$('.'+enClassHidden).removeClass(enClassHidden);
$('.'+enClassBlackDone).removeClass(enClassBlackDone);
}
function EnstylerBlacklistUnhide() {
enBlacklisted=0;
$('.'+enClassHidden).removeClass(enClassHidden);
}
// Main Nav will stay on TOP of the screen =========================
function EnstylerFixedNav() {
var myFixedCssMain='.nav { display: block; position: fixed; width: 100%; z-index: 120;} .subNav, .userProfile, .tabbedInterface {margin-top: 4.4em;}';
var myFixedCssSub ='.subNav {margin-top: 0 !important;} .nav-subheadline {margin-top: 4.4em;}';
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(/^\/$|^\/hot|^\/new|^\/settings|^\/discussed/)) {
// 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();
if (!myTime.startsWith('vor ')|| $(this).hasClass(EnstylerTimeSeen)) { 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 mySec='off';
function EnstylerLastSeen(){
// only in main categories
if(enSection.match(/^\/$|new$|hot$/)) {
mySec=enSection.replace(/\//, '');
// mark last seen article
EnstylerLastSeenDo();
// save actual last seen
$('article').each(function () {
// get ThreadID an return if not thread_
var myThread = $(this).attr('id');
if (!myThread.startsWith('thread_')) {return;}
// store newest article
GM_config.setValue('enNewestDeal'+enSection.replace(/\//, ''), myThread);
return false;
});
}
}
function EnstylerLastSeenDo(){
// only in main categories
if(mySec != 'off') {
var myArticle=GM_config.getValue('enNewestDeal'+ mySec);
// mark last seen article
if ( typeof myArticle != 'undefined') {
$('#'+myArticle).addClass('enClassMarkArticle');
$('#'+myArticle).prev().addClass('comments-marker');
}
}
}
// 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 ==============
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.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("nav-link");
myElement = Elements[3];
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);
}
}
// needed for Enstyler Homepage
function EnstylerHomeButton() {
// add Enstyler Button to ...
input.value = 'Options';
var myElement = document.getElementById('style-settings');
input.setAttribute('style', 'font-size: 1.28571em; padding: 0.8em;');
// 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 =======================================
var enJSAutoUpdate=GM_info.scriptWillUpdate;
var enUpdateJSurl = "https://greasyfork.org/de/scripts/24243"; // production version
var enUpdateInterval=1 * (24*60); // 1 day between update checks
// define EnstylerJS GM_config elements
var 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
//GM_config.setValue('enEnVersion', 'no');
//GM_config.save();
//GM_config.open();
window.open('https://userstyles.org/styles/128262#style-info');
}
},
'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
//GM_config.setValue('enJSVersion', 'no');
//GM_config.save();
//GM_config.open();
window.open(enUpdateJSurl);
}
},
'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/'); }
},
/* 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': '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': false // 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': false // Default value if user doesn't change it
},
/* DISBALED check for Updates
'enConfCheckEnUpdate': {
'label': 'Check for Updates', // Appears next to field
'type': 'checkbox', // Makes this setting a checkbox input
'default': true // Default value if user doesn't change it
},
'enLastUpdateCheck': {
'type': 'hidden', // Makes this setting a text input
'default': '0' // Default value if user doesn't change it
},
'enJSVersion': {
'type': 'hidden', // Makes this setting a text input
'value': 'no' // Value stored
},
'enEnVersion': {
'type': 'hidden', // Makes this setting a text input
'value': 'no' // Value stored
},/* end DISABLED */
// 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(); }
},
// store newest loaded deal
'enNewestDealnew': {
'type': 'hidden', // Makes this setting a text input
'default': '0' // Default value if user doesn't change it
},
'enNewestDealhot': {
'type': 'hidden', // Makes this setting a text input
'default': '0' // Default value if user doesn't change it
},
'enNewestDeal': {
'type': 'hidden', // Makes this setting a text input
'default': '0' // 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
},
};
// define EnstylerJS GM_config elements
var 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_copnfig as div, so we can apply CSS easy!!
var enGMOptChange = false;
var enGMFrame = document.createElement('div');
enGMFrame.setAttribute('class','GM_config');
document.body.appendChild(enGMFrame);
// basic config panel formatting, everything else is formatted by enstyler
var 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; }',
'#GM_config_enstyler_var:after {content: ". <- please install CSS!"; font-weight: bold;}',
'.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(
{
id: 'GM_config',
title: !DEBUG ? 'EnstylerJS - Settings' : ' EnstylerJS - >> Debug <<',
fields: enJSfieldDefs,
'events': // Callback functions object
{
//'init': function() { alert('onInit()'); },
// remove elements ich switch is checked or not
'open': function() {
var enRemoveConfig = [
// { check: 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() {
if (!GM_config.get('enConfNavFixed') && GM_config.get('enConfNavFixed')!=enFixedNav) {window.location.reload(false);}
EnstylerButtonCreate();
EnstylerPagePickerCreate();
EnstylerBlacklistRemove();
EnstylerBlacklist();
EnstylerFixedNav();
EnstylerDealTime()
GM_config.close();
GM_config.open();
},
'close': function() { enGMConfigOpen=false;},
},
'frame': enGMFrame // Element used for the panel
}
);
// dummy, do not delete
function enGetHome() {;}
// HACK: we are NOT on Amazon
if (enAmazonMobileRedirect()) {
// =============== START EnstyerJS ===================
EnstylerInit();
EnstylerFixedNav();
EnstylerLastSeen();
EnstylerDealTime();
EnstylerBlacklist();
EnstylerAvatarPopup();
EnstylerDealActions();
// delay Pagepicker and repaeting 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,
$('.fGrid-last2, .thread-list--type-card').bind("DOMSubtreeModified",$.debounce( 300, function(){
EnstylerLastSeenDo();
EnstylerDealTimeDo();
EnstylerBlacklistDo();
EnstylerAvatarPopupDo();
EnstylerDealActionsDo();
EnstylerPagePickerCreateDo();
}));
}
// 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(); });
}
} // NOT on Amazon
// ============= EnStyler UserScript Homepage functions =======
// experimental support for EnStyler2 export / import
} else {
// we are on ujserstyle
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');
myOptions=myOptions.replace(/\n/g,'');
var mySettings = myOptions.split(';');
// abort if no options found
if (myOptions=='' || !myOptions.startsWith('#')) {return;}
for (var i=0; i< mySettings.length; i++) {
// each Setting has 3 fields seperated by :, but only 2 used
var myField=mySettings[i].split(':');
if (myField[0].match(/^#setting/i)) {
// select
$(myField[0]).val('');
$(myField[0]).val(myField[1]);
} else if (myField[0].startsWith('#option')) {
// option
$(myField[0]).prop('checked', true);
} else {
if (myField[0] != '') {alert('unkown option: "' + myField +'"');}
}
}
}
// 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(); GM_config.close();},
'close': function() { enGMConfigOpen=false; },
},
'frame': enGMFrame // Element used for the panel
}
);
// START Enstyler 2 Homepage
EnstylerHomeButton();
// set saved options
enSetHome();
}
//=========== Support functions for actual use ======
// add CSS in to document
function addStyleString(str) {
var node = document.createElement('style');
node.innerHTML = str;
document.body.appendChild(node);
}
// insertAfter like .insertBefore but as support function
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
// from https://gist.github.com/TheDistantSea/8021359
// returns 0 on equal, 1 on v1 newer, -1 on v2 newer
function versionCompare(v1, v2) {
var lexicographical = false,
zeroExtend = true,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) { return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x); }
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {return NaN; }
if (zeroExtend) {
while (v1parts.length < v2parts.length) v1parts.push("0");
while (v2parts.length < v1parts.length) v2parts.push("0");
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length == i) { return 1; }
if (v1parts[i] == v2parts[i]) { continue; }
else if (v1parts[i] > v2parts[i]) { return 1; }
else { return -1; }
}
if (v1parts.length != v2parts.length) { return -1; }
return 0;
}
// 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));
}