JoeSimmons' Library

A JavaScript library used by JoeSimmons

لا ينبغي أن لا يتم تثبيت هذا السكريت مباشرة. هو مكتبة لسكبتات لتشمل مع التوجيه الفوقية // @require https://update.greatest.deepsurf.us/scripts/1885/7915/JoeSimmons%27%20Library.js

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