vanilla-lib

Vanilla JS library

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/369430/756036/vanilla-lib.js

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