🏠 Home 

JoeSimmons' Library

A JavaScript library used by JoeSimmons

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