// ==UserScript==
// @name Enstyler Develop
// @namespace dealz.rrr.de/develop
// @description MyDealz Enstyler enhanced features
// @author gnadelwartz
// @license LGPL-3.0; http://www.gnu.org/licenses/lgpl-3.0.txt
// @include https://www.dealabs.com/*
// @include https://nl.pepper.com/*
// @include https://www.preisjaeger.at/*
// @include https://www.mydealz.de/*
// @include https://www.hotukdeals.com/*
// @include https://userstyles.org/styles/128262/*
// @include https://www.amazon.*
// @version 20.07.292
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @require https://unpkg.com/umbrellajs
// @require https://cdn.jsdelivr.net/gh/gnadelwartz/Enstyler@808bbfb40f089845f4d96e07eca419b14c73d86c/translations.js
// @require https://cdn.jsdelivr.net/gh/gnadelwartz/GM_config@3bfccb1cb4238694566ec491ee83d8df94da18d5/GM_config-min.js
// @require https://cdn.jsdelivr.net/gh/gnadelwartz/lz-string@a96e60cb8df3892ef8e4c4c700af9110122fbe61/lz-string.min.js
// @require https://cdn.jsdelivr.net/gh/gnadelwartz/sjcl@20de886688dcabda2da1a42cd89790aacc987b09/sjcl.js
// @require https://cdn.jsdelivr.net/gh/delight-im/ShortURL@5ddbfe89528637ff73212200773db876bbd0bebd/JavaScript/ShortURL.min.js
// @run-at document-start
// ==/UserScript==
// @ the original development source with comments can be found here: https://greasyfork.org/de/scripts/24244-enstylerjs-develop
// @ if you don't trust this minimized script use the development source.
// ========== INIT EnstylerJS =====================================
// init Enstyler environment
var DEBUG=true;
var DEBUGXX=true;
var DEBUGXXX=false;
var DEBUGINT=false;
// google such URL fur mydealz ...
// https://www.google.de/search?hl=de&output=search&q=test%20site:mydealz.de
// Parse location info
var enLocParser=location;
// get name of international site without www and domain
var enInterSite=enLocParser.hostname.replace('www\.','');
var enInterName=capitalizeFirstLetter(enInterSite.replace(/^\.|\..*/g,''));
var enInter=(enInterName != 'Mydealz');
// 1 day between update checks (in minutes)
var enUpdInt=1440;
var enMs2Min=60000; // will be shortend if Debug is on, use it for dealy
var enTime2Min=enMs2Min; // will stay with Debug, use it for real time conversion
var enUserLogin = false;
var enUserName = '';
var enSection = enLocParser.pathname.replace(/\/([^\/]+\/*).*/,'/$1');
var enValOff='off';
var enSyncKey=enValOff, enAutoSync=false;
// simple FF detection, for more browsers see section support function
// Firefox 1.0+
var isFirefox = typeof InstallTrigger !== 'undefined';
// simple mobile detection
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// FF on Android lie about OS if you select desktop view
if (/Linux/i.test(navigator.userAgent) && typeof GM_info.scriptHandler !== 'undefined' && GM_info.scriptHandler.startsWith('USI')) { isMobile=true;}
// simple debug output function for mobile
// alert(navigator.userAgent);
var saveLog = 'enMobileLog';
var enDebugLog = console.error;
if (DEBUGINT) {
enDebugLog = mobileLog;
} else {
mobileClearLog();
}
if (DEBUG) {
var enInitTime = performance.now();
enDebugLog('DEBUG activated');
enDebugLog('International Site: '+enInterSite);
enDebugLog('International Name: '+enInterName);
}
// set update/sync intervals 10 times faster
if (DEBUGXX) { enMs2Min=10000; }
if (DEBUGXXX) { enMs2Min=1000; }
// disable script if regex matches to path
var enDisableScript=/settings$/;
// Get default lang from site config
var enLangPat=/<EN-LANG:(.*?)>/g;
var enSiteLANG=typeof enSiteConfig[enInterName] === 'undefined' ? 'en' : enSiteConfig[enInterName]['lang'];
var enLANG=enSiteLANG;
var enHostpath=enLocParser.protocol+'//'+enLocParser.host+enLocParser.pathname;
// Basic Initialisation ==========================
function EnstylerInit () {
// get LoginStatus and Username
if ((enUserLogin = u('.avatar--type-nav').length)) {
enUserName = u('.navDropDown a').attr(enHREF);
enUserName = enUserName.replace(/.*\/profile\/([^\/]+).*/,'$1');
GM_setValue('enCSyncUser', enUserName);
} else {
// get last user
enUserName = GM_getValue('enCSyncUser','');
//restore old last seen if user logs in
// use this variant for dynamic loaded content click ...
// $ (document).on("click",'.test-loginButton', EnstylerLastSeenLast);
}
if(DEBUG) {
enDebugLog('User: ' +enUserName);
enDebugLog('SyncKey: ' +enSyncKey);
enDebugLog('AutoSync: ' +enAutoSync);
}
// get Section (first element in path)
enSection= enLocParser.pathname.replace(/\/([^\/]+\/*).*/,'/$1');
if(DEBUG) enDebugLog('Section: ' +enSection);
}
// add actions ==================================
// additional Deal Actions =======================
// code used for MyDealz Dealz actions, thanks to mydealz :-)
var enDealAction = [ '<a title="<EN-LANG:post>" class="space--h-1" style="font-size: xx-large"'+ // comment 0
'href="<ENSTYLER-HREF-HERE>#comment-form" data-handler="track" data-track="{"action":"scroll_to_comment_add_form","label":"engagement"}">+</a>',
'<a title="<EN-LANG:remove>"class="space--h-1" style="font-size: x-large"' + //un-bookmark 1
'data-handler="track replace" data-replace="{"endpoint":"https:\/\/www.mydealz.de\/threads\/<ENSTYLER-ID-HERE>/remove","method":"post"}" data-track="{"action":"save_thread","label":"engagement"}">-</a>',
'<a title="<EN-LANG:edit>" class="space--h-1" style="font-size: x-large"'+ // edit 2
'href="<ENSTYLER-HREF-HERE>/edit" data-handler="track" data-track="{"action":"goto_Update startededit_form","beacon":true}"><span>E</a>',
'<a title="<EN-LANG:mail>" class="space--h-1" style="font-size: x-large"'+ // mail 3
'href="mailto:?subject=<ENSTYLER-TEXT-HERE>" <span class="hide--toW3"><span>M</a>',
'<a title="<EN-LANG:mail>" class="btn btn--messenger btn--mail space--ml-2" style="background-color: #69BE28;"'+ //Mail social
'href="mailto:?subject=<ENSTYLER-TEXT-HERE>"><span class="ico ico--type-mail-white ico--reduce size--all-xxl"></span><svg width="22px" height="22px" class="icon icon--mail icon-u--1"><use xlink:href="/assets/img/ico_39d8b.svg#mail"></use></svg><span class="space--ml-2"><EN-LANG:mail></span></a>',
'<a title="<EN-LANG:telegram>" class="space--h-1" style="font-size: x-large" target="blank"'+ // Telegram 5
'href="https://telegram.me/share/url?url=<ENSTYLER-HREF-HERE>&text=<ENSTYLER-TITLE-HERE>" <span class="hide--toW3"><span>T</a>',
'<a title="<EN-LANG:telegram>" class="btn btn--messenger btn--mail space--ml-2" style="background-color: #10a0d0;" target="blank"'+ //Telegram social 6
'href="https://telegram.me/share/url?url=<ENSTYLER-HREF-HERE>&text=<ENSTYLER-TITLE-HERE>"><span class="ico ico--type-mail-white ico--reduce size--all-xxl"></span><svg width="17px" height="17px" class="icon icon--external icon-u--1"><use xlink:href="/assets/img/ico_39d8b.svg#external"></use></svg><span class="space--ml-2"><EN-LANG:telegram></span></a>',
];
//var enDealSearch='<div class="tGrid-cell vAlign--all-m hAlign--all-r space--r-3 width--all-4"><div class="aGrid box--all-i vAlign--all-m width--all-12 hAlign--all-l zIndex--popover" action="">'+
// '<div><div><button type="submit" aria-label="Finde">'+
// '<span class="search-button search-button--inactive text--color-grey"><svg width="20px" height="20px" class="icon icon--search"><use xlink:href="/assets/img/ico_0d108.svg#search"></use></svg></span></button>'+
// '<input id="enCommentSearch" name="enCom" value="" class="search-input input input--search width--all-12" type="search" placeholder="<EN-LANG:commentsearch>"> </div></div></div></div>';
var enDealFlame='<span class="vote-tempIco ico ico--type-flame2-red threadTempBadge-icon" style="position: absolute; display: block; margin-left: .3em; font-size: 2em;"></span>';
var enDealIce='<span class="vote-tempIco ico ico--type-snowflake-blueTint threadTempBadge-icon" style="position: absolute; display: block; margin-left: .3em; font-size: 2em;"></span>';
var enDealMarker='#thread_';
//var enDealNoAction='.ico--type-clock-grey, .vote-temp--colder';
var enDealAdd='', enSocialAdd, enCommentAdd; //enDealUnbook=false;
var enDealFooter='', enCCMail, enMyCSSID;
var myDealAction, myTouch, myFixHtml, myVotebar, myVotescale, myCompact, myPrice, myDealTime;
function EnstylerDealActions(){
enTransTags=5;
// if enabled ...
if (myDealAction) {
// compose Footer
if (u('footer logo--brandmark').length) enDealFooter = ('%0D%0A%0D%0A-- %0D%0A'+ u('#footer > div:nth-child(1) p.size--all-l.text--b').first().text());
// 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 (u('.ico--type-grid-subNavActive, .thread-list--type-card ').length) { myText=1; }
// default for all Dealz: first comment
enDealAdd = enDealAction[0];
// Action for special locations only ===========
switch(true) {
/*case (pathname.endsWith('profile/saved-deals')):
// add for user saved-dealz: un-bookmark
enDealAdd += enDealAction[1];
//enDealUnbook=true;
break;
*/
case (pathname.endsWith('profile/diskussion')):
case (enUserLogin && pathname.endsWith(enUserName)):
// add user dealz and discussions: comment edit
enDealAdd += enDealAction[2];
break;
}
// default last: add mail and mail social
// Compose final Deal Actions
enDealAdd= enLangLocalize(enDealAdd + enDealAction[3] + enDealAction[5],
enDealLang, enLANG);
enSocialAdd= enLangLocalize(enDealAction[6] + enDealAction[4] , enDealLang, enLANG);
enCommentAdd= enLangLocalize(enDealAction[3] + enDealAction[5], enDealLang, enLANG);
// add search box to Comment header section
//if (DEBUGXX) { enDebugLog('DealActionDo: add Search Box for Comments'); }
//u('#thread-comments').find('div.cept-sort-order').before(enLangLocalize(enDealSearch, enDealLang, enLANG));
//u('#enCommentSearch').handle('change', EnstylerCommentSearch);
}
EnstylerDealActionsDo();
}
// simple "search" callback for commenst search form
//function EnstylerCommentSearch(e) {
// e is the triggered event
// e.currentTarget dom element that triggers the event
// alert("event triggered, element value: "+ u('#enCommentSearch').first().value);
//}
// hide comments on actual page not containing text
//function EnstylerHideComments(text) {
//
//
//}
function EnstylerDealActionsDo() {
// if enabled ...
// every thread on thread page ...
var myID, myDesc, myDeal, myHref, newHtml, dealTemp, voteTemp, myTemp, overview, noComment, myBlack, myVotepos;
enMyCSSID=enUserKey('');
if (!(myDealAction || myTouch || myFixHtml || myVotebar || myPrice || enBlackTemp == enValOff)) { if(DEBUGXX) {enDebugLog('No DealAction active ... ');} return; }
// process all articles not already seen
if (DEBUGXXX) { enDebugLog('DealActionDo: Start'); }
overview=u('.thread--type-list, .cept-listing--card').length;
u('article').not('.enClassActionDone, .'+enClassHidden).each(function (that) {
if (DEBUGXXX) { enDebugLog('DealActionDo: Article'); }
u(that).addClass('enClassActionDone');
// abort if not an "real" article
if (u(that).hasClass('threadWidget--type-card--item')) { return; }
myID=u(that).attr(enID);
if(myID === null) { u(that).remove(); return; }
if (DEBUGXXX) { enDebugLog('DealActionDo: get init values'); }
// get userhtml
myDeal =u(that).find('.thread-title a'); //title
// overview=myDeal.length;
// check if this is (not) a comment
noComment=myID[0]!='c';
// blacklist is now here
if(enBlackTemp != enValOff) {
if (DEBUGXXX) { enDebugLog('BlacklistDo id: '+ myID);}
// this is NOT a comment
if(noComment) {
if (overview) {
if (DEBUGXXX) { enDebugLog('BlacklistDo Deal ...'); }
// get title, categorie, user, comment and remove unwanted chars
myBlack = (u(that).find('.thread-title a').text() + ' @' +u(that).find('a.user').text()).replace(unwantedRegex[1] ,' ');
myTemp=u(that).find('.vote-temp');
if(myTemp.text() !== null) { myTemp=parseInt(myTemp.text());} else { myTemp=9999; }
} else {
myTemp=9999;
myBlack="";
}
} else {
if (DEBUGXXX) { enDebugLog('BlacklistDo Comment ...');}
// comment: get text and quoted user from comments
myBlack = (u(that).find('.userHtml').text() + ' @' +u(that).find('a.user').text()).replace(unwantedRegex[1] ,' ');
myTemp=0;
}
if (DEBUGXXX) { enDebugLog('BlacklistDo '+ (noComment? 'deal: ': 'comment: ') + myBlack); }
// skip blacklist if whitelisted
if ( !(enWhiteTrue && myBlack.match(enWhite))) {
// vote temp & blacklist
if (DEBUGXXX) { enDebugLog('BlacklistDo Dealtemp: '+ myTemp); }
if (myTemp <= enBlackTemp || enBlackTrue && myBlack.match(enBlack)) {
if (DEBUGXXX) { enDebugLog('BlacklistDo BLACK'); }
var parent=u(that).parent();
if (parent.hasClass("threadCardLayout--card")) {
parent.addClass(enClassHidden);
} else {
u(that).addClass(enClassHidden);
}
enBlacklisted++;
//EnstylerLastSeenSkip('#'+myID));
}
}
}
// remove unwanted HTML from deal text and comment
if(myFixHtml) {
u(that).find('.userHtml').each(function (that) {
if (DEBUGXXX) { enDebugLog('DealActionDo: FixHmtl'); }
// get inner html
// remove unwanted Stuff: combined <div><br><br> stuff, created by cut'npaste html
// not elegant, but works ...
u(that).html(u(that).html().replace(/[^ -~¡-ÿ✘►○●✰€≠]+|( )+|(\n\r)+|<\/p>|<div>/g,' ')
.replace(/<\/div>/g, '<br>')
.replace(/<p>|<br>( *<br>)+/g,'<br><br>')
.replace(/(<li>)(<br>)+|<br>*(<br><\/li>)/g, '$1')
);
});
}
// place price in Button
if (myPrice && noComment) {
if (DEBUGXXX) { enDebugLog('DealActionDo: PriceButton'); }
myBlack=u(that).find('.thread-price.text--b');
if (myBlack.length) {
newHtml=u(that).find('.cept-dealBtn');
if (newHtml.hasClass('ico--type-redirect-white')) {
newHtml.html(myBlack.html());
} else {
newHtml.html(myBlack.html()+'<span class="ico ico--type-redirect-white size--all-xl space--l-1"></span>');
}
u(that).find('.cept-tb').html(myBlack.html());
}
}
// add deal actions to deal only, not comment
if(myDealAction && noComment) {
if (!overview) {
// in deal detail we have no deal link
myDeal =u(that).find('.thread-title');
enTransTags=20; // translate more in deal
}
if (DEBUGXXX) { enDebugLog('DealActionDo: myDeal: ' + myDeal.text()); }
// translate deal titles
enTranslateGoogle(myDeal);
// check Deal temp and do action based on it
dealTemp=u(that).find('.vote-temp');
if (dealTemp.length) {
dealTemp = parseInt(dealTemp.text());
// deal meter
if (myVotebar) {
// get temp color
myTemp=u(that).find('.vote-temp').attr('class').replace(/.*charcoal|.*vote-temp--/i, '');
if (DEBUGXXX) { enDebugLog('DealActionDo: Temp ' + dealTemp + ' color: ' + myTemp); }
voteTemp=dealTemp/(myVotescale/70)+5;
if (voteTemp <0) voteTemp *=-3;
// votebar position
myVotepos=that;
if (!overview) { myVotepos=u(u(that).find('div').first());}
u(myVotepos).prepend('<div class="votebar vote-progress voteBar--'+myTemp+'" style="width: '+voteTemp+'%;"></div>');
// place flame/ice if not type card
if (u(that).hasClass('thread--type-card')) { dealTemp=0; }
if (dealTemp > myVotescale/2.51) {
if (DEBUGXXX) { enDebugLog('DealActionDo: HOT ' + myDeal); }
u(myVotepos).prepend(enDealFlame);
} else if (dealTemp < -myVotescale/10.1) {
if (DEBUGXXX) { enDebugLog('DealActionDo: COLD ' + myDeal); }
u(myVotepos).prepend(enDealIce);
}
}
}
// we are in Deal overview
if(overview) {
if (DEBUGXXX) { enDebugLog('DealActionDo: add Action Overview'); }
u(that).find('span.meta-ribbon.hide--toW3').removeClass('hide--toW3');
// let price flow
u(that).find('.threadGrid-title .thread-title.lineClamp--2').removeClass('lineClamp--2');
// get link and compose final HTML for deal actions/1
myHref=enMinimzeHref(myDeal.first().outerHTML.replace(/\n|\r|\t/g,'').replace(/^.*href="/, '').replace(/".*/,''));
newHtml = enDealComposeAction(u(that), myDeal.text(), myHref, enDealAdd.replace(enPATTERN[enTITLE],myDeal.text()));
// append aextra actions to Deal
u(that).find('.cept-comment-link').append(newHtml);
} else { // we are in deal detail
if (DEBUGXXX) { enDebugLog('DealActionDo: add Action Deal'); }
// compose final HTML for social buttons
newHtml=enDealComposeAction(u(that), myDeal.text(), enMinimzeHref(enLocParser.toString()), enSocialAdd.replace(enPATTERN[enTITLE],myDeal.text()));
// append HTML to Deal
u(that).find('a.btn--twitter').parent().append(newHtml);
}
}
// translate description
myDesc=u(that).find('.userHtml');
if (myDesc.length) { enTranslateGoogle(myDesc); }
});
// set label for unBlacklist button
EnstylerBlacklistShow();
}
// helper function for composing mail deal action
function enDealComposeAction(deal, subject, href, action) {
if (DEBUGXX && typeof href === 'undefined') { href=''; enDebugLog('enDealComposeMail href missing!');}
// compose mail subject
subject=encodeURIComponent((enUserName == 'Gnadelwartz' ? 'KayDealz' : enInterName )+': '+subject.replace(/\r|\n|\t/g, ' ').replace(/ */g, ' '));
if (subject.length < 100 && deal.find('span.thread-price').length) {
// add ' ->\xa0price'
subject += encodeURIComponent(' ->\xa0' + deal.find('span.thread-price').text().replace(/ |\t/g, ''));
}
// replace href minimized href
href=enMinimzeHref(href);
// compose final HTML
return (action.replace(enPATTERN[enHREF], '\n\r'+href)
.replace(enPATTERN[enTEXT], truncStringWord(subject, 160, '%20') +'&body=' +subject +'%0D%0A%0D%0A' + href +enDealFooter));
}
// helper function to create a short MD Deal link
function enMinimzeHref(href) {
href=href.replace(/(.*)\/.*-/, '$1/md-');
if (enCCMail && !href.startsWith('https://dealz.rrr.de/')) { href=href.replace(/^https:\/\/.*?\//, 'https://dealz.rrr.de/'+ enInterName +'/') +'?ID=' + enMyCSSID; }
return href;
}
// blacklist do not show dealz containing blacklistet words ==========================
// search in kategorie, dealtitle, and username
var enClassHidden = 'enClassHidden';
var enClassBlackDone = 'enClassBlackDone';
var enBlacklisted=0;
var unwantedRegex = [ /[\[\]\(\)\{\}\?\:\;\!\"\*\+\ ]/g, // in White/Backlist
/[\[\]\(\)\{\}\?\.\:\;\!\"\*\+\,\n\r\t]+/g // in Dealtext
];
var enBlack, enBlackTrue;
var enWhite, enWhiteTrue;
var enBlackTemp;
function EnstylerBlacklist() {
// if logged in and user is not in whitelist
if (enUserLogin && ! GM_config.get('enConfWhitelist').includes(enUserName)) {
// add actual user to whitelist
GM_config.set('enConfWhitelist', '@'+enUserName +',' + GM_config.get('enConfWhitelist'));
//GM_config.setValue('enConfWhitelist', GM_config.get('enConfWhitelist'));
}
if (DEBUGXXX) { enDebugLog('Blacklist: init ...'); }
// convert Black/Whitelist to RegEx, escape regex characters but keep '.' (any char)
var myBlack=GM_config.get('enConfBlacklist').replace(unwantedRegex[0], '');
enBlack=RegExp(myBlack.replace(/^,|,$/g,'').replace(/(.),(.)/g,'$1|$2'),'i');
enBlackTrue=!' '.match(enBlack);
// warn user about regex error
if (!enBlackTrue && myBlack != '') alert(confLang('regexfailed'));
enWhite=RegExp(GM_config.get('enConfWhitelist').replace(/^,|,$/g,'').replace(/(.),(.)/g,'$1|$2'),'i');
enWhiteTrue=!' '.match(enWhite);
enBlackTemp= GM_config.get('enCBlackC');
// only if blacktemp is not off
enBlackTrue=(enBlackTrue && enBlackTemp != enValOff);
if (DEBUGXXX) { enDebugLog('Blacklist: ' + (enBlackTrue ? 'on': 'off') +' Temp: ' + enBlackTemp); }
//moved to EnstylerDealActions
//EnstylerBlacklistDo();
}
// blacklist support functions ....
function EnstylerBlacklistShow() {
enConfDefs.default.enCUnblackL.label=enUnblackText.replace(enPATTERN[enTEXT],enBlacklisted);
}
function EnstylerBlacklistUnhide() {
enBlacklisted=0;
EnstylerBlacklistShow();
u('.'+enClassHidden).removeClass(enClassHidden);
}
// create select page or scrollwheel for page navigation =============
/*
var enPEnum='enPageEnum';
var selectList = document.createElement("select");
selectList.id = enPEnum;
selectList.setAttribute('class', enPEnum);
selectList.onchange = EnstylerPageAction;
function EnstylerPagePickerCreate() {
// revome existing picker
//EnstylerPagePickerRemove();
// if enabled
if (GM_config.get('enCPageP')) {
// init values and clear select list
var page=1, max=1, i, diff, last, option;
u(selectList).empty();
// get page and max from pagenav
var lastpage = u('nav .cept-last-page');
if ( lastpage.length ) {
// get from page menu
var actpage = u('nav .pagination-page .hide--fromW2');
//locate actual page and last page
if( isNaN(page = parseInt(actpage.html()) )) { page=1;}
if( isNaN(max = parseInt(lastpage.html()) )) { max=page;}
} else {
// get from URL
if (enLocParser.search.length) {
page = parseInt(enLocParser.search.replace(/.*=/,''));
} else {
page=1;
}
max=page;
}
if (DEBUGXXX) { enDebugLog('PickerCreate: page: '+ page + ' max: ' +max); }
// create page select element
for (var x = 1; x <= max; ) {
option = document.createElement("option");
option.text = x;
selectList.add(option);
last = x;
// non linear increment
diff = Math.abs(page-x);
if ( x < 10 || diff < 5) { x++; }
else if ( x < 1000 && diff > 600) { x += diff/3.5; }
else { x += Math.floor(diff/2); }
if (diff>9 && x>9) { if (diff<50 || x<50) { x=Math.floor(x/2)*2; } else { x=Math.floor(x/5)*5; } }
if (DEBUGXXX) { enDebugLog('PickerCreate: next picker: '+ x); }
}
// add last page
if (page > max) { max=page;}
if (last < max ) {
option = document.createElement("option");
option.text = max;
selectList.add(option);
}
// set default value
selectList.value = page;
// placement of MAIN Picker
var MainPicker= ['.js-navDropDown-messages', //Element
enPEnum+' js-navDropDown-messages vAlign--all-m' //class
];
// login button present in Mainnav
if (u('.test-loginButton').length) {
MainPicker[0]='.test-loginButton'; //Element
}
// in deal always in sticky votebar (was in subnav)
if (u('.voteBar').length) {
MainPicker= [ '.voteBar--sticky-off--hide.hAlign--all-r', // Element
enPEnum +' subNavMenu-link subNavMenu-btn voteBar--sticky-off--hide' //class
];
}
// Main Picker add class and palce before element
selectList.setAttribute('class',MainPicker[1]);
u(MainPicker[0]).before(selectList);
}
}
function EnstylerPagePickerDo() {
// get page from URL
if (enLocParser.search.length ) {
if(DEBUG) enDebugLog('Picker from URL: '+ location.search);
selectList.value = parseInt(location.search.replace(/.*=/,''));
} else {
selectList.value = 1;
}
}
*/
// goto selected Page
//function EnstylerPageAction() {
// var e = document.getElementById(enPEnum);
// var enPage = 'page=' + e.options[e.selectedIndex].value;
// 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;
//}
// Main Nav will stay on TOP of the screen =========================
var myFixedCSS = { every: '.enFixedNav { display: block; position: fixed; width: 100%; z-index: 120;} .listingProfile {margin-bottom: -55px}'+
'.listingProfile, .subNav, .profileHeader, .tabbedInterface, .splitPage-wrapper {margin-top: 55px}',
subnav: '.subNav {margin-top: 0 !important;} .nav-subheadline {margin-top: 55px}',
discus: '.tGrid.page2-center.height--all-full {margin-top: calc(55px + 10px);} #footer .page-content { padding-top: calc(55px + 10px);}'
};
function EnstylerFixedNav() {
// place emergency button in case menu dores not work, addd zahnrad
var myButton=u('.subNavMenu .subNavMenu-layer').first();
if (GM_config.get('enCNavF')) {
// 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 = u('header.js-sticky').html();
u('header.forceLayer').replace('<header class="enFixedNav">'+mySavedHtml+'</header>');
// fixed NAV for everywhere
var myFixedStyle=myFixedCSS['every'];
// additionla CSS for different sections
if (enSection == EnstylerSiteConfig('discussion')) {
if (DEBUGXX) { enDebugLog('FixedNav DICUSSION detected'); }
myFixedStyle+=myFixedCSS['discus'];
}
if (u('.nav-subheadline').length || enSection=='/profile/') {
// additional CSS for categories
myFixedStyle+=myFixedCSS['subnav'];
}
//myFixedStyle= myFixedStyle.replace(enPATTERN[enTEXT], enMainHeigth);
addStyleString(myFixedStyle);
} else {
// not in deal or gutscheine, place in sticky nav
myButton=u('.vote-box').first();
}
}
if (myButton) { myButton.after(enMenuButton); }
}
// the return of "gestern xx:xx Uhr" ==============
var DealDate=new Date();
var TodayStart=new Date();
var EnstylerTimeSeen='enTimeSeen';
var today='', oclock='', yesterday='';
function EnstylerDealTime() {
TodayStart.setHours(0,0,0,0);
// get localized values onyl once, set today and clock only if not in card
//if (!u('section.thread-list--type-card').length) {
today=enLangLocalize('<span class="hide--toW2"><EN-LANG:today> </span>', enTimeLang, enLANG);
oclock=enLangLocalize('<span class="hide--toW2"> <EN-LANG:oclock></span>', enTimeLang, enLANG);
//}
yesterday=enLangLocalize('<EN-LANG:yesterday> ', enTimeLang, enLANG);
EnstylerDealTimeDo();
}
function EnstylerDealTimeDo() {
if (myDealTime) {
var myNow=Date.now();
var myTimeText, myDealDiff, myOclock;
// process every article, optimization: not if class TiemSeen
u('.meta-ribbon, time, .metaRibbon').not('.'+EnstylerTimeSeen).each(function (that) {
u(that).addClass(EnstylerTimeSeen);
// we have no datetime, lets parse text
myTimeText= u(that).html();
// german long tome ago fix
if (myTimeText.includes(" am ")) { return; }
// parse time string directly
DealDate.setTime(myNow - ((parseInt(myTimeText.replace(/.* ([0-9].*) [hu].*|.*/, '$1'))*60+parseInt(myTimeText.replace(/.* ([0-9].*) m.*|.*/, '$1')))*enTime2Min));
// calculate Diff
myDealDiff=((myNow-DealDate)/enTime2Min); //diff in minutes
myOclock=DealDate.toString().slice(16, 21);
if (DEBUGXXX) { enDebugLog('DealTimeDo: myTimeText: ' + myTimeText); }
switch (true) {
case (myOclock.length <5 || myDealDiff < 60 ): // < 1h > 5 days
return;
case (myDealDiff > 1440): // > 24h
myTimeText += ' (' + myOclock + oclock +')';
break;
case (DealDate < TodayStart): // < last midnigth
myTimeText= yesterday + myOclock + oclock;
break;
default:
myTimeText += ' (' + today + myOclock + oclock +')';
}
u(that).html(myTimeText);
});
}
}
// mark last seen Deal in Highligth, Hot and New ============================
// GM variables used here
// store newest loaded deal
// 'enNewestDeal...new'
// 'enNewestDeal...hot'
// 'enNewestDeal...'
// international support added
var enNewestBase='enNewest'+enInterSite;
var enSec= enNewestBase +'-'+ enSection.replace(/\//, '');
var LastSeenOnce=true;
var enSeArt='', enLaArt='';
function EnstylerLastSeen(){
// only once and in main categories
if(LastSeenOnce) {
LastSeenOnce=false;
if (DEBUGXX) { enDebugLog('Enter LastSeen : '+ enSec ); }
// store last seen for Main catergories
if(enSection.match(enMainSectionMatch)) {
// get section and save
// GM_setValue(enNewestBase+'LastSec', enSec)
// get last seen article
enSeArt=GM_getValue(enSec, '');
if (DEBUGXXX) { enDebugLog('LastSeen old: '+ enSec +' '+ enSeArt); }
SyncLastSeen();
EnstylerLastSeenDo();
// save actual last seen
if(enLocParser.search == '') {
var mySkip=false;
u('article').not('.threadWidget-item').each(function (that) {
// already found or pinned ?
if (mySkip || u(that).find('.cept-pinned-flag').length!=0) {return;}
if (DEBUGXXX) { enDebugLog('LastSeen found actual last: '+ enSec + ': '+u(that).attr(enID)); }
//store actual seen
GM_setValue(enSec, u(that).attr(enID));
//store last seen last
GM_setValue(enSec+'Last', enSeArt);
if (DEBUGXXX) { enDebugLog('LastSeen stored: '+ enSec + ': ' +GM_getValue(enSec,'') +' last: ' +GM_getValue(enSec+'Last','')); }
SaveLastSeen();
// found, break loop
mySkip=true;
return;
});
}
} else {
// if we are not in main categorie => restore last value
EnstylerLastSeenLast();
}
}
}
function EnstylerLastSeenDo(){
// only in main categories
if (DEBUGXX) { enDebugLog('Enter LastSeenDo: ' + enSec); }
if(enSec != '') {
// mark last seen article
if (DEBUGXX) { enDebugLog('LastSeenDo Execution: '+ enSec +' '+ enSeArt); }
GM_setValue('enLastCheck' + enSec, Date.now()/enMs2Min);
if (enSeArt) {
//store last marked
GM_setValue(enSec+'Last', enSeArt);
// mark NEXT availible article
var myMark=u('#'+enSeArt);
if (DEBUGXX) { enDebugLog('LastSeenDo Marking: #'+ enSeArt + ' #'+enLaArt); }
while(!typeof myMark === "undefined" && ( myMark.hasClass(enClassHidden) || myMark.nodes[0].tagName == "DIV") ) {
myMark=u(myMark.nodes[0].nextElementSibling);
//alert(myMark.html())
}
myMark.addClass('enClassMarkArticle');
// mark next LAST availible article
if(enLaArt.startsWith('thread_')) {
myMark=u('#'+enLaArt);
while(!typeof myMark === "undefined" && ( myMark.hasClass(enClassHidden) || myMark.nodes[0].tagName == "DIV" )) {
myMark=u(myMark.nodes[0].nextElementSibling);
}
myMark.addClass('enClassMarkArticleLoad');
}
} else {
// first time
GM_setValue(enSec, 'thread_1');
}
}
}
// restore last seen from last_last seen
function EnstylerLastSeenLast(){
// restore last value
var lastSec=GM_getValue(enNewestBase+'LastSec','');
GM_setValue(lastSec, GM_getValue(lastSec +'Last',''));
}
// check and get Updates of Enstyler2 CSS ================================
var enUpdateUrl = 'https://userstyles.org/styles/128262/enstyler2-style-your-mydealz.css'; // production version
function enCheckUpdates() {
// get time and convert to minutes
var myDiff= (Date.now()/enMs2Min) - GM_getValue('enLastUpdateCheck','0');
// css is empty
if (GM_getValue('MyCSS','').length < 10) {
if(DEBUGXX) enDebugLog('No chached CSS found');
myDiff=-1;
}
// if option set and time expired
if(DEBUGXX) enDebugLog('Update requested, intervall '+enUpdInt+' minutes , actual diff '+parseInt(myDiff));
if (myDiff > enUpdInt || myDiff < 0 || DEBUGXXX) {
// store actual time
if(DEBUGXX) enDebugLog('CSS Update started');
enUpdateCSS();
}
}
var MyCSS='Enstyler2_CSS';
function enUpdateCSS() {
var myTime=parseInt(Date.now()/enMs2Min);
var myOptions=enComposeUpdateOpt();
enCacheExternalResource( enUpdateUrl + myOptions, MyCSS);
GM_setValue('enLastUpdateCheck', myTime);
enSaveMyCSS();
}
var enCssOpt='EnstylerCssOpt';
function enComposeUpdateOpt() {
// get saved options, remove newlines and split to settings array
var myOptions=GM_getValue(enCssOpt, '');
// abort if no options found
if (myOptions=='' || !myOptions.startsWith('#')) {return "";}
myOptions=myOptions.replace(/\n/g,'');
var mySettings = myOptions.split(';');
// start composing options
myOptions='';
for (var i=0; i< mySettings.length; i++) {
//if(DEBUG) enDebugLog('process:' + mySettings[i]);
if(mySettings[i]=='') continue;
// each Setting has 3 fields seperated by :
var myField=mySettings[i].split(':');
if(myField.length < 2) continue;
// add &setting=value
myOptions += '&' +myField[1].slice(0, -1) + '=' + myField[1];
}
// replace first & by ? and returns string
myOptions = '?'+myOptions.slice(1);
if(DEBUG) enDebugLog(myOptions);
return myOptions;
}
// compose Nav Menu items =======================================
// i.e. create button for display Config ======================
// define pattern actions here, incl. international support
// Main sections, no deal or details
var enMainSectionMatch=/^\/$|^\/hot$|^\/new$|^\/settings$|^\/discussed$|^\/hei%C3%9F$|^\/diskutiert$/;
var enHREF='href', enID='id', enTEXT='text', enTITLE='title';
var enPATTERN = { href: /<ENSTYLER-HREF-HERE>/g, // pattern to insert link ...
id: /<ENSTYLER-ID-HERE>/g, // pattern to insert ID
text: /<ENSTYLER-TEXT-HERE>/g, // pattern to insert Text
title: /<ENSTYLER-TITLE-HERE>/g, // pattern to insert Title
};
var enNavEntry='enNavEntry';
var enMenuItemCode = { Main: '<a class="space--h-2 lbox--v-4 enNavEntry navMenu-link" id="<ENSTYLER-ID-HERE>" href="<ENSTYLER-HREF-HERE>" data-handler="track" data-track="{"action":"goto_main_target","beacon":true}"><span class="lbox--h-4"><svg width="24px" height="20px" class="icon icon--comments"></span><ENSTYLER-TEXT-HERE></a>',
Sub: '<li class="enNavEntry subNavMenu-item--separator test-tablink-discussed"><a href="<ENSTYLER-HREF-HERE>" class="subNavMenu-item subNavMenu-link space--h-4 vAlign--all-m" id="<ENSTYLER-ID-HERE>" data-handler="track" data-track="{"action":"goto_menu_target sort","label":"diskutiert","beacon":true}"><span class="box--all-i size--all-xl vAlign--all-m"><ENSTYLER-TEXT-HERE></span><span class="js-vue-container--threadcount" data-handler="vue" data-vue="{"count":null}"></span></a></li>',
MainButton: '<a class="space--h-2 lbox--v-4 enNavEntry navMenu-link" id="<ENSTYLER-ID-HERE>"><span class="lbox--h-4"><svg width="24px" height="20px" class="icon icon--comments"></span><ENSTYLER-TEXT-HERE></a>',
//SubButton: '<li class="enNavEntry subNavMenu-item--separator test-tablink-discussed"><a class="subNavMenu-item subNavMenu-link space--h-4 vAlign--all-m" id="<ENSTYLER-ID-HERE>"><span class="box--all-i size--all-xl vAlign--all-m"><ENSTYLER-TEXT-HERE></span></a></li>'
};
var enNavGrid ='<div id="enButt"><a title="Grid Layout" id="enGrid" href="'+enHostpath+'?layout=grid"><img src="https://dealz.rrr.de/enstyler/grid.png"></a>'+
'<a title="List Layout" id="enList" href="'+enHostpath+'?layout=horizontal"><img src="https://dealz.rrr.de/enstyler/list.png"></a>'+
'<a title="Text Layout" id="enText" href="'+enHostpath+'?layout=text"><img src="https://dealz.rrr.de/enstyler/text.png"></a></div>';
var enNavCSS='#enGrid, #enList, #enText { padding: 0.5em; } #enButt {left: 3em; top: 1em; padding-left: 4em; display: inline-block;}';
//var var enMenuSub=1; var enMenuSubButton=3;
var enMenuItemLength= enMenuItemCode.length;
var EnstylerButton = 'EnstylerButton';
// emergency config button
var enMenuButton = document.createElement('input');
enMenuButton.type = 'button';
enMenuButton.setAttribute(enID, 'emergency');
enMenuButton.onclick = showEnstylerConfig;
enMenuButton.value = 'Enstyler';
enMenuButton.setAttribute('style', 'cursor: pointer; padding-left: 1em; padding-bottom: 0.5em; font-weight: 600; font-size: 10px !important; color: #FFCC00 !important;');
enMenuButton.setAttribute('title', 'Enstyler Einstellungen');
// compose default Enstyler Menu
function EnstylerMenuActions(){
// add Enstyler Homepage
EnstylerAddNav('Main', '<EN-LANG:enstyler>', '<EN-LANG:enhref>" target="_blank','enMainHomepage', 'home');
EnstylerAddNav('Main', 'Enstyler Discussion', 'https://t.me/joinchat/IvvRthRhMcX6rDQU-pZrWw','enMainHomepage', 'page');
EnstylerAddNav('MainButton', '<EN-LANG:settings>', showEnstylerConfig, EnstylerButton, 'gear-grey');
EnstylerAddNav('Main', 'Deal-O-Mat / Telegram Groups', 'https://dealz.rrr.de/deal-O-mat/','enMainHomepage', 'home');
// add to Sub Nav
//EnstylerAddNav(enMenuSubButton, '<EN-LANG:settings>' , showEnstylerConfig, EnstylerButton)
}
// add to Nav ======================
// nav = menu action
// text = menu text
// target = URL to show, in case of Button function to call
// Icon can be home-navMenuLayerText, tag-navMenuLayerText, scissors-navMenuLayerText, free-navMenuLayerText,
// discussion-navMenuLayerText (default), building-navMenuLayerText, star-navMenuLayerText, snowflake-navMenuLayerText,
// page-navMenuLayerText (button), star-navMenuLayerText (button) or any regular icon
var enNavIconPat='#discussion';
///assets/img/ico_4c8c5.svg#
function EnstylerAddNav(nav,text,target,ID, Icon) {
if (typeof Icon === 'undefined' || Icon == '') Icon=enNavIconPat;
var isFunc=false;
if(DEBUGXXX) enDebugLog('MenuAdd meue entry: '+text);
// compose menu entry
var myEntry = enMenuItemCode[nav].replace(enPATTERN[enID],ID).replace(enPATTERN[enTEXT],text);
if(Icon !=enNavIconPat) { myEntry = myEntry.split(enNavIconPat).join('#'+Icon);}
// target can be a function
if (typeof target === "function") { isFunc=true;
} else {
myEntry = myEntry.replace(enPATTERN[enHREF],target);
}
if (nav[0]== 'M') { // Main Menu
// first Main menu entry, start listen to klick
if(enAddMain == '') {
if(DEBUGXXX) enDebugLog('MenuAdd assign on(\'click\') to .nav-link.navMenu-trigger');
u('.nav-link.navMenu-trigger').on('click', function() {setTimeout(EnstylerMainDo, 200);});
}
enAddMain += myEntry;
if (isFunc) { enAddMainFunc[enAddMainCount++]= { ID: ID , target: target}; }
}
/* else {
// ad to Subnav, click if visible
u('.subNavMenu-list').append(myEntry);
if(isFunc) { u('#'+ID).on('click', target); }
// handler if dropdown, start listen to klick
if(enAddSub == '') { u('.subNavMenu-trigger').on('click', function() {setTimeout(EnstylerSubDo,300); enAddSub='done';} }
if(isFunc) { enAddSubFunc[enAddSubCount++]= { ID: ID , target: target}; }
}
/**/
}
// Show items in Sub / Main Menu =====
// store ID and function to call on click
var enAddMain='';
var enAddMainFunc= [ ];
var enAddMainCount=0;
var enAddMainDone=false;
function EnstylerMainDo() {
//add items
if(DEBUGXXX) enDebugLog('MenuMain started ...');
if(enAddMainDone) { if(DEBUGXXX) enDebugLog('... Main menu already done'); return; }
enAddMainDone=true;
u('.popover-content nav .navMenu-div').first().insertAdjacentHTML('beforebegin',enLangLocalize(enAddMain, enMenuLang, enLANG));
// create space for new entrys
var myMenu=u('.popover--mainNav');
// +35px per new items
var myHeigth= 35*(enAddMain.split(enNavEntry).length -1) + parseInt(myMenu.attr('style').split('height: ')[1]);
myMenu.attr('style',myMenu.attr('style').replace(/height: [0-9.]*px/,'height: '+myHeigth+'px'));
// add button callbacks
for (var i=0; i<enAddMainCount; i++ ) {
u('section #' + enAddMainFunc[i].ID).on('click', enAddMainFunc[i].target);
}
}
/*
var enAddSub='';
var enAddSubFunc= [ ];
var enAddSubCount=0;
function EnstylerSubDo() {
// klick event handler, call with debounce( 300) to wait until menu is created and avoid double klicks
//add items
if(DEBUG) enDebugLog('Add Menu Items to Sub ...')
for (var i=0; i<enAddSubCount; i++ ) {
$ ('section #' + enAddSubFunc[i].ID).click(enAddSubFunc[i].target);
}
}
/**/
/*
function EnstylerNavRemove() {
// Clear Menu Items stored
enAddMain='';
enAddMainFunc= [ ];
enAddMainCount=0;
u('.navMenu-page').off('click');
// remove visible items
u('.'+enNavEntry).remove();
} */
// ============= GM_config functions =======================================
//var enJSAutoUpdate=GM_info.scriptWillUpdate;
var enUpdateWindow;
var enUnblackText = enLangLocalize('<EN-LANG:unblack> <ENSTYLER-TEXT-HERE> Dealz', enConfigLang, enLANG);
function confLang(key) { return enLangLocalize('<EN-LANG:' + key + '>', enConfigLang, enLANG); }
function confMess(key) { return enLangLocalize('<EN-LANG:' + key + '>', enMessageLang, enLANG); }
// define EnstylerJS GM_config elements
var enConfDefs= [];
enConfDefs['default'] = {
// Part one: load external content --------
'enCSS': {
'label': confLang('configcss'),
'title': confMess('configcss'),
'type': 'button', // a button input
'click': function() { // Function to call when button is clicked
enUpdateWindow=window.open('https://userstyles.org/styles/128262','UserCSS', 'left=0,top=0');
GM_setValue('enLastUpdateCheck', 0);
}
},
'enJS': {
'label': confLang('userscript'),
'title': confMess('userscript'),
'type': 'button',
'click': function() { // Function to call when button is clicked
enUpdateWindow=window.open(!DEBUG ? 'https://greasyfork.org/scripts/24243-enstylerjs/code/EnstylerJS.user.js' :
' https://greasyfork.org/scripts/24244-enstylerjs-develop/code/EnstylerJS Develop.user.js',
'UserScript', 'width=210,height=210,left=0,top=0');
// give 5s to start update, then close
setTimeout(enUpdateWindow.close,5000);
}
},
// part two: EnstylerJS internal configuration options ------
// fixed main nav
'enCNavF': {
'label': confLang('navfixed'),
'title': confMess('navfixed'),
'type': 'checkbox', // a checkbox input
'default': true,
'section': [confLang('config'), '']
},
// width of deal panel
'enCMax': {
'label': confLang('max'),
'title': confMess('max'),
'type': 'select', // a checkbox input
'options': enSiteConfig['width'], // Possible choices
'default': '1450'
},
// more Deal actions
'enCDealA': {
'label': confLang('dealaction'),
'title': confMess('dealaction'),
'type': 'checkbox', // a checkbox input
'default': true
},
// votebar
'enCDealVbar': {
'label': confLang('dealvotebar'),
'title': confMess('dealvotebar'),
'type': 'select', // a dropdown
'options': enSiteConfig['votescale'], // Possible choices
'default': '500'
},
// enable Touch support (bigger Avatar, touch on Deal Image)
'enCTouch': {
'label': confLang('touch'),
'title': confMess('touch'),
'type': 'checkbox', // a checkbox input
'default': true
},
// canvas on mobile
'enCWidth': {
'label': confLang('width'),
'title': confMess('width'),
'type': 'select', // a checkbox input
'options': enSiteConfig['width'], // Possible choices
'default': enValOff
},
// show price in button, min compacter
'enCPrice': {
'label': confLang('price'),
'title': confMess('price'),
'type': 'checkbox', // a checkbox input
'default': false
},
// minimize deal boxes to show maximum deals
'enCCompact': {
'label': confLang('compact'),
'title': confMess('compact'),
'type': 'checkbox', // a checkbox input
'default': false
},
// use dealz.rrr.de and inject user selected CSS
'enCCMail': {
'label': confLang('cssdealz'),
'title': confMess('cssdealz'),
'type': 'checkbox', // a checkbox input
'default': false
},
// enable filtering of external links
'enCRedirect': {
'label': confLang('redir'),
'title': confMess('redir'),
'type': 'checkbox', // a checkbox input
'default': true
},
// Page picker
'enCPageP': {
'label': confLang('picker'),
'title': confMess('picker'),
'type': 'checkbox', // a checkbox input
'default': true
},
// show real Dealtime
'enCDealT': {
'label': confLang('dealtime'),
'title': confMess('dealtime'),
'type': 'checkbox', // a checkbox input
'default': true
},
// fix bad userhmtl (cut'n paste crap)
'enCFixHtml': {
'label': confLang('fixhtml'),
'title': confMess('fixhtml'),
'type': 'checkbox', // a checkbox input
'default': true
},
// blacklist if colder
'enCBlackC': {
'label': confLang('blacklist'),
'title': confMess('blacklist'),
'type': 'select', // a dropdown
'options': enSiteConfig['blackcold'], // Possible choices
'default': '-20'
},
// blacklist, whitelist string
'enConfBlacklist': {
'label': confLang('black'),
'title': confMess('black'),
'type': 'text', // a text input
'size': 70, // Limit length of input (default is 25)
'default': ''
},
'enConfWhitelist': {
'label': confLang('white'),
'title': confMess('white'),
'type': 'text', // a text input
'size': 70, // Limit length of input (default is 25)
'default': ''
},
'enCUnblackL': {
'label': confLang('unblack'),
'title': confMess('unblack'),
'type': 'button', // a button input
'click': EnstylerBlacklistUnhide // Function to call when button is clicked
},
// language for enstyler messages and translation to
'enCLang':
{
'label': confLang('lang'),
'title': confMess('lang'),
'type': 'select', // a dropdown
'options': enSiteConfig['languages'], // Possible choices
'default': 'auto'
},
};
enConfDefs['sync'] = {
// part three: EnstylerJS configuration sync options ------
'enCAutoS': {
'label': confLang('autosync'),
'title': confMess('autosync'),
'type': 'checkbox', // a checkbox input
'section': [confLang('syncconf'), ''],
'default': true
},
'enCSyncKey':
{
'label': confLang('synckey'),
'title': confMess('synckey'),
'type': 'text',
'size': 10, // Limit length of input (default is 25)
'default': enValOff
},
'enCSync': {
'label': confLang('sync'),
'title': confMess('sync'),
'type': 'button', // a button input
'click': function() { // Function to call when button is clicked
SyncSettings(); }
}
};
if (DEBUGINT) {
enConfDefs['debug'] = {
// DEBUG buttons on mobile
'mDebugC': {
'label': 'Clear Debuglog',
'type': 'button', // a button input
'section': ['DEBUG', ''],
'click': function () { // Function to call when button is clicked
mobileClearLog(); }
},
'mDebugS': {
'label': 'Show Debuglog',
'type': 'button', // a button input
'click': function () { // Function to call when button is clicked
mobileShowLog(); }
}
};
}
// setings to save / sync
var enSaveSettings=[ 'enCNavF','enCDealA','enCDealVbar','enCTouch','enCRedirect','enCPageP',
'enCFixHtml','enCDealT', 'enCBlackC',
'enConfBlacklist', 'enConfWhitelist','enCAutoS' ];
// prepare GM_config div, apply class GM_config so we can apply CSS easy!! ========
var enGMFrame = document.createElement('div');
enGMFrame.setAttribute('class','GM_config');
// EnstylerJS Config Panel anzeigen =====================
var enGMConfigOpen=false;
function showEnstylerConfig() {
//if(!enGMConfigOpen) {
// add dim div and open menu
u('body').prepend('<div id="enOverDim"></div>');
GM_config.open();
document.getElementById('main').click();
enGMConfigOpen=true;
//}
}
function closeEnstylerConfig() {
//if(enGMConfigOpen) {
// add dim div and open menu
u('#enOverDim').remove();
//GM_config.close();
enGMConfigOpen=false;
//}
}
var enRemConf = [
{ val: enValOff, field: 'enCBlackC', rem: 'enConfWhitelist'},
{ val: enValOff, field: 'enCBlackC', rem: 'enConfBlacklist'},
{ val: enValOff, field: 'enCBlackC', rem: 'enCUnblackL'},
{ val: enValOff, field: 'enCSyncKey', rem: 'enCSync'},
{ val: enValOff, field: 'enCSyncKey', rem: 'enCAutoS'},
//{ val: enValOff, field: 'enCSyncKey', rem: 'enCSyncT'},
];
function confLangOpen() {
//GM_config.set('enCLang', GM_getValue('enLang',''));
// set save and close strings
u('.GM_config button[id$="_saveBtn"]').html(confLang('save'));
//u('.GM_config button[id$="_saveBtn"]').prop('title', confMess('save'));
u('.GM_config button[id$="_closeBtn"]').html(confLang('close'));
//u('.GM_config button[id$="_closeBtn"]').prop('title', confMess('close'));
u('#GM_config_resetLink').html(confMess('reset'));
}
//========================================
// SYNC functions
function EnstylerSyncIDShow() {
if (enSyncKey != enValOff) {
u('.GM_config input[id$="_field_enCSync"]').first().value=confLang('sync')+" "+enUserKey('');
}
}
// Save/Sync CSS and Enstyler Settings to server
var enSettings='enSettings';
function SyncSettings() {
// sync CSS and Config Settings via Callback
EnGetValue('', enSetValue);
EnGetValue(enSettings, enSetSettings);
}
function SaveSettings() {
EnSaveValue(enCssOpt, GM_getValue(enCssOpt,''));
enSaveMyCSS();
// iterate over Settings and save string
var settings='';
for (var val=0; val< enSaveSettings.length; val++) {
if (DEBUGXXX) enDebugLog('SaveSettings: '+val+ ' of '+ enSaveSettings.length+ ' ' + enSaveSettings[val]);
settings += enSaveSettings[val] + '=' + GM_config.get(enSaveSettings[val]) + '&';
}
if (DEBUGXX) enDebugLog('SaveSettings: ' + settings);
EnSaveValue(enSettings, settings);
}
// Save/Sync LasteSeen to server
function SaveLastSeen() {
if (enAutoSync) {
// delay save x/2 minutes after script start
var dealy=1*enMs2Min/2;
var myTime=Date.now()/enMs2Min;
var myDiff= myTime - GM_getValue('enLastCheck' + enSec,'0');
// if time more than 5 Minutes delay = 500ms
if (myDiff > 5 ) { delay=500; }
if (DEBUGXX) { enDebugLog('SaveLastSeen '+ 'enLastCheck' + enSec +' dealy: ' + dealy + 'ms'); }
// wait until synxc time expires
setTimeout(function() {
// do save
if(DEBUG) enDebugLog('LastSeen SAVE started');
EnSaveValue(enSec, GM_getValue(enSec,''));
}, 1*enMs2Min/2); //.then(
}
}
function SyncLastSeen() {
// if enabled
if (enAutoSync) {
// get time and convert to minutes
var myTime=Date.now()/enMs2Min;
var myDiff= myTime - GM_getValue('enLastCheck' + enSec,'0');
// if option set and time expired
if(DEBUG) { enDebugLog('LastSeen '+enSec+' requested, intervall '+1+' minutes , actual diff '+parseInt(myDiff));}
if (myDiff > 1 || myDiff < 0) {
if(DEBUG) enDebugLog('LastSeeen Update started');
// store actual time
GM_setValue('enLastCheck' + enSec, myTime);
// request last seen
EnGetValue(enSec, enSetLastSeen);
}
}
}
// Last Seen callback, set settings loaded from server
function enSetLastSeen(key, value) {
enLaArt=value;
EnstylerLastSeenDo();
if(enLaArt.replace(/thread_/i, '') > enSeArt.replace(/thread_/i, '')) {
enSetValue(key+'Last', value);
}
}
function enSaveMyCSS() {
//enEarlyInit();
EnSaveValue('', extraCSS +GM_getValue(MyCSS,'').replace(/^.*?{/, '').replace(/} *@-moz-document.*/,''),false);
}
// callback for enGetValue to set settings loaded from server
function enSetSettings(key, value) {
// Save value to greasemonky variable
if (DEBUGXX) { enDebugLog('enSetSettings: '+value); }
var array = value.split('&');
for (var pair=0; pair< array.length; pair++) {
var val = array[pair].split('=');
if (val.length<2 || !enSaveSettings.includes(val[0])) { continue; }
if (val[1] == 'false') {
GM_config.fields[val[0]].value = false;
if (DEBUGXX) enDebugLog(val[0] + '=' + '>FALSE<');
} else {
GM_config.fields[val[0]].value = val[1];
if (DEBUGXX) enDebugLog(val[0] + '=' + '>'+val[1]+'<');
}
GM_config.fields[val[0]].reload();
}
}
// 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.href;
// do we run on amazon?
if (enMyLocation.startsWith("https://www.amazon")) {
// if( enMyLocation.includes(".amazon.de") && enMyLocation.length > 100 && ! enMyLocation.includes("tag=offtopic-21"))
// {
// if (/[?&]tag=/i.test(enMyLocation)) {
// window.location.href=enMyLocation.replace(/([?&])tag=.*(-21)*/i, "$1tag=offtopic-21");
// } else {
// if(enMyLocation.includes("?")) {
// window.location.href=enMyLocation+"&tag=offtopic";
// } else {
// window.location.href=enMyLocation+"?tag=offtopic";
// }
// }
// }
// redirect enabled?
if (DEBUGXX) { enDebugLog('On AMAZON ...'); }
if (GM_config.get('enCRedirect')) {
if (DEBUGXX) { enDebugLog('AMAZON redirect enabled ...'); }
// do it
if (enMyLocation.includes("/gp/aw/d/")) { window.location.replace(enMyLocation.replace("/gp/aw/d/", "/dp/")); }
else if (enMyLocation.includes("/gp/aw/ol/")) { window.location.replace(enMyLocation.replace("/gp/aw/ol/", "/gp/offer-listing/")); }
}
return true;
}
return false;
}
// get colors of page to integrate better in international pages
// and save hight of navigation ...
//var enMainHeigth;
//=============== startup helper functions ==========================================
// Start Enstyler Magic
function EnstylerStart() {
var t0,t1;
EnstylerShowPage();
EnstylerFixedNav();
if(DEBUG) { t0 = Date.now(); }
EnstylerLastSeen();
if(DEBUG) {t1 =Date.now();
enDebugLog("Call lastseen took ms : " + (t1 - t0) );
t0 = Date.now();}
EnstylerDealTime();
if(DEBUG) {t1 = Date.now();
enDebugLog("Call dealtime took ms : " + (t1 - t0) );
t0 = Date.now(); }
EnstylerBlacklist();
if(DEBUG) {t1 =Date.now();
enDebugLog("Call blacklist took ms : " + (t1 - t0) );
t0 = Date.now();}
EnstylerDealActions();
if(DEBUG) {t1 = Date.now();
enDebugLog("Call dealaction took ms: " + (t1 - t0) );
}
}
function EnstylerRedo(){
var ts, t0,t1;
EnstylerShowPage();
if(DEBUG) {ts=Date.now(); t0 = Date.now();}
EnstylerLastSeenDo();
//EnstylerPagePickerDo();
if(DEBUG) {t1 = Date.now();
enDebugLog("Call lastseen took ms : " + (t1 - t0) );
t0 =Date.now();}
EnstylerDealTimeDo();
if(DEBUG) {t1 = Date.now();
enDebugLog("Call dealtime took ms : " + (t1 - t0) );
t0 = Date.now(); }
EnstylerDealActionsDo();
if(DEBUG) {t1 = Date.now();
enDebugLog("Call dealaction took ms: " + (t1 - t0) );
}
if(DEBUG) enDebugLog('DOMSubtreeModified took ms: '+ (t1 - ts));
}
var enShowDate=new Date().toLocaleString('de-DE', { hour: 'numeric', minute: 'numeric' });
function EnstylerShowPage() {
// show section and load time in search box
if (myDealTime) {
var search= u('.search-input');
if (! search.lenth) {
search.nodes[0].setAttribute("placeholder",enShowDate + enLangLocalize(' <EN-LANG:oclock>', enTimeLang, enLANG) +
(enLocParser.pathname == '/'? ' (home': ' ('+enLocParser.pathname.replace(/(^.*)[\/]|-.*/g,''))+
(enLocParser.search.length ? ' |' +enLocParser.search.replace(/.*=/,'')+'|' :'') + ')');
}
}
}
// delayed actions after finishing everything else
function EnstylerDelayedInit() {
// calc colors and topx
var myBgColor=shadeRGBColor(getStyle(u('.nav').first(),'background-color'),0.1);
var myButtonColor=shadeRGBColor(getStyle(u('.btn--mode-special').first(), 'background-color'), 0.1);
var myColor=shadeRGBColor(myBgColor, 0.7);
// if background is dark, then use ligther text and borders
var myDarkStyle=medainRGBColor(getStyle(u('#main').first(), 'background-color'))>100 ? '': '.notification-item {color: #111;}'+
' body, .user, .thread-title, .subNavMenu-link, .mute--text2 {color: #aaa !important}'+
' .notification-item--read, .card-title, .mute--text, .userHtml-quote, .userHtml .userHtml-quote-source, .widget-title, .linkGrey,'+
' .thread-userOptionLink, .btn--mode-white--dark, .btn--mode-boxSec, .thread--expired.thread--type-card, .thread--expired.thread--type-list {color: #888;}' +
' article, section {border: 1px #666 solid} img, .votebar, .vote-tempIco, .vote-temp, .vote-temp--hot, .text--color-red,'+
' .vote-btn, .emoji, .ico, .dot {filter: grayscale(.25);}';
addStyleString(
// set Panel background color to nav background, text color to 70% brigther
' .GM_config {background-color: '+ myBgColor + ' !important; color: '+ myColor + ';}'+
' .GM_config select {background-color: '+ myBgColor + ' !important;}'+
// set section header background color to nav -30%, text color to 70% brigther
' .GM_config .section_header, .GM_config .config_header {background-color: '+ shadeRGBColor(myBgColor, -0.25) +' !important; color: '+ myColor +' !important;}'+
// set main nav background hover effekt to nac background +10%
' .nav-link-text:hover, .js-navDropDown-messages:hover, .js-navDropDown-activities:hover { background-color: '+ shadeRGBColor(myBgColor, 0.1)+ ' !important;}' +
// adjust panel button colors to add deal button color
' .GM_config input[type=button] { background-color: ' + myButtonColor + ' !important; border-color: '+ myButtonColor + ' !important; min-width: 10em;}'+
' .GM_config input[type=button]:hover, .btn--mode-special:hover { background-color: ' + shadeRGBColor(myButtonColor, 0.2)+
' !important; border-color: ' + shadeRGBColor(myButtonColor, 0.2) + ' !important;}' +
' .bg--inverted { background-color: ' + myButtonColor + ' }' +
// change text color if background is to dark
myDarkStyle
);
// don't know why, but works only if called with delay ...
EnstylerMenuActions();
//EnstylerPagePickerCreate();
// track DOM change Events, debounce: wait 100ms after mutiple events
// mobile browsers display less stuff than e.g. desktop browsers, so we listen to #main on mobile browsers
// this work also for desktop browsers, but ceates to much calls to redo
if (isMobile) {
u('#main').on("DOMSubtreeModified", debounce( 300, function() {window.requestAnimationFrame(EnstylerRedo);}));
//$ ('#main').bind("DOMSubtreeModified", debounce( 300, function() {EnstylerRedo();}));
//document.addEventListener("DOMSubtreeModified", debounce( 300, function() {EnstylerRedo();}),false);
} else {
u('.js-pagi-bottom').on("DOMSubtreeModified", debounce( 200, function() {window.requestAnimationFrame(EnstylerRedo);}));
//$ ('.js-pagi-bottom').bind("DOMSubtreeModified", debounce( 100, function() {EnstylerRedo();}));
}
}
//=============== MAIN function START at DOM READY =============================================
// normally script is started at DOM ready, we try early as possible inject css after document start
// HACK: wait for *MINIMAL* needed DOM is availible
function WaitForBody () {
// check if special handling for external site is needed, e.g. amazon
if(enAmazonMobileRedirect()) return;
// element 'messages-list' is needed, 'footer' as fallback
if (u('#messages-list').length || u('#footer').length) {
enEarlyInit();
// disable enstyler if regex matches pepper pathname, e.g. breaks pepper functionality
if (enLocParser.pathname.match(enDisableScript)) {
if (DEBUGXX) {
enDebugLog("disable Enstyler for path: " + enLocParser.pathname);
enDebugLog("regex: " + enDisableScript.toString());
}
return;
}
// wait until DOM Ready to start MAIN
WaitForDOM ();
} else {
// wait for nextframe (1/60s)and check again
// sleep on modern browsersf window/tab is hidden:
// https://swizec.com/blog/how-to-properly-wait-for-dom-elements-to-show-up-in-modern-browsers/swizec/6663
window.requestAnimationFrame(WaitForBody);
}
}
//HACK2: wait for footer aka close to DOM READY
function WaitForDOM () {
// footer is last (visible) element on page
var myColor=getStyle(u('.nav, #navigation').first(), 'background-color').replace(/[^\(]*/, "");
if (DEBUGXXX) {enDebugLog(myColor);}
if (u('.vwo-deal-button, #footer').length && !(myColor=="")) {
// start MAIN on DOM Ready
MAIN();
} else {
// wait for next frame (1/60s) plus 80ms and check again
// sleep on modern browsersf window/tab is hidden:
// https://swizec.com/blog/how-to-properly-wait-for-dom-elements-to-show-up-in-modern-browsers/swizec/6663
window.requestAnimationFrame(function(){setTimeout(WaitForDOM, 80);});
}
}
// basic config panel formatting, everything else is formatted by enstyler
var enCSS = ['.card-inner::after {display: none !important}', // remove Enstyler2 (c) message
'.GM_config {color: white !important; opacity: 0.92 !important; left: 5% !important; height: auto !important; padding-bottom: 10px !important; top: 1.4em !important;',
'box-shadow: 10px 10px 20px black; min-width: 21em; max-width: 40em !important; border-radius: 10px}',
'#enOverDim {background-color: black; z-index: 999; position: fixed; top: 0; right: 0; bottom: 0; left: 0; opacity: 0.5}',
'.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; }',
/* Header and section header */
'.GM_config .config_header {font-size: 14pt !important; border: none !important; padding: 0.2em; font-weight: bold; text-align: center;}',
'.GM_config .section_header { border: none !important; background-color:#005293 !important; !important; text-align: center; margin-top: 1em;}',
'.GM_config .field_label:hover {color: gray;} .GM_config a:hover {text-decoration: underline; color: darkgray;}',
/* config items can float */
'.GM_config .config_var {display: inline-block;} .GM_config .field_label {display: inline-block; min-width: 14em; margin-left: 2em; }',
'.GM_config button, .GM_config input[type=button] { font-weight: bold; text-align: center; color: #fff; background-color: #58a618 !important; }',
'.GM_config button:hover {background-color: #a5d867 !important; border-color: #a5d867 !important;}',
'.enClassHidden, #EnPopup_closeBtn, .voteBar-- { display: none !important; }',
// minimum votebar values
'.votebar {display: inline-block; position: relative; top: .3em; height: .5em; margin-left: 2.5em; max-width: 80% }',
'.voteBar--warm { background-color: #ffb612 } .voteBar--hot { background-color: #e00034 } .voteBar--burn { background-color: #e00034 }',
'.voteBar--cold, .voteBar--colder { background-color:: #00a9e0 } .voteBar--cold, .voteBar--colder { background-color: #5bc6e8 }',
// hide original temp
'.threadTempBadge { display: none; } .flex--justify-space-between { justify-content: unset !important; }',
// max window width
'.gridLayout { width: unset; max-width: '+ GM_getValue('enMax','')+'px !important;}',
'.page2-center, .thread-list--type-list--sideAds, .thread-list--type-list, .listLayout { max-width: '+ GM_getValue('enMax','')+'px !important;}',
].join(" ");
// extra CSS addded by script
var extraCSS="";
// inject CSS as early as possible
function enEarlyInit() {
// inject cached userstyle
addStyleString(GM_getValue('Enstyler2_CSS',''), 'domain("' + enLocParser.hostname);
// get config vars
enCCMail=GM_config.get('enCCMail');
myDealAction=GM_config.get('enCDealA');
myTouch=GM_config.get('enCTouch');
myCompact=GM_config.get('enCCompact');
myPrice=GM_config.get('enCPrice');
myFixHtml=GM_config.get('enCFixHtml');
myVotescale=GM_config.get('enCDealVbar');
myVotebar=(myVotescale!=enValOff);
myDealTime=GM_config.get('enCDealT');
if(myPrice) {
extraCSS += '.threadGrid-title .flex, .threadGrid-title .overflow--fade {display: none;}';
}
if(myCompact) {
extraCSS += '.threadGrid {padding: .3em !important;} .threadGrid-headerMeta, .threadGrid-title {height: 2.8em;}'+
'.thread-title {white-space: nowrap;} .threadGrid-headerMeta {height: 2.3em;} .thread--compact .threadGrid-image {display: none}' +
'.space--mt-2, .space--mv-2 {margin-top: .25em;} .vote-box {height: 2.1em} .votebar {top: 0;} .threadTempBadge-icon {font-size: 1.3em !important;}';
if(myPrice) {enCSS += '.threadGrid-headerMeta, .threadGrid-title {height: 2em !important;}';}
}
if(myTouch) {
// bigger dealaction icons etc on touch
extraCSS += 'article .footerMeta-actionSlot .ico::before, article .threadItem-footerMeta .ico::before, article .threadCardLayout--row--small .ico::before' +
', .thread-userOptionLink.ico:before {-webkit-transform: scale(1.7); transform: scale(1.7); width: 1.5em; left: .4em;}' +
'.ico--reduce3 {left: .5em;} #emergency {transform: scale(1.5); margin-left: .7em;}' +
'article a.btn--ctrl--fixed {-webkit-transform: scale(1.2); transform: scale(1.2); left: 0; margin-left: 3em; min-width: 4em;}' +
'.vote-down, .vote-up {padding-top: 0.25em; padding-bottom: 0.25em} .thread-avatar { width: 2.3em; height: 2.3em;}';
}
if(!myVotebar) {
// display original flame/ice
extraCSS += '.threadTempBadge { display: unset; }';
}
if(myDealAction) {
// remove ... and show full text in dealaction
extraCSS +='button.meta-ribbon-btn.hide--fromW3 {display: none}';
}
// add composed CSS
addStyleString(enCSS+extraCSS+enNavCSS);
}
// script time mesurement starts here
var EnstylerStartTime=Date.now();
function MAIN() {
if(DEBUG) enInitTime = performance.now() - enInitTime;
if(DEBUG) enDebugLog('Inittime: ' + enInitTime + ' ms');
if (DEBUG) enDebugLog('Start Init');
EnstylerInit();
if (DEBUG) enDebugLog('Start CheckUpdate');
enCheckUpdates();
// add frame for GM_config to body
document.body.appendChild(enGMFrame);
// EnstylerJS START ============================================
// get main color and set main BG color
var myBGColor=shadeRGBColor(getStyle(u('.bg--main').first(), 'background-color'), -0.08);
addStyleString('.bg--off {background-color: ' +myBGColor + '!important;}');
// workaround for zepto (no .outerHeigth())
//enMainHeigth = getOuterHeight('header');
// give time to start render page
setTimeout(EnstylerDelayedInit, 400);
EnstylerStart();
//var EnstylerStartupDelay=Date.now()-EnstylerStartTime;
if(DEBUG) enDebugLog('Startup in ms: ' +Date.now()-EnstylerStartTime);
} // END MAIN
// =================== EnStyler UserScript Homepage functions =============
function enUserstyleDo() {
// ============== we are on USERSTYLE ===================
// add frame for GM_config to body
if (DEBUGXX) { enDebugLog('USerstyleDo ...');}
if (u('#EnstylerButton').length) {
if (DEBUGXX) { enDebugLog('USerstyleDo already done ...');}
return;
}
document.body.appendChild(enGMFrame);
// inject userstyle directly
addStyleString(enCSS+'.advancedsettings_hidden {max-width: 640px; border-radius: 8px; background-color: #ffffff; margin-bottom: 30px;'
+ ' padding: 30px; display: flex; flex-direction: row; flex-wrap: wrap; margin-top: 14px;}'
+ ' #ownedButtons {visibility: visible; border: 1px solid red;}'
+ ' #top_android_button, .android_button_banner, .walking, .overlay_background { display: none !important; }');
addStyleString(GM_getValue('Enstyler2_CSS','') , 'url(https://userstyles.org');
// START Enstyler 2 Homepage
//setTimeout(createEnstylerButton, 2000);
var checkExist = setInterval(function() {
if (u('#style-settings').length) {
if (! u('#EnstylerButton').length) {
createEnstylerButton();
}
clearInterval(checkExist);
}
}, 1000); // check every 500ms
}
// button for saving options
var input = document.createElement('input');
input.type = 'button';
input.setAttribute(enID, EnstylerButton);
input.onclick = saveEnstylerCSS;
input.value = confLang('options');
function createEnstylerButton() {
if (DEBUG) enDebugLog('createEnstylerButton');
// open advanced settings and remove advanced button
u('#advancedsettings_area').attr('class' ,'advancedsettings_shown');
u('.advanced_button').remove();
//u('select, input').off();
// style, add and scroll to button
input.setAttribute('style', 'font-size: 1.3em; padding: 0.7em; background-color: #69be28; color: white; border-radius: 8px; border: 1px solid grey; margin-top: 1em; font-weight: bold;');
u('#style-settings').before(input);
window.scrollTo(100, 400);
// set saved options
enSetOptions();
}
function saveEnstylerCSS () {
//enSaveOptions();
//showEnstylerConfig();
enSaveOptions();
setTimeout(window.close, 1000);
}
// read values from options
function enSaveOptions() {
if (DEBUG) enDebugLog('enSaveOptions');
var myOptions='', myID, myValue, myText;
u('#style-settings select').each(function(that) {
myID = u(that).attr(enID);
myValue = that.value;
myText = u('option[value='+ myValue +']').text();
myOptions +='#' + myID + ':' + myValue +':' + myText +';\n';
});
u('#style-settings input[type=text]').each(function(that) {
myID = u(that).attr(enID);
myValue = that.value;
myText = "RGB-Clolor";
myOptions +='#' + myID + ':' + myValue +':' + myText +';\n';
});
u('#style-settings input:checked').each(function(that) {
myID = u(that).attr(enID);
myValue = that.value;
myText = u('label[for='+ myID +']').text();
myOptions +='#' + myID + ':' + myValue +':' + myText +';\n';
});
GM_config.set('saveOpt', myOptions);
GM_setValue(enCssOpt, myOptions);
}
//set values from stored options
function enSetOptions() {
if (DEBUG) enDebugLog('enSetOptions');
input.value = confLang('options');
// get saved options,remove newlines and split to settings array
var myOptions=GM_getValue(enCssOpt, '');
// if(DEBUG) enDebugLog('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(DEBUGXXX) enDebugLog('process:' + mySettings[i]);
// each Setting has 3 fields seperated by :, but only 2 used
var myField=mySettings[i].split(':');
if (myField[0].startsWith('#setting')) {
// text input, select
u(myField[0]).first().selectedIndex = "-1";
u(myField[0]).first().value =myField[1];
} else if (myField[0].startsWith('#option')) {
// option
u(myField[0]).first().checked =true;
} else {
if (myField[0] != '') {enDebugLog('ignoring unkown option: "' + myField +'"');}
}
}
// hide install button if enstyler is active
addStyleString('#button { display: none; }');
// update shown otions
}
function enComposeCSS() {
// get saved options, remove newlines and split to settings array
var myOptions=GM_getValue(enCssOpt, '');
// abort if no options found
if (myOptions=='' || !myOptions.startsWith('#')) {return "";}
myOptions=myOptions.replace(/\n/g,'');
var mySettings = myOptions.split(';');
// start composing options
myOptions='';
for (var i=0; i< mySettings.length; i++) {
//if(DEBUG) enDebugLog('process:' + mySettings[i]);
if(mySettings[i]=='') continue;
// each Setting has 3 fields seperated by :
var myField=mySettings[i].split(':');
if(myField.length < 2) continue;
// add &setting=value
myOptions += '&' +myField[0] + '=' + myField[1];
}
// replace first & by ? and returns string
myOptions = '?'+myOptions.slice(1).replace(/#/g, '');
//if(DEBUG)
enDebugLog(myOptions);
return myOptions;
}
// END USI HACK ======================================================================
//=========== Support functions for actual use ======
// Browser detection from https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
// move the needed detecion to beginning of script!
// Opera 8.0+
//var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
// Firefox 1.0+
//var isFirefox = typeof InstallTrigger !== 'undefined';
// Safari 3.0+ "[object HTMLElementConstructor]"
//var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
// Internet Explorer 6-11
//var isIE = /*@cc_on!@*/false || !!document.documentMode;
// Edge 20+
//var isEdge = !isIE && !!window.StyleMedia;
// Chrome 1+
//var isChrome = !!window.chrome && !!window.chrome.webstore;
// Blink engine detection
//var isBlink = (isChrome || isOpera) && !!window.CSS;
// set width on mobile if enabled ============
var enWidth=GM_getValue('enWidth',enValOff);
if (isMobile && enWidth != enValOff) {
u('meta[name=viewport]').attr('content','width='+enWidth+'px, initial-scale=1');
}
// simple log function, will be used for mobile devices
function mobileLog(string) {
GM_setValue(saveLog, GM_getValue(saveLog,'') + string + '\n');
}
function mobileShowLog() {
(GM_getValue(saveLog,''));
}
function mobileClearLog() {
GM_setValue(saveLog, 'Enstyler Log\n');
}
// get global site defaults
function EnstylerSiteConfig(key) { return enGetSiteConfig(enInterName, key); }
function enGetSiteConfig(site, key) {
return (enSiteConfig.hasOwnProperty(site) && enSiteConfig[site].hasOwnProperty(key)) ? enSiteConfig[site][key] : '';
}
// translate/replace text by strings given in object trans[lang]
// assoc object: trans{ lang: { field: 'string', ... }, lang2: { field: 'string2', ... } }
function enLangLocalize(text, trans, lang) {
// check if lang exist. if not set to english
if (!enDealLang.hasOwnProperty(lang)|| typeof trans[lang] === 'undefined') { lang='en'; }
// iterate over trans replace by idea from
// http://stackoverflow.com/questions/7192436/
text=text.replace(enLangPat,
function(match,key){
if (DEBUGXXX) {
enDebugLog('enLangLocalize: ' +text+' '+[key]+' '+lang);
enDebugLog('enLangLocalize: '+ (trans.hasOwnProperty(lang)) ? trans[lang] : "Does not exist!");
}
// if key exist return translation, else key
return (trans.hasOwnProperty(lang) && trans[lang].hasOwnProperty(key)) ? trans[lang][key] : key;
});
// repeat until no more match for enLangPat
return text.match(enLangPat) ? enLangLocalize(text, trans, lang) : text;
}
// add CSS in to document
// new: remove moz-document rules
var enUserScript = { detect: /.*?@-moz-document .*?\{\s*/,
split: /^.*?\{/,
next: /}\s*@-moz-document.*/,
//repeat: /}\s*@-moz-document.*?{/g,
//end: /}\s*$/
};
var enCSSmax=16100; // split CSS if longer then
function addStyleString(str, host) {
if (typeof host === 'undefined') host='';
// check if style contains @-moz-document rules
if (str.match(enUserScript['detect'])) {
// if no host is given use actual hostname
if (host=='') { host= enLocParser.hostname; }
//split userstyle in parts
var myPart = str.split(host);
// recreate style string for host from parts
str='';
for (var i=1; i< myPart.length; i++) {
// skip parts with no CSS
if (myPart[i].indexOf('{') == -1) continue;
str += myPart[i].replace(enUserScript['split'],'').replace(enUserScript['next'],'');
}
}
// split long style to avoid caching problems on andoid
// while remaining string is longer than exCSSmax and we can split
var myPos, myStart=0, mySplit=enCSSmax;
while (str.length > mySplit &&
(myPos=str.substring(mySplit).indexOf('}.')) > 0) {
// add substring of style
addStyleString(str.slice(myStart, mySplit+= myPos+1));
// adjust for next Start
myStart=mySplit; mySplit+=enCSSmax;
}
// add style string to document
if(DEBUG) {
enDebugLog('applyed style length: ' + (str.length - myStart));
//enDebugLog(str.slice(myStart));
}
var node = document.createElement('style');
node.innerHTML = str.slice(myStart);
document.body.appendChild(node);
}
function capitalizeFirstLetter(string) {
return string[0].toUpperCase() + string.slice(1);
}
// truncate String add word boundary
function truncStringWord(string, n, worddelim ){
if (typeof worddelim === 'undefined') worddelim=' ';
if (string.length > n) {
string = string.substr(0,n-1);
return string.substr(0,string.lastIndexOf(worddelim)) + '...';
}
return string;
}
// make colors ligther or darker
// http://stackoverflow.com/questions/5560248
//color = "rbg(63,131,163)";
//lighterColor = shadeRGBColor(color, 0.5); // rgb(159,193,209)
//darkerColor = shadeRGBColor(color, -0.25); // rgb(47,98,122)
function shadeRGBColor(color, percent) {
if (typeof percent === "undefined") { percent=0.1; }
if (typeof color === "undefined") { return("rgba(0,0,0,0)"); }
var f=color.split(","),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=parseInt(f[0].slice(4)),G=parseInt(f[1]),B=parseInt(f[2]);
return "rgb("+(Math.round((t-R)*p)+R)+","+(Math.round((t-G)*p)+G)+","+(Math.round((t-B)*p)+B)+")";
}
// calculates the medain of RGB color, ignores alpha
// returns value 0-255
function medainRGBColor(color) {
var f=color.split(","),R=parseInt(f[0].slice(4)),G=parseInt(f[1]),B=parseInt(f[2]);
return Math.round((R+G+B)/3);
}
// Ensytler debounce Funtionen, modified: parameter swapped, no args passed
// can also be used as simple sleep async
// example debounce: $ ('input.username').click(debounce(250, function));
// example sleep async: debounce(500, function)
// https://remysharp.com/2010/07/21/throttling-function-calls
function debounce(delay, fn) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(function () {
fn.call(this);
}, delay);
};
}
/* get external file and store to GM_variable */
/* curreently we assume its a CSS or JS File, so we strip comments and @namespace @moz-document... */
function enCacheExternalResource(stylesheet_uri, GM_variable) {
if (DEBUGXX) enDebugLog('CacheExternalResource ...');
GM_xmlhttpRequest({
method: "GET",
url: stylesheet_uri,
onload: function(response) {
//we got the file!, remove linebreaks and strip simple /*comments */ unneded blanks
var myResponse=response.responseText.replace(/\r\n/g, ' ')
.replace(/\/\*.*?\*\/| *|\t/g, '')
.replace(/([:;]) /g, '$1')
.replace(/1111.11%/g, "100%");
if (DEBUGXX) enDebugLog([
response.status,
response.statusText,
response.readyState,
response.finalUrl,
stylesheet_uri,
GM_getValue(enCssOpt, ''),
response.responseHeaders
].join("\n")+'\n'+ myResponse.replace(/.*?(btn--mode-special[^}]*).*/, '$1}'));
if (myResponse.length > 60) {
GM_setValue(GM_variable, myResponse);
addStyleString(myResponse);
} else {
if (DEBUGXX) { enDebugLog('CacheExternalResource ... response to short!'); }
}
},
onerror: function(response) {
alert(confMess('cssfailed'));
// GM_setValue('enLastUpdateCheck', 0);
}
});
}
// workaround for no .outerHeigth()
//function getOuterHeight(el) {
// return u("menu").first().offsetHeight;
//}
// translate query string with google chrome translation service
// client=gt google translation for chrome
// sl= source lang, tl= target lang, dt=t do tranlation ;-)
// q= URI encoded query string to translate from sl to tl
var enGoogleTransURL='https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=<ENSTYLER-LANG-HERE>&dt=t&q=<ENSTYLER-HTML-HERE>';
// parameter jquery/javascript obj
// translates text up to first <a> or <img> tag, max translation length is 300 chars
var enMaxTrans=300;
var enTransTags=5;
function enTranslateGoogle(thisObj) {
// no translation needed
if (enLANG==enSiteLANG || !thisObj.length) { return; }
// get HTML, remove unwanted chars
var text=thisObj.html().replace(/[#\(\)]|\n|\r|\t/g, '');
// remove all html tags and store in array
var html="", transTags = [];
var count, match, buff="", last=0, regex = /<.*?>/gi; //<(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>/ig;
for (count=0; (match = regex.exec(text)); count++) {
// break if longer max translation length
if(enTransTags<count || buff.length+(match.index-last) > enMaxTrans) {
var space=text.lastIndexOf(' ', last+(enMaxTrans-buff.length));
buff += text.slice(last, last+space);
html=text.slice(last+space);
break;
}
// cut out text and store match
transTags[count] = match[0];
buff +=text.slice(last, match.index)+'<a href="'+count+'">';
last=regex.lastIndex;
}
if (!buff.length) { buff=text; if (buff.length > enMaxTrans) return;}
if (DEBUGXXX) {
//enDebugLog(text);
enDebugLog(count+': '+transTags);
enDebugLog(buff);
//enDebugLog(html);
}
// create query string
var query=enGoogleTransURL //.replace(/<ENSTYLER-SITELANG-HERE>/, enSiteLANG)
.replace(/<ENSTYLER-LANG-HERE>/, enLANG).replace(/<ENSTYLER-HTML-HERE>/, encodeURI(buff));
// request translation from google
var ret = GM_xmlhttpRequest({
method: "GET",
url: query,
onload: function(res) {
// read JSON rsponse ---
text=eval("(" + res.responseText + ")")[0][0][0];
try {
// sometimes translation is diveded in many parts
for (x = 1; x < 5; x++) {
text += eval("(" + res.responseText + ")")[0][x][0];
}
} catch(e){}
// reassemble translation
for (count--; count>=0; count--){
// replace ID with stored match
text=text.replace(RegExp('< *a href *= *"'+count+'" *>'),transTags[count]);
//console.error(count+': '+text);
}
thisObj.html(text +' '+ html);
if (DEBUGXXX) {
enDebugLog('Translate from: ' + buff);
enDebugLog('===> '+ text ); //alert(eval("(" + res.responseText + ")")[0][0][0]);
}
}
});
}
// save given value string on RRR webserver
var enSaveURL='https://dealz.rrr.de/enstyler/save.php?';
var enSaveUrlLast='';
function EnSaveValue(key,value,crypt) {
if (enUserName != '' && enSyncKey != enValOff ) {
if (typeof crypt === "undefined") { crypt=true; }
if (DEBUGXX) { enDebugLog('EnSaveValue crypt: ' + crypt + ' key: ' + key + ' value: '+value); }
// compose URL and return if duplicate to avoid traffic
if (key+value == enSaveUrlLast) { return; }
enSaveUrlLast=key+value;
var cval = crypt ? enEncrypt(value) : LZString.compressToEncodedURIComponent(value);
var save=enSaveURL + 'ID=' + enUserKey(key) + '&value=' + cval ;
// save value to webserver
if(DEBUGXX) {
enDebugLog('EnSaveValue URL: ' + save);
enDebugLog('EnSaveValue Key: ' + key + ' Value: ' + value );
}
// simplest possible request log param only
GM_xmlhttpRequest({
method: "GET",
url: save
}
);
}
}
// load given value string FROM RRR webserver and execute given funktion as callback
// var enGetURL='https://dealz.rrr.de/enstyler/load.php?';
function EnGetValue(key,callback,crypt) {
if (enUserName == '') { enUserName = GM_getValue('enCSyncUser',''); }
if (DEBUGXX) { enDebugLog('enGetValue user: '+enUserName+ ' SyncID: ' + enSyncKey+ ' key: '+ key); }
if (enUserName != '' && enSyncKey != enValOff ) {
if (typeof crypt === "undefined") { crypt=true; }
var get='https://dealz.rrr.de/enstyler/load.php?' + 'ID=' + enUserKey(key);
if(DEBUGXX) { enDebugLog('EnGetValue: ' + get); }
// fallback if not function provided
// if (typeof callback !== "function") { callback=enSetValue; }
// request key value from server
GM_xmlhttpRequest({
method: "GET",
url: get,
onload: function(response) {
if (DEBUGXX) { enDebugLog('EnCallback Key: ' + key + ' status=' + response.status); }
// call callback function if status is 200
if (response.status === 200 && response.responseText.length > 30) {
if (DEBUGXX) { enDebugLog('EnCallback Value: '+ crypt ? enDecrypt(response.responseText) : LZString.decompressFromEncodedURIComponent(response.responseText)); }
callback( key, crypt ? enDecrypt(response.responseText) : LZString.decompressFromEncodedURIComponent(response.responseText));
} else if (DEBUGXX) {
enDebugLog('EnCallback Value: No stored value for ' + key + ': ' + response.responseText);
}
}
});
}
}
// default callback for enGetValue to set key to value from server
function enSetValue(key, value) {
// Save value to greasemonky variable
if (DEBUGXX) { enDebugLog('enSetValue '+key+' old: '+GM_getValue(key,'')+ ' new: '+value); }
GM_setValue(String(key), String(value));
}
// function for encrypt / devcrypt sha226
function enEncrypt(string){
return btoa(sjcl.encrypt(enUserKey(''), string));
}
function enDecrypt(string){
return sjcl.decrypt(enUserKey(''), atob(string));
}
function enUserKey(key) {
return enShortKey(enUserName + enSyncKey + key);
}
function enShortKey(string) {
if (DEBUGXX) {
enDebugLog('enShortKey IN: '+ string);
}
// ShortURL Bijective conversion between natural numbers (IDs) and short strings
// the map function converts characters (and numbers) from strings to unique IDs (not bijective)
// letter sequence is taken from: https://en.oxforddictionaries.com/explore/which-letters-are-used-most
// example: earshot idiot => 12381556412456 <=> hQGZ69Ly
string= ShortURL.encode(parseInt(string.toLowerCase().split('').map(function(c){
return 'eariotnslcudpmhgbfywkvxzjq'.indexOf(c)+1 || '9876543210'.indexOf(c)+1 || '';
// 12345678901234567890123456
}).join('')));
if (DEBUGXX) {
enDebugLog('enShortKey OUT: '+ string);
}
return string;
}
// alternative for jquery .css() get method
// https://www.htmlgoodies.com/html5/css/referencing-css3-properties-using-javascript.html#fbid=b2-TgWC-yGY
function getStyle(oElm, css3Prop){
// FF, Chrome etc.
if(window.getComputedStyle){
try { return getComputedStyle(oElm).getPropertyValue(css3Prop); }
catch (e) {}
} else {
// IE
if (oElm.currentStyle){
try { return oElm.currentStyle[css3Prop]; }
catch (e) {}
}
}
return "";
}
// ============= DEBUG INIT =======================================================================
if (DEBUG) {
enDebugLog('Site Lang: '+enSiteLANG);
enDebugLog('Selected Lang: '+enLANG);
// output location, remove * to activate
enDebugLog('URL : '+enLocParser.href); // http://example.com:300/pathname?search=test#hash
//enDebugLog('protocol : '+enLocParser.protocol); // => "http:"
enDebugLog('hostname : '+enLocParser.hostname); // => "example.com"
//enDebugLog(' port : '+enLocParser.port); // => "3000"
//enDebugLog('host+port: '+enLocParser.host); // => "example.com:3000"
enDebugLog('pathname : '+enLocParser.pathname); // => "/pathname/"
enDebugLog(' search : '+enLocParser.search); // => "?search=test"
enDebugLog(' hash : '+enLocParser.hash); // => "#hash"
//enDebugLog(GM_getValue('Enstyler2_CSS',''));
/**/
}
var enGMSave=false;
// ================ GM_config INIT Starts here ============================================
// GM_Config Init for MyDealz and Enstyler Home
if (!window.location.hostname.endsWith('userstyles.org')) {
var enFixedNavLast=false;
GM_config.init(
{
// international sites support
id: enInter ? 'GM_config' + enInterSite : 'GM_config',
title: !DEBUG ? confLang('headline') : confLang('headline') + ' >> Debug <<',
fields: Object.assign(enConfDefs['default'], enConfDefs['sync'],enConfDefs['debug']),
'events': // Callback functions object
{
// init
'init': function() {
},
// remove elements ich switch is checked or not
'open': function(doc) {
// see if fixed nav
enFixedNavLast=GM_config.get('enCNavF');
// init lang on open
confLangOpen();
// show SyncID if sync is activated
EnstylerSyncIDShow();
// add grid, list switch to config dialog
u('.GM_config [id$="_enJS_var"]').after(enNavGrid);
// remove unneeded controls
for (var i = 0; i < enRemConf.length; i++) {
if (GM_config.get(enRemConf[i].field) == enRemConf[i].val) {
GM_config.fields[enRemConf[i].rem].remove();
}
}
// remove in some cases e.g. auto update
//if (enJSAutoUpdate) {GM_config.fields['enJS'].remove();}
if (!isMobile) {GM_config.fields['enCWidth'].remove();}
},
// relaod page on close after save
'save': function() {
// save lang selection or '' if auto
//if (GM_config.get('enCLang') == 'auto') { GM_setValue('enLang', ''); } else { GM_setValue('enLang', GM_config.get('enCLang')); }
SaveSettings(); // sync
GM_setValue('enWidth', GM_config.get('enCWidth'));
GM_setValue('enMax', GM_config.get('enCMax'));
// allow sync after save
enSyncKey = GM_config.get('enCSyncKey');
enAutoSync = GM_config.get('enCAutoS');
// call close:, show again
GM_config.close();
showEnstylerConfig();;
//enSyncIn=GM_config.get('enCSyncT', enSyncIn);
if (enGMSave) { window.location.reload();} else { enGMSave=true; }
},
'close': function() { closeEnstylerConfig(); enCheckUpdates();},
},
'frame': enGMFrame // Element used for the panel
}
);
// init values read from GM_config vars
enSyncKey = GM_config.get('enCSyncKey');
enAutoSync = GM_config.get('enCAutoS');
//enSyncIn=GM_config.get('enCSyncT', enSyncIn);
// read user selected LANG
if(GM_config.get('enCLang') != 'auto' ) {
//GM_setValue('enLANG', '');
enLANG=GM_config.get('enCLang');
}
} else {
// activate config for Enstyler Homepage
GM_config.init(
{
id: 'GM_config',
//title: confLang('headline') + ' CSS',
fields: {
// Part one: load external content --------
'saveOpt': {
// 'section': [ confLang('savecss'), ''],
// 'label': confLang('howtocss'), // Appears near textarea
'type': 'textarea',
// 'size': 70,
},
},
/*
'events': // Callback functions object
{
'open': function() { confLangOpen(); },
'save': function() {
//enSendCSS();
enSetOptions();
setTimeout(window.close, 1000);
//closeEnstylerConfig();
},
'close': function() { closeEnstylerConfig();},
},*/
'frame': enGMFrame // Element used for the panel
}
);
if (DEBUGXX) enDebugLog('On Userstyle ...');
// fallback in case next test is failing
setTimeout(enUserstyleDo, 5000);
// test if already loaded
if (document.readyState === "complete" || document.readyState !== "loading"){
enUserstyleDo();
} else {
document.addEventListener('DOMContentLoaded', enUserstyleDo);
}
return;
}
// RUN Enstyler MAIN
window.eval("window['ga-disable-UA-2467049-1'] = true;");
WaitForBody();
// =========== Support functions for LATER use not needed for production !!!
/* Perfomance test code
var t0 = performance.now();
//Code to test here ...
var t1 = performance.now();
enDebugLog("Call XXXX took " + (t1 - t0) + " milliseconds.")
*/
/*
// 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;
}
function toHex(string) {
var hex, i;
var result = "";
for (i=0; i<string.length; i++) {
hex = string.charCodeAt(i).toString(16);
result += ("000"+hex).slice(-4);
}
return result
}
function fromHex(hex) {
var j;
var hexes = hex.match(/.{1,4}/g) || [];
var back = "";
for(j = 0; j<hexes.length; j++) {
back += String.fromCharCode(parseInt(hexes[j], 16));
}
return back;
}
*/
/* Options for uglifyJS3 https://skalman.github.io/UglifyJS-online/
// Documentation of the options is available at https://github.com/mishoo/UglifyJS2
{
parse: {
bare_returns : true,
ecma : 8,
expression : false,
filename : null,
html5_comments : true,
shebang : true,
strict : false,
toplevel : null
},
compress: {
arrows : true,
booleans : true,
collapse_vars : true,
comparisons : true,
computed_props : true,
conditionals : true,
dead_code : true,
drop_console : false,
drop_debugger : true,
ecma : 5,
evaluate : true,
expression : false,
global_defs : {DEBUG:false, DEBUGX:false, DEBUGXX:false, DEBUGXXX:false, DEBUGINT:false},
hoist_funs : true,
hoist_props : true,
hoist_vars : false,
ie8 : false,
if_return : true,
inline : true,
join_vars : true,
keep_classnames : false,
keep_fargs : false,
keep_fnames : false,
keep_infinity : false,
loops : true,
negate_iife : true,
passes : 2,
properties : true,
pure_getters : "strict",
pure_funcs : null,
reduce_funcs : true,
reduce_vars : true,
sequences : true,
side_effects : true,
switches : true,
top_retain : null,
toplevel : false,
typeofs : true,
unsafe : true,
unsafe_arrows : false,
unsafe_comps : true,
unsafe_Function : true,
unsafe_math : true,
unsafe_methods : false,
unsafe_proto : false,
unsafe_regexp : true,
unsafe_undefined : false,
unused : true,
warnings : false
},
mangle: {
eval : false,
ie8 : false,
keep_classnames : false,
keep_fnames : false,
properties : false,
reserved : [],
safari10 : false,
toplevel : false
},
output: {
ascii_only : false,
beautify : true,
bracketize : false,
comments : /@license|@preserve|^!|@|UserScript==/,
ecma : 5,
ie8 : false,
indent_level : 2,
indent_start : 0,
inline_script : true,
keep_quoted_props: false,
max_line_len : 1600,
preamble : null,
preserve_line : false,
quote_keys : false,
quote_style : 0,
safari10 : false,
semicolons : true,
shebang : true,
source_map : null,
webkit : false,
width : 200,
wrap_iife : false
},
wrap: false
}
*/