const log = (...msg) => console.log('[%cUserJS%c] %cDBG', 'color: rgb(29, 155, 240);', '', 'color: rgb(255, 212, 0);', ...msg),
info = (...msg) => console.info('[%cUserJS%c] %cINF', 'color: rgb(29, 155, 240);', '', 'color: rgb(0, 186, 124);', ...msg),
err = (...msg) => console.error('[%cUserJS%c] %cERROR', 'color: rgb(29, 155, 240);', '', 'color: rgb(249, 24, 128);', ...msg),
isNull = obj => Object.is(obj,null) || Object.is(obj,undefined),
isEmpty = obj => typeof obj === 'string' && Object.is(obj.trim(),'') || Object.is(Object.keys(obj).length,0),
estr = str => isNull(str) || isEmpty(str);
info('Loading...');
let langs = {
en: {
daily: 'Daily Installs',
close: 'No longer show',
search: 'Search',
searcher: 'Title | Description | Author...',
install: 'Install',
issue: 'New Issue',
version: 'Version',
updated: 'Last Updated',
legacy: 'Legacy',
total: 'Total Installs',
rating: 'Ratings',
good: 'Good',
ok: 'Ok',
bad: 'Bad',
created: 'Created',
redirect: 'Greasy Fork for adults',
filter: 'Filter out other languages',
dtime: 'Display Timeout',
save: 'Save',
},
es: {
daily: 'Instalaciones diarias',
close: 'Ya no se muestra',
search: 'Busque en',
searcher: 'Título | Descripción | Autor...',
install: 'Instalar',
issue: 'Nueva edición',
version: 'Versión',
updated: 'Última actualización',
legacy: 'Legado',
total: 'Total de instalaciones',
rating: 'Clasificaciones',
good: 'Bueno',
ok: 'Ok',
bad: 'Malo',
created: 'Creado',
redirect: 'Greasy Fork para adultos',
filter: 'Filtrar otros idiomas',
dtime: 'Mostrar el tiempo de espera',
save: 'Guardar',
},
ru: {
daily: 'Ежедневные установки',
close: 'Больше не показывать',
search: 'Поиск',
searcher: 'Название | Описание | Автор...',
install: 'Установите',
issue: 'Новый выпуск',
version: 'Версия',
updated: 'Последнее обновление',
legacy: 'Наследие',
total: 'Всего установок',
rating: 'Рейтинги',
good: 'Хорошо',
ok: 'Хорошо',
bad: 'Плохо',
created: 'Создано',
redirect: 'Greasy Fork для взрослых',
filter: 'Отфильтровать другие языки',
dtime: 'Тайм-аут отображения',
save: 'Сохранить',
},
ja: {
daily: 'デイリーインストール',
close: '表示されなくなりました',
search: '検索',
searcher: 'タイトル|説明|著者...',
install: 'インストール',
issue: '新刊のご案内',
version: 'バージョン',
updated: '最終更新日',
legacy: 'レガシー',
total: '総インストール数',
rating: 'レーティング',
good: 'グッド',
ok: '良い',
bad: '悪い',
created: '作成',
redirect: '大人のGreasyfork',
filter: '他の言語をフィルタリングする',
dtime: '表示タイムアウト',
save: '拯救',
},
fr: {
daily: 'Installations quotidiennes',
close: 'Ne plus montrer',
search: 'Recherche',
searcher: 'Titre | Description | Auteur...',
install: 'Installer',
issue: 'Nouveau numéro',
version: 'Version',
updated: 'Dernière mise à jour',
legacy: 'Héritage',
total: 'Total des installations',
rating: 'Notations',
good: 'Bon',
ok: 'Ok',
bad: 'Mauvais',
created: 'Créé',
redirect: 'Greasy Fork pour les adultes',
filter: 'Filtrer les autres langues',
dtime: `Délai d'affichage`,
save: 'Sauvez',
},
zh: {
daily: '日常安装',
close: '不再显示',
search: '搜索',
searcher: '标题|描述|作者...',
install: '安装',
issue: '新问题',
version: '版本',
updated: '最后更新',
legacy: '遗产',
total: '总安装量',
rating: '评级',
good: '好的',
ok: '好的',
bad: '不好',
created: '创建',
redirect: '大人的Greasyfork',
filter: '过滤掉其他语言',
dtime: '显示超时',
save: '拯救',
},
},
alang = [],
clang = navigator.language.split('-')[0] ?? 'en',
defcfg = {
cache: true,
autoexpand: false,
lang: langs[clang],
filterlang: false,
sleazyredirct: false,
time: 10000,
blacklist: [
{
enabled: true,
regex: true,
flags: '',
name: 'Blacklist 1',
url: '(gov|cart|checkout|login|join|signin|signup|sign-up|password|reset|password_reset)',
},
{
enabled: true,
regex: true,
flags: '',
name: 'Blacklist 2',
url: '(pay|bank|money|localhost|authorize|checkout|bill|wallet|router)',
},
{
enabled: true,
regex: false,
flags: '',
name: 'Blacklist 3',
url: 'https://home.bluesnap.com',
},
{
enabled: true,
regex: false,
flags: '',
name: 'Blacklist 4',
url: [
'zalo.me',
'skrill.com'
],
},
],
engines: [
{
enabled: true,
name: 'greasyfork',
url: 'https://greasyfork.org',
},
{
enabled: true,
name: 'sleazyfork',
url: 'https://sleazyfork.org',
},
{
enabled: false,
name: 'openuserjs',
url: 'https://openuserjs.org/?q=',
},
{
enabled: false,
name: 'github',
url: 'https://github.com/search?l=JavaScript&o=desc&q="==UserScript=="+',
},
{
enabled: false,
name: 'gist',
url: 'https://gist.github.com/search?l=JavaScript&o=desc&q="==UserScript=="+',
},
]
},
cfg = {},
urls = [],
sitegfcount = 0,
sitesfcount = 0,
checkGMSupport = typeof GM !== 'undefined',
MU = {
getValue(key,v = {}) {
v = JSON.stringify(v ?? {});
if(checkGMSupport) {
return Promise.resolve(
JSON.parse( GM_getValue(key,v) )
);
};
try {
if(key.includes('Config')) {
return Promise.resolve(
JSON.parse( self.localStorage.getItem(`MUJS${key}`) )
);
};
return Promise.resolve(
JSON.parse( self.localStorage.getItem(key) )
);
} catch(ex) {
err(ex)
};
return Promise.resolve(null);
},
info: {
script: {
version: 'Bookmarklet'
}
},
openInTab(url,params = {}) {
if(!params) {
params = {
active: true,
insert: true,
};
};
if(checkGMSupport) {
return GM_openInTab(url, params);
} else {
return window.open(url,'_blank');
};
// let dwnbtn = self.document.createElement('a');
// dwnbtn.className = 'magicuserjs-dwnbtn';
// dwnbtn.href = url;
// dwnbtn.target = '_blank';
// dwnbtn.rel = 'noopener';
// dwnbtn.dispatchEvent(new MouseEvent('click'));
// return dwnbtn.remove();
},
setValue(key,v) {
return new Promise((resolve) => {
v = typeof v !== 'string' ? JSON.stringify(v ?? {}) : v;
if(checkGMSupport) {
if(!cfg.cache) {
if(key.includes('Config')) {
resolve(self.localStorage.setItem(`MUJS${key}`,v));
};
resolve(self.localStorage.setItem(key,v));
};
resolve(GM_setValue(key,v));
};
if(key.includes('Config')) {
resolve(self.localStorage.setItem(`MUJS${key}`,v));
};
resolve(self.localStorage.setItem(key,v));
});
},
xmlhttpRequest: false,
};
if(checkGMSupport) {
Object.assign(MU, {
info: GM_info,
xmlhttpRequest: GM_xmlhttpRequest,
});
};
class Timeout {
constructor() {
this.ids = [];
}
set = (delay, reason) => {
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
isNull(reason) ? resolve() : reject(reason);
this.clear(id);
}, delay);
this.ids.push(id);
});
};
clear = (...ids) => {
this.ids = this.ids.filter(id => {
if (ids.includes(id)) {
clearTimeout(id);
return false;
};
return true;
});
};
};
function main() {
let unsaved = false;
const win = self ?? window,
doc = win.document,
save = () => {
try {
MU.setValue('Config',cfg);
unsaved = false;
log('Saved:',cfg);
} catch(e) {err(e)};
},
qs = (element, root) => {
root = root ?? doc ?? doc.body;
return root.querySelector(element);
},
qsA = (element, root) => {
root = root ?? doc ?? doc.body;
return root.querySelectorAll(element);
},
/** Wait until element exists */
query = async (element, root) => {
root = root ?? doc ?? doc.body;
while(isNull(root.querySelector(element))) {
await new Promise(resolve => requestAnimationFrame(resolve))
};
return root.querySelector(element);
},
/** Create various elements */
make = (element,cname,attrs = {}) => {
let el;
try {
el = doc.createElement(element);
if(!estr(cname)) {
el.className = cname;
};
if(attrs) {
for(let key in attrs) el[key] = attrs[key];
};
return el;
} catch(ex) {
err(ex)
}
};
let container = make('magic-userjs','mujs-primary'),
ifram = make('iframe','mujs-iframe', {
src: 'about:blank',
onload: () => {
try {
ifram.contentDocument.body.append(container);
container.attachShadow({ mode: 'open' });
} catch(ex) {
err(ex);
ifram.contentDocument.body.setAttribute('style','background-color: black;color: white;');
ifram.contentDocument.body.innerHTML = 'Error occured while injecting Container [ yeah still need to work on that :) ]';
delay(5000).then(() => ifram.remove());
};
},
}),
injContainer = () => {
info('Injecting Container...');
if(isNull(container.attachShadow)) {
let ifcss = make('style', 'frame-stylesheet', {
innerHTML: `iframe.mujs-iframe {
position: fixed;
bottom: 1rem;
right: 1rem;
height: 525px;
width: 90%;
margin-left: 1rem;
margin-right: 1rem;
z-index: 100000000000000020 !important;
}`,
});
doc.body.append(ifcss,ifram);
} else {
container.attachShadow({ mode: 'open' });
doc.body.append(container);
};
};
injContainer();
const timeout = new Timeout(),
sh = element => container.shadowRoot.querySelector(element),
shA = element => container.shadowRoot.querySelectorAll(element),
delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
// clk = e => e.dispatchEvent(new MouseEvent('click')),
isMobile = () => {
let a = navigator.userAgent || win.opera;
return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4));
},
halt = (e) => {
e.preventDefault();
e.stopPropagation();
},
ael = (elm, event, callback) => {
try {
elm = elm ?? doc;
if(isMobile()) {
if(event === 'click') {
event = 'mouseup';
// elm.addEventListener('mouseup', callback);
elm.addEventListener('touchstart', callback);
elm.addEventListener('touchend', callback);
};
};
if(event === 'fclick') {event = 'click'};
return elm.addEventListener(event, callback);
} catch(error) {
err(error);
};
},
formatBytes = (bytes, decimals = 2) => {
if (!+bytes) return '0 Bytes';
const k = 1024,
dm = decimals < 0 ? 0 : decimals,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
},
pbar = (e) => {
if(Object.is(e.total,0)) {
return info(`Progress: ${formatBytes(e.loaded)}`);
};
return info(`Progress: ${e.loaded / e.total}%`);
},
showError = (msg) => {
err(msg);
let txt = make('mujs-row','error', {
innerHTML: msg
});
for(let u of urls) {
let dwnbtn = make('a','magicuserjs-urls', {
href: u,
target: '_blank',
rel: 'noopener',
innerHTML: u
});
txt.append(dwnbtn);
};
if(sh('.magicuserjs-body')) {
sh('.magicuserjs-body').prepend(txt);
};
},
fetchURL = (url,method = 'GET',responseType = 'json',params = {}, forcefetch) => {
return new Promise((resolve, reject) => {
if(checkGMSupport && !forcefetch) {
try {
MU.xmlhttpRequest({
method: method,
url,
responseType,
onprogress: pbar,
onerror: reject,
...params,
onload: (r) => {
if(r.status !== 200) reject(`${r.status} ${url}`);
resolve(r.response);
},
});
} catch (error) {
info(url,method,responseType,params);
showError(error);
}
} else {
fetch(url, {
method: method,
...params,
}).then((response) => {
if(!response.ok) reject(response);
if(responseType.match(/json/gi)) {
resolve(response.json());
} else if(responseType.match(/text/gi)) {
resolve(response.text());
} else if(responseType.match(/blob/gi)) {
resolve(response.blob());
};
resolve(response);
}).catch((error) => {
info(url,method,responseType,params);
showError(error);
});
};
});
},
createjs = (ujs, issleazy) => {
let frame = make('magic-userjs',`frame ${issleazy ? 'sf' : ''}`),
fname = make('magic-userjs','magicuserjs-name'),
ftitle = make('magicuserjs-a','magicuserjs-homepage', {
title: ujs.name,
innerHTML: ujs.name,
onclick: (e) => {
halt(e);
MU.openInTab(ujs.url);
}
}),
fver = make('magic-userjs','magicuserjs-list', {
innerHTML: `${cfg.lang.version}: ${ujs.version}`,
}),
fcreated = make('magic-userjs','magicuserjs-list', {
innerHTML: `${cfg.lang.created}: ${new Intl.DateTimeFormat(navigator.language).format(new Date(ujs.created_at))}`,
}),
fmore = make('mujs-column','magicuserjs-list hidden', {
style: 'margin-top: 3px;',
}),
ftotal = make('magic-userjs','magicuserjs-list', {
innerHTML: `${cfg.lang.total}: ${ujs.total_installs}`,
}),
fgood = make('magic-userjs','magicuserjs-list', {
title: cfg.lang.good,
innerHTML: `${cfg.lang.rating}: ${ujs.good_ratings}`,
}),
fok = make('magic-userjs','magicuserjs-list', {
title: cfg.lang.ok,
innerHTML: ujs.ok_ratings,
}),
fbad = make('magic-userjs','magicuserjs-list', {
title: cfg.lang.bad,
innerHTML: ujs.bad_ratings,
}),
fdesc = make('magic-userjs','magicuserjs-list', {
style: 'cursor: pointer; margin-top: 3px;',
title: ujs.description,
innerHTML: ujs.description,
onclick: (e) => {
halt(e);
if(fmore.classList.contains('hidden')) {
fmore.classList.remove('hidden');
} else {
fmore.classList.add('hidden');
}
},
}),
eframe = make('magic-userjs','magicuserjs-eframe'),
uframe = make('magic-userjs','magicuserjs-uframe'),
fdaily = make('magic-userjs','magicuserjs-list', {
title: cfg.lang.daily,
innerHTML: ujs.daily_installs,
}),
fupdated = make('magic-userjs','magicuserjs-list', {
title: cfg.lang.updated,
innerHTML: new Intl.DateTimeFormat(navigator.language).format(new Date(ujs.code_updated_at)),
}),
fdwn = make('magicuserjs-btn','install', {
title: `${cfg.lang.install} '${ujs.name}'`,
innerHTML: cfg.lang.install,
onclick: (e) => {
halt(e);
MU.openInTab(ujs.code_url);
},
});
for(let u of ujs.users) {
let user = make('magicuserjs-a','magicuserjs-euser', {
innerHTML: u.name,
onclick: (e) => {
halt(e);
MU.openInTab(u.url);
},
});
uframe.append(user);
};
eframe.append(uframe,fdaily,fupdated,fdwn);
fmore.append(ftotal,fgood,fok,fbad,fver,fcreated);
fname.append(ftitle,fdesc,fmore);
frame.append(fname,eframe);
sh('.magicuserjs-body').append(frame);
},
countsite = () => {
try {
if(/greasyfork\.org/.test(doc.location.hostname) && cfg.sleazyredirct) {
let otherSite = /greasyfork\.org/.test(document.location.hostname) ? 'sleazyfork' : 'greasyfork';
qs('span.sign-in-link') ? /scripts\/\d+/.test(document.location.href) ? !qs('#script-info') && (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) ? location.href = location.href.replace(/\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, '//$1' + otherSite + '.org') : false : false : false;
};
if(!container.attachShadow) return false;
let host = location.hostname.split('.').splice(-2).join('.'),
rebuild = false,
siteujs = [],
main = make('magic-userjs','main hidden'),
usercss = make('style', 'primary-stylesheet', {innerHTML: main_css,}),
tbody = make('magic-userjs','magicuserjs-body'),
header = make('magic-userjs','magicuserjs-header'),
cfgpage = make('mujs-row','magicuserjs-cfg hidden'),
makerow = (desc,type,nm,attrs = {}) => {
let sec = make('mujs-section','', {
style: nm === 'cache' ? 'display: none;' : ''
}),
lb = make('label'),
divDesc = make('magic-userjs','', {
innerHTML: desc,
}),
inp = make('input','', {
type: type,
id: nm,
name: nm,
...attrs
});
if(type === 'checkbox') {
let inlab = make('magic-userjs','magicuserjs-inlab'),
la = make('label','', {
onclick: () => inp.click()
});
inlab.append(inp,la);
lb.append(divDesc,inlab);
if(nm.match(/((greasy|sleazy)fork|openuserjs|gi(thub|st))/gi)) {
for(let i of cfg.engines) {
if(i.name === nm) {
inp.checked = i.enabled;
ael(inp,'change', (e) => {
unsaved = true;
i.enabled = e.target.checked;
rebuild = true;
});
};
};
} else {
inp.checked = cfg[nm];
if(nm.match(/(autoexpand|sleazyredirct)/gi)) {
ael(inp,'change', (e) => {
unsaved = true;
cfg[nm] = e.target.checked;
});
} else {
ael(inp,'change', (e) => {
unsaved = true;
cfg[nm] = e.target.checked;
rebuild = true;
});
};
};
} else {
lb.append(divDesc,inp);
};
sec.append(lb);
cfgpage.append(sec);
return inp;
},
countframe = make('mujs-column'),
gfcountframe = make('magic-userjs', 'counterframe', {
style: 'background: #00b7ff;'
}),
sfcountframe = make('magic-userjs', 'counterframe', {
style: 'background: #ed3f14;'
}),
gfcounter = make('count-frame','count', {
title: 'https://greasyfork.org + https://sleazyfork.org',
}),
sfcounter = make('count-frame','count', {
title: 'https://openuserjs.org',
}),
buildlist = async () => {
const template = {
bad_ratings: 0,
good_ratings: 0,
ok_ratings: 0,
daily_installs: 0,
total_installs: 0,
name: 'Not found',
description: 'Not found',
version: '0.0.0',
url: 'about:blank',
code_url: 'about:blank',
created_at: Date.now(),
code_updated_at: Date.now(),
users: [{
name: '',
url: '',
}]
};
let sites = [],
custom = [];
for(let i of cfg.engines) {
if(i.enabled) {
if(i.url.match(/fork.org/gi)) {
if(cfg.filterlang) {
if(alang.length > 1) {
for(let a of alang) {
// log(`${i.url}/${a}/scripts/by-site/${host}.json`);
urls.push(`${i.url}/${a}/scripts/by-site/${host}.json`);
sites.push(fetchURL(`${i.url}/${a}/scripts/by-site/${host}.json`),);
};
} else {
urls.push(`${i.url}/${clang}/scripts/by-site/${host}.json`);
sites.push(fetchURL(`${i.url}/${clang}/scripts/by-site/${host}.json`),);
};
} else {
urls.push(`${i.url}/scripts/by-site/${host}.json`);
sites.push(fetchURL(`${i.url}/scripts/by-site/${host}.json`),);
}
};
if(i.url.match(/(openuserjs.org|github.com)/gi)) {
urls.push(`${i.url}${host}`);
custom.push(fetchURL(`${i.url}${host}`,'GET','text'),);
};
};
};
let data = await Promise.all(sites).catch(err);
if(data) {
for(let d of data) {
for(let ujs of d) {
if(ujs.deleted) continue;
if(cfg.filterlang) {
if(alang.length > 1) {
for(let a of alang) {
if(!ujs.locale.includes(a)) continue;
};
} else if(!ujs.locale.includes(clang)) continue;
};
siteujs.push(
{
url: ujs,
sleazy: false,
},
);
sitegfcount++;
};
};
for(let ujs of siteujs) {
createjs(ujs.url,ujs.sleazy);
};
} else {
showError('Error occured while loading UserJS for this webpage')
};
gfcounter.innerHTML = sitegfcount;
mainbtn.innerHTML = sitesfcount + sitegfcount;
if(custom.length > 0) {
let c = await Promise.all(custom).catch(err),
parser = new DOMParser(),
htmlDocument = parser.parseFromString(c,'text/html'),
selected = htmlDocument.documentElement;
if(qs('.col-sm-8 .tr-link',selected)) {
for(let i of qsA('.col-sm-8 .tr-link',selected)) {
await query('.script-version',i);
let fixurl = qs('.tr-link-a',i).href.replaceAll(doc.location.origin,'https://openuserjs.org'),
layout = {
name: qs('.tr-link-a',i).textContent,
description: qs('p',i).textContent,
version: qs('.script-version',i).textContent,
url: fixurl,
code_url: `${fixurl.replaceAll('/scripts','/install')}.user.js`,
total_installs: qs('td:nth-child(2) p',i).textContent,
created_at: qs('td:nth-child(4) time',i).getAttribute('datetime'),
code_updated_at: qs('td:nth-child(4) time',i).getAttribute('datetime'),
users: [{
name: qs('.inline-block a',i).textContent,
url: qs('.inline-block a',i).href,
}]
};
for(const key in template) {
if(!Object.hasOwn(layout, key)) {
layout[key] = template[key];
};
};
createjs(layout, true);
sitesfcount++;
sfcounter.innerHTML = sitesfcount;
};
};
if(qs('.repo-list-item',selected)) {
for(let r of qsA('.repo-list-item',selected)) {
let layout = {},
fixurl = qs('a',r).href.replaceAll(doc.location.origin,'https://github.com');
layout = Object.assign(layout, {
name: qs('a',r).textContent,
description: qs('p.mb-1',r).textContent.trim(),
url: fixurl,
code_url: fixurl,
code_updated_at: qs('relative-time.no-wrap',r).getAttribute('datetime'),
total_installs: qs('a.Link--muted:nth-child(1)',r) ? qs('a.Link--muted:nth-child(1)',r).textContent : 0,
users: [{
name: qs('a',r).href.match(/\/[\w\d-]+\//gi)[0].replaceAll('/',''),
url: `https://github.com${qs('a',r).href.match(/\/[\w\d-]+\//gi)}`,
}]
});
for (const key in template) {
if(!Object.hasOwn(layout, key)) {
layout[key] = template[key];
};
};
createjs(layout, true);
sitesfcount++;
sfcounter.innerHTML = sitesfcount;
};
};
if(qs('div.gist-snippet',selected)) {
for(let g of qsA('div.gist-snippet',selected)) {
if(qs('span > a:nth-child(2)',g).textContent.includes('.user.js')) {
let layout = {},
fixurl = qs('span > a:nth-child(2)',g).href.replaceAll(doc.location.origin,'https://gist.github.com');
layout = Object.assign(layout, {
url: fixurl,
code_url: `${fixurl}/raw/${qs('span > a:nth-child(2)',g).textContent}`,
created_at: qs('time-ago.no-wrap',g).getAttribute('datetime'),
users: [{
name: qs('span > a[data-hovercard-type]',g).textContent,
url: qs('span > a[data-hovercard-type]',g).href.replaceAll(doc.location.origin,'https://gist.github.com'),
}]
});
for(let i of qsA('.file-box table tr .blob-code',g)) {
let txt = i.textContent,
headers = txt.match(/\/\/\s@[\w][\s\S]+/gi) || [];
if(headers.length > 0) {
let crop = headers[0].split(/\/\/\s@(name|description|author|version)\s+/gi);
if(headers[0].includes('@name') && !headers[0].includes('@namespace')) {
layout = Object.assign(layout, {
name: crop[2].trim(),
});
};
if(headers[0].includes('@description')) {
layout = Object.assign(layout, {
description: crop[2].trim(),
});
};
if(headers[0].includes('@version')) {
layout = Object.assign(layout, {
version: crop[2].trim(),
});
};
}
};
for (const key in template) {
if(!Object.hasOwn(layout, key)) {
layout[key] = template[key];
};
};
createjs(layout, true);
sitesfcount++;
sfcounter.innerHTML = sitesfcount;
};
};
};
sfcounter.innerHTML = sitesfcount;
mainbtn.innerHTML = sitesfcount + sitegfcount;
};
if(typeof cfg.time === 'number' && !isNaN(cfg.time)) {
timeout.clear(...timeout.ids);
await timeout.set(cfg.time);
if(!mainframe.classList.contains('hidden')) {
mainframe.classList.add('hidden');
ifram.setAttribute('style','display:none;');
};
timeout.clear(...timeout.ids);
};
if(data) {
if(Object.is(data.length,0) && Object.is(custom.length,0)) showError('No available UserJS for this webpage');
};
},
preBuild = () => {
const bhref = win.top.document.location.href;
let blacklisted = false;
siteujs = [];
urls = [];
sitegfcount = 0;
sitesfcount = 0;
tbody.innerHTML = '';
gfcounter.innerHTML = sitegfcount;
sfcounter.innerHTML = sitesfcount;
mainbtn.innerHTML = sitegfcount;
for(let b of cfg.blacklist) {
if(!b.enabled) continue;
if(b.regex) {
let reg = new RegExp(b.url,b.flags),
testurl = reg.test(bhref);
if(!testurl) continue;
blacklisted = true;
};
if(!Array.isArray(b.url)) {
if(!bhref.includes(b.url)) continue;
blacklisted = true;
};
for(let c of b.url) {
if(!bhref.includes(c)) continue;
blacklisted = true;
};
};
if(blacklisted) {
urls.push(bhref);
return showError('Blacklisted');
};
return buildlist();
},
makecfg = () => {
makerow('Sync with GM','checkbox','cache');
makerow('Auto Fullscreen','checkbox','autoexpand', {
onchange: (e) => {
if(e.target.checked) {
btnfullscreen.classList.add('expanded');
main.classList.add('expanded');
btnfullscreen.innerHTML = fcclose;
} else {
btnfullscreen.classList.remove('expanded');
main.classList.remove('expanded');
btnfullscreen.innerHTML = fcopen;
};
},
});
makerow(cfg.lang.redirect,'checkbox','sleazyredirect');
makerow(cfg.lang.filter,'checkbox','filter');
makerow('Greasy Fork','checkbox','greasyfork');
makerow('Sleazy Fork','checkbox','sleazyfork');
makerow('Open UserJS','checkbox','openuserjs');
makerow('GitHub','checkbox','github');
makerow('Gist (GitHub)','checkbox','gist');
let rtime = makerow(`${cfg.lang.dtime} (ms)`,'number','time', {
defaultValue: 10000,
value: cfg.time,
min: 0,
step: 500,
onbeforeinput: (e) => {
if(e.target.validity.badInput) {
e.target.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;');
} else {
e.target.setAttribute('style','');
}
},
oninput: (e) => {
unsaved = true;
let t = e.target;
if(t.validity.badInput || t.validity.rangeUnderflow && t.value !== '-1') {
t.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;');
} else {
t.setAttribute('style','');
cfg.time = estr(t.value) ? cfg.time : parseFloat(t.value);
}
}
});
let isvalid = true,
txta = make('textarea','tarea', {
name: 'blacklist',
id: 'blacklist',
rows: '10',
autocomplete: false,
spellcheck: false,
wrap: 'soft',
value: JSON.stringify(cfg.blacklist, null, ' '),
oninput: (e) => {
try {
cfg.blacklist = JSON.parse(e.target.value);
if(!isvalid) {
isvalid = true;
e.target.setAttribute('style','');
};
} catch(error) {
isvalid = false;
err(error);
};
},
}),
cbtn = make('magic-userjs', 'b', {
style: 'display: flex'
}),
savebtn = make('mujs-btn', 'save', {
style: 'margin: auto;',
innerHTML: cfg.lang.save,
onclick: (e) => {
halt(e);
if(rtime.validity.badInput || rtime.validity.rangeUnderflow && rtime.value !== '-1') {
return rtime.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;');
};
if(!isvalid) {
return txta.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;');
};
save();
if(rebuild) {
preBuild();
rebuild = false;
};
if(/greasyfork\.org/.test(doc.location.hostname) && cfg.sleazyredirct) {
let otherSite = /greasyfork\.org/.test(document.location.hostname) ? 'sleazyfork' : 'greasyfork';
qs('span.sign-in-link') ? /scripts\/\d+/.test(document.location.href) ? !qs('#script-info') && (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) ? location.href = location.href.replace(/\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, '//$1' + otherSite + '.org') : false : false : false;
};
},
}),
resetbtn = make('mujs-btn', 'reset', {
style: 'margin: auto;',
innerHTML: 'Reset',
onclick: async (e) => {
halt(e);
unsaved = true;
cfg = defcfg;
txta.value = JSON.stringify(cfg.blacklist, null, ' ');
for(let i of cfg.engines) {
if(sh(`#${i.name}`)) {
sh(`#${i.name}`).checked = i.enabled;
};
};
for(let i of shA('.magicuserjs-inlab input[type="checkbox"]')) {
if(!i.name.match(/((greasy|sleazy)fork|openuserjs|gi(thub|st))/gi)) {
i.checked = cfg[i.name];
};
};
},
});
cbtn.append(savebtn,resetbtn);
cfgpage.append(txta,cbtn);
},
fcopen = ``,
fcclose = ``,
btnfullscreen = make('mujs-btn','fullscreen', {
title: 'Fullscreen',
innerHTML: ``,
onclick: (e) => {
halt(e);
if(btnfullscreen.classList.contains('expanded')) {
btnfullscreen.classList.remove('expanded');
main.classList.remove('expanded');
btnfullscreen.innerHTML = fcopen;
} else {
btnfullscreen.classList.add('expanded');
main.classList.add('expanded');
btnfullscreen.innerHTML = fcclose;
};
}
}),
mainframe = make('magic-userjs','mainframe', {
onclick: (e) => {
e.preventDefault();
main.classList.remove('hidden');
mainframe.classList.add('hidden');
if(cfg.autoexpand) {
btnfullscreen.classList.add('expanded');
main.classList.add('expanded');
btnfullscreen.innerHTML = fcclose;
};
}
}),
mainbtn = make('count-frame','mainbtn', {
innerHTML: '0',
}),
fsearch = make('mujs-btn','hidden'),
searcher = make('input','searcher', {
style: 'width: 170px;',
autocomplete: 'off',
spellcheck: false,
type: 'text',
placeholder: cfg.lang.searcher,
oninput: (e) => {
e.preventDefault();
let v = e.target.value;
if(!estr(v)) {
let reg = new RegExp(v,'gi');
for(let ujs of shA('.frame')) {
let m = ujs.children[0],
n = ujs.children[1],
final = m.textContent.match(reg) || n.textContent.match(reg) || [];
if(final.length === 0) {
ujs.classList.add('hidden');
} else {
ujs.classList.remove('hidden');
};
};
} else {
for(let ujs of shA('.frame')) {
ujs.classList.remove('hidden')
};
};
},
}),
searchbtn = make('mujs-btn','search', {
title: cfg.lang.search,
innerHTML: ``,
onclick: (e) => {
e.preventDefault();
fsearch.classList.toggle('hidden');
}
}),
closebtn = make('mujs-btn','close', {
title: cfg.lang.close,
innerHTML: ``,
onclick: async (e) => {
halt(e);
main.classList.add('hidden');
mainframe.classList.remove('hidden');
if(typeof cfg.time === 'number' && !isNaN(cfg.time)) {
timeout.clear(...timeout.ids);
await timeout.set(cfg.time);
mainframe.classList.add('hidden');
ifram.setAttribute('style','display:none;');
return timeout.clear(...timeout.ids);
};
}
}),
btnframe = make('mujs-column', 'btnframe'),
btncfg = make('mujs-btn','settings', {
title: 'Settings',
innerHTML: ``,
onclick: (e) => {
e.preventDefault();
if(sh('.saveerror')) {
sh('.saveerror').remove();
};
if(unsaved) {
let txt = make('mujs-row','saveerror', {
innerHTML: 'Unsaved changes'
});
tbody.prepend(txt);
delay(10000).then(() => txt.remove());
};
if(cfgpage.classList.contains('hidden')) {
cfgpage.classList.remove('hidden');
tbody.classList.add('hidden');
if(!main.classList.contains('expanded')) {
main.setAttribute('style','height: auto;');
};
if(ifram) {
ifram.setAttribute('style','height: 100%;');
};
} else {
cfgpage.classList.add('hidden');
tbody.classList.remove('hidden');
main.setAttribute('style','');
if(ifram) {
ifram.setAttribute('style','');
};
};
rebuild = false;
},
}),
btnhome = make('mujs-btn','github hidden', {
title: `GitHub (v${MU.info.script.version.includes('.') || MU.info.script.version.includes('Book') ? MU.info.script.version : MU.info.script.version.slice(0,5)})`,
innerHTML: ``,
onclick: (e) => {
halt(e);
MU.openInTab('https://github.com/magicoflolis/Userscript-Plus');
}
}),
btnissue = make('mujs-btn','issue hidden', {
title: cfg.lang.issue,
innerHTML: ``,
onclick: (e) => {
e.preventDefault();
MU.openInTab('https://github.com/magicoflolis/Userscript-Plus/issues/new');
}
}),
btngreasy = make('mujs-btn','greasy hidden', {
title: 'Greasy Fork',
innerHTML: ``,
onclick: (e) => {
e.preventDefault();
MU.openInTab('https://greasyfork.org/scripts/421603');
}
}),
btnnav = make('mujs-btn','nav', {
title: 'Navigation',
innerHTML: ``,
onclick: (e) => {
halt(e);
if(btngreasy.classList.contains('hidden')) {
btnissue.classList.remove('hidden');
btnhome.classList.remove('hidden');
btngreasy.classList.remove('hidden');
} else {
btnissue.classList.add('hidden');
btnhome.classList.add('hidden');
btngreasy.classList.add('hidden');
};
}
});
gfcountframe.append(gfcounter);
sfcountframe.append(sfcounter);
countframe.append(gfcountframe,sfcountframe);
fsearch.append(searcher);
btnframe.append(fsearch,searchbtn,btncfg,btnissue,btnhome,btngreasy,btnnav,btnfullscreen,closebtn);
header.append(countframe,btnframe);
main.append(header,tbody,cfgpage);
mainframe.append(mainbtn);
container.shadowRoot.append(usercss,mainframe,main);
makecfg();
preBuild();
} catch(error) {err(error)}
};
if(!estr(navigator.languages)) {
for(let nlang of navigator.languages) {
let lg = nlang.split('-')[0];
if(alang.indexOf(lg) === -1) {
alang.push(lg);
};
};
};
if(doc.readyState === 'complete') {
countsite();
} else {
ael(win,'load',countsite);
};
};
async function setupConfig() {
try {
cfg = await MU.getValue('Config',defcfg).catch(err) ?? defcfg;
for (const key in defcfg) {
if(!Object.hasOwn(cfg, key)) {
cfg[key] = defcfg[key];
} else if (key === 'lang') {
for (const keyl in defcfg[key]) {
if(!Object.hasOwn(cfg[key], keyl)) {
cfg[key][keyl] = defcfg[key][keyl];
};
};
} else if (key === 'engines') {
for (const key2 in defcfg[key]) {
if(!Object.hasOwn(cfg[key], key2)) {
cfg[key][key2] = defcfg[key][key2];
};
};
} else if (key === 'blacklist') {
for (const key3 in defcfg[key]) {
if(!Object.hasOwn(cfg[key], key3)) {
cfg[key][key3] = defcfg[key][key3];
};
};
}
};
log('Config:',cfg);
} catch(ex) {
err(ex)
};
};
setupConfig().then(main);