สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greasyfork.org/scripts/22443/142782/JoeSimmons%27%20Library.js
// ==UserScript==
// @name JoeSimmons' Library
// @namespace http://userscripts-mirror.org/users/23652
// @description A JavaScript library used by JoeSimmons
// @include *
// @copyright JoeSimmons
// @author JoeSimmons
// @version 1.3.0
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @grant GM_addStyle
// ==/UserScript==
/**
WIKI ==> https://github.com/joesimmons/jsl/wiki/
SOURCE ==> https://raw.githubusercontent.com/joesimmons/jsl/master/versions/jsl-1.3.0.js
**/
(function (window, undefined) {
'use strict'; // use strict mode in ECMAScript-5
var version = '1.3.0'; // this will be used for JSL.prototype.version
var intervals = []; // for the setInterval/clearInterval methods
// regular expressions
var rSelector = /^\*|^\.[a-z][\w\d-]*|^#[^ ]+|^[a-z]+|^\[a-z]+/i; // matches a CSS selector
var rXpath = /^\.?\/{1,2}[a-zA-Z\*]+/; // matches an XPath query
var rHTML = /<[^>]+>/; // matches a string of HTML
var rHyphenated = /-([a-zA-Z])/g; // matches alphabetic, hyphenated strings
var rElementObject = /^\[object HTML([a-zA-Z]+)?Element\]$/; // matches the toString value of an element
var rWindowObject = /^\[object Window\]$/; // matches the toString value of a window object
var rValidVarname = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/; // matches a valid variable name
// compatibility methods for browsers that don't support ECMAScript-5 completely
var compat = {
'arr_indexOf' : function (searchElement, fromIndex) {
var index = parseInt(fromIndex || 0, 10), len = this.length;
index = index < 0 ? len + index : index; // handle negative fromIndex
index = !(index > 0) ? 0 : index; // handle out of range and/or NaN fromIndex
while (index < len && index >= 0) {
if (this[index] === searchElement) {
return index;
}
index += 1;
}
return -1;
},
/*
'filter' : function (fn, oThis) {
var index, value, len = this.length, ret = [];
for (index = 0; index < len; index += 1) {
value = this[index];
if ( fn.call(oThis, value, index, this) ) {
ret.push(value);
}
}
return ret;
},
*/
'forEach' : function (fn, oThis) {
var index, len;
for (index = 0, len = this.length; index < len; index += 1) {
fn.call(oThis, this[index], index, this);
}
},
'map' : function (fn, oThis) {
var index, newArr = [], len;
for (index = 0, len = this.length; index < len; index += 1) {
newArr[index] = fn.call(oThis, this[index], index, this);
}
return newArr;
},
'reduce' : function (fn, initialValue) {
var index, len, value, isValueSet = false;
if (arguments.length > 1) {
value = initialValue;
isValueSet = true;
}
for (index = 0, len = this.length; index < len; index += 1) {
if (isValueSet) {
value = fn(value, this[index], index, this);
} else {
value = this[index];
isValueSet = true;
}
}
return value;
}
};
// gets a method from an object's prototype. returns undefined if not found
var getMethod = function (obj, method) {
if (typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function') {
obj = XPCNativeWrapper.unwrap(obj);
} else if (obj.wrappedJSObject) {
obj = obj.wrappedJSObject;
}
if (obj.prototype && typeof obj.prototype[method] === 'function') {
return obj.prototype[method];
}
};
// original methods for some common uses
var core = {
// array
'arr_indexOf' : getMethod(Array, 'indexOf') || compat.arr_indexOf,
'concat' : getMethod(Array, 'concat'),
'filter' : getMethod(Array, 'filter') || compat.filter,
'forEach' : getMethod(Array, 'forEach') || compat.forEach,
'map' : getMethod(Array, 'map') || compat.map,
'reduce' : getMethod(Array, 'reduce') || compat.reduce,
'slice' : getMethod(Array, 'slice'),
// object
'hasOwnProperty' : getMethod(Object, 'hasOwnProperty'),
'toString' : getMethod(Object, 'toString'),
};
var JSL = function JSL(selector, context) {
return new JSL.fn.init(selector, context);
};
// a simple class for dealing with event listener handlers
var handlers = {
stack : [],
add : function (thisElement, type, fn) {
this.stack.push({
element : thisElement,
type : type,
fn : fn
});
},
get : function (thisElement, type) {
var events = [];
type = typeof type === 'string' ? type : '*';
JSL.each(this.stack, function (thisEventObj) {
if (thisElement === thisEventObj.element) {
if (type === '*' || thisEventObj.type === type) {
events.push(thisEventObj);
}
}
});
return events;
},
remove : function (thisElement, type) {
var handlerIndices = [], that = this;
// find all the indices of what we need to remove
JSL.each(handlers.get(thisElement, type), function (thisEventObj, index, array) {
handlerIndices.push(
core.arr_indexOf.call(that.stack, thisEventObj)
);
});
// remove all the indices here, using a separate array of indices
// we can't do this as we loop over the (stack) array itself, because
// we would be removing values as they are being iterated through
JSL.each(handlerIndices, function (thisIndex) {
that.stack.splice(thisIndex, 1);
});
}
};
// Node.prototype.matchesSelector compat for vendor prefixes
function matchesSelector(element, selector) {
if (element && typeof selector === 'string') {
if (typeof element.mozMatchesSelector === 'function') {
// Mozilla
return element.mozMatchesSelector(selector);
} else if (typeof element.webkitMatchesSelector === 'function') {
// Webkit
return element.webkitMatchesSelector(selector);
} else if (typeof element.oMatchesSelector === 'function') {
// Opera
return element.oMatchesSelector(selector);
} else if (typeof element.msMatchesSelector === 'function') {
// IE
return element.msMatchesSelector(selector);
}
}
return false;
}
// calls 'this' with the first parameter as the first argument
function call(a) {
return this(a);
}
function toCamelCase(string) {
return string.replace(rHyphenated, function (fullMatch, firstGroup) {
return firstGroup.toUpperCase();
});
}
// walkTheDom by Douglas Crockford
function walkTheDom(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDom(node, func);
node = node.nextSibling;
}
}
// can pluck a key out of an object
function pluck(obj) {
var subs = this.split('.'),
ret = obj, i;
for (i = 0; i < subs.length; i += 1) {
ret = ret[ subs[i] ];
if (ret == null) {
return '';
}
}
return ret;
}
function sum(curValue, nextValue) {
return curValue + nextValue;
}
function sumInt(curValue, nextValue) {
return parseInt(curValue, 10) + parseInt(nextValue, 10);
}
// internal function for throwing errors, so the user gets
// some sort of hint as to why their operation failed
function error(errorString) {
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(errorString);
}
return null; // always return null
}
// will copy an element and return a new copy with the same event listeners
function cloneElement(thisElement) {
var newElement = thisElement.cloneNode(true);
// clone event listeners of element
JSL.each(handlers.get(thisElement), function (thisEventObj) {
JSL.addEvent(newElement, thisEventObj.type, thisEventObj.fn);
});
return newElement;
}
function getEachElements(array, selector, key, type) {
var newElementsArray = [],
isValidSelector = typeof selector === 'string' && selector.trim() !== '';
JSL.each(array, function (currentElement) {
while ( currentElement = currentElement[key] ) { // note: intentional assignment
if (type > 0 ? currentElement.nodeType === type : true) {
if ( isValidSelector === false || JSL(currentElement).filter(selector).exists ) {
newElementsArray.push(currentElement);
return;
}
}
}
});
return newElementsArray;
}
// this will take
function doElementOperationOnEach(args, op) {
var newElementsArray = [], newElement,
passedElements = JSL.create.apply(JSL, args);
if (this.exists) {
if (JSL.typeOf(passedElements) === 'array') {
this.each(function (thisElement) {
JSL.each(passedElements, function (passedElement) {
// clone the element
var newElement = cloneElement(passedElement);
// add the new elements to an array
newElementsArray.push(newElement);
// perform the passed operation on the element
op(thisElement, newElement);
});
});
} else {
this.each(function (thisElement) {
// clone the element
var newElement = cloneElement(passedElements);
// add the new elements to an array
newElementsArray.push(newElement);
// perform the passed operation on the element
op(thisElement, newElement);
});
}
}
return newElementsArray;
}
// define JSL's prototype, aka JSL.fn
JSL.fn = JSL.prototype = {
isJSL : true,
constructor : JSL,
length : 0,
version : version,
// similar to jQuery. JSL is just the init constructor
init : function (selector, context) {
var selectorStringValue = core.toString.call(selector),
that = this,
elems = [];
switch (typeof selector) {
case 'string': { // -- STRING --
if ( selector.match(rXpath) ) {
// handle an XPath expression
elems = JSL.xpath({expression : selector, type : 7, context : context});
} else if ( selector.match(rHTML) ) {
// reserved for html code creation
// not sure if I want to implement it
} else if ( selector.match(rSelector) ) {
if (JSL.typeOf(context) === 'array') {
// handle an array being passed as the context
return that.find.call(context, selector);
} else if (typeof context === 'string') {
// handle a selector being passsed as the context
context = JSL(context);
if (context.exists) {
return JSL(selector, context[0]);
}
} else if (context != null && context.isJSL === true && context.exists) {
// handle a JSL object being passsed as the context
return JSL( selector, context[0] );
} else {
// handle a regular element being passed as the context
context = context != null && context.querySelectorAll ? context : document;
elems = context.querySelectorAll(selector);
}
}
break;
}
// ---------------------------------------------------
case 'object': { // -- OBJECT --
if (selector != null) {
if (selector.isJSL === true) {
// handle a JSL object
return selector;
} else if ( core.hasOwnProperty.call(selector, 'length') ) {
// handle an array-like object
elems = selector;
} else if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
// handle a single element
elems = [selector];
}
}
break;
}
// ---------------------------------------------------
default: { // -- UNKNOWN --
if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
// handle elements that are typeof === 'function'
// e.g., object, applet, embed
elems = [selector];
}
}
}
// define the length property of our object wrapper
that.length = elems.length;
// bind the elements to array-like key:value pairs in our wrapper
// e.g., this[0] ==> element
JSL.each(elems, function (value, index) {
that[index] = value;
});
return that;
},
// --- STARTING LINE FOR THE JSL WRAPPER METHODS
add : function (selector, context) {
var newElements = JSL(selector, context).raw(),
allElements = core.concat.call(this.raw(), newElements);
return JSL(allElements);
},
addEvent : function (type, fn) {
return this.each(function (thisElement) {
JSL.addEvent(thisElement, type, fn);
});
},
after : function () {
var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
var parent = baseElement.parentNode,
next = baseElement.nextSibling;
if (parent) {
if (next) {
// add the newElement after the current element
parent.insertBefore(newElement, next);
} else {
// nextSibling didn't exist. just append to its parent
parent.appendChild(newElement);
}
}
});
return JSL(newElementsArray);
},
append : function () {
var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
baseElement.appendChild(newElement);
});
return JSL(newElementsArray);
},
attribute : function (name, value) {
var ret = '', valueIsValid = value != null;
if ( typeof name === 'string' && this.exists ) {
this.each(function (elem) {
if (valueIsValid) {
elem.setAttribute(name, value);
} else {
ret += elem.getAttribute(name) || '';
}
});
}
return valueIsValid ? this : ret;
},
before : function () {
var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
var parent = baseElement.parentNode;
// add the newElement before the current element
if (parent) {
parent.insertBefore(newElement, baseElement);
}
});
return JSL(newElementsArray);
},
center : function () {
return this.each(function (thisElement) {
thisElement = JSL(thisElement);
thisElement.css('position', 'fixed');
thisElement.css('top', Math.floor( (window.innerHeight - thisElement.height) / 2 ) + 'px');
thisElement.css('left', Math.floor( (window.innerWidth - thisElement.width) / 2 ) + 'px');
});
},
clone : function () {
var clonedElements = core.map.call(this, cloneElement); // variable for clarity
return JSL(clonedElements);
},
css : function (name, value) {
if (typeof name === 'string') {
// convert the hyphenated string to camel-case
name = toCamelCase(name);
if (typeof value === 'string') {
return this.each(function (thisElement) {
if (name in thisElement.style) {
thisElement.style[name] = value;
}
});
}
return core.map.call(this, pluck, 'style.' + name).join('');
} else {
return error('.css() was not passed a string for the first argument.');
}
},
each : function (fn, oThis) {
if (this.exists) {
JSL.each(this, fn, oThis);
}
return this;
},
get exists() {
return this.length > 0 && this[0] != null;
},
filter : function (selector) {
var newElementsArray = [];
if (typeof selector === 'string') {
this.each(function (thisElement) {
if ( matchesSelector(thisElement, selector) ) {
newElementsArray.push(thisElement);
}
});
}
// returns an empty JSL object if no elements are matched
return JSL(newElementsArray);
},
find : function (selector) {
var arrayOfMatchesArrays = core.map.call(this, function (thisElement) {
var matches = thisElement.querySelectorAll(selector);
return JSL.toArray(matches);
});
var singleArrayOfMatches = arrayOfMatchesArrays.length > 0 ?
core.reduce.call(arrayOfMatchesArrays, function (a, b) {
return core.concat.call(a, b);
}) : [];
return JSL(singleArrayOfMatches);
},
first : function () {
return this.get(0);
},
get : function (index) {
index = index === 'first' ? 0 : index === 'last' ? -1 : parseInt(index, 10);
if ( !isNaN(index) ) {
return JSL( index < 0 ? this[this.length + index] : this[index] );
}
return JSL.toArray(this);
},
get height() {
var arrayOfElemHeights = core.map.call(this, pluck, 'offsetHeight');
return core.reduce.call(arrayOfElemHeights, sum);
},
has : function (selector) {
var newElementsArray = [];
if ( typeof selector === 'string' && selector.match(rSelector) ) {
this.each(function (thisElement) {
if ( JSL(selector, thisElement).exists ) {
newElementsArray.push(thisElement);
}
});
}
return JSL(newElementsArray);
},
hide : function () {
return this.css('display', 'none');
},
/*
get inView(passedContainer) {
var isInView = false;
this.each(function (thisElement) {
var container = passedContainer || thisElement.parentNode;
var visible = !!( (container.scrollTop + container.offsetHeight) >= thisElement.offsetTop &&
(container.scrollTop - thisElement.offsetHeight) <= thisElement.offsetTop );
if (visible) {
isInView = true;
return 'stop';
}
});
return isInView;
},
*/
is : function (selector) {
for (var i = 0; i < this.length; i += 1) {
if ( matchesSelector(this[i], selector) ) {
return true;
}
}
return false;
},
isnt : function (selector) {
return !this.is(selector);
},
last : function (selector) {
return this.get(-1);
},
next : function (selector) {
return JSL( getEachElements(this, selector, 'nextSibling', 1) );
},
not : function (selector) {
var newElementsArray = [];
if ( typeof selector === 'string' && selector.match(rSelector) ) {
this.each(function (thisElement) {
if ( JSL(thisElement).isnt(selector) ) {
newElementsArray.push(thisElement);
}
});
}
return JSL(newElementsArray);
},
parent : function (selector) {
return JSL( getEachElements(this, selector, 'parentNode', 1) );
},
prepend : function () {
var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
var firstChild = baseElement.firstChild;
if (firstChild) {
baseElement.insertBefore(newElement, firstChild);
}
});
return JSL(newElementsArray);
},
prev : function (selector) {
return JSL( getEachElements(this, selector, 'previousSibling', 1) );
},
prop : function (name, value) {
var valueIsValid = value != null, ret;
if (typeof name === 'string' && this.exists) {
this.each(function (thisElement) {
if (valueIsValid) {
thisElement[name] = value;
} else {
if (typeof ret === 'undefined') {
ret = thisElement[name];
} else {
ret += thisElement[name];
}
}
});
}
return valueIsValid ? this : ret;
},
raw : function () {
return core.slice.call(this, 0);
},
remove : function () {
return this.each(function (element) {
var parent = element.parentNode;
if (element && parent) {
parent.removeChild(element);
}
});
},
removeAttribute : function (attributeName) {
if (typeof attributeName === 'string') {
return this.each(function (thisElement) {
thisElement.removeAttribute(attributeName);
});
} else {
return error('.removeAttribute() was not passed a string.');
}
},
removeEvent : function (type) {
if (typeof type === 'string') {
return this.each(function (thisElement) {
JSL.removeEvent(thisElement, type);
});
} else {
return error('.removeEvent() was not passed a string.');
}
},
replace : function () {
var newElementsArray = doElementOperationOnEach.call(this, JSL.toArray(arguments), function (baseElement, newElement) {
var parent = baseElement.parentNode;
if (parent) {
parent.replaceChild(newElement, baseElement);
}
});
return JSL(newElementsArray);
},
show : function (value) {
value = typeof value === 'string' ? value : 'inline';
return this.css('display', value);
},
text : function (passedText, append) {
// handle setting text
if (typeof passedText === 'string') {
if (append !== true) {
this.each(function (thisElement) {
JSL('.//text()', thisElement).each(function (textNode) {
textNode.data = '';
});
});
}
this.append('text', passedText);
return this;
}
// handle getting text
return core.reduce.call(this, function (curValue, nextElement) {
return curValue + nextElement.textContent;
}, '');
},
toggle : function () {
return this.each(function (thisElement) {
thisElement = JSL(thisElement);
if (thisElement.visible) {
thisElement.hide();
} else {
thisElement.show();
}
});
},
value : function (passedValue) {
var elem = this[0],
tagName = elem && elem.tagName || '',
selectedOptions = [],
rInputTypeBlacklist = /button|checkbox|file|image|radio|reset|submit/,
passedValueType = JSL.typeOf(passedValue);
if (passedValue == null) {
// no arguments were passed, return a value
if (tagName === 'SELECT') {
if ( elem.hasAttribute('multiple') ) {
JSL.each(elem.options, function (thisOption) {
if (thisOption.selected) {
selectedOptions.push(thisOption.value);
}
});
return selectedOptions;
} else {
return elem.options[elem.selectedIndex].value;
}
} else if ( tagName === 'INPUT' && !elem.type.match(rInputTypeBlacklist) ) {
return elem.value;
}
if (tagName === 'TEXTAREA') {
return elem.value;
}
} else {
// an argument was passed, set the value on each element
return this.each(function (thisElement) {
var tagName = thisElement.tagName;
if (tagName === 'SELECT') {
if (thisElement.hasAttribute('multiple') && passedValueType === 'array') {
JSL.each(thisElement.options, function (thisOption) {
JSL.each(passedValue, function (thisPassedValue) {
if (thisOption.value == thisPassedValue) {
thisOption.selected = true;
return 'stop';
} else {
thisOption.selected = false;
}
});
});
} else {
JSL.each(thisElement.options, function (thisOption) {
thisOption.selected = thisOption.value == passedValue;
});
}
} else if (tagName === 'INPUT') {
if ( !thisElement.type.match(rInputTypeBlacklist) ) {
thisElement.value = passedValue;
} else if (thisElement.type === 'checkbox' || thisElement.type === 'radio') {
if (passedValueType === 'array') {
JSL.each(passedValue, function (thisPassedValue) {
if (thisElement.value == thisPassedValue) {
thisElement.checked = true;
return 'stop';
} else {
thisElement.checked = false;
}
});
} else if (thisElement.value == passedValue) {
thisElement.checked = true;
}
}
} else if (tagName === 'TEXTAREA') {
thisElement.value = passedValue;
}
});
}
return null;
},
get visible() {
return Math.max(this.width, this.height) > 0;
},
get width() {
var arrayOfElemHeights = core.map.call(this, pluck, 'offsetWidth');
return core.reduce.call(arrayOfElemHeights, sum);
},
};
// give the init function the JSL prototype for later instantiation
JSL.fn.init.prototype = JSL.fn;
// extend method. can extend any object it's run upon
JSL.fn.extend = JSL.extend = function (obj) {
var name, copy;
for (name in obj) {
copy = obj[name];
if ( !core.hasOwnProperty.call(this, name) && typeof copy !== 'undefined' ) {
this[name] = copy;
}
}
};
// --- STARTLING LINE FOR THE DIRECT JSL METHODS
JSL.extend({
addEvent : function addEvent(thisElement, type, fn) {
if (thisElement != null && typeof type === 'string' && typeof fn === 'function') {
if (typeof thisElement.addEventListener === 'function') {
thisElement.addEventListener(type, fn, false);
} else if (typeof thisElement.attachEvent === 'function') {
type = 'on' + type;
thisElement.attachEvent(type, fn);
} else {
return;
}
handlers.add(thisElement, type, fn);
}
},
addScript : function addScript(contents, id, node) {
var newElement = document.createElement('script');
newElement.id = id || ( 'jsl-script-' + JSL.random(999) );
newElement.innerHTML = contents;
node = node || document.head || document.querySelector('html > head');
node.appendChild(newElement);
return {
remove : function () {
node.removeChild(newElement);
}
};
},
addStyle : function addStyle(css, id, node) {
id = id || ( 'jsl-style-' + JSL.random(999) );
node = node || document.head || document.querySelector('html > head');
if (node) {
node.appendChild(
JSL.create('style', {id : id, type : 'text/css'}, [ JSL.create('text', css) ] )
);
}
},
alias : function alias(newAlias) {
if (typeof newAlias === 'string' && newAlias.match(rValidVarname) && typeof window[newAlias] === 'undefined') {
window[newAlias] = JSL;
}
},
clearInterval : function clearInterval(index) {
if (typeof index === 'number' && index < intervals.length) {
window.clearTimeout( intervals[index] );
intervals[index] = null;
}
},
create : function create(elementName, descObj, kidsArray) {
var argsLength = arguments.length,
typeValue, prop, val, HTMLholder, ret, i;
if (argsLength === 2 && elementName === 'text' && typeof descObj === 'string') {
// handle text node creation
return document.createTextNode(descObj);
} else if ( argsLength === 1 && typeof elementName === 'string' && elementName.match(rHTML) ) {
// handle HTML strings
// take the HTML string and put it inside a div
HTMLholder = document.createElement('div');
HTMLholder.innerHTML = elementName;
// add each childNode to an array to return
ret = [];
ret.push.apply(ret, HTMLholder.childNodes);
return ret.length > 0 ? (ret.length === 1 ? ret[0] : ret) : null;
} else if (argsLength > 1 && typeof elementName === 'string' && typeof descObj === 'object') {
// handle the normal element name and descriptor object
ret = document.createElement(elementName + '');
for (prop in descObj) {
if ( core.hasOwnProperty.call(descObj, prop) ) {
val = descObj[prop];
if (prop.indexOf('on') === 0 && typeof val === 'function') {
JSL.addEvent(ret, prop.substring(2), val);
} else if ( prop !== 'style' && prop !== 'class' && prop in ret && typeof ret[prop] !== 'undefined' ) {
ret[prop] = val;
} else {
ret.setAttribute(prop, val);
}
}
}
if (JSL.typeOf(kidsArray) === 'array') {
JSL.each(kidsArray, function (kid) {
var val, item, i;
if (typeof kid === 'string') {
val = JSL.create(kid)
if (JSL.typeOf(val) === 'array') {
for (i = 0; i < val.length; i += 1) {
ret.appendChild( val[i] );
}
} else if (JSL.typeOf(kid) === 'element') {
ret.appendChild(kid);
}
} else if (JSL.typeOf(kid) === 'element') {
ret.appendChild(kid);
}
});
}
return ret;
} else if (argsLength === 1 && JSL.typeOf(elementName) === 'element') {
// handle an element
return elementName;
}
},
each : function each(passedArray, fn, oThis) {
var isOthisUndefined = typeof oThis !== 'undefined',
index, len, otherThis, value;
for (index = 0; index < passedArray.length; index += 1) {
value = passedArray[index];
otherThis = isOthisUndefined ? oThis : value;
if (fn.call(otherThis, value, index, passedArray) === 'stop') {
break;
}
}
},
loop : function loop(maxIterations, fn) {
var args = JSL.toArray(arguments), i;
if (typeof maxIterations === 'number' && maxIterations > 0 && typeof fn === 'function') {
args = args.slice(2);
for (i = 0; i < maxIterations; i += 1) {
fn.apply(null, args);
}
}
},
random : function random(maxInteger, minInteger) {
var rand = -1;
while (rand < 0 || rand > maxInteger || rand < minInteger) {
rand = Math.floor( Math.random() * maxInteger ) + Math.round( Math.random() );
}
return rand;
},
removeEvent : function removeEvent(thisElement, type) {
JSL.each(handlers.get(thisElement, type), function (thisEventObj) {
if (typeof thisElement.removeEventListener === 'function') {
thisEventObj.element.removeEventListener(thisEventObj.type, thisEventObj.fn, false);
} else if (typeof thisElement.detachEvent === 'function') {
type = 'on' + type;
thisEventObj.element.detachEvent(thisEventObj.type, thisEventObj.fn);
}
handlers.remove(thisElement, type);
});
},
runAt : function runAt(state, func, oThis) {
var args = JSL.toArray(arguments), intv,
// compose a list of the 4 states, to use .indexOf() upon later
states = ['uninitialized', 'loading', 'interactive', 'complete'],
// in-case they pass [start/end] instead of [loading/complete]
state = state.replace('start', 'loading').replace('end', 'complete');
// this will run their function with the specified arguments, if any,
// and a custom 'this' value, if specified
function runFunc() {
func.apply( oThis, args.slice(3) );
}
// this will run on each state change if the specified state is
// not achieved yet. it will run their function when it is achieved
function checkState() {
if (document.readyState === state) {
runFunc();
JSL.clearInterval(intv);
}
}
if ( core.arr_indexOf.call(states, state) <= core.arr_indexOf.call(states, document.readyState) ) {
// we are at, or have missed, our desired state
// run the specified function
runFunc();
} else {
intv = JSL.setInterval(checkState, 200);
}
},
setInterval : function setInterval(func, delay) {
var index = intervals.length,
delay_orig = delay,
count = 1, startTime;
function doRe(func, delay) {
return window.setTimeout(function () {
// drift accomodation
var difference = ( new Date().getTime() ) - startTime,
correctTime = delay_orig * count,
drift = difference - correctTime;
// execute the function before setting a new timeout
func.call(null);
// fix for when a timeout takes longer than double the original delay time to execute
if (drift > delay_orig) {
drift = delay_orig;
}
// save the reference of the new timeout in our 'intervals' stack
if (intervals[index] !== null) {
intervals[index] = doRe(func, delay_orig - drift);
}
count += 1;
}, delay);
}
startTime = new Date().getTime();
intervals[index] = doRe(func, delay_orig);
return index;
},
toArray : function toArray(arr) {
var newArr = [], // new array to store the values into
len = arr.length || arr.snapshotLength,
item, i;
if (typeof len === 'number' && len > 0) {
if (typeof arr.snapshotItem === 'function') {
for (i = 0; ( item = arr.snapshotItem(i) ); i += 1) {
newArr.push(item);
}
} else {
// if the specified 'list' is array-like, use slice on it
// to convert it to an array
newArr = core.slice.call(arr, 0);
}
}
return newArr;
},
toString : function toString(item) {
var key, value, values = [];
function stringifyValue(val) {
var typeOfVal = JSL.typeOf(val),
toStringValue = core.toString.call(val);
if (typeOfVal === 'null' || typeOfVal === 'undefined') {
val = typeOfVal;
} else if (typeof val === 'string') {
if (val.length > 15) { // truncate strings longer than 15 characters
val = '"' + val.substring(0, 12) + '"...';
} else {
val = '"' + val + '"';
}
} else if (typeOfVal === 'function') {
val = val.toString().substring(0, 20);
} else if (typeOfVal !== 'number' && typeOfVal !== 'boolean') {
val = toStringValue;
}
return val;
}
switch( JSL.typeOf(item) ) {
case 'object': {
for (key in item) {
if ( item.hasOwnProperty(key) ) {
value = stringifyValue( item[key] );
values.push( '"' + key + '" : ' + value );
}
}
return '{\n ' + values.join(',\n ') + '\n}';
}
// --------------------------------------
case 'array': {
item = core.map.call(item, function (thisValue) {
return stringifyValue(thisValue);
});
return '[\n ' + item.join(',\n ') + '\n]';
}
// --------------------------------------
case 'string': {
return '"' + item + '"';
}
// --------------------------------------
case 'number': {
item = parseInt(item, 10);
if ( isNaN(item) ) { // no ternary operator, for clarity
return 'NaN';
} else {
return item.toString();
}
}
// --------------------------------------
case 'regexp': {
if (item.toString().length <= 20) {
item.toString();
} else {
return '[object RegExp]';
}
}
// --------------------------------------
case 'function': case 'boolean': {
return item.toString();
}
// --------------------------------------
case 'null': {
return 'null';
}
// --------------------------------------
case 'undefined': {
return 'undefined';
}
// --------------------------------------
default: {
return core.toString.call(item);
}
}
},
// typeOf by Douglas Crockford. modified by JoeSimmons
typeOf : function typeOf(value) {
var s = typeof value,
ostr = core.toString.call(value);
if (s === 'object' || s === 'function') {
if (value) {
if (ostr === '[object Array]') {
s = 'array';
} else if ( ostr === '[object Text]' || ostr.match(rElementObject) ) {
s = 'element';
} else if (ostr === '[object HTMLCollection]') {
s = 'collection';
} else if (ostr === '[object NodeList]') {
s = 'nodelist';
} else if (ostr === '[object Arguments]') {
s = 'arguments';
} else if (ostr === '[object RegExp]') {
s = 'regexp';
}
} else {
s = 'null';
}
}
return s;
},
waitFor : function waitFor(info) {
var verifier = function () { return true; },
done = info ? info.done : null,
i, selector, context, waitForInterval;
if (info == null || typeof done !== 'function') { return; }
switch ( JSL.typeOf(info.selector) ) {
case 'string': case 'element': case 'array': {
selector = info.selector;
break;
}
default: {
return error('Invalid selector passed to JSL.waitFor()');
}
}
switch ( JSL.typeOf(info.context) ) {
case 'string': case 'element': case 'array': {
context = info.context;
}
}
if (typeof info.verifier === 'function' && info.verifier.toString().indexOf('return ') !== -1) {
verifier = info.verifier;
}
function clear() {
JSL.clearInterval(waitForInterval);
}
function check() {
var elem = JSL(selector, context);
if (elem.exists && verifier(elem) === true) {
done(elem);
return clear();
}
if (i >= 150) { // check for 30 seconds max
return clear();
}
i += 1;
}
waitForInterval = JSL.setInterval(check, 200);
},
xpath : function xpath(obj) {
var type = obj.type || 7,
types = {
'1' : 'numberValue',
'2' : 'stringValue',
'3' : 'booleanValue',
'8' : 'singleNodeValue',
'9' : 'singleNodeValue'
},
expression = obj.expression,
context = obj.context || document,
doc = document, xp;
if (typeof context.evaluate === 'function') {
doc = context;
} else if (typeof context.ownerDocument.evaluate === 'function') {
doc = context.ownerDocument;
}
xp = doc.evaluate(expression, context, null, type, null);
if (!expression) {
error('An expression must be supplied for JSL.xpath()');
return null;
}
if ( types[type] ) {
return xp[ types[ type ] ];
} else {
return JSL.toArray(xp);
}
}
});
// assign JSL to the window object
window.JSL = window._J = JSL;
// just for testing purposes
//unsafeWindow.JSL = unsafeWindow._J = JSL;
}(window));
/*
// JSL test button
// use it to test code on user click (non-automatic)
(function () {
var mo = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
var target = mutation.target;
if (mutation.attributeName === 'value' && target.value !== 'Run JSL test') {
target.value = 'Run JSL test';
}
});
});
JSL(document.body).append(
'input',
{
id : 'jsl_user_test',
type : 'button',
value : 'Run JSL test',
style : 'display: block; position: fixed; top: 4px; right: 4px; z-index: 999999; padding: 2px 14px; font-size: 11pt; font-family: Arial, Verdana;',
onclick : function () {
// ---- ENTER ONCLICK CODE HERE ----
window.setTimeout(function () {
JSL(document.body).append('<div id="waitForTest">I\'m a JSL.waitFor() test DIV!</div>');
}, 1500);
JSL.waitFor({
selector : '#waitForTest',
done : function (elem) {
alert('#waitForTest is loaded!');
}
});
// ---------------------------------
}
}
);
mo.observe( JSL('#jsl_user_test')[0], { attributes : true } );
}());
*/