JoeSimmons' Library

A JavaScript library used for bypass copy paste

当前为 2017-01-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name JoeSimmons' Library
  3. // @namespace https://greasyfork.org/en/users/83064-pakdefndr
  4. // @description A JavaScript library used for bypass copy paste
  5. // @include *
  6. // @copyright Pakdefndr
  7. // @author Pakdefndr
  8. // @version 1.0
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11.  
  12. (function (window, undefined) {
  13.  
  14. 'use strict'; // use strict mode
  15.  
  16. var version = '1.3.0'; // this will be used for JSL.prototype.version
  17. var intervals = []; // for the setInterval/clearInterval methods
  18.  
  19. // regular expressions
  20. var rSelector = /^\*|^\.[a-z][\w\d-]*|^#[^ ]+|^[a-z]+|^\[a-z]+/i; // matches a CSS selector
  21. var rXpath = /^\.?\/{1,2}[a-zA-Z\*]+/; // matches an XPath query
  22. var rHTML = /<[^>]+>/; // matches a string of HTML
  23. var rHyphenated = /-([a-zA-Z])/g; // matches alphabetic, hyphenated strings
  24. var rElementObject = /^\[object HTML([a-zA-Z]+)?Element\]$/; // matches the toString value of an element
  25. var rWindowObject = /^\[object Window\]$/; // matches the toString value of a window object
  26. var rValidVarname = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/; // matches a valid variable name
  27.  
  28. // compatibility methods for browsers that don't support ECMAScript-5 completely
  29. var compat = {
  30. 'arr_indexOf' : function (searchElement, fromIndex) {
  31. var index = parseInt(fromIndex || 0, 10), len = this.length;
  32. index = index < 0 ? len + index : index; // handle negative fromIndex
  33. index = !(index > 0) ? 0 : index; // handle out of range and/or NaN fromIndex
  34.  
  35. while (index < len && index >= 0) {
  36. if (this[index] === searchElement) {
  37. return index;
  38. }
  39. index += 1;
  40. }
  41.  
  42. return -1;
  43. },
  44. /*
  45. 'filter' : function (fn, oThis) {
  46. var index, value, len = this.length, ret = [];
  47.  
  48. for (index = 0; index < len; index += 1) {
  49. value = this[index];
  50. if ( fn.call(oThis, value, index, this) ) {
  51. ret.push(value);
  52. }
  53. }
  54.  
  55. return ret;
  56. },
  57. */
  58. 'forEach' : function (fn, oThis) {
  59. var index, len;
  60.  
  61. for (index = 0, len = this.length; index < len; index += 1) {
  62. fn.call(oThis, this[index], index, this);
  63. }
  64. },
  65. 'map' : function (fn, oThis) {
  66. var index, newArr = [], len;
  67.  
  68. for (index = 0, len = this.length; index < len; index += 1) {
  69. newArr[index] = fn.call(oThis, this[index], index, this);
  70. }
  71.  
  72. return newArr;
  73. },
  74. 'reduce' : function (fn, initialValue) {
  75. var index, len, value, isValueSet = false;
  76.  
  77. if (arguments.length > 1) {
  78. value = initialValue;
  79. isValueSet = true;
  80. }
  81.  
  82. for (index = 0, len = this.length; index < len; index += 1) {
  83. if (isValueSet) {
  84. value = fn(value, this[index], index, this);
  85. } else {
  86. value = this[index];
  87. isValueSet = true;
  88. }
  89. }
  90.  
  91. return value;
  92. }
  93. };
  94.  
  95. // gets a method from an object's prototype. returns undefined if not found
  96. var getMethod = function (obj, method) {
  97. if (typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function') {
  98. obj = XPCNativeWrapper.unwrap(obj);
  99. } else if (obj.wrappedJSObject) {
  100. obj = obj.wrappedJSObject;
  101. }
  102.  
  103. if (obj.prototype && typeof obj.prototype[method] === 'function') {
  104. return obj.prototype[method];
  105. }
  106. };
  107.  
  108. // original methods for some common uses
  109. var core = {
  110. // array
  111. 'arr_indexOf' : getMethod(Array, 'indexOf') || compat.arr_indexOf,
  112. 'concat' : getMethod(Array, 'concat'),
  113. 'filter' : getMethod(Array, 'filter') || compat.filter,
  114. 'forEach' : getMethod(Array, 'forEach') || compat.forEach,
  115. 'map' : getMethod(Array, 'map') || compat.map,
  116. 'reduce' : getMethod(Array, 'reduce') || compat.reduce,
  117. 'slice' : getMethod(Array, 'slice'),
  118.  
  119. // object
  120. 'hasOwnProperty' : getMethod(Object, 'hasOwnProperty'),
  121. 'toString' : getMethod(Object, 'toString'),
  122. };
  123.  
  124. var JSL = function JSL(selector, context) {
  125. return new JSL.fn.init(selector, context);
  126. };
  127.  
  128. // a simple class for dealing with event listener handlers
  129. var handlers = {
  130. stack : [],
  131.  
  132. add : function (thisElement, type, fn) {
  133. this.stack.push({
  134. element : thisElement,
  135. type : type,
  136. fn : fn
  137. });
  138. },
  139.  
  140. get : function (thisElement, type) {
  141. var events = [];
  142. type = typeof type === 'string' ? type : '*';
  143.  
  144. JSL.each(this.stack, function (thisEventObj) {
  145. if (thisElement === thisEventObj.element) {
  146. if (type === '*' || thisEventObj.type === type) {
  147. events.push(thisEventObj);
  148. }
  149. }
  150. });
  151.  
  152. return events;
  153. },
  154.  
  155. remove : function (thisElement, type) {
  156. var handlerIndices = [], that = this;
  157.  
  158. // find all the indices of what we need to remove
  159. JSL.each(handlers.get(thisElement, type), function (thisEventObj, index, array) {
  160. handlerIndices.push(
  161. core.arr_indexOf.call(that.stack, thisEventObj)
  162. );
  163. });
  164.  
  165. // remove all the indices here, using a separate array of indices
  166. // we can't do this as we loop over the (stack) array itself, because
  167. // we would be removing values as they are being iterated through
  168. JSL.each(handlerIndices, function (thisIndex) {
  169. that.stack.splice(thisIndex, 1);
  170. });
  171. }
  172. };
  173.  
  174. // Node.prototype.matchesSelector compat for vendor prefixes
  175. function matchesSelector(element, selector) {
  176. if (element && typeof selector === 'string') {
  177. if (typeof element.mozMatchesSelector === 'function') {
  178. // Mozilla
  179. return element.mozMatchesSelector(selector);
  180. } else if (typeof element.webkitMatchesSelector === 'function') {
  181. // Webkit
  182. return element.webkitMatchesSelector(selector);
  183. } else if (typeof element.oMatchesSelector === 'function') {
  184. // Opera
  185. return element.oMatchesSelector(selector);
  186. } else if (typeof element.msMatchesSelector === 'function') {
  187. // IE
  188. return element.msMatchesSelector(selector);
  189. }
  190. }
  191.  
  192. return false;
  193. }
  194.  
  195. // calls 'this' with the first parameter as the first argument
  196. function call(a) {
  197. return this(a);
  198. }
  199.  
  200. function toCamelCase(string) {
  201. return string.replace(rHyphenated, function (fullMatch, firstGroup) {
  202. return firstGroup.toUpperCase();
  203. });
  204. }
  205.  
  206. // walkTheDom by Douglas Crockford
  207. function walkTheDom(node, func) {
  208. func(node);
  209. node = node.firstChild;
  210.  
  211. while (node) {
  212. walkTheDom(node, func);
  213. node = node.nextSibling;
  214. }
  215. }
  216.  
  217. // can pluck a key out of an object
  218. function pluck(obj) {
  219. var subs = this.split('.'),
  220. ret = obj, i;
  221.  
  222. for (i = 0; i < subs.length; i += 1) {
  223. ret = ret[ subs[i] ];
  224. if (ret == null) {
  225. return '';
  226. }
  227. }
  228.  
  229. return ret;
  230. }
  231.  
  232. function sum(curValue, nextValue) {
  233. return curValue + nextValue;
  234. }
  235.  
  236. function sumInt(curValue, nextValue) {
  237. return parseInt(curValue, 10) + parseInt(nextValue, 10);
  238. }
  239.  
  240. // internal function for throwing errors, so the user gets
  241. // some sort of hint as to why their operation failed
  242. function error(errorString) {
  243. if (typeof console !== 'undefined' && typeof console.error === 'function') {
  244. console.error(errorString);
  245. }
  246.  
  247. return null; // always return null
  248. }
  249.  
  250. // will copy an element and return a new copy with the same event listeners
  251. function cloneElement(thisElement) {
  252. var newElement = thisElement.cloneNode(true);
  253.  
  254. // clone event listeners of element
  255. JSL.each(handlers.get(thisElement), function (thisEventObj) {
  256. JSL.addEvent(newElement, thisEventObj.type, thisEventObj.fn);
  257. });
  258.  
  259. return newElement;
  260. }
  261.  
  262. function getEachElements(array, selector, key, type) {
  263. var newElementsArray = [],
  264. isValidSelector = typeof selector === 'string' && selector.trim() !== '';
  265.  
  266. JSL.each(array, function (currentElement) {
  267. while ( currentElement = currentElement[key] ) { // note: intentional assignment
  268. if (type > 0 ? currentElement.nodeType === type : true) {
  269. if ( isValidSelector === false || JSL(currentElement).filter(selector).exists ) {
  270. newElementsArray.push(currentElement);
  271. return;
  272. }
  273. }
  274. }
  275. });
  276.  
  277. return newElementsArray;
  278. }
  279.  
  280. // this will take
  281. function doElementOperationOnEach(args, op) {
  282. var newElementsArray = [], newElement,
  283. passedElements = JSL.create.apply(JSL, args);
  284.  
  285. if (this.exists) {
  286. if (JSL.typeOf(passedElements) === 'array') {
  287. this.each(function (thisElement) {
  288. JSL.each(passedElements, function (passedElement) {
  289. // clone the element
  290. var newElement = cloneElement(passedElement);
  291.  
  292. // add the new elements to an array
  293. newElementsArray.push(newElement);
  294.  
  295. // perform the passed operation on the element
  296. op(thisElement, newElement);
  297. });
  298. });
  299. } else {
  300. this.each(function (thisElement) {
  301. // clone the element
  302. var newElement = cloneElement(passedElements);
  303.  
  304. // add the new elements to an array
  305. newElementsArray.push(newElement);
  306.  
  307. // perform the passed operation on the element
  308. op(thisElement, newElement);
  309. });
  310. }
  311. }
  312.  
  313. return newElementsArray;
  314. }
  315.  
  316. // define JSL's prototype, aka JSL.fn
  317. JSL.fn = JSL.prototype = {
  318. isJSL : true,
  319. constructor : JSL,
  320. length : 0,
  321. version : version,
  322.  
  323. // similar to jQuery. JSL is just the init constructor
  324. init : function (selector, context) {
  325. var selectorStringValue = core.toString.call(selector),
  326. that = this,
  327. elems = [];
  328.  
  329. switch (typeof selector) {
  330. case 'string': { // -- STRING --
  331. if ( selector.match(rXpath) ) {
  332. // handle an XPath expression
  333. elems = JSL.xpath({expression : selector, type : 7, context : context});
  334. } else if ( selector.match(rHTML) ) {
  335. // reserved for html code creation
  336. // not sure if I want to implement it
  337. } else if ( selector.match(rSelector) ) {
  338. if (JSL.typeOf(context) === 'array') {
  339. // handle an array being passed as the context
  340. return that.find.call(context, selector);
  341. } else if (typeof context === 'string') {
  342. // handle a selector being passsed as the context
  343. context = JSL(context);
  344. if (context.exists) {
  345. return JSL(selector, context[0]);
  346. }
  347. } else if (context != null && context.isJSL === true && context.exists) {
  348. // handle a JSL object being passsed as the context
  349. return JSL( selector, context[0] );
  350. } else {
  351. // handle a regular element being passed as the context
  352. context = context != null && context.querySelectorAll ? context : document;
  353. elems = context.querySelectorAll(selector);
  354. }
  355. }
  356. break;
  357. }
  358. // ---------------------------------------------------
  359. case 'object': { // -- OBJECT --
  360. if (selector != null) {
  361. if (selector.isJSL === true) {
  362. // handle a JSL object
  363. return selector;
  364. } else if ( core.hasOwnProperty.call(selector, 'length') ) {
  365. // handle an array-like object
  366. elems = selector;
  367. } else if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
  368. // handle a single element
  369. elems = [selector];
  370. }
  371. }
  372. break;
  373. }
  374. // ---------------------------------------------------
  375. default: { // -- UNKNOWN --
  376. if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
  377. // handle elements that are typeof === 'function'
  378. // e.g., object, applet, embed
  379. elems = [selector];
  380. }
  381. }
  382. }
  383.  
  384. // define the length property of our object wrapper
  385. that.length = elems.length;
  386.  
  387. // bind the elements to array-like key:value pairs in our wrapper
  388. // e.g., this[0] ==> element
  389. JSL.each(elems, function (value, index) {
  390. that[index] = value;
  391. });
  392.  
  393. return that;
  394. },
  395.  
  396. // --- STARTING LINE FOR THE JSL WRAPPER METHODS
  397. add : function (selector, context) {
  398. var newElements = JSL(selector, context).raw(),
  399. allElements = core.concat.call(this.raw(), newElements);
  400. return JSL(allElements);
  401. },
  402.  
  403. addEvent : function (type, fn) {
  404. return this.each(function (thisElement) {
  405. JSL.addEvent(thisElement, type, fn);
  406. });
  407. },
  408.  
  409. after : function () {
  410. var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
  411. var parent = baseElement.parentNode,
  412. next = baseElement.nextSibling;
  413.  
  414. if (parent) {
  415. if (next) {
  416. // add the newElement after the current element
  417. parent.insertBefore(newElement, next);
  418. } else {
  419. // nextSibling didn't exist. just append to its parent
  420. parent.appendChild(newElement);
  421. }
  422. }
  423. });
  424.  
  425. return JSL(newElementsArray);
  426. },
  427.  
  428. append : function () {
  429. var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
  430. baseElement.appendChild(newElement);
  431. });
  432.  
  433. return JSL(newElementsArray);
  434. },
  435.  
  436. attribute : function (name, value) {
  437. var ret = '', valueIsValid = value != null;
  438.  
  439. if ( typeof name === 'string' && this.exists ) {
  440. this.each(function (elem) {
  441. if (valueIsValid) {
  442. elem.setAttribute(name, value);
  443. } else {
  444. ret += elem.getAttribute(name) || '';
  445. }
  446. });
  447. }
  448.  
  449. return valueIsValid ? this : ret;
  450. },
  451.  
  452. before : function () {
  453. var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
  454. var parent = baseElement.parentNode;
  455.  
  456. // add the newElement before the current element
  457. if (parent) {
  458. parent.insertBefore(newElement, baseElement);
  459. }
  460. });
  461.  
  462. return JSL(newElementsArray);
  463. },
  464.  
  465. center : function () {
  466. return this.each(function (thisElement) {
  467. thisElement = JSL(thisElement);
  468. thisElement.css('position', 'fixed');
  469. thisElement.css('top', Math.floor( (window.innerHeight - thisElement.height) / 2 ) + 'px');
  470. thisElement.css('left', Math.floor( (window.innerWidth - thisElement.width) / 2 ) + 'px');
  471. });
  472. },
  473.  
  474. clone : function () {
  475. var clonedElements = core.map.call(this, cloneElement); // variable for clarity
  476. return JSL(clonedElements);
  477. },
  478.  
  479. css : function (name, value) {
  480. if (typeof name === 'string') {
  481. // convert the hyphenated string to camel-case
  482. name = toCamelCase(name);
  483.  
  484. if (typeof value === 'string') {
  485. return this.each(function (thisElement) {
  486. if (name in thisElement.style) {
  487. thisElement.style[name] = value;
  488. }
  489. });
  490. }
  491.  
  492. return core.map.call(this, pluck, 'style.' + name).join('');
  493. } else {
  494. return error('.css() was not passed a string for the first argument.');
  495. }
  496. },
  497.  
  498. each : function (fn, oThis) {
  499. if (this.exists) {
  500. JSL.each(this, fn, oThis);
  501. }
  502.  
  503. return this;
  504. },
  505.  
  506. get exists() {
  507. return this.length > 0 && this[0] != null;
  508. },
  509.  
  510. filter : function (selector) {
  511. var newElementsArray = [];
  512.  
  513. if (typeof selector === 'string') {
  514. this.each(function (thisElement) {
  515. if ( matchesSelector(thisElement, selector) ) {
  516. newElementsArray.push(thisElement);
  517. }
  518. });
  519. }
  520.  
  521. // returns an empty JSL object if no elements are matched
  522. return JSL(newElementsArray);
  523. },
  524.  
  525. find : function (selector) {
  526. var arrayOfMatchesArrays = core.map.call(this, function (thisElement) {
  527. var matches = thisElement.querySelectorAll(selector);
  528. return JSL.toArray(matches);
  529. });
  530. var singleArrayOfMatches = arrayOfMatchesArrays.length > 0 ?
  531. core.reduce.call(arrayOfMatchesArrays, function (a, b) {
  532. return core.concat.call(a, b);
  533. }) : [];
  534.  
  535. return JSL(singleArrayOfMatches);
  536. },
  537.  
  538. first : function () {
  539. return this.get(0);
  540. },
  541.  
  542. get : function (index) {
  543. index = index === 'first' ? 0 : index === 'last' ? -1 : parseInt(index, 10);
  544.  
  545. if ( !isNaN(index) ) {
  546. return JSL( index < 0 ? this[this.length + index] : this[index] );
  547. }
  548.  
  549. return JSL.toArray(this);
  550. },
  551.  
  552. get height() {
  553. var arrayOfElemHeights = core.map.call(this, pluck, 'offsetHeight');
  554. return core.reduce.call(arrayOfElemHeights, sum);
  555. },
  556.  
  557. has : function (selector) {
  558. var newElementsArray = [];
  559.  
  560. if ( typeof selector === 'string' && selector.match(rSelector) ) {
  561. this.each(function (thisElement) {
  562. if ( JSL(selector, thisElement).exists ) {
  563. newElementsArray.push(thisElement);
  564. }
  565. });
  566. }
  567.  
  568. return JSL(newElementsArray);
  569. },
  570.  
  571. hide : function () {
  572. return this.css('display', 'none');
  573. },
  574.  
  575. /*
  576. get inView(passedContainer) {
  577. var isInView = false;
  578. this.each(function (thisElement) {
  579. var container = passedContainer || thisElement.parentNode;
  580. var visible = !!( (container.scrollTop + container.offsetHeight) >= thisElement.offsetTop &&
  581. (container.scrollTop - thisElement.offsetHeight) <= thisElement.offsetTop );
  582.  
  583. if (visible) {
  584. isInView = true;
  585. return 'stop';
  586. }
  587. });
  588.  
  589. return isInView;
  590. },
  591. */
  592.  
  593. is : function (selector) {
  594. for (var i = 0; i < this.length; i += 1) {
  595. if ( matchesSelector(this[i], selector) ) {
  596. return true;
  597. }
  598. }
  599. return false;
  600. },
  601. isnt : function (selector) {
  602. return !this.is(selector);
  603. },
  604.  
  605. last : function (selector) {
  606. return this.get(-1);
  607. },
  608.  
  609. next : function (selector) {
  610. return JSL( getEachElements(this, selector, 'nextSibling', 1) );
  611. },
  612.  
  613. not : function (selector) {
  614. var newElementsArray = [];
  615.  
  616. if ( typeof selector === 'string' && selector.match(rSelector) ) {
  617. this.each(function (thisElement) {
  618. if ( JSL(thisElement).isnt(selector) ) {
  619. newElementsArray.push(thisElement);
  620. }
  621. });
  622. }
  623.  
  624. return JSL(newElementsArray);
  625. },
  626.  
  627. parent : function (selector) {
  628. return JSL( getEachElements(this, selector, 'parentNode', 1) );
  629. },
  630.  
  631. prepend : function () {
  632. var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
  633. var firstChild = baseElement.firstChild;
  634.  
  635. if (firstChild) {
  636. baseElement.insertBefore(newElement, firstChild);
  637. }
  638. });
  639.  
  640. return JSL(newElementsArray);
  641. },
  642.  
  643. prev : function (selector) {
  644. return JSL( getEachElements(this, selector, 'previousSibling', 1) );
  645. },
  646.  
  647. prop : function (name, value) {
  648. var valueIsValid = value != null, ret;
  649.  
  650. if (typeof name === 'string' && this.exists) {
  651. this.each(function (thisElement) {
  652. if (valueIsValid) {
  653. thisElement[name] = value;
  654. } else {
  655. if (typeof ret === 'undefined') {
  656. ret = thisElement[name];
  657. } else {
  658. ret += thisElement[name];
  659. }
  660. }
  661. });
  662. }
  663.  
  664. return valueIsValid ? this : ret;
  665. },
  666.  
  667. raw : function () {
  668. return core.slice.call(this, 0);
  669. },
  670.  
  671. remove : function () {
  672. return this.each(function (element) {
  673. var parent = element.parentNode;
  674.  
  675. if (element && parent) {
  676. parent.removeChild(element);
  677. }
  678. });
  679. },
  680.  
  681. removeAttribute : function (attributeName) {
  682. if (typeof attributeName === 'string') {
  683. return this.each(function (thisElement) {
  684. thisElement.removeAttribute(attributeName);
  685. });
  686. } else {
  687. return error('.removeAttribute() was not passed a string.');
  688. }
  689. },
  690.  
  691. removeEvent : function (type) {
  692. if (typeof type === 'string') {
  693. return this.each(function (thisElement) {
  694. JSL.removeEvent(thisElement, type);
  695. });
  696. } else {
  697. return error('.removeEvent() was not passed a string.');
  698. }
  699. },
  700.  
  701. replace : function () {
  702. var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
  703. var parent = baseElement.parentNode;
  704.  
  705. if (parent) {
  706. parent.replaceChild(newElement, baseElement);
  707. }
  708. });
  709.  
  710. return JSL(newElementsArray);
  711. },
  712.  
  713. show : function (value) {
  714. value = typeof value === 'string' ? value : 'inline';
  715. return this.css('display', value);
  716. },
  717. text : function (passedText, append) {
  718. // handle setting text
  719. if (typeof passedText === 'string') {
  720. if (append !== true) {
  721. this.each(function (thisElement) {
  722. JSL('.//text()', thisElement).each(function (textNode) {
  723. textNode.data = '';
  724. });
  725. });
  726. }
  727.  
  728. this.append('text', passedText);
  729. return this;
  730. }
  731.  
  732. // handle getting text
  733. return core.reduce.call(this, function (curValue, nextElement) {
  734. return curValue + nextElement.textContent;
  735. }, '');
  736. },
  737.  
  738. toggle : function () {
  739. return this.each(function (thisElement) {
  740. thisElement = JSL(thisElement);
  741.  
  742. if (thisElement.visible) {
  743. thisElement.hide();
  744. } else {
  745. thisElement.show();
  746. }
  747. });
  748. },
  749.  
  750. value : function (passedValue) {
  751. var elem = this[0],
  752. tagName = elem && elem.tagName || '',
  753. selectedOptions = [],
  754. rInputTypeBlacklist = /button|checkbox|file|image|radio|reset|submit/,
  755. passedValueType = JSL.typeOf(passedValue);
  756.  
  757. if (passedValue == null) {
  758. // no arguments were passed, return a value
  759. if (tagName === 'SELECT') {
  760. if ( elem.hasAttribute('multiple') ) {
  761. JSL.each(elem.options, function (thisOption) {
  762. if (thisOption.selected) {
  763. selectedOptions.push(thisOption.value);
  764. }
  765. });
  766.  
  767. return selectedOptions;
  768. } else {
  769. return elem.options[elem.selectedIndex].value;
  770. }
  771. } else if ( tagName === 'INPUT' && !elem.type.match(rInputTypeBlacklist) ) {
  772. return elem.value;
  773. }
  774. if (tagName === 'TEXTAREA') {
  775. return elem.value;
  776. }
  777. } else {
  778. // an argument was passed, set the value on each element
  779. return this.each(function (thisElement) {
  780. var tagName = thisElement.tagName;
  781.  
  782. if (tagName === 'SELECT') {
  783. if (thisElement.hasAttribute('multiple') && passedValueType === 'array') {
  784. JSL.each(thisElement.options, function (thisOption) {
  785. JSL.each(passedValue, function (thisPassedValue) {
  786. if (thisOption.value == thisPassedValue) {
  787. thisOption.selected = true;
  788. return 'stop';
  789. } else {
  790. thisOption.selected = false;
  791. }
  792. });
  793. });
  794. } else {
  795. JSL.each(thisElement.options, function (thisOption) {
  796. thisOption.selected = thisOption.value == passedValue;
  797. });
  798. }
  799. } else if (tagName === 'INPUT') {
  800. if ( !thisElement.type.match(rInputTypeBlacklist) ) {
  801. thisElement.value = passedValue;
  802. } else if (thisElement.type === 'checkbox' || thisElement.type === 'radio') {
  803. if (passedValueType === 'array') {
  804. JSL.each(passedValue, function (thisPassedValue) {
  805. if (thisElement.value == thisPassedValue) {
  806. thisElement.checked = true;
  807. return 'stop';
  808. } else {
  809. thisElement.checked = false;
  810. }
  811. });
  812. } else if (thisElement.value == passedValue) {
  813. thisElement.checked = true;
  814. }
  815. }
  816. } else if (tagName === 'TEXTAREA') {
  817. thisElement.value = passedValue;
  818. }
  819. });
  820. }
  821.  
  822. return null;
  823. },
  824.  
  825. get visible() {
  826. return Math.max(this.width, this.height) > 0;
  827. },
  828.  
  829. get width() {
  830. var arrayOfElemHeights = core.map.call(this, pluck, 'offsetWidth');
  831. return core.reduce.call(arrayOfElemHeights, sum);
  832. },
  833. };
  834.  
  835. // give the init function the JSL prototype for later instantiation
  836. JSL.fn.init.prototype = JSL.fn;
  837.  
  838. // extend method. can extend any object it's run upon
  839. JSL.fn.extend = JSL.extend = function (obj) {
  840. var name, copy;
  841.  
  842. for (name in obj) {
  843. copy = obj[name];
  844.  
  845. if ( !core.hasOwnProperty.call(this, name) && typeof copy !== 'undefined' ) {
  846. this[name] = copy;
  847. }
  848. }
  849. };
  850.  
  851. // --- STARTLING LINE FOR THE DIRECT JSL METHODS
  852. JSL.extend({
  853. addEvent : function addEvent(thisElement, type, fn) {
  854. if (thisElement != null && typeof type === 'string' && typeof fn === 'function') {
  855. if (typeof thisElement.addEventListener === 'function') {
  856. thisElement.addEventListener(type, fn, false);
  857. } else if (typeof thisElement.attachEvent === 'function') {
  858. type = 'on' + type;
  859. thisElement.attachEvent(type, fn);
  860. } else {
  861. return;
  862. }
  863.  
  864. handlers.add(thisElement, type, fn);
  865. }
  866. },
  867.  
  868. addScript : function addScript(contents, id, node) {
  869. var newElement = document.createElement('script');
  870. newElement.id = id || ( 'jsl-script-' + JSL.random(999) );
  871. newElement.innerHTML = contents;
  872.  
  873. node = node || document.head || document.querySelector('html > head');
  874. node.appendChild(newElement);
  875.  
  876. return {
  877. remove : function () {
  878. node.removeChild(newElement);
  879. }
  880. };
  881. },
  882.  
  883. addStyle : function addStyle(css, id, node) {
  884. id = id || ( 'jsl-style-' + JSL.random(999) );
  885. node = node || document.head || document.querySelector('html > head');
  886. if (node) {
  887. node.appendChild(
  888. JSL.create('style', {id : id, type : 'text/css'}, [ JSL.create('text', css) ] )
  889. );
  890. }
  891. },
  892.  
  893. alias : function alias(newAlias) {
  894. if (typeof newAlias === 'string' && newAlias.match(rValidVarname) && typeof window[newAlias] === 'undefined') {
  895. window[newAlias] = JSL;
  896. }
  897. },
  898.  
  899. clearInterval : function clearInterval(index) {
  900. if (typeof index === 'number' && index < intervals.length) {
  901. window.clearTimeout( intervals[index] );
  902. intervals[index] = null;
  903. }
  904. },
  905.  
  906. create : function create(elementName, descObj, kidsArray) {
  907. var argsLength = arguments.length,
  908. typeValue, prop, val, HTMLholder, ret, i;
  909.  
  910. if (argsLength === 2 && elementName === 'text' && typeof descObj === 'string') {
  911. // handle text node creation
  912. return document.createTextNode(descObj);
  913. } else if ( argsLength === 1 && typeof elementName === 'string' && elementName.match(rHTML) ) {
  914. // handle HTML strings
  915.  
  916. // take the HTML string and put it inside a div
  917. HTMLholder = document.createElement('div');
  918. HTMLholder.innerHTML = elementName;
  919.  
  920. // add each childNode to an array to return
  921. ret = [];
  922. ret.push.apply(ret, HTMLholder.childNodes);
  923. return ret.length > 0 ? (ret.length === 1 ? ret[0] : ret) : null;
  924. } else if (argsLength > 1 && typeof elementName === 'string' && typeof descObj === 'object') {
  925. // handle the normal element name and descriptor object
  926. ret = document.createElement(elementName + '');
  927.  
  928. for (prop in descObj) {
  929. if ( core.hasOwnProperty.call(descObj, prop) ) {
  930. val = descObj[prop];
  931. if (prop.indexOf('on') === 0 && typeof val === 'function') {
  932. JSL.addEvent(ret, prop.substring(2), val);
  933. } else if ( prop !== 'style' && prop !== 'class' && prop in ret && typeof ret[prop] !== 'undefined' ) {
  934. ret[prop] = val;
  935. } else {
  936. ret.setAttribute(prop, val);
  937. }
  938. }
  939. }
  940.  
  941. if (JSL.typeOf(kidsArray) === 'array') {
  942. JSL.each(kidsArray, function (kid) {
  943. var val, item, i;
  944.  
  945. if (typeof kid === 'string') {
  946. val = JSL.create(kid)
  947.  
  948. if (JSL.typeOf(val) === 'array') {
  949. for (i = 0; i < val.length; i += 1) {
  950. ret.appendChild( val[i] );
  951. }
  952. } else if (JSL.typeOf(kid) === 'element') {
  953. ret.appendChild(kid);
  954. }
  955. } else if (JSL.typeOf(kid) === 'element') {
  956. ret.appendChild(kid);
  957. }
  958. });
  959. }
  960.  
  961. return ret;
  962. } else if (argsLength === 1 && JSL.typeOf(elementName) === 'element') {
  963. // handle an element
  964. return elementName;
  965. }
  966. },
  967.  
  968. each : function each(passedArray, fn, oThis) {
  969. var isOthisUndefined = typeof oThis !== 'undefined',
  970. index, len, otherThis, value;
  971.  
  972. for (index = 0; index < passedArray.length; index += 1) {
  973. value = passedArray[index];
  974. otherThis = isOthisUndefined ? oThis : value;
  975. if (fn.call(otherThis, value, index, passedArray) === 'stop') {
  976. break;
  977. }
  978. }
  979. },
  980.  
  981. loop : function loop(maxIterations, fn) {
  982. var args = JSL.toArray(arguments), i;
  983.  
  984. if (typeof maxIterations === 'number' && maxIterations > 0 && typeof fn === 'function') {
  985. args = args.slice(2);
  986. for (i = 0; i < maxIterations; i += 1) {
  987. fn.apply(null, args);
  988. }
  989. }
  990. },
  991.  
  992. random : function random(maxInteger, minInteger) {
  993. var rand = -1;
  994.  
  995. while (rand < 0 || rand > maxInteger || rand < minInteger) {
  996. rand = Math.floor( Math.random() * maxInteger ) + Math.round( Math.random() );
  997. }
  998.  
  999. return rand;
  1000. },
  1001.  
  1002. removeEvent : function removeEvent(thisElement, type) {
  1003. JSL.each(handlers.get(thisElement, type), function (thisEventObj) {
  1004. if (typeof thisElement.removeEventListener === 'function') {
  1005. thisEventObj.element.removeEventListener(thisEventObj.type, thisEventObj.fn, false);
  1006. } else if (typeof thisElement.detachEvent === 'function') {
  1007. type = 'on' + type;
  1008. thisEventObj.element.detachEvent(thisEventObj.type, thisEventObj.fn);
  1009. }
  1010.  
  1011. handlers.remove(thisElement, type);
  1012. });
  1013. },
  1014.  
  1015. runAt : function runAt(state, func, oThis) {
  1016. var args = JSL.toArray(arguments), intv,
  1017.  
  1018. // compose a list of the 4 states, to use .indexOf() upon later
  1019. states = ['uninitialized', 'loading', 'interactive', 'complete'],
  1020.  
  1021. // in-case they pass [start/end] instead of [loading/complete]
  1022. state = state.replace('start', 'loading').replace('end', 'complete');
  1023.  
  1024. // this will run their function with the specified arguments, if any,
  1025. // and a custom 'this' value, if specified
  1026. function runFunc() {
  1027. func.apply( oThis, args.slice(3) );
  1028. }
  1029.  
  1030. // this will run on each state change if the specified state is
  1031. // not achieved yet. it will run their function when it is achieved
  1032. function checkState() {
  1033. if (document.readyState === state) {
  1034. runFunc();
  1035. JSL.clearInterval(intv);
  1036. }
  1037. }
  1038.  
  1039. if ( core.arr_indexOf.call(states, state) <= core.arr_indexOf.call(states, document.readyState) ) {
  1040. // we are at, or have missed, our desired state
  1041. // run the specified function
  1042. runFunc();
  1043. } else {
  1044. intv = JSL.setInterval(checkState, 200);
  1045. }
  1046. },
  1047.  
  1048. setInterval : function setInterval(func, delay) {
  1049. var index = intervals.length,
  1050. delay_orig = delay,
  1051. count = 1, startTime;
  1052.  
  1053. function doRe(func, delay) {
  1054. return window.setTimeout(function () {
  1055. // drift accomodation
  1056. var difference = ( new Date().getTime() ) - startTime,
  1057. correctTime = delay_orig * count,
  1058. drift = difference - correctTime;
  1059.  
  1060. // execute the function before setting a new timeout
  1061. func.call(null);
  1062.  
  1063. // fix for when a timeout takes longer than double the original delay time to execute
  1064. if (drift > delay_orig) {
  1065. drift = delay_orig;
  1066. }
  1067.  
  1068. // save the reference of the new timeout in our 'intervals' stack
  1069. if (intervals[index] !== null) {
  1070. intervals[index] = doRe(func, delay_orig - drift);
  1071. }
  1072.  
  1073. count += 1;
  1074. }, delay);
  1075. }
  1076.  
  1077. startTime = new Date().getTime();
  1078. intervals[index] = doRe(func, delay_orig);
  1079.  
  1080. return index;
  1081. },
  1082.  
  1083. toArray : function toArray(arr) {
  1084. var newArr = [], // new array to store the values into
  1085. len = arr.length || arr.snapshotLength,
  1086. item, i;
  1087.  
  1088. if (typeof len === 'number' && len > 0) {
  1089. if (typeof arr.snapshotItem === 'function') {
  1090. for (i = 0; ( item = arr.snapshotItem(i) ); i += 1) {
  1091. newArr.push(item);
  1092. }
  1093. } else {
  1094. // if the specified 'list' is array-like, use slice on it
  1095. // to convert it to an array
  1096. newArr = core.slice.call(arr, 0);
  1097. }
  1098. }
  1099.  
  1100. return newArr;
  1101. },
  1102.  
  1103. toString : function toString(item) {
  1104. var key, value, values = [];
  1105.  
  1106. function stringifyValue(val) {
  1107. var typeOfVal = JSL.typeOf(val),
  1108. toStringValue = core.toString.call(val);
  1109.  
  1110. if (typeOfVal === 'null' || typeOfVal === 'undefined') {
  1111. val = typeOfVal;
  1112. } else if (typeof val === 'string') {
  1113. if (val.length > 15) { // truncate strings longer than 15 characters
  1114. val = '"' + val.substring(0, 12) + '"...';
  1115. } else {
  1116. val = '"' + val + '"';
  1117. }
  1118. } else if (typeOfVal === 'function') {
  1119. val = val.toString().substring(0, 20);
  1120. } else if (typeOfVal !== 'number' && typeOfVal !== 'boolean') {
  1121. val = toStringValue;
  1122. }
  1123.  
  1124. return val;
  1125. }
  1126.  
  1127. switch( JSL.typeOf(item) ) {
  1128. case 'object': {
  1129. for (key in item) {
  1130. if ( item.hasOwnProperty(key) ) {
  1131. value = stringifyValue( item[key] );
  1132. values.push( '"' + key + '" : ' + value );
  1133. }
  1134. }
  1135. return '{\n ' + values.join(',\n ') + '\n}';
  1136. }
  1137. // --------------------------------------
  1138. case 'array': {
  1139. item = core.map.call(item, function (thisValue) {
  1140. return stringifyValue(thisValue);
  1141. });
  1142. return '[\n ' + item.join(',\n ') + '\n]';
  1143. }
  1144. // --------------------------------------
  1145. case 'string': {
  1146. return '"' + item + '"';
  1147. }
  1148. // --------------------------------------
  1149. case 'number': {
  1150. item = parseInt(item, 10);
  1151. if ( isNaN(item) ) { // no ternary operator, for clarity
  1152. return 'NaN';
  1153. } else {
  1154. return item.toString();
  1155. }
  1156. }
  1157. // --------------------------------------
  1158. case 'regexp': {
  1159. if (item.toString().length <= 20) {
  1160. item.toString();
  1161. } else {
  1162. return '[object RegExp]';
  1163. }
  1164. }
  1165. // --------------------------------------
  1166. case 'function': case 'boolean': {
  1167. return item.toString();
  1168. }
  1169. // --------------------------------------
  1170. case 'null': {
  1171. return 'null';
  1172. }
  1173. // --------------------------------------
  1174. case 'undefined': {
  1175. return 'undefined';
  1176. }
  1177. // --------------------------------------
  1178. default: {
  1179. return core.toString.call(item);
  1180. }
  1181. }
  1182. },
  1183.  
  1184. // typeOf by Douglas Crockford. modified by JoeSimmons
  1185. typeOf : function typeOf(value) {
  1186. var s = typeof value,
  1187. ostr = core.toString.call(value);
  1188.  
  1189. if (s === 'object' || s === 'function') {
  1190. if (value) {
  1191. if (ostr === '[object Array]') {
  1192. s = 'array';
  1193. } else if ( ostr === '[object Text]' || ostr.match(rElementObject) ) {
  1194. s = 'element';
  1195. } else if (ostr === '[object HTMLCollection]') {
  1196. s = 'collection';
  1197. } else if (ostr === '[object NodeList]') {
  1198. s = 'nodelist';
  1199. } else if (ostr === '[object Arguments]') {
  1200. s = 'arguments';
  1201. } else if (ostr === '[object RegExp]') {
  1202. s = 'regexp';
  1203. }
  1204. } else {
  1205. s = 'null';
  1206. }
  1207. }
  1208. return s;
  1209. },
  1210.  
  1211. waitFor : function waitFor(info) {
  1212. var verifier = function () { return true; },
  1213. done = info ? info.done : null,
  1214. i, selector, context, waitForInterval;
  1215.  
  1216. if (info == null || typeof done !== 'function') { return; }
  1217.  
  1218. switch ( JSL.typeOf(info.selector) ) {
  1219. case 'string': case 'element': case 'array': {
  1220. selector = info.selector;
  1221. break;
  1222. }
  1223. default: {
  1224. return error('Invalid selector passed to JSL.waitFor()');
  1225. }
  1226. }
  1227.  
  1228. switch ( JSL.typeOf(info.context) ) {
  1229. case 'string': case 'element': case 'array': {
  1230. context = info.context;
  1231. }
  1232. }
  1233.  
  1234. if (typeof info.verifier === 'function' && info.verifier.toString().indexOf('return ') !== -1) {
  1235. verifier = info.verifier;
  1236. }
  1237.  
  1238. function clear() {
  1239. JSL.clearInterval(waitForInterval);
  1240. }
  1241.  
  1242. function check() {
  1243. var elem = JSL(selector, context);
  1244.  
  1245. if (elem.exists && verifier(elem) === true) {
  1246. done(elem);
  1247. return clear();
  1248. }
  1249.  
  1250. if (i >= 150) { // check for 30 seconds max
  1251. return clear();
  1252. }
  1253.  
  1254. i += 1;
  1255. }
  1256.  
  1257. waitForInterval = JSL.setInterval(check, 200);
  1258. },
  1259.  
  1260. xpath : function xpath(obj) {
  1261. var type = obj.type || 7,
  1262. types = {
  1263. '1' : 'numberValue',
  1264. '2' : 'stringValue',
  1265. '3' : 'booleanValue',
  1266. '8' : 'singleNodeValue',
  1267. '9' : 'singleNodeValue'
  1268. },
  1269. expression = obj.expression,
  1270. context = obj.context || document,
  1271. doc = document, xp;
  1272.  
  1273. if (typeof context.evaluate === 'function') {
  1274. doc = context;
  1275. } else if (typeof context.ownerDocument.evaluate === 'function') {
  1276. doc = context.ownerDocument;
  1277. }
  1278.  
  1279. xp = doc.evaluate(expression, context, null, type, null);
  1280.  
  1281. if (!expression) {
  1282. error('An expression must be supplied for JSL.xpath()');
  1283. return null;
  1284. }
  1285.  
  1286. if ( types[type] ) {
  1287. return xp[ types[ type ] ];
  1288. } else {
  1289. return JSL.toArray(xp);
  1290. }
  1291. }
  1292. });
  1293.  
  1294. // assign JSL to the window object
  1295. window.JSL = window._J = JSL;
  1296.  
  1297. // just for testing purposes
  1298. //unsafeWindow.JSL = unsafeWindow._J = JSL;
  1299.  
  1300. }(window));
  1301.  
  1302.  
  1303. /*
  1304. // JSL test button
  1305. // use it to test code on user click (non-automatic)
  1306. (function () {
  1307. var mo = new MutationObserver(function (mutations) {
  1308. mutations.forEach(function (mutation) {
  1309. var target = mutation.target;
  1310.  
  1311. if (mutation.attributeName === 'value' && target.value !== 'Run JSL test') {
  1312. target.value = 'Run JSL test';
  1313. }
  1314. });
  1315. });
  1316.  
  1317. JSL(document.body).append(
  1318. 'input',
  1319. {
  1320. id : 'jsl_user_test',
  1321. type : 'button',
  1322. value : 'Run JSL test',
  1323. style : 'display: block; position: fixed; top: 4px; right: 4px; z-index: 999999; padding: 2px 14px; font-size: 11pt; font-family: Arial, Verdana;',
  1324. onclick : function () {
  1325.  
  1326. // ---- ENTER ONCLICK CODE HERE ----
  1327. window.setTimeout(function () {
  1328. JSL(document.body).append('<div id="waitForTest">I\'m a JSL.waitFor() test DIV!</div>');
  1329. }, 1500);
  1330.  
  1331. JSL.waitFor({
  1332. selector : '#waitForTest',
  1333. done : function (elem) {
  1334. alert('#waitForTest is loaded!');
  1335. }
  1336. });
  1337. // ---------------------------------
  1338. }
  1339. }
  1340. );
  1341.  
  1342. mo.observe( JSL('#jsl_user_test')[0], { attributes : true } );
  1343. }());
  1344. */