vanilla-lib

Vanilla JS library

当前为 2018-08-26 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/369430/623830/vanilla-lib.js

  1. function VanillaLib( ) {
  2. 'use strict';
  3. let self = { version:'1.2.180826.1519' },
  4. undefined; // ensure an 'undefined' reference
  5.  
  6. // Logging related
  7. self.logging = true;
  8. self.log = function( ) { return ! self.logging ? false : console.debug.apply(console, arguments); };
  9. self.logGroup = function( ) { return ! self.logging ? false : console.groupCollapsed.apply(console, arguments); };
  10. self.logEnd = function( ) { return ! self.logging ? false : console.groupEnd.apply(console, arguments); };
  11. self.time = function( ) { return ! self.logging ? false : console.time.apply(console, arguments); };
  12. self.timeEnd = function( ) { return ! self.logging ? false : console.timeEnd.apply(console, arguments); };
  13. self.warn = function( ) { return ! self.logging ? false : console.warn.apply(console, arguments); };
  14. // Informative functionality
  15. self.isobj = ( expr,type ) => ( 'object' === typeof expr && ( ! type || null !== expr && ( true === type ||
  16. !! expr.constructor && type === ( self.isstr(type) ? expr.constructor.name : expr.constructor ) ) ) );
  17. self.ownsIt = ( obj,prop ) => ( !! prop && self.isobj(obj, true) && obj.hasOwnProperty(prop) );
  18. self.hasval = expr => ( null !== expr && ! self.ndef(expr) );
  19. self.isbool = expr => ( 'boolean' === typeof expr );
  20. self.ifnan = ( expr,value ) => ( isNaN(expr) ? value : expr );
  21. self.ifndef = ( expr,value ) => ( self.ndef(expr) ? value : expr );
  22. self.ispojo = expr => self.isobj(expr, Object);
  23. self.isarr = expr => self.isobj(expr, Array);
  24. self.isnum = expr => ( 'number' === typeof expr );
  25. self.isstr = expr => ( 'string' === typeof expr );
  26. self.isfn = expr => ( 'function' === typeof expr );
  27. self.defn = expr => ( 'undefined' !== typeof expr );
  28. self.ndef = expr => ( 'undefined' === typeof expr );
  29. // Miscelaneous
  30. self.mapFlat = ( array,func ) => array.map( x => func(x) ).reduce( (a,b) => a.concat(b) );
  31. self.test = ( expr,func,other ) => ( !! expr ? func(expr) : self.isfn(other) ? other(expr) : other );
  32. // DOM related
  33. self.parenth = ( elem,nth ) => self.traverse(elem, self.ifndef(nth, 1), 0);
  34. self.html = ( elem,val ) => self.test(elem, el => self.ndef(val) ? el.innerHTML : (el.innerHTML = val) );
  35. self.text = ( elem,val ) => self.test(elem, el => self.ndef(val) ? el.innerText : (el.innerText = val) );
  36. self.$$ = ( sel,elem ) => Array.slice((elem || document).querySelectorAll(sel));
  37. self.$ = ( sel,elem ) => (elem || document).querySelector(sel);
  38. // Number related
  39. self.aggRate = ( amnt,rate,times ) => ( times < 1 ? amnt : self.aggRate(amnt * rate, rate, times - 1) );
  40. self.between = ( value,from,to,open ) => ( from > to ? self.between(value, to, from, open)
  41. : open ? from < value && value < to : from <= value && value <= to );
  42. self.toDec = expr => ( Math.round(parseFloat((expr +'').replace(/\$|,/g, '')) * 100) / 100 );
  43. // Date/Time related
  44. self.secondsIn = ( from,to,other ) => self.ifnan((to - from) / 1000, self.ifndef(other, NaN));
  45. self.minutesIn = ( from,to,other ) => self.ifnan((to - from) / 60000, self.ifndef(other, NaN));
  46. self.hoursIn = ( from,to,other ) => self.ifnan((to - from) / 3600000, self.ifndef(other, NaN));
  47. self.daysIn = ( from,to,other ) => self.ifnan((to - from) / 86400000, self.ifndef(other, NaN));
  48. self.secondsSince = ( from,other ) => self.secondsIn(from, Date.now(), other);
  49. self.minutesSince = ( from,other ) => self.minutesIn(from, Date.now(), other);
  50. self.hoursSince = ( from,other ) => self.hoursIn(from, Date.now(), other);
  51. self.daysSince = ( from,other ) => self.daysIn(from, Date.now(), other);
  52.  
  53.  
  54. self.addClass = function( element, name ) {
  55. if ( !! element && !! name ) {
  56. if ( self.isarr(element) ) {
  57. return element.map( elem => self.addClass(elem, name) );
  58. }
  59.  
  60. // ToDo: Use .classList if available
  61. name = ( self.isarr(name) ? name : name.split(',') );
  62. name = name.map( nm => nm.trim() )
  63. .filter( nm => ! element.classList.contains(nm) );
  64. element.className = (element.className +' '+ name.join(' ')).trim();
  65. return true;
  66. }
  67. return false;
  68. };
  69.  
  70. self.appendTo = function( element, parent, reference ) {
  71. if ( self.isarr(element) ) {
  72. return element.map( elem => self.appendTo(elem, parent, reference) );
  73. }
  74.  
  75. if ( !! reference ) {
  76. parent = reference.parentNode;
  77. reference = reference.nextSibling;
  78. }
  79.  
  80. if ( !! reference ) {
  81. return self.prependTo(element, parent, reference);
  82. } else if ( !! parent ) {
  83. parent.append(element);
  84. } else {
  85. self.warn('*** appendTo() could not add element: No parent or reference element provided');
  86. }
  87.  
  88. return element;
  89. };
  90.  
  91. self.attr = function( element, name, value ) {
  92. if ( !! element ) {
  93. if ( self.isarr(element) ) {
  94. return element.map( elem => self.attr(elem, name, value) );
  95. }
  96.  
  97. return self.keysAndValues(name, value, ( n,v ) => {
  98. return ( self.hasval(v) ? element.setAttribute(n, v)
  99. : null === v ? element.removeAttribute(n) : element.getAttribute(n) );
  100. } );
  101. }
  102. return element;
  103. };
  104.  
  105. self.choose = function( index, values ) {
  106. return Array.slice(arguments)[ index ];
  107. }
  108.  
  109. self.create = function( html, containerType ) {
  110. let container = null,
  111. result = null,
  112. attrs, style;
  113.  
  114. if ( self.isobj(containerType) ) {
  115. attrs = containerType.attrs;
  116. style = containerType.style;
  117. containerType = containerType.container;
  118. }
  119.  
  120. containerType = containerType || 'div';
  121. create[ containerType ] =
  122. container = self.create[ containerType ] || document.createElement(containerType);
  123. container.innerHTML = html;
  124. result = Array.slice(container.childNodes)
  125. .map( elem => (elem.remove(), elem) );
  126.  
  127. if ( !! attrs ) {
  128. self.attr(result, attrs);
  129. }
  130. if ( !! style ) {
  131. self.css(result, style);
  132. }
  133.  
  134. if ( 1 == result.length ) {
  135. result = result[ 0 ];
  136. }
  137. return result;
  138. };
  139.  
  140. self.createXHR = function( ) {
  141. let xhr = new XMLHttpRequest( );
  142. xhr.onabort = function( ev, xhr ) { self.log('XHR Abort:', ev, xhr, this); };
  143. xhr.onerror = function( ev, xhr ) { self.log('XHR Error:', ev, xhr, this); };
  144. xhr.onload = function( ev, xhr ) { self.log('XHR Load:', ev, xhr, this); };
  145. self.on(xhr, {
  146. 'abort': function( ev ) { self.isfn(this.onabort) && this.onabort(ev, this); },
  147. 'error': function( ev ) { self.isfn(this.onerror) && this.onerror(ev, this); },
  148. 'load': function( ev ) { self.isfn(this.onload) && this.onload(ev, this); },
  149. });
  150. return xhr;
  151. };
  152.  
  153. self.css = function( element, key, value ) {
  154. if ( isarr(element) ) {
  155. return element.map( elem => self.css(elem, key, value) );
  156. }
  157.  
  158. keysAndValues(key, value, ( k,v ) => element.style[ k ] = v );
  159. return element;
  160. };
  161.  
  162. self.extend = function( target, sources ) {
  163. for ( let i = 1, n = arguments.length; i < n; i ++ ) {
  164. self.isobj(arguments[ i ], true) && self.copyMembers(arguments[ i ], target);
  165. }
  166. return target;
  167. };
  168.  
  169. self.extendProperties = function( target, source, overwrite, writable, enumerable, configurable ) {
  170. if ( !! target && !! source ) {
  171. overwrite = !! overwrite;
  172. writable = !! writable;
  173. enumerable = !! enumerable;
  174. configurable = !! configurable;
  175.  
  176. if ( self.isarr(source) ) {
  177. for ( let i = 0, n = source.length; i < n; i ++ ) {
  178. self.extendProperties(target, source[ i ], overwrite, writable, enumerable, configurable);
  179. }
  180. } else if ( self.isobj(source, true) ) {
  181. for ( let prop in source ) {
  182. if ( overwrite || self.ndef(target[ prop ]) ) {
  183. Object.defineProperty(target, prop,
  184. self.propDef(source[ prop ], writable, enumerable, configurable));
  185. }
  186. }
  187. }
  188. }
  189. return target;
  190. };
  191.  
  192. self.fire = function( element, event, args ) {
  193. if ( isarr(element) ) {
  194. return element.map( elem => self.fire(elem, event, args) );
  195. }
  196.  
  197. if ( self.isstr(event) ) {
  198. args = self.ifndef(args, { 'bubbles':true, 'cancelable':true });
  199. event = new Event( event, args );
  200. }
  201. return element.dispatchEvent(event);
  202. };
  203.  
  204. self.keysAndValues = function( key, value, action ) {
  205. if ( self.ndef(action) && self.isfn(value) ) {
  206. action = value;
  207. value = undefined;
  208. }
  209.  
  210. // Case 1: key is an object (and there is no value)
  211. if ( self.isobj(key) && ! value ) {
  212. return Object.keys(key)
  213. .map( k => action(k, key[ k ]) );
  214. // Case 2: key is an array
  215. } else if ( self.isarr(key) ) {
  216. // Case 1.a: value is an array of the same length
  217. if ( self.isarr(value) && key.length === value.length ) {
  218. return key.map( ( k,i ) => action(k, value[ i ]) );
  219. // Case 1.b: value is considered a simple, plain value
  220. } else {
  221. return key.map( k => action(k, value) );
  222. }
  223. // Default Case: key and value considered as simple, plain values
  224. } else {
  225. return action(key, value);
  226. }
  227. };
  228.  
  229. self.lazy = function( func, storage ) {
  230. // If the argument is a function (expected), set it as lazy getter
  231. if ( self.isfn(func) ) {
  232. let name = 'initializer::'+ Math.random(),
  233. me = self.lazy;
  234. storage = storage || me.storage || (me.storage = { });
  235. return ( ) => ( self.defn(storage[ name ]) ? storage[ name ] : (storage[ name ] = func()) );
  236. // If the argument was "something" else (not undefined), set it as the result
  237. } else if ( self.defn(func) ) {
  238. return ( ) => func;
  239. }
  240. // Otherwise: No idea
  241. throw 'lazy(): The first argument (lazy getter / value) must be provided';
  242. //return null;
  243. };
  244.  
  245. self.localJson = function( key, value ) {
  246. if ( !! key && self.isstr(key) ) {
  247. try {
  248. if ( self.ndef(value) ) {
  249. return JSON.parse(localStorage.getItem(key));
  250. } else if ( null === value ) {
  251. return localStorage.removeItem(key);
  252. } else {
  253. return localStorage.setItem(key, JSON.stringify(value));
  254. }
  255. } catch ( error ) {
  256. self.warn('* localJson() error:', error, '\n\tfor:', key, value);
  257. }
  258. }
  259. return null;
  260. };
  261.  
  262. self.off = function( element, event, callback ) {
  263. if ( self.ndef(callback) && self.isobj(event) ) {
  264. return self.keysAndValues(event, ( k,v ) => self.off(element, k, v) );
  265. } else if ( self.isarr(element) ) {
  266. return element.map( elem => self.off(elem, event, callback) );
  267. }
  268. return element.removeEventListener(event, callback);
  269. };
  270.  
  271. self.on = function( element, event, callback ) {
  272. if ( self.ndef(callback) && self.isobj(event) ) {
  273. return self.keysAndValues(event, ( k,v ) => self.on(element, k, v) );
  274. } else if ( self.isarr(element) ) {
  275. return element.map( elem => self.on(elem, event, callback) );
  276. }
  277. return element.addEventListener(event, callback);
  278. };
  279.  
  280. self.onmutate = function( element, callback, config ) {
  281. if ( !! element && self.isfn(callback) ) {
  282. config = config || { 'attributes':false, 'childList':true, 'subtree':false };
  283.  
  284. if ( self.isarr(element) ) {
  285. return element.map( elem => self.onmutate(elem, callback, config) );
  286. }
  287.  
  288. let observer = new MutationObserver( callback );
  289. observer.initialConfig = ( ) => config;
  290. observer.reconnect = function( newConfig ) {
  291. this.observe(element, newConfig || this.initialConfig());
  292. return this;
  293. };
  294. return observer.reconnect();
  295. }
  296. return null;
  297. };
  298.  
  299. self.pojo2query = function( pojo ) {
  300. if ( self.isobj(pojo) && !! pojo ) {
  301. let query = Object.keys(pojo)
  302. .map( key => escape(key) +'='+ escape(pojo[ key ]) )
  303. .join('&');
  304. return '?'+ query;
  305. }
  306. return null;
  307. };
  308.  
  309. self.prependTo = function( element, parent, reference ) {
  310. if ( ! reference && !! parent ) {
  311. reference = parent.childNodes[ 0 ];
  312. }
  313.  
  314. if ( isarr(element) ) {
  315. return element.map( elem => self.prependTo(elem, parent, reference) );
  316. }
  317.  
  318. if ( !! reference ) {
  319. reference.parentNode.insertBefore(element, reference);
  320. } else if ( !! parent ) {
  321. parent.append(element);
  322. } else {
  323. self.warn('*** prependTo() could not add element: No parent or reference element provided');
  324. }
  325.  
  326. return element;
  327. };
  328.  
  329. self.propDef = function( value, writable, enumerable, configurable ) {
  330. enumerable = !! enumerable;
  331. var def = {
  332. 'enumerable': enumerable,
  333. 'configurable': !! configurable,
  334. };
  335.  
  336. if ( self.isfn(value) && ( enumerable || self.isfn(writable) ) ) {
  337. def.get = value;
  338. if ( self.isfn(writable) ) {
  339. def.set = writable;
  340. }
  341. } else {
  342. def.value = value;
  343. def.writable = !! writable;
  344. }
  345.  
  346. return def;
  347. }
  348.  
  349. self.query2pojo = function( query ) {
  350. query = (self.ifndef(query, location.search) +'')
  351. .replace(/^[?&]+|$&+/g, '');
  352. if ( !! query ) {
  353. let segs, key, val, pojo = { };
  354. query.split('&')
  355. .forEach( item => {
  356. [ key, val ] =
  357. segs = item.split('=');
  358. val = ( self.ndef(val) ? null : segs.slice(1).join('=') );
  359. pojo[ unescape(key) ] = unescape(val);
  360. } );
  361. return pojo;
  362. }
  363. return null;
  364. };
  365.  
  366. self.range = function( from, to, inc ) {
  367. inc = ( ! inc || isNaN(inc) ? 1 : inc );
  368. if ( from == to - inc ) {
  369. return [ from, to ];
  370. } else if ( from == to ) {
  371. return [ to ];
  372. } else if ( (to - from) % inc || ! self.between(from + inc, from, to) ) {
  373. throw 'Cannot create range '+ from +'..'+ to +' with increments of '+ inc;
  374. //return null ;//[ ];
  375. }
  376. return self.range(from, to - inc, inc).concat(to);
  377. };
  378.  
  379. self.request = function( url, verb, data, callback ) {
  380. self.log('* request of:', verb, url, data, callback);
  381.  
  382. // "Validate" HTTP Verb, to an extent
  383. verb = (verb || 'GET').toUpperCase();
  384. switch ( verb ) {
  385. case 'P': verb = 'POST'; break;
  386. case 'G': verb = 'GET'; break;
  387. case 'H': verb = 'HEAD'; break;
  388. }
  389.  
  390. // Switch callback and data when ther is no data
  391. if ( self.ndef(callback) && self.isfn(data) ) {
  392. callback = data;
  393. data = null;
  394. }
  395.  
  396. // Set the data to a string or null
  397. data = self.ifndef(data, null);
  398. data = ( !! data && self.isobj(data) ? self.pojo2query(data).replace('?', '') : data.toString() );
  399. self.log('- request data:', data);
  400.  
  401. // Add data to the URL for GET requests
  402. if ( 'GET' === verb && !! data ) {
  403. url += ( url.includes('?') ? '&' : '?' ) + data;
  404. data = null;
  405. }
  406.  
  407. // Create & open XHR object...
  408. let xhr = self.createXHR();
  409. self.log('-> opening request:', verb, url);
  410. xhr.open(verb, url);
  411. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  412.  
  413. // Set the internal event handler, which removes itself on execution
  414. xhr.onabort =
  415. xhr.onerror =
  416. xhr.onload = callback;
  417.  
  418. // Send the actual request
  419. self.log('-> sending request:', data, xhr.readyState, xhr);
  420. return xhr.send(data);
  421. };
  422.  
  423. self.table2json = function( table, headers, filter ) {
  424. if ( !! table && !! table.rows ) {
  425. let obj = { head:[ ], data:[ ] };
  426.  
  427. obj.head = ( self.isfn(headers) ? headers(table) : table.rows[ 0 ] );
  428. if ( self.isobj(obj.head) && !! obj.head.cells ) {
  429. obj.head = Array.map(obj.head.cells, th => th.innerText.trim() );
  430. }
  431.  
  432. if ( obj.head.length ) {
  433. filter = filter || (( row,i ) => ( i && 'TBODY' === row.parentNode.nodeName ));
  434.  
  435. for ( let r = 0, nr = table.rows.length; r < nr; r ++ ) {
  436. let row = table.rows[ r ];
  437.  
  438. if ( filter(row, r) ) {
  439. let item = { };
  440. obj.head.forEach( ( col,i ) => {
  441. col[ 0 ] && (item[ col ] = row.cells[ i ].innerText.trim());
  442. } );
  443. obj.data.push(item);
  444. }
  445. }
  446. }
  447. return obj.data;
  448. }
  449. return null;
  450. };
  451.  
  452. self.toArray = function( expr ) {
  453. if ( self.hasval(expr) && ! self.isarr(expr) ) {
  454. return ( self.ndef(expr.length) ? [ expr ] : Array.slice(expr) );
  455. }
  456. return expr || [ ];
  457. };
  458.  
  459. self.toDec2 = function( amount ) {
  460. amount = self.toDec(amount);
  461. if ( isNaN(amount) ) {
  462. return null;
  463. }
  464. let segs = (amount +'').split('.');
  465. return segs[ 0 ] +'.'+ ((segs[ 1 ] || 0) +'0').slice(0, 2);
  466. };
  467.  
  468. self.toMoney = function( amount ) {
  469. let dec2 = self.toDec2(amount);
  470. return ( isNaN(dec2) ? null : dec2 < 0 ? '-$ '+ (-dec2) : '$ '+ dec2 );
  471. };
  472.  
  473. self.traverse = function( elem, up, sideways, elementsOnly, lastIfNull ) {
  474. let last = elem;
  475. while ( !! elem && up -- > 0 ) elem = (last = elem, elem.parentNode);
  476.  
  477. let prop = ( elementsOnly ? 'Element' : '' ) +'Sibling';
  478. if ( sideways < 0 ) {
  479. while ( !! elem && sideways ++ < 0 ) elem = (last = elem, elem[ 'previous'+ prop ]);
  480. } else if ( sideways > 0 ) {
  481. while ( !! elem && sideways -- > 0 ) elem = (last = elem, elem[ 'next'+ prop ]);
  482. }
  483.  
  484. return ( ! lastIfNull ? elem : elem || last );
  485. };
  486.  
  487. self.wrapWith = function( content, wrapper ) {
  488. let wrap = content;
  489. if ( !! content && !! wrapper ) {
  490. wrap = self.toArray( self.isstr(wrapper) ? self.create(wrapper) : wrapper )[ 0 ];
  491. if ( !! wrap ) {
  492. let cont = self.toArray(content);
  493.  
  494. self.prependTo(wrap, null, cont[ 0 ]);
  495. cont.forEach( c => self.appendTo(c, wrap) );
  496. }
  497. }
  498. return wrap;
  499. };
  500.  
  501.  
  502. // Object for feature-detection (modern browsers only --e.g., uses lambdas)
  503. self.detected = Object.create(null, {
  504. // Detect support for passive event listeners
  505. // Reference: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
  506. 'passiveEvents': self.propDef(self.lazy( ( ) => {
  507. var supported = false;
  508. try {
  509. var opts = Object.defineProperty({ }, 'passive', self.propDef( ( ) => supported = true ));
  510. window.addEventListener('testPassive', null, opts);
  511. window.removeEventListener('testPassive', null, opts);
  512. } catch ( e ) {
  513. }
  514. return supported;
  515. } )),
  516. });
  517.  
  518. // ----------------------------------------------------
  519. // Intended for Internal Use
  520.  
  521. self.copyMembers = function( source, target, members, preserve ) {
  522. //self.log('* Copying from', source, '\n\tto', target, '\n\t'+ members, preserve);
  523. if ( ! self.isobj(source) || ! self.isobj(target) ) {
  524. self.warn('=> Cannot copy from/to non-objects');
  525. return false;
  526. }
  527.  
  528. let names = Object.keys(source);
  529. preserve = ( self.isobj(preserve) ? preserve : false );
  530. //self.log('- Full list of members:', names, '\n\t', source);
  531.  
  532. if ( self.isstr(members) ) {
  533. members = members.split(',').map( nm => nm.trim() );
  534. }
  535. if ( self.isarr(members) ) {
  536. //self.log('* Member filter:', members);
  537. names = names.filter( nm => members.includes(nm) );
  538. } else if ( self.isfn(members) ) {
  539. names = names.filter(members);
  540. }
  541. //self.log('- Filtered list of members:', names);
  542.  
  543. names.forEach( nm => {
  544. if ( !! target[ nm ] && !! preserve ) {
  545. preserve[ nm ] = target[ nm ];
  546. }
  547. target[ nm ] = source[ nm ];
  548. //self.log('- Target members', nm, target[ nm ]);
  549. } );
  550. //self.log('=>', target);
  551. return (preserve || target);
  552. };
  553.  
  554. self.export = function( scope, members, overwriten ) {
  555. if ( ! scope ) {
  556. return false;
  557. }
  558. if ( '*' === (members +'').trim() ) {
  559. members = null;
  560. }
  561. return self.copyMembers(self, scope, members, overwriten);
  562. };
  563.  
  564. // Avoid needing a 'new' operator; this is just a wrapper
  565. return self;
  566. }