JoeSimmons' Library

A JavaScript library used by JoeSimmons

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/22443/142782/JoeSimmons%27%20Library.js

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