Vanilla JS library
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/369430/756036/vanilla-lib.js
// ==UserScript==
// @name vanilla-lib
// @namespace http://dev.rsalazar.name/js/
// @version 1.2.191207.1426
// @description Vanilla JS library
// @author rsalazar
// @grant none
// ==/UserScript==
function VanillaLib( ) {
'use strict';
let self = { version:'1.2.191207.1426' },
undefined; // ensure an 'undefined' reference
// Logging related
self.logging = true;
self.log = function( ) { return ! self.logging ? false : console.debug.apply(console, arguments); };
self.logGroup = function( ) { return ! self.logging ? false : console.groupCollapsed.apply(console, arguments); };
self.logEnd = function( ) { return ! self.logging ? false : console.groupEnd.apply(console, arguments); };
self.time = function( ) { return ! self.logging ? false : console.time.apply(console, arguments); };
self.timeEnd = function( ) { return ! self.logging ? false : console.timeEnd.apply(console, arguments); };
self.warn = function( ) { return ! self.logging ? false : console.warn.apply(console, arguments); };
// Informative functionality
self.isobj = ( expr,type ) => ( 'object' === typeof expr && ( ! type || null !== expr && ( true === type ||
!! expr.constructor && type === ( self.isstr(type) ? expr.constructor.name : expr.constructor ) ) ) );
self.ownsIt = ( obj,prop ) => ( !! prop && self.isobj(obj, true) && obj.hasOwnProperty(prop) );
self.hasval = expr => ( null !== expr && ! self.ndef(expr) );
self.isbool = expr => ( 'boolean' === typeof expr );
self.ifnan = ( expr,value ) => ( isNaN(expr) ? value : expr );
self.ifndef = ( expr,value ) => ( self.ndef(expr) ? value : expr );
self.ispojo = expr => self.isobj(expr, Object);
self.isarr = expr => self.isobj(expr, Array);
self.isnum = expr => ( 'number' === typeof expr );
self.isstr = expr => ( 'string' === typeof expr );
self.isfn = expr => ( 'function' === typeof expr );
self.defn = expr => ( 'undefined' !== typeof expr );
self.ndef = expr => ( 'undefined' === typeof expr );
// Miscelaneous
self.mapFlat = ( array,func ) => array.map( x => func(x) ).reduce( (a,b) => a.concat(b) );
self.test = ( expr,func,other ) => ( !! expr ? func(expr) : self.isfn(other) ? other(expr) : other );
// DOM related
self.parenth = ( elem,nth ) => self.traverse(elem, self.ifndef(nth, 1), 0);
self.html = ( elem,val ) => self.test(elem, el => self.ndef(val) ? el.innerHTML : (el.innerHTML = val) );
self.text = ( elem,val ) => self.test(elem, el => self.ndef(val) ? el.innerText : (el.innerText = val) );
self.$$ = ( sel,elem ) => self.toArray((elem || document).querySelectorAll(sel));
self.$ = ( sel,elem ) => (elem || document).querySelector(sel);
// Number related
self.aggRate = ( amnt,rate,times ) => ( times < 1 ? amnt : self.aggRate(amnt * rate, rate, times - 1) );
self.between = ( value,from,to,open ) => ( from > to ? self.between(value, to, from, open)
: open ? from < value && value < to : from <= value && value <= to );
self.toDec = expr => ( Math.round(parseFloat((expr +'').replace(/\$|,/g, '')) * 100) / 100 );
// Date/Time related
self.secondsIn = ( from,to,other ) => self.ifnan((to - from) / 1000, self.ifndef(other, NaN));
self.minutesIn = ( from,to,other ) => self.ifnan((to - from) / 60000, self.ifndef(other, NaN));
self.hoursIn = ( from,to,other ) => self.ifnan((to - from) / 3600000, self.ifndef(other, NaN));
self.daysIn = ( from,to,other ) => self.ifnan((to - from) / 86400000, self.ifndef(other, NaN));
self.secondsSince = ( from,other ) => self.secondsIn(from, Date.now(), other);
self.minutesSince = ( from,other ) => self.minutesIn(from, Date.now(), other);
self.hoursSince = ( from,other ) => self.hoursIn(from, Date.now(), other);
self.daysSince = ( from,other ) => self.daysIn(from, Date.now(), other);
self.addClass = function( element, name ) {
if ( !! element && !! name ) {
if ( self.isarr(element) ) {
return element.map( elem => self.addClass(elem, name) );
}
// ToDo: Use .classList if available
name = ( self.isarr(name) ? name : name.split(',') );
name = name.map( nm => nm.trim() )
.filter( nm => ! element.classList.contains(nm) );
element.className = (element.className +' '+ name.join(' ')).trim();
return true;
}
return false;
};
self.appendTo = function( element, parent, reference ) {
if ( self.isarr(element) ) {
return element.map( elem => self.appendTo(elem, parent, reference) );
}
if ( !! reference ) {
parent = reference.parentNode;
reference = reference.nextSibling;
}
if ( !! reference ) {
return self.prependTo(element, parent, reference);
} else if ( !! parent ) {
parent.append(element);
} else {
self.warn('*** appendTo() could not add element: No parent or reference element provided');
}
return element;
};
self.attr = function( element, name, value ) {
if ( !! element ) {
if ( self.isarr(element) ) {
return element.map( elem => self.attr(elem, name, value) );
}
return self.keysAndValues(name, value, ( n,v ) => {
return ( self.hasval(v) ? element.setAttribute(n, v)
: null === v ? element.removeAttribute(n) : element.getAttribute(n) );
} );
}
return element;
};
self.choose = function( index, values ) {
return self.toArray(arguments)[ index ];
}
self.create = function( html, containerType ) {
let container = null,
result = null,
attrs, style;
if ( self.isobj(containerType) ) {
attrs = containerType.attrs;
style = containerType.style;
containerType = containerType.container;
}
containerType = containerType || 'div';
create[ containerType ] =
container = self.create[ containerType ] || document.createElement(containerType);
container.innerHTML = html;
result = self.toArray(container.childNodes)
.map( elem => (elem.remove(), elem) );
if ( !! attrs ) {
self.attr(result, attrs);
}
if ( !! style ) {
self.css(result, style);
}
if ( 1 == result.length ) {
result = result[ 0 ];
}
return result;
};
// Create sandbox, optionally adding it to `cache` as (read-only) `property` (or `_sandbox`, if missing)
self.createSandbox = function( cache, property ) {
const frame = document.createElement('iframe');
frame.src = 'about:blank', frame.style.display = 'none';
document.body.appendChild(frame);
const context = frame.contentWindow;
!! cache && Object.defineProperty(cache, (property || '_sandbox'), { value:context, enumerable:false, writable:false, configurable:false });
document.body.removeChild(frame);
return context;
};
self.createXHR = function( ) {
let xhr = new XMLHttpRequest( );
xhr.onabort = function( ev, xhr ) { self.log('XHR Abort:', ev, xhr, this); };
xhr.onerror = function( ev, xhr ) { self.log('XHR Error:', ev, xhr, this); };
xhr.onload = function( ev, xhr ) { self.log('XHR Load:', ev, xhr, this); };
self.on(xhr, {
'abort': function( ev ) { self.isfn(this.onabort) && this.onabort(ev, this); },
'error': function( ev ) { self.isfn(this.onerror) && this.onerror(ev, this); },
'load': function( ev ) { self.isfn(this.onload) && this.onload(ev, this); },
});
return xhr;
};
self.css = function( element, key, value ) {
if ( isarr(element) ) {
return element.map( elem => self.css(elem, key, value) );
}
keysAndValues(key, value, ( k,v ) => element.style[ k ] = v );
return element;
};
self.extend = function( target, sources ) {
for ( let i = 1, n = arguments.length; i < n; i ++ ) {
self.isobj(arguments[ i ], true) && self.copyMembers(arguments[ i ], target);
}
return target;
};
self.extendProperties = function( target, source, overwrite, writable, enumerable, configurable ) {
if ( !! target && !! source ) {
overwrite = !! overwrite;
writable = !! writable;
enumerable = !! enumerable;
configurable = !! configurable;
if ( self.isarr(source) ) {
for ( let i = 0, n = source.length; i < n; i ++ ) {
self.extendProperties(target, source[ i ], overwrite, writable, enumerable, configurable);
}
} else if ( self.isobj(source, true) ) {
for ( let prop in source ) {
if ( overwrite || self.ndef(target[ prop ]) ) {
Object.defineProperty(target, prop,
self.propDef(source[ prop ], writable, enumerable, configurable));
}
}
}
}
return target;
};
self.fire = function( element, event, args ) {
if ( isarr(element) ) {
return element.map( elem => self.fire(elem, event, args) );
}
if ( self.isstr(event) ) {
args = self.ifndef(args, { 'bubbles':true, 'cancelable':true });
event = new Event( event, args );
}
return element.dispatchEvent(event);
};
self.keysAndValues = function( key, value, action ) {
if ( self.ndef(action) && self.isfn(value) ) {
action = value;
value = undefined;
}
// Case 1: key is an object (and there is no value)
if ( self.isobj(key) && ! value ) {
return Object.keys(key)
.map( k => action(k, key[ k ]) );
// Case 2: key is an array
} else if ( self.isarr(key) ) {
// Case 1.a: value is an array of the same length
if ( self.isarr(value) && key.length === value.length ) {
return key.map( ( k,i ) => action(k, value[ i ]) );
// Case 1.b: value is considered a simple, plain value
} else {
return key.map( k => action(k, value) );
}
// Default Case: key and value considered as simple, plain values
} else {
return action(key, value);
}
};
self.lazy = function( func, storage ) {
// If the argument is a function (expected), set it as lazy getter
if ( self.isfn(func) ) {
let name = 'initializer::'+ Math.random(),
me = self.lazy;
storage = storage || me.storage || (me.storage = { });
return ( ) => ( self.defn(storage[ name ]) ? storage[ name ] : (storage[ name ] = func()) );
// If the argument was "something" else (not undefined), set it as the result
} else if ( self.defn(func) ) {
return ( ) => func;
}
// Otherwise: No idea
throw 'lazy(): The first argument (lazy getter / value) must be provided';
//return null;
};
self.localJson = function( key, value ) {
if ( !! key && self.isstr(key) ) {
try {
if ( self.ndef(value) ) {
return JSON.parse(localStorage.getItem(key));
} else if ( null === value ) {
return localStorage.removeItem(key);
} else {
return localStorage.setItem(key, JSON.stringify(value));
}
} catch ( error ) {
self.warn('* localJson() error:', error, '\n\tfor:', key, value);
}
}
return null;
};
self.off = function( element, event, callback ) {
if ( self.ndef(callback) && self.isobj(event) ) {
return self.keysAndValues(event, ( k,v ) => self.off(element, k, v) );
} else if ( self.isarr(element) ) {
return element.map( elem => self.off(elem, event, callback) );
}
return element.removeEventListener(event, callback);
};
self.on = function( element, event, callback ) {
if ( self.ndef(callback) && self.isobj(event) ) {
return self.keysAndValues(event, ( k,v ) => self.on(element, k, v) );
} else if ( self.isarr(element) ) {
return element.map( elem => self.on(elem, event, callback) );
}
return element.addEventListener(event, callback);
};
self.onmutate = function( element, callback, config ) {
if ( !! element && self.isfn(callback) ) {
config = config || { 'attributes':false, 'childList':true, 'subtree':false };
if ( self.isarr(element) ) {
return element.map( elem => self.onmutate(elem, callback, config) );
}
let observer = new MutationObserver( callback );
observer.initialConfig = ( ) => config;
observer.reconnect = function( newConfig ) {
this.observe(element, newConfig || this.initialConfig());
return this;
};
return observer.reconnect();
}
return null;
};
self.pojo2query = function( pojo ) {
if ( self.isobj(pojo) && !! pojo ) {
let query = Object.keys(pojo)
.map( key => escape(key) +'='+ escape(pojo[ key ]) )
.join('&');
return '?'+ query;
}
return null;
};
self.prependTo = function( element, parent, reference ) {
if ( ! reference && !! parent ) {
reference = parent.childNodes[ 0 ];
}
if ( isarr(element) ) {
return element.map( elem => self.prependTo(elem, parent, reference) );
}
if ( !! reference ) {
reference.parentNode.insertBefore(element, reference);
} else if ( !! parent ) {
parent.append(element);
} else {
self.warn('*** prependTo() could not add element: No parent or reference element provided');
}
return element;
};
self.propDef = function( value, writable, enumerable, configurable ) {
enumerable = !! enumerable;
var def = {
'enumerable': enumerable,
'configurable': !! configurable,
};
if ( self.isfn(value) && ( enumerable || self.isfn(writable) ) ) {
def.get = value;
if ( self.isfn(writable) ) {
def.set = writable;
}
} else {
def.value = value;
def.writable = !! writable;
}
return def;
}
self.query2pojo = function( query ) {
query = (self.ifndef(query, location.search) +'')
.replace(/^[?&]+|$&+/g, '');
if ( !! query ) {
let segs, key, val, pojo = { };
query.split('&')
.forEach( item => {
[ key, val ] =
segs = item.split('=');
val = ( self.ndef(val) ? null : segs.slice(1).join('=') );
pojo[ unescape(key) ] = unescape(val);
} );
return pojo;
}
return null;
};
self.range = function( from, to, inc ) {
inc = ( ! inc || isNaN(inc) ? 1 : inc );
if ( from == to - inc ) {
return [ from, to ];
} else if ( from == to ) {
return [ to ];
} else if ( (to - from) % inc || ! self.between(from + inc, from, to) ) {
throw 'Cannot create range '+ from +'..'+ to +' with increments of '+ inc;
//return null ;//[ ];
}
return self.range(from, to - inc, inc).concat(to);
};
self.request = function( url, verb, data, callback ) {
self.log('* request of:', verb, url, data, callback);
// "Validate" HTTP Verb, to an extent
verb = (verb || 'GET').toUpperCase();
switch ( verb ) {
case 'P': verb = 'POST'; break;
case 'G': verb = 'GET'; break;
case 'H': verb = 'HEAD'; break;
}
// Switch callback and data when ther is no data
if ( self.ndef(callback) && self.isfn(data) ) {
callback = data;
data = null;
}
// Set the data to a string or null
data = self.ifndef(data, null);
data = ( !! data && self.isobj(data) ? self.pojo2query(data).replace('?', '') : data.toString() );
self.log('- request data:', data);
// Add data to the URL for GET requests
if ( 'GET' === verb && !! data ) {
url += ( url.includes('?') ? '&' : '?' ) + data;
data = null;
}
// Create & open XHR object...
let xhr = self.createXHR();
self.log('-> opening request:', verb, url);
xhr.open(verb, url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// Set the internal event handler, which removes itself on execution
xhr.onabort =
xhr.onerror =
xhr.onload = callback;
// Send the actual request
self.log('-> sending request:', data, xhr.readyState, xhr);
return xhr.send(data);
};
// Sandboxed `Function([exportedValues,] arg1, ..., argN, funcBody)`
self.sandbox = function( exports, funcArgsAndCode ) {
const C = self.sandbox._sandbox || self.createSandbox(self.sandbox),
F = C.Function.bind(C);
const args = self.toArray(arguments);
if ( 'string' !== typeof exports ) {
args.shift();
}
// Do we need to pass-in any "exports" values (as arguments?)
if ( !! exports && self.ispojo(exports) ) {
let inrArgs = Object.keys(exports);
if ( inrArgs.length > 0 ) {
inrArgs = inrArgs.concat(args);
const vals = Object.values(exports),
fn = F.apply(null, inrArgs);
(arguments[ arguments.length - 1 ] || '').includes('arguments') && console.warn('sandbox() - sandboxed code with `exports` should not rely on `arguments`');
return function _sandboxed_withExports( ){ return fn.apply(null, vals.concat(toArray(arguments))); };
}
}
return F.apply(null, args);
};
self.table2json = function( table, headers, filter ) {
if ( !! table && !! table.rows ) {
let obj = { head:[ ], data:[ ] };
obj.head = ( self.isfn(headers) ? headers(table) : table.rows[ 0 ] );
if ( self.isobj(obj.head) && !! obj.head.cells ) {
obj.head = Array.map(obj.head.cells, th => th.innerText.trim() );
}
if ( obj.head.length ) {
filter = filter || (( row,i ) => ( i && 'TBODY' === row.parentNode.nodeName ));
for ( let r = 0, nr = table.rows.length; r < nr; r ++ ) {
let row = table.rows[ r ];
if ( filter(row, r) ) {
let item = { };
obj.head.forEach( ( col,i ) => {
col[ 0 ] && (item[ col ] = row.cells[ i ].innerText.trim());
} );
obj.data.push(item);
}
}
}
return obj.data;
}
return null;
};
self.toArray = function( expr ) {
if ( self.hasval(expr) && ! self.isarr(expr) ) {
return ( self.ndef(expr.length) ? [ expr ] : Array.prototype.slice.call(expr) );
}
return expr || [ ];
};
self.toDec2 = function( amount ) {
amount = self.toDec(amount);
if ( isNaN(amount) ) {
return null;
}
let segs = (amount +'').split('.');
return segs[ 0 ] +'.'+ ((segs[ 1 ] || 0) +'0').slice(0, 2);
};
self.toMoney = function( amount ) {
let dec2 = self.toDec2(amount);
return ( isNaN(dec2) ? null : dec2 < 0 ? '-$ '+ (-dec2) : '$ '+ dec2 );
};
self.traverse = function( elem, up, sideways, elementsOnly, lastIfNull ) {
let last = elem;
while ( !! elem && up -- > 0 ) elem = (last = elem, elem.parentNode);
let prop = ( elementsOnly ? 'Element' : '' ) +'Sibling';
if ( sideways < 0 ) {
while ( !! elem && sideways ++ < 0 ) elem = (last = elem, elem[ 'previous'+ prop ]);
} else if ( sideways > 0 ) {
while ( !! elem && sideways -- > 0 ) elem = (last = elem, elem[ 'next'+ prop ]);
}
return ( ! lastIfNull ? elem : elem || last );
};
self.wrapWith = function( content, wrapper ) {
let wrap = content;
if ( !! content && !! wrapper ) {
wrap = self.toArray( self.isstr(wrapper) ? self.create(wrapper) : wrapper )[ 0 ];
if ( !! wrap ) {
let cont = self.toArray(content);
self.prependTo(wrap, null, cont[ 0 ]);
cont.forEach( c => self.appendTo(c, wrap) );
}
}
return wrap;
};
// Object for feature-detection (modern browsers only --e.g., uses lambdas)
self.detected = Object.create(null, {
// Detect support for passive event listeners
// Reference: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
'passiveEvents': self.propDef(self.lazy( ( ) => {
var supported = false;
try {
var opts = Object.defineProperty({ }, 'passive', self.propDef( ( ) => supported = true ));
window.addEventListener('testPassive', null, opts);
window.removeEventListener('testPassive', null, opts);
} catch ( e ) {
}
return supported;
} )),
});
// ----------------------------------------------------
// Intended for Internal Use
self.copyMembers = function( source, target, members, preserve ) {
//self.log('* Copying from', source, '\n\tto', target, '\n\t'+ members, preserve);
if ( ! self.isobj(source) || ! self.isobj(target) ) {
self.warn('=> Cannot copy from/to non-objects');
return false;
}
let names = Object.keys(source);
preserve = ( self.isobj(preserve) ? preserve : false );
//self.log('- Full list of members:', names, '\n\t', source);
if ( self.isstr(members) ) {
members = members.split(',').map( nm => nm.trim() );
}
if ( self.isarr(members) ) {
//self.log('* Member filter:', members);
names = names.filter( nm => members.includes(nm) );
} else if ( self.isfn(members) ) {
names = names.filter(members);
}
//self.log('- Filtered list of members:', names);
names.forEach( nm => {
if ( !! target[ nm ] && !! preserve ) {
preserve[ nm ] = target[ nm ];
}
target[ nm ] = source[ nm ];
//self.log('- Target members', nm, target[ nm ]);
} );
//self.log('=>', target);
return (preserve || target);
};
self.export = function( scope, members, overwriten ) {
if ( ! scope ) {
return false;
}
if ( '*' === (members +'').trim() ) {
members = null;
}
return self.copyMembers(self, scope, members, overwriten);
};
// Avoid needing a 'new' operator; this is just a wrapper
return self;
}