My Function library

enter something useful

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.org/scripts/9160/219970/My%20Function%20library.js

/**
 * Created by Magnus Fohlström on 2016-09-24 at 22:56
 * with file name
 * in project TampermonkeyUserscripts.
 */
"use strict";
//// ==UserScript==
// @name            My Function library
// @namespace       http://use.i.E.your.homepage/
// @version         0.52
// @description     enter something useful
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_deleteValue
// @grant           GM_listValues
// @grant           GM_xmlhttpRequest
// @run-at          document-start

// @created         2015-04-06
// @released        2014-00-00
// @updated         2014-00-00
// @history         @version 0.25 - first version: public@released - 2015-04-12
// @history         @version 0.30 - Second version: public@released - 2015-12-10
// @history         @version 0.35 - Second version: public@released - 2016-03-04
// @history         @version 0.45 - Second version: public@released - 2016-09-24
// @history         @version 0.45 - third version: public@released - 2017-01-10
// @history         @version 0.51 - third version: public@released - 2017-08-17
// @history         @version 0.52 - third version: public@released - 2017-09-23

// @compatible      Greasemonkey, Tampermonkey
// @license         GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html)
// @copyright       2014+, Magnus Fohlström
// ==/UserScript==
/*global $, jQuery*/
/*jshint -W014, -W030, -W082*/
// -W014, laxbreak, Bad line breaking before '+'
// -W030, Expected assignment or function call instead saw an expression
// -W082, a function declaration inside a block statement

/*
 $("li").not(function() {
 // returns true for those elements with at least one span as child element
 return $(this).children('span').length > 0
 }).each(function() { /* ...  })
 */
//noinspection JSUnresolvedFunction,JSUnresolvedVariable,BadExpressionStatementJS,JSPotentiallyInvalidConstructorUsage, JSPrimitiveTypeWrapperUsage
//noinspection BadExpressionStatementJS,JSUnresolvedVariable,JSDeprecatedSymbols
performance;

function setGM(window2) {
    window2 = window2 || window;
    console.log('setGM' );
    var localStorage = localStorage;
    window2.GM_getValue      = function( key, def ){
		return localStorage[key] || def;
	};
	window2.GM_setValue      = function( key, value ){
		localStorage[key] = value;
	};
	window2.GM_deleteValue   = function( key ){
        delete localStorage[ key ];
	};
	window2.GM_listValues    = function( ){
		return Object.keys( localStorage );
	};
	window2.GM_lister        = function( remove, item ){
		var keys = GM_listValues(), i = 0, val;
		for ( i; i <= keys.length; i++ ) {
			val = keys[i];
			//noinspection JSUnresolvedVariable
            val !== undefined && (
				console.log( 'GM_ListItem: ' + val + ':', GM_getValue( val ) ),
				( ( item !== undefined && val.inElem( item ) ) || item === undefined )
				&& Boolean.parse( remove ) && GM_deleteValue( val ) );
		}
	};
}

console.log('isFunction( GM_getValue() )', $.isFunction( window.GM_getValue ) );

!!$.isFunction( window.GM_getValue ) || setGM();

(function(){

	var eventMatchers = {
		'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
		'MouseEvents': /^(?:click|mouse(?:down|up|over|move|enter|out))$/
		//'MouseEvents': /^(?:click|dblclick|hover|contextmenu|mouse(?:down|up|over|move|enter|leave|out))$/
	};

	var defaultOptions = {
		pointerX: 0,
		pointerY: 0,
		button: 0,
		ctrlKey: false,
		altKey: false,
		shiftKey: false,
		metaKey: false,
		bubbles: true,
		cancelable: true
	};

	jQuery.fn.simulate = function(eventName) {
		var element = this[0];
		var options = $.extend(true, defaultOptions, arguments[2] || { });
		var oEvent, eventType = null;

		for (var name in eventMatchers) {
			if (eventMatchers[name].test(eventName)) { eventType = name; break; }
		}

		if (!eventType)
			throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');
		 //noinspection JSUnresolvedVariable
        if (document.createEvent) {
            //noinspection JSUnresolvedFunction
			oEvent = document.createEvent(eventType);
			if (eventType == 'HTMLEvents') {
				oEvent.initEvent(eventName, options.bubbles, options.cancelable);
			}
			else {
                //noinspection JSDeprecatedSymbols,noinspection JSUnresolvedVariable
                oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
					options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
					options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
			}
			element.dispatchEvent(oEvent);
		}
		else {
			options.clientX = options.pointerX;
			options.clientY = options.pointerY;
            //noinspection JSUnresolvedFunction
            oEvent = $.extend(document.createEventObject(), options);
            element.fireEvent('on' + eventName, oEvent);
		}
		return element;
	};
})();

/*window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {
	console.debug('Error: ' + errorMsg + '\nScript: ' + url + '\nLine: ' + lineNumber
		+ '\nColumn: ' + column + '\nStackTrace: ' +  errorObj);
};*/
/**
 *  @namespace waitUntilExists_Intervals
 */
$.fn.waitUntilExists = function (handler, shouldRunHandlerOnce, isChild){
	var found	= 'found',
		$this	= $(this.selector),
		$elements	= $this.not(function () { return $(this).data(found); }).each(handler).data(found, true);
	if( !isChild ) {
        (window.waitUntilExists_Intervals = window.waitUntilExists_Intervals || {})[this.selector] =
			window.setInterval(function () {
				$this.waitUntilExists(
					handler, shouldRunHandlerOnce, true);
			}, 500);
	}
	else if (shouldRunHandlerOnce && $elements.length){
        window.clearInterval(window.waitUntilExists_Intervals[this.selector]);
	}
	return $this;
};

$.extend( $.easing,{
	easeOutBounceSmall 	: function(x, t, b, c, d) {
		var ts=(t/=d)*t;
		var tc=ts*t;
		return b+c*(-16.195*tc*ts + 49.935*ts*ts + -53.785*tc + 21.795*ts + -0.75*t);
	},
	easeOutSmoothBounce : function(x, t, b, c, d) {
		var ts=(t/=d)*t;
		var tc=ts*t;
		return b+c*(-19.4293*tc*ts + 53.3838*ts*ts + -49.8485*tc + 15.8081*ts + 1.08586*t);
	},
	easeInOutCubic		: function(x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	}
});
$.extend( $.fn, {
	// Name of our method & one argument (the parent selector)
	/**
	 * Suppress all rules containing "unused" in this
	 * class
	 *
	 * @SuppressWarnings("unused")
	 */
	//noinspection JSUnusedProperty
    isNode              : function ( node ) {
        var e = this[0] || $('<undefined/>'),
            node_name = e.nodeName;
        return node_name !== undefined && e.nodeName.toLowerCase() === node.toLowerCase();
    }
});

$.extend( $.fn, {
	// Name of our method & one argument (the parent selector)
	/**
	 * Suppress all rules containing "unused" in this
	 * class
	 *
	 * @SuppressWarnings("unused")
	 */
	//noinspection JSUnusedProperty
	within: function( pSelector ) {
		// Returns a subset of items using jQuery.filter
		return this.filter(function(){
			// Return truthy/falsey based on presence in parent
			return $(this).closest( pSelector ).length;
		});
		/* Example
		 $("li").within(".x").css("background", "red");

		 This selects all list items on the document, and then filters to only
		 those that have .x as an ancestor. Because this uses jQuery internally,
		 you could pass in a more complicated selector:

		 $("li").within(".x, .y").css("background", "red");

		 http://stackoverflow.com/questions/2389540/jquery-hasparent
		 http://stackoverflow.com/a/2389549
		 */
	}
});

$.fn.extend({
	exists              : function () {
		return !!this.length;
	},
	Exists              : function ( callback ) {
		var self = this;
		var wrapper = (function(){
			function notExists(){}
			//noinspection JSPotentiallyInvalidConstructorUsage
			notExists.prototype.ExistsNot = function( fallback ){
				!self.length && fallback.call(); };
			//noinspection JSPotentiallyInvalidConstructorUsage
			return new notExists;
		})();
		self.length && callback.call();
		return wrapper;

		/*  And now i can write code like this -
		 $("#elem").Exists(function(){
		 alert ("it exists");
		 }).ExistsNot(function(){
		 alert ("it doesn't exist");
		 });
		 */
	},
	ifExists            : function ( fn ) {
		this.length && fn( this );
		return this.length && this;
		/*
		 $("#element").ifExists( function( $this ){
		 $this.addClass('someClass').animate({marginTop:20},function(){alert('ok')});
		 });
		 */
	},
	iff            		: function ( bool, fn ) {
		( bool && this.length || !bool && !this.length) && fn( this );
        return ( bool && this.length || !bool && !this.length ) && this;
		/*
		 $("#element").if( true, function( $this ){);
		 $("#element").if( false, function( $this ){);
		 */
	},
	ifs            		: function ( fnTrue, fnFalse ) {
		fn( this.length ? fnTrue : fnFalse );
		/*
		 $("#element").ifs( function( $this ){}, function( $this ){});
		 */
	},

	hasId               : function ( id ) {
		return id === this.attr('id');
	},
	hasClasses          : function ( classes, any ) {
		classes = classes.split( classes.inElem(',') ? ',' : ' ' );
		var check = 0, i = 0;
		for ( i; i < classes.length; i++ ) {
			this.hasClass( classes[ i ] ) && check++;
			if ( any !== undefined && check !== 0 ) return true;
		}
		return check === classes.length;
	},
	hasClassLongerThan  : function ( length, addClass ) {
        var str = $( this ).addClass('å').attr('class'),
			classes = str.split( ' ' ),
			check = 0, i = 0;

        for ( i; i < classes.length; i++ ) {
            check = classes[ i ].length >= length ? check++ : check;
            classes[ i ].length >= length && $( this ).addClass( addClass );
            $( this ).removeClass('å');
        }
        return check;
    },
    hasIdLongerThan     : function ( length, addClass ) {
        var str = $( this ).hasAttr('id') ? $( this ).attr('id') : ' ', check = 0;
            check = str.length >= length ? check++ : check;

        str.length >= length && $( this ).addClass( addClass );

        return check;
    },
	hasAttrLength       : function ( attar, length, addClass ) {
        var str = $( this ).attr( attar ),
            attribs = str.attr( attar, $( this ).attr( attar ) + '  ' ).split( ' ' ),
            check = 0, i = 0;

        for ( i; i < attribs.length; i++ ) {
            check = attribs[ i ].length >= length ? check++ : check;
            //attribs[ i ].length >= length
			check && $( this ).addClass( addClass );
        }
        return check;
    },
	hasSiblings			: function ( find ) {
		return !!$(this).siblings( find ).length;
    },
	hasChildren			: function () {
        return $(this).children().length !== 0;
    },
	hasNoChildren       : function ( selection ) {
		return $( this ).filter( function(){
			return $( this ).children( selection ).length === 0;
		});
	},
	/*        hasParent       : function( parentSelection ){
	 return parentSelection.inElem('#')
	 ? this.parent().hasId( parentSelection.split('#').shift() )
	 : this.parent().hasClass( parentSelection.split('.').shift() );
	 },
	 */
	hasAncestor         : function ( Ancestor ) {
		return this.filter(function() {
			return !!$( this ).closest( Ancestor ).length;
		});
		//$('.element').hasAncestor('.container').myAction();
	},
	hasParent           : function ( selection ) {
		return !!$( this ).parent( selection ).length;
	},
	hasParents          : function ( selection ) {
		return !!$( this ).parents( selection ).length;
	},
	hasQuery            : function ( query ) {
        return document.querySelector(query).length;
	},
	hasAttr             : function ( name, val ) {
		var thisAttr = $( this ).attr( name );
		thisAttr =
			val !== undefined
				? thisAttr === val
				: thisAttr;
		return ( typeof thisAttr !== "undefined" && thisAttr !== false && thisAttr !== null );

		//return val !== undefined
		//    ? attrName === val
		//    : typeof( attrName ) !== 'undefined';  //$( this )[0].hasAttribute( name );
	},
	getThisAttr			: function ( destinationObj ) {
        var that = this,
			allAttributes = ($(that) && $(that).length > 0) ? $(that).prop("attributes") : null;

        allAttributes && $(destinationObj) && $(destinationObj).length == 1 &&
            $.each(allAttributes, function() {
                this.name == "class" ? $(destinationObj).addClass(this.value) : destinationObj.attr(this.name, this.value);
            });

        return  destinationObj !== undefined && $(destinationObj).length == 1 ? $(destinationObj) : allAttributes;
	},
    getAttrFrom			: function ( sourceObj ) {
        var that = this,
            allAttributes = ($(sourceObj) && $(sourceObj).length > 0) ? $(sourceObj).prop("attributes") : null;

        allAttributes && $(that) && $(that).length == 1 &&
			$.each(allAttributes, function() {
				this.name == "class" ? $(that).addClass(this.value) : that.attr(this.name, this.value);
			});

        return that;
    },
	isTag               : function ( tag ) {
		var e = this[0] || $('<undefined/>'),
            nodename = e.nodeName;
        return nodename !== undefined && e.nodeName.toLowerCase() === tag.toLowerCase();
	},
	isNode              : function ( node ) {
		var e = this[0] || $('<undefined/>'),
		nodename = e.nodeName;
		return nodename !== undefined && e.nodeName.toLowerCase() === node.toLowerCase();
	},

    swapClass           : function ( replace, newClass) {
        this.className.replace(replace, newClass);
    },
    toggleClasses       : function ( add, remove, if_none) {
        var $this = $(this.selector);
        if_none !== undefined && !$this.hasClass(add) && !$this.hasClass(remove) && $this.addClass(if_none);
        $this.addClass(add).removeClass(remove);
    },
	searchAttr          : function ( search, type, chkLen ) { //bool name value length or 1 2 3 4
		var Attributes = this[0].attributes;

		if ( arguments.length === 0 ) {
			var obj = {};
			$.each( Attributes, function() {
                this.specified && ( obj[ this.name ] = this.value );
			});
			return obj;
		} else if( search != undefined ) {
			var name = '', val = '';
			$.each( Attributes, function() {
                if( this.specified && type == 'length' ) {
					if( this.name.length > chkLen ) {
						name = this.name;
						c.i('Attributes', Attributes);
						return false;
					}
				}
				else {
                    if( this.specified && this.name.inElem( search ) ) {
                                        name = this.name;
                                        val = this.value;
                                        return false;
                                    }
                }
			});
			return ( type == 'bool' || type == 1 ) ? name.length :
				( type == 'name' || type == 2 ) ? name :
					( type == 'value' || type == 3 ) ? val :
					( type == 'length' || type == 4 ) && name;
		}
	},
	findClass           : function ( Class ) {
		return this.find('.' + Class);
	},
	findNextSibling     : function ( thisSelector, bool ) {
		bool = bool || true;
		return bool ? this.eq(0).nextAll( thisSelector ).eq(0) : this.nextAll( thisSelector ).eq(0) ;
	},
	findPrevSibling     : function ( thisSelector, bool ) {
		bool = bool || true;
		return bool ? this.eq(0).prevAll( thisSelector ).eq(0) : this.prevAll( thisSelector ).eq(0) ;
	},
    nthChildClass		: function(expr) {
        return this.filter(':nth-child('+ expr +')');
    },

	href                : function ( newURL ) {
		return arguments.length === 0 ? this.attr('href') : this.attr('href', newURL);
	},
	src                 : function ( newSRC ) {
		return arguments.length === 0 ? this.attr('src') : this.attr('src', newSRC);
	},
    refreshElement      : function ( speed, parentBoolean ) {
        var $elem = parentBoolean ? this.parent() : this, data = $elem.html();
        $elem.hide( speed / 2 ).empty().html( data ).fadeIn( speed );
    },
    replaceElementOld   : function ( newTagName, addAttr ) {

        function stringifyAttr( attrs ){
            var attrString = '', newAttr = addAttr || '';
            $.each( attrs, function( index ){
                attrString += ' ' + attrs[ index ].name + '="' + attrs[ index ].value + '"';
            });
            return newAttr.length ? attrString + ' ' + newAttr : attrString;
        }

        $( this ).each(function(){
            var attributes = stringifyAttr( this.attributes ),
                StartTag = '<' + newTagName + attributes +'>',
                EndTag = '</' + newTagName + '>';
            $( this ).replaceWith( StartTag + $( this ).html() + EndTag );
        });
    },
    replaceElement      : function ( newNodeName, htmlElement ) {

        newNodeName = $('<'+ newNodeName +'/>');
        htmlElement = htmlElement !== undefined ? htmlElement : $('<span/>',{ 'data-tmp':'TemparyAttribute'});

        function newElem_addAttr( attrs, newAttr, newElem ){
            $.each( attrs, function( index ){
                newElem.attr( attrs[ index ].name, attrs[ index ].value ); });
            $.each( newAttr, function( index ){
                newElem.attr( newAttr[ index ].name, newAttr[ index ].value ); });
            return newElem;
        }

        $( this ).each(function(){
            $( this ).replaceWith( newElem_addAttr(
                this.attributes, htmlElement[0].attributes, newNodeName ).html( $( this ).html() ).removeAttr('data-tmp') );
        });

        return this;
    },
	equals              : function ( compareTo ) {
		if (!compareTo || this.length != compareTo.length)
			return false;

		for (var i = 0; i < this.length; ++i) {
			if (this[i] !== compareTo[i])
				return false;
		}
		return true;
	},

    cleaner			: function () {
        $( this ).contents().filter(function () {
            return this.nodeType !== 1 && this.nodeType !== 3 && this.nodeType !== 10;
        }).remove();
        return this;
    },

    defragText			: function (){
        this.nodeType === 3 && this.normalize();
        return this;
    },
	removeText			: function () {
		$( this ).contents().filter(function () {
			 return this.nodeType === 3;
		}).remove();
		return this;
    },
	selectText			: function(){

        var range,
            selection,
            obj = this[0],
            type = {
                func:'function',
                obj:'object'
            },
            // Convenience
            is = function(type, o){
                return typeof o === type;
            };

        if( is(type.obj, obj.ownerDocument)
            && is(type.obj, obj.ownerDocument.defaultView)
            && is(type.func, obj.ownerDocument.defaultView.getSelection) ){

            selection = obj.ownerDocument.defaultView.getSelection();

            if( is(type.func, selection.setBaseAndExtent) ){
                // Chrome, Safari - nice and easy
                selection.setBaseAndExtent(obj, 0, obj, $(obj).contents().size());
            }
            else if( is(type.func, obj.ownerDocument.createRange) ){

                range = obj.ownerDocument.createRange();

                if( is(type.func, range.selectNodeContents)
                    && is(type.func, selection.removeAllRanges)
                    && is(type.func, selection.addRange)){
                    // Mozilla
                    range.selectNodeContents(obj);
                    selection.removeAllRanges();
                    selection.addRange(range);
                }
            }
        }
        else if( is(type.obj, document.body) && is(type.obj, document.body.createTextRange) ){

            range = document.body.createTextRange();

            if( is(type.obj, range.moveToElementText) && is(type.obj, range.select) ){
                // IE most likely
                range.moveToElementText(obj);
                range.select();
            }
        }

        // Chainable
        return this;
    },
	selectThisText : function(){
		var doc = document
			, element = this[0]
			, range, selection
		;
		if (doc.body.createTextRange) {
			range = document.body.createTextRange();
			range.moveToElementText(element);
			range.select();
		} else if (window.getSelection) {
			selection = window.getSelection();
			range = document.createRange();
			range.selectNodeContents(element);
			selection.removeAllRanges();
			selection.addRange(range);
		}
        return this;
	},
	justText            : function ( newText, prepend ) {
		var $children = null,
			placement = prepend === undefined ? 'prepend':'append';
		if ( newText ) {
			$children = $( this )
				.children()
				.clone();
			$( this )
				.children()
				.remove()
				.end()
				.text( newText )
				[ placement ]( $children );
			return $(this);
		}
		else {
			return $.trim( $( this )
				.clone()
				.children()
				.remove()
				.end()
				.text());
		}
	},
	justThisText		: function () {
		var array = [];
		this.contents().each(function(){
			this.nodeType == 3 && array.push( this.textContent || this.innerText || '' ); });
    	return array;
    },
    toComment			: function ( textNode ) {
        textNode = textNode || false;
        var $this = $( this ),
            comment = function( elem ){
                return document.createComment( !textNode ? elem.get(0).outerHTML : elem );
            };

        textNode !== undefined && textNode
            ? $this.contents().filter(function(){ return this.nodeType === 3; })
                .each(function(){ $( this ).replaceWith( comment( this.nodeValue ) ); })
            : $this.replaceWith( comment( $this ) );
    },
    uncomment           : function ( recurse ) {
        <!-- hidden --> // this will reveal, whats inside. In this case it will bi the word hidden
        recurse = recurse || false;
        $( this ).contents().each(function(){
            this.nodeType === 8 && $( this ).replaceWith( this.nodeValue ); });

        recurse && $( this ).hasChildren()
        	&& $( this ).find('> *').not('iframe').uncomment( recurse );

        /*
        $( this ).contents().each(function() {
            if ( recurse && this.hasChildNodes() ) {
                $( this ).uncomment(recurse);
            } else {
                if ( this.nodeType == 8 ) {
                                // Need to "evaluate" the HTML content,
                                // otherwise simple text won't replace
                    var e = $('<span>' + this.nodeValue + '</span>');
                                $( this ).replaceWith( e.contents() );
                            }
            }
        });
    */
        // $('#uncomment').uncomment( true );
        // http://stackoverflow.com/a/22439787
    },

	getComment          : function ( getValue ) {
		var COMMENT_NODE = this.contents().filter(function(){
            return this.nodeType == Node.COMMENT_NODE;
		});
		return getValue
			? COMMENT_NODE.each(function(){
                return $( this ).nodeValue.str2html(); })
			: COMMENT_NODE;
	},
	getURL              : function ( url, how ) {
        //$.get('defaultComp/foot.html', function(dataContent) {$('#foot').replaceWith(dataContent); });
		how = how || 'html';
		var that = this;
		$.get( url, function(dataContent) {
			$(that)[ how ](dataContent);
		});
		return this;
	},

	scrollTuneAdv       : function ( opt  ){
		// $("body").scrollTune({ pps: 1700, pageY: config.pageY, easing:'easeOutSmoothBounce', hsc: true }).promise()
		//          .done( function() { setTimeout(function(){ toggleClassState( config, 'fullPlayer', type ); },100); })
		console.log('scrollTune');

		var position,
			defaults 		= {
				tune:		0,
				speed:		0,
				pps:        false, // pixel per second
				ppsM:		1000,
				pageYmini:	0,
				pageY:      false, //{ pps: 300, pageY: event.pageY }
				hsc:		false, // height speed compensation
				animate:	false,
				// require	http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js or jQueryUI - if other than ' swing or linear '
				easing:     "easeInOutCubic", // easeInOutCubic  easeInOutQuad  easeInOutElastic http://easings.net/
				delay:		0,
				varStore:	'',
				varAltStore:false,
				name:		false,
				start:   	false,
				startTime: 	0,
				step:   	false,
				stepTime: 	0,
				complete:   false,
				completeTime: 0,
				done:   	false,
				doneTime: 	0,
				goTo:		$('body')
			},
			heightSpeedComp	= function(){
                return opt.hsc ? 1 + ( ( $(document).height() / opt.pageY ) / 1.4 ) : 1 ;
			},
			varStore = function( action, step ){
				(console.log(action,step),
				opt.name !== false
					? opt.varAltStore !== false
						? ( console.log('Store'), opt.varAltStore[opt.name][ action ](step) )
						: ( console.log('Store false'), opt.name[ action ](step) )
					: opt.pageYmini < opt.pageY || opt.varStore === config
						? ( console.log('config'), opt.varStore[ action ](step) )
						: ( console.log('config false'), opt.varStore(step) ));
			};
		console.log('opt.pageY',opt.pageY);
		opt = $.extend( {}, defaults, opt );
		position = ( $( this ).offset().top + opt.tune ) + 'px';

		opt.pps !== false || opt.animate !== false || ( typeof opt.speed === 'string' ? opt.speed.length !== 0 : opt.speed !== 0 )
			? (
			opt.speed = opt.pps !== false ? parseInt( ( opt.pageY / opt.pps * heightSpeedComp() ) * opt.ppsM ) : opt.speed,
				opt.goTo.delay( opt.delay ).animate(
					{ scrollTop	: position },
					{ duration	: opt.speed, easing: opt.easing,
						start		: function(){
							opt.start && setTimeout(function(){
								varStore('start');
							}, opt.startTime );
						},
						step		: function(i){
							opt.step && setTimeout(function(){
								varStore('step',i);
							}, opt.stepTime );
						},
						complete	: function(){
							opt.complete && setTimeout(function(){
								varStore('complete');
							}, opt.completeTime );
						},
						done		: function(){
							opt.done && setTimeout(function(){
								varStore('done');
							}, opt.doneTime );
						}
					}
				)
			)
			: opt.goTo.scrollTop( position );

		return this; // for chaining...

	},
	scrollTuneNew       : function ( opt ){
		var defaultOptions = {
				tune:		0,
				speed:		512,
				animate:	true,
				easing:     'swing',
				goTo:		$('html, body')
			},
			element  = $( this ),
			options  = $.extend({}, defaultOptions, $.fn.scrollTuneNew.changeDefaults, opt ),
			position = ( element.offset().top + options.tune ) + 'px';

		options.animate
			? options.goTo.animate({ scrollTop: position }, options.speed, options.easing )
			: options.goTo.scrollTop( position );

		return element; // for chaining...
	},
	simulateMouseEvent  : function ( MouseEvents ){
		var $this = this[0], evt,
			mEvent = MouseEvents || 'click';
		//noinspection JSUnresolvedVariable, noinspection JSUnresolvedFunction
        document.createEvent
            //noinspection JSUnresolvedFunction
			? ( evt = document.MouseEvents('MouseEvents'),
				//evt.initCustomEvent(type, bubbles, cancelable, detail)
				//evt.initCustomEvent("MyEventType", true, true, "Any Object Here");
				evt.initEvent(mEvent, true, false),
				$this.dispatchEvent(evt) )

			: //noinspection JSUnresolvedFunction
			document.createEventObject
				? $this.fireEvent('onclick')
				: typeof node['on' + mEvent] == 'function' && $this['on' + mEvent]();
	},
	simulateMouse       : function ( typeOfEvent ){
		var $this = this[0],
			evt     = document.createEvent("MouseEvents"),
			mEvent  = typeOfEvent || 'click';
		//noinspection JSDeprecatedSymbols
        evt.initMouseEvent( mEvent, true, true, $this.ownerDocument.defaultView,
			0, 0, 0, 0, 0, false, false, false, false, 0, null);
        $this !== undefined && $this.dispatchEvent(evt);
		return this;
	},
    simulateMouseAdv    : function ( typeOfEvent, options ){

        var target   =  this[0],
            event    =  document.createEvent("MouseEvents"),
            defaults =  {
                mButton     : 'left',
                type        : typeOfEvent || 'click',
                canBubble   : true,
                cancelable  : true,
                view        : target.ownerDocument.defaultView, //window, //
                detail      : 1,
                screenX     : 0, //The coordinates within the entire page
                screenY     : 0,
                clientX     : 0, //The coordinates within the viewport
                clientY     : 0,
                ctrlKey     : false,
                altKey      : false,
                shiftKey    : false,
                metaKey     : false, //I *think* 'meta' is 'Cmd/Apple' on Mac, and 'Windows key' on Win. Not sure, though!
                button      : 0, //0 = left, 1 = middle, 2 = right
                relatedTarget  : null
            },
            opts     =  $.extend({}, defaults, $.fn.simulateMouseAdv.changeDefaults, options ),
            oButton  = {
                left:0, middle:1, right:2
            };

        console.log('oButton', opts.mButton );
        opts.button  =  oButton[ opts.mButton ];

        //Pass in the options
        //noinspection JSDeprecatedSymbols
        event.initMouseEvent(
            opts.type,
            opts.canBubble,
            opts.cancelable,
            opts.view,
            opts.detail,
            opts.screenX,
            opts.screenY,
            opts.clientX,
            opts.clientY,
            opts.ctrlKey,
            opts.altKey,
            opts.shiftKey,
            opts.metaKey,
            opts.button,
            opts.relatedTarget
        );

        //Fire the event
        target.dispatchEvent(event);
    },

	hasEvent            : function ( event ) {
        var eventHandlerType;
        $( this ).on( event, clickEventHandler ).triggerHandler( event );
        function clickEventHandler( e ) {
            eventHandlerType = e.type;
        }
        return eventHandlerType === event;
    },
	qUnWrap             : function (){
		$( this ).find(':first-child').unwrap();
	},
	designMode          : function ( state, contenteditable ) {
		state = ( state === true || state === 1 || state === 'on' ) ? 'on' :
				( state === false || state === 0 || state === 'off' ) && 'off';
		contenteditable = contenteditable || 'off';
		contenteditable !== 'off'
			? contenteditable === 'document'
				? document.designMode = state
				: contenteditable
					? this.attr('contenteditable', true)
					: this.removeAttr('contenteditable')
			: this.contentWindow.document.designMode = state;
	},

	getThisComputedStyle : function ( style ) {
        return  window.getComputedStyle( $(this), null ).getPropertyValue( style );
    },
    thisCompStyle       : function ( cssStyle ) {
        return cssStyle !== undefined ? this.getComputedStyle().getPropertyValue( cssStyle ) : this.getComputedStyle();
    },
    getStyle       		: function( propertyKey ) {
        var cssPropAll = window.getComputedStyle(this, null);
        return propertyKey === undefined ? cssPropAll : cssPropAll.getPropertyValue( propertyKey );
    }
});

//$.fn.scrollTune.changeDefault = {};

$.extend({
	confirm: function (title, message, yesText, noText, yesCallback) {
		//dialog needs jQueryUI
		/*
		 $.confirm(
		 "CONFIRM", //title
		 "Delete " + filename + "?", //message
		 "Delete", //button text
		 deleteOk //"yes" callback
		 );
		 */
		$("<div></div>").dialog( {
			buttons: [{
				text: yesText,
				click: function() {
					yesCallback();
					$( this ).remove();
				}
			},
				{
					text: noText,
					click: function() {
						$( this ).remove();
					}
				}
			],
			close: function (event, ui) { $(this).remove(); },
			resizable: false,
			title: title,
			modal: true
		}).text(message).parent().addClass("alert");
	}
});
$.extend( $.expr[":"], {

});
$.extend( $.expr[":"], {
	ldata: function(el, idx, selector) {
		var attr = selector[3].split(", ");
		return el.dataset[attr[0]] === attr[1];
	},
	value: function(el, idx, selector) {
		return el.value === selector[selector.length - 1];
	},
	isEmptyTrimmed: function(el){
		return !$.trim($(el).html());
	},
    lengthBetween: function(elem, i, match) {
        var params = match[3].split(",");  //split our parameter string by commas
        var elemLen = $(elem).val().length;   //cache current element length
        return ((elemLen >= params[0] - 0) && (elemLen <= params[1] - 0));  //perform our check
    },
	data: $.expr.createPseudo
		? $.expr.createPseudo(function( dataName ) {
		return function( elem ) {
			return !!$.data( elem, dataName );
		};
	})
		// support: jQuery <1.8
		: function( elem, i, match ) {
		return !!$.data( elem, match[ 3 ] );
	}
});
$.extend( $.expr[":"], {
    'nth-class': function(elem, index, match) {
        return $(elem).is(':nth-child('+ match[3] +')');
    },
    'nth-last-class': function(elem, index, match) {
        return $(elem).is(':nth-last-child('+ match[3] +')');
    },
    'nth-class-of-type': function(elem, index, match) {
        return $(elem).is(':nth-of-type('+ match[3] +')');
    },
    'nth-last-class-of-type': function(elem, index, match) {
        return $(elem).is(':nth-last-of-type('+ match[3] +')');
    },
    'only-class-of-type': function(elem, index, match) {
        return $(elem).is(':first-of-type');
    },
    'first-class-of-type': function(elem, index, match) {
        return $(elem).is(':first-of-type');
    },
    'last-class-of-type': function(elem, index, match) {
        return $(elem).is(':last-of-type');
    },
    'first-class': function(elem, index, match) {
        return $(elem).is(':first-child');
    },
    'last-class': function(elem, index, match) {
        return $(elem).is(':last-child');
    },
    'has': function(elem, index, match) {
        return $(elem).has( match[3] );
    },
    'nth-class-leftOver' : function(elem, index, match, array){
        var elemLen = $(elem).length,
            div = index % match[3],
            indexLT = ( ( $(elem).length % match[3] === 0 ) * match[3] ) + 1;

        var N= document.getElementsByTagName('*');
        c.i('indexOf',Array.prototype.indexOf.call(elem, $(elem).parent()));

        c.i('elem.eq', $(elem).eq( ));
        c.i('elem', $(elem));
        c.i('elemLen', elemLen);
        c.i('index', index);
        c.i('div', div);
        c.i('indexLT', indexLT);
        return $(elem).is(':lt(' + indexLT + ')');
        //return $(elem).lt( ( ( $(elem).length % match[3] === 0 ) * match[3] ) + 1 );
    },
    external: function(element,index,match) {
        if(!element.href) {return false;}
        return element.hostname && match[3] !== undefined || false
            ? element.hostname !== window.location.hostname
            	&& element.hostname.search( match[3] ) === window.location.hostname.search( match[3] )
            : element.hostname !== window.location.hostname;
    },
    inView: function(element) {
        var st = (document.documentElement.scrollTop || document.body.scrollTop),
            ot = $(element).offset().top,
            wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();
        return ot > st && ($(element).height() + ot) < (st + wh);
    },
    width: function(element,index,match) {
        // Usage: $('div:width(>200)'); // Select all DIVs that have a width greater than 200px
        // Alternative usage: ('div:width(>200):width(<300)');
        if(!match[3]||!(/^(<|>)d+$/).test(match[3])) {return false;}
        return match[3].substr(0,1) === '>' ?
            $(element).width() > match[3].substr(1) : $(element).width() < match[3].substr(1);
    },
    containsNC: function(elem, i, match, array) {
        return (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
    },
    containsExact: function( element, index, details, collection ){
        return $(element).text() === details[3];
    }

});

jQuery.expr[":"].Contains = $.expr.createPseudo(function(selector) {
    return function( elem ) {
        return jQuery(elem).text().toUpperCase().indexOf(selector.toUpperCase()) >= 0;
    };
});
jQuery.expr[':']['new-nth-class'] = function(elem, index, match) {
    return $(elem).is(':nth-child('+ match[3] +')');
};
jQuery.expr[':']['new-nth-class'] = $.expr.createPseudo( function( matchSelection ) {
    return function( elem ) {
        return $(elem).is(':nth-child('+ matchSelection +')');
    };
});

$.isInArray = function( item, array ) {
	return !!~$.inArray(item, array);
};
$.allVar = function( array, value, all, atLeast ) {
	var count = 0,
		arrLength = array.length,
		isBool = typeof value === 'boolean';

	$.each( array, function( i, e ){
		value === ( isBool ? !!e : e ) && count++;
	});

	return all ? count === arrLength : count >= atLeast;
};
/*
 Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
 //$( selector ).get(0).playing;
 get: function(){
 return !!( this.currentTime > 0 && !this.paused && !this.ended && this.readyState > 2 );
 }
 });
 */
/*   Object.prototype.hasAttribute   = function( attrName, val ){
 var thisAttr =
 this.attr
 ? val !== undefined
 ? this.attr( attrName ) === val
 : this.attr( attrName )
 : this.getAttribute( attrName );
 return ( typeof thisAttr !== "undefined" && thisAttr !== false && thisAttr !== null );
 };
 */
Array.prototype.findArrayObj    = function( findKey, exactValue ){
	return $.grep( this, function( obj ){
		return obj[ findKey ] === exactValue;
	})[0];
	//This prototype doesn't modify the array,
	// it gets the element that contains key with correct value and
	// the returns that element
};
Array.prototype.removeArrayObj  = function( findKey, exactValue ){
	//my own example test:  http://jsfiddle.net/aPH7m/82/
	//This prototype doesn't modify the array, needs to be overwritten or put into new var array
	return $.grep( this, function( obj ) {
		return obj[ findKey ] !== exactValue;
	});
};
Array.prototype.addKeyToArrayObj = function( findKey, exactValue, newKey, newValue ){
	return $.grep( this, function( obj ){
		return obj[ findKey ] === exactValue ? obj[ newKey ] = newValue : obj[ findKey ] !== exactValue;
	});
	// This prototype doesn't modify the array,
	// it gets the element that contains key with correct value and
	// adds a new key with its value to that element
};

Array.prototype.filterArrayObj  = function( doWhat, findKey, exactValue, newKey, newValue ){
	return  doWhat === 'remove'
		? this.filter(function( obj ) {
		return obj[ findKey ] !== exactValue;
	})
		: doWhat === 'addKey'
		? this.filter(function( obj ){
		return obj[ findKey ] === exactValue
			?  obj[ newKey ]  = newValue
			:  obj[ findKey ] !== exactValue;
	})
		: doWhat === 'find'
	&& this.filter(function( obj ){
		return obj[ findKey ] === exactValue;
	})[0];
};
Array.prototype.grepArrayObj    = function( doWhat, idKey, uniqueValue, theKey, theValue ){
	doWhat = doWhat === 'updateKey' ? 'addKey' : doWhat;
	return doWhat === 'remove'
		? $.grep( this, function( obj ){
				return obj[ idKey ] !== uniqueValue;
			})
		:  doWhat === 'addKey'
			? $.grep( this, function( obj ){
				return obj[ idKey ] === uniqueValue
					? (
						obj[ theKey ] = theValue,
						obj[ idKey ] === uniqueValue
					)
					: obj[ idKey ] !== uniqueValue;
			})
		:  doWhat === 'find'
			? $.grep( this, function( obj ){
				return obj[ idKey ] === uniqueValue;
			})[0]
		:  doWhat === 'deleteKey'
			&& $.grep( this, function( obj ){
				return obj[ idKey ] === uniqueValue
					? (
						delete obj[ theKey ],
						obj[ idKey ] === uniqueValue
					)
					: obj[ idKey ] !== uniqueValue;
			});
};
/*Array.prototype.sortObjArray    = function( key, reverse ){
	this.sort(function(a, b){
		return ( reverse || false ) ? b[key] - a[key] : a[key] - b[key];
	})
};*/

//noinspection JSPrimitiveTypeWrapperUsage
Boolean.parse                   = function(val) {
    //    http://stackoverflow.com/a/24744599
    var falsely = /^(?:f(?:alse)?|no?|0+)$/i;
    return !falsely.test(val) && !!val;
};
Boolean.prototype.isFalse 		= function( ){
    return this.toString() == "false";
};
Boolean.prototype.isTrue 		= function( ){
    return this.toString() == "true";
};
Boolean.prototype.ifs 			= function ( fnTrue, fnFalse ){
    fn( this ? fnTrue : fnFalse );
    /*
     $("#element").ifs( function( $this ){}, function( $this ){});
     */
};
Boolean.prototype.ifBool 		= function ( Boolean, fn ){
    ( this == ( typeof Boolean === 'string' ) ?  parseBoolean( Boolean ) : Boolean ) && fn();
};

/*
 Object.prototype.isObjectType   = function( type, showType ){ //"[object Number]"
 var objectString = Object.prototype.toString.call( this );
 return  showType === 'show' ? objectString :
 showType === 'exact' ? objectString === type :
 showType === 'search' && objectString.toLowerCase().inElem( type.toLowerCase() );
 };

Object.defineProperty(String.prototype, "_defineBool", {
    //myBool.toString()._defineBool()
    get : function() {
        var falsely = /^(?:f(?:alse)?|no?|off?|n?|failure?|sudumoo?|0+)$/i,
            truely = /^(?:t(?:rue)?|yes?|on?|y?|success?|yabbdoo?|1+)$/i,
            stateBool = !!this;

        return ( truely.test( this ) ===  stateBool ) ? true : ( falsely.test( this ) ===  stateBool ) ? false : !isNaN(parseFloat( this )) && isFinite( this ) ? true : null;
    }
});
Object.prototype.obj2Str        = function( obj ){
    var objArr = $.makeArray( obj );
    return objArr[0].outerHTML;
};
*/
String.prototype.splitEvery     = function( splitter, every ){
	var array = this.split( splitter ), returnString = '';
	$.each( array, function( index, elem ){
		returnString += elem + ( index < array.length - 1 || index % every === 0 ) ? '' : splitter;
	});
	return returnString;
};
String.prototype.advSplit       = function( chr, nbr ){
	var str = this.split(chr),
		strLen = str.length,
		chrLen = chr.length,
		returnStr = ['',''],
		newArr = [];

	$.each( str, function( index ){
		returnStr[ index < nbr ? 0 : 1 ] += str[ index ] + chr;
	});

	$.each( returnStr, function( index ){
		returnStr[ index ] = returnStr[ index ].slice(0, - chrLen);
		returnStr[ index ].length > 0 && newArr.push( returnStr[ index]  );
	});

	return newArr;
};
String.prototype.advSplitJoin   = function( chr, nbr, ips ){

	var str = this.split(chr),
		strLen = str.length,
		ipsLen = ips.length,
		returnStr = '',
		returnStrLen;

	$.each( str, function( index ) {
		var add = index < strLen - 1
			? chr
			: '';
		returnStr += index + 1 === nbr
			? str[index] + ips
			: str[index] + add;
	});

	returnStrLen = returnStr.length;
	returnStr.slice( returnStrLen - ipsLen ) === ips
	&& ( returnStr = returnStr.slice( 0, returnStrLen - ipsLen ) );

	return returnStr;
};
String.prototype.extract        = function( start, end, inside, newWay ){
	var str = this,
		myArray = [ true, 1, 'yes', 'inside' ],
		startCharIndex = str.indexOf( start ),
		endCharIndex = str.indexOf( end );

	newWay = newWay !== undefined ? $.inArray( newWay, myArray ) !== -1 : false;
	inside = inside !== undefined ? $.inArray( inside, myArray ) !== -1 : false;

	function simpler() {
		return inside
			? str.split( start ).pop().split( end ).shift()
			: start + str.split( start ).pop().split( end ).shift() + end;
	}
	function older() {
		return inside //old buggy way, some old scripts may depends on it
			? str.replace( start, '').replace( end, '')
			: str.substr( startCharIndex, endCharIndex );
	}
	return newWay ? simpler() : older();
};
String.prototype.extractNew     = function( start, end, inside ){
	var str = this;
	return inside !== undefined && inside
		? str.split( start ).pop().split( end ).shift()
		: inside
			|| start + str.split( start ).pop().split( end ).shift() + end;
};

String.prototype.charTrim       = function( char ){
	// alert("...their.here.".charTrim('.'));
	var first_pos = 0,
		last_pos = this.length- 1, i ;
	//find first non needle char position
	for( i = 0; i < this.length; i++ ){
		if( this.charAt( i ) !== char ){
			first_pos = ( i == 0 ? 0 : i );
			break;
		}
	}
	//find last non needle char position
	for( i = this.length - 1; i > 0; i-- ){
		if( this.charAt( i ) !== char ){
			last_pos = ( i == this.length ? this.length: i + 1 );
			break;
		}
	}
	return this.substring( first_pos, last_pos );
};
String.prototype.reduceWhiteSpace = function(){
	return this.replace(/\s+/g, ' ');
};
String.prototype.formatString   = function(){

	var inputStr = this.toString().reduceWhiteSpace()
			.split('!').join(' !').split('!;').join("!important;")
			.split(/\s+/g).join(' ')
			.split('{').join('{\n\t')
			.split('; ').join(';')



			.split('( ').join('(')
			.split(' )').join(')')

			.split(' :').join(':')

			.split(';').join(';\n\t')
			.split('*/').join('*/\n')
			.split(')*(').join(') * (')
			.split('}').join('}\n'),
		returnStr = '\t', pop;

	$.each( inputStr.split('\n'), function ( i, elem ) {

		elem.search( '{' ) === -1 && elem.search( ': ' ) === -1
		&& ( elem.search( ':' ) > 1
			? ( pop = elem.split(': ').join(':').split( ':' ).pop(), elem = elem.split( pop ).shift() + ' ' + pop )
			: elem.search(':') === 1 && ( elem = elem.split(': ').join(':').split( ':' ).join( ': ' ) ) );
		//    : elem.search( '{' ) === 1 && ( elem.search( ': ' ) !== -1 || elem.search( ' :' ) !== -1 || elem.search( ' : ' ) !== -1 )
		//        && ( elem = elem.split( ': ' ).join( ' :' ).split( ' :' ).join( ':' ).split( ' : ' ).join( ': ' ) );

		returnStr += elem + '\n\t';
	});
	returnStr = returnStr.split('>').join(' > ').split('  >  ').join(' > ').split( ': //' ).join( '://' ).split( ':url' ).join( ': url' );
	return returnStr.slice( 0, returnStr.lastIndexOf('}') ) + '}';
};

/**
 * Parses mixed type values into booleans. This is the same function as filter_var in PHP using boolean validation
 * //@return {Boolean|Null}
 */
String.prototype.parseBooleanStyle = function( onNull ){
    //myBool.toString().parseBooleanStyle()
    onNull = onNull || false;
    var bool, $this = this.toString().toLowerCase();
    switch( $this ){
        case 'true':
        case '1':
        case 'on':
        case 'y':
        case 'yes':
            bool = true;
            break;
        case 'false':
        case '0':
        case 'off':
        case 'n':
        case 'no':
            bool = false;
            break;
        default:
            bool = typeof bool === 'boolean' ? bool : null;
            break;
    }
    return onNull && bool == null || parseInt( $this ) > 0 || bool;
};
String.prototype.parseBool      = function(){
    //myBool.toString().parseBool()
    var thisStr = ( parseInt( this ) ? this === 0 ? 'false' : !isNaN( parseFloat( this ) ) && 'true' : '' + this ).toLowerCase().trim(),
        trueArray = [ 'on', 'yes','y', 'j', 'success', 'true', true ],
        falseArray = [ 'off', 'no', 'n', 'failure', 'false', false ];

    thisStr = thisStr.toLowerCase().trim();

    return  $.inArray( thisStr, trueArray ) !== -1 ? true : $.inArray( thisStr, falseArray ) !== -1 ? false : null;
};
String.prototype.defineBool     = function(){
    var falsely = /^(?:f(?:alse)?|no?|off?|n?|failure?|sudumoo?|0+)$/i,
        truely = /^(?:t(?:rue)?|yes?|on?|y?|success?|yabbdoo?|1+)$/i,
        stateBool = !!this,
		$string = this;

    return ( truely.test( this ) === stateBool )
		? true : ( falsely.test( this ) === stateBool )
			? false : $string != 0 && !isNaN(parseFloat($string)) && ( (typeof $string === 'number' || typeof $string === 'string') && isFinite($string) ) ? true : null;
};

String.prototype.isType         = function( type ){
	return !!$.type( this ) === type;
};
String.prototype.undef          = function( replace ){
	return this === undefined ? replace : this;
};
String.prototype.isUndefined    = function( state, replace ){
	state = state !== undefined ? state : true;
	replace = replace !== undefined ? replace : true;
	return state ? this === undefined ? replace : this : state;
};
String.prototype.isNumber       = function ( float ) {
	var reg = new RegExp( "^[-]?[0-9]+[\.]?[0-9]+$" );
	return reg.test( this );
};

String.prototype.inURL          = function(){
	var winLoc = window.location.href;
	return winLoc.search(this) !== -1;
};
String.prototype.inString       = function( string ){
	return string !== undefined ? string.search(this) !== -1 : false;
};
String.prototype.inElem         = function( search ){
	return this !== undefined ? this.search(search) !== -1 : false;
};
String.prototype.inElemX        = function( search, exact ){

    var isAll,isNone,inArr,checkInArr,
        string = this,
        countAll = 0, countNone = 0;

    exact = exact || false;
    isAll = exact === 'all';
    isNone = exact === 'none';
    exact = isNone || isAll ? false : exact;

    checkInArr = function( search, all ){
        inArr = false;
        $.each( search, function( i, e ){
            if(string.search( e ) !== -1 || string.search( e ) === -1){
                inArr = true;
                if( all ) string.search( e ) !== -1 ? countAll++ : string.search( e ) === -1 && countNone++;
                else return false;
            }
        });
        return all ? inArr && ( string.length === countAll || string.length === countNone ) : inArr;
    };

    return exact
        ? string === search
        : $.isArray( search )
            ? checkInArr( search, isAll || isNone )
            : isNone
                ? string.search( search ) === -1
                : string.search( search ) !== -1;
};
String.prototype.count          = function( char, UpperCase ){
	var numberOf = this.toString().match( new RegExp( char, ( UpperCase ? "gi" : "g" ) ) );
	return numberOf != null ? numberOf.length : 0;
};
String.prototype.startsWith     = function( str ){
	return this.slice(0, str.length) == str;
};
String.prototype.removeStarts   = function( many ){
	return this.substring( many - 1, this.length );
};
String.prototype.removeEnds     = function( many ){
	return this.substring( 0, this.length - many );
};
String.prototype.endsWith       = function( str ){
	return this.slice( -str.length ) == str;
};
String.prototype.capitalizeFirst = function(){
	return this.charAt(0).toUpperCase() + this.slice(1);
};
String.prototype.lpad           = function( padString, length ){
	var str = this;
	while ( str.length < length ) {
		str = padString + str; }
	return str;
};

// use full to convert String URL, so that you can use location commands
String.prototype.toLocation     = function(){
	var a = document.createElement('a');
	a.href = this;
	return a;
};
String.prototype.toStyle        = function( styleId, append ){
    append = append || 'head';
    var cssID,
        cssSplit = this.split('¤'),
        cssStyled = cssSplit.pop().formatString();
    styleId = styleId !== undefined ? styleId : cssSplit.shift();
    cssID = $( append + ' #' + styleId );
    cssID.length
        ? cssID.html( cssStyled )
        : $( $( '<style/>',{ id: styleId, class:'mySuperStyles', html: cssStyled } ) ).appendTo( append );
};

String.prototype.HTMLEncodedParserHTML = function(){
    return $('<div/>').html( $( $.parseHTML( decodeURI(this) ) ) ).contents();
};
String.prototype.strParserHTML  = function(){
    return $($.parseHTML(this));
};
String.prototype.strDOMParser   = function(){
    return new DOMParser().parseFromString(this, "text/html");
};
String.prototype.str2html       = function(){
	return $('<div/>').html( this ).contents();
};

String.prototype.autoCopyToClipboard = function( cssElement, how ) {
	cssElement = cssElement || 'body';
	how = how || 'append';

	var mainID = 'autoCopyToClipboard',
		copyThis = $( '#' + mainID );

	$( cssElement )[ how ](
		$('<div/>',{ id: mainID + 'Wrapper', style: 'position:relative;' }).append(
			$('<textarea/>',{
				id: mainID, rows:"5", cols:"500", type:"text", value: this.toString(),
				style: 'position:absolute; top:-300px; display:block;' }) ) );

	setTimeout(function (){
        copyThis.focus().select();
        document.execCommand("copy");
        var remove = setInterval(function(){
			$( '#' + mainID + 'Wrapper' ).ifExists(function( $this ){
				$this.remove();
				clearInterval( remove );
			});
		},8);
    },2);
};

//HTMLObjectElement.prototype.obj2Str = function(){var objArr = $.makeArray( this ); return objArr[0].outerHTML;};
/*
 String.prototype.replaceAll = function( target, replacement ) {
 return this.split(target).join(replacement);
 };
 */
function ScrollZoomTune( selection, zooms, tune, ani, speed ){
	//ScrollZoomTune("div.thumb .title a",1,-25,1,'slow');
	var body = $('body'), sel = $( selection), position;
	//noinspection JSValidateTypes
	sel.size() !== 0 && (
		body.css('zoom',zooms),
			position = sel.position().top + tune,
			ani === 1
				? body.animate({ scrollTop: position * zooms }, speed )
				: body.scrollTop( position * zooms )
	);
}
function refreshElement( elem , speed ){ //refreshElement('.videoPlayer','slow');
	var $elem = $( elem ), data = $elem.html();
	$elem.empty().html( data ).fadeIn( speed );
}

!!$.isFunction( $.fn.execCommand ) || ($.fn.execCommand = function(){});

function autoCopy2Clipboard( input, cssElement, how ){
	cssElement = cssElement || 'body';
	how = how || 'append';

	var mainID = 'autoCopyToClipboard',
		copyThis = $( '#' + autoCopyToClipboard );

	'#autoCopyToClipboard { position:absolute; top:-300px; display:block; }'.toStyle( mainID );
	$( cssElement )[ how ]( $('<textarea/>',{ id:mainID, rows:"5", cols:"500", type:"text", value: input }) );

	copyThis.focus().select();
	document.execCommand("copy");
	copyThis.remove();
}
function toStyle( styleId, str, append ){
	append = append || 'head';
	var $id = $( append + ' #' + styleId ),
		cssID = str.formatString();

	$id.length
		? $id.html( cssID )
		: $( $( '<style/>',{ id: styleId, class:'mySuperStyles', html: cssID } ) ).appendTo( append );
}
function getTheStyle( id ){
    var elem = document.getElementById("elem-container");
    var theCSSprop;
    theCSSprop = window.getComputedStyle(elem, null).getPropertyValue("height");
    document.getElementById("output").innerHTML = theCSSprop;
}
function obj2Str( obj ){
	var objArr = $.makeArray(obj);
	return objArr[0].outerHTML;
}
function orderBy(key, reverse){
    return function (a, b) {
        return ( reverse || false ) ? b[ key ] - a[ key ] : a[ key ] - b[ key ];
    };
}
function sortBy(key, reverse){
    var R = reverse || false;
    return function (a, b) {
        var A = a[ key ], B = b[ key ];
        return A < B ? R ? 1 : -1 : A > B ? R ? -1 : 1 : 0;
    };
}
function sortByOLD(key, reverse){
	// Usage: array.sort( sortBy( key, reverse ) )
	// Move smaller items towards the front
	// or back of the array depending on if
	// we want to sort the array in reverse
	// order or not.
	var moveSmaller = reverse ? 1 : -1;
	// Move larger items towards the front
	// or back of the array depending on if
	// we want to sort the array in reverse
	// order or not.
	var moveLarger = reverse ? -1 : 1;
	/**
	 * @param  {*} a
	 * @param  {*} b
	 * @return {Number}
	 */
	return function (a, b) {
		if (a[key] < b[key]) {
			return moveSmaller;
		}
		if (a[key] > b[key]) {
			return moveLarger;
		}
		return 0;
	};
}

/**
 @function sec2timeFormat
 @description convert second to time format in 4+ different ways into @example DD:HH:MM:SS.ms
 @param {(string|Number)} totalSeconds - @desc it will convert to millisecond inside of @function
 @param {Boolean} [complete] - @example true 02:45:05 or 02h 45m 05s false 2:45:05 or 2h 45m 05s
 @param {(string|Boolean)} [divider] - @example common ':' or '-' or '/'
 @param {Number} [toFix] - @desc millisecond after second - @default 2 - @example 45:05.44 or 5.44s
 @return {string}
 @copyright May 2017 - Magnus Fohlström
 @license MIT
 */
function sec2timeFormat( totalSeconds, complete, divider, toFix ){
    totalSeconds = typeof totalSeconds === 'string' ? parseInt(totalSeconds) : totalSeconds;
    toFix = toFix !== undefined ? ( toFix === 0 ? 0 : toFix ) : 2;
    var bFull = complete !== undefined ? complete && true : false,
        div = divider !== undefined ? divider !== false : false,

        date = new Date( totalSeconds * 1000 ),
        d = Math.floor( totalSeconds / 3600 / 24 ),
        //    d = date.getUTCDate() - 1,
        h = date.getUTCHours(),
        m = date.getUTCMinutes(),
        s = date.getSeconds(),
        ms = totalSeconds - Math.floor( totalSeconds ),

        preD = div ? divider : 'd ', dPreD = d + preD,
        dd = bFull
            ? d === 0 ? '00' + preD : (d < 10 ? '0':'') + dPreD
            : d === 0 ? '' : dPreD,

        preH = div ? divider : 'h ', hPreH = h + preH,
        hh = dd.length > 0
            ? h === 0 ? '00' + preH : (h < 10 ? '0':'') + hPreH
            : h === 0 ? bFull ? h < 10 ? '0' + hPreH : hPreH : '' : hPreH,

        preM = div ? divider : 'm ', mPreM = m + preM,
        mm = hh.length > 0
            ? m === 0 ? '00' + preM : (m < 10 ? '0':'') + mPreM
            : m === 0 ? bFull ? m < 10 ? '0' + mPreM : mPreM : '' : mPreM,

        preMS = ( s + ms ).toFixed( toFix ),
        preS = div ? '' : 's', sPreS =  preMS + preS,
        ss = mm.length > 0
            ? s === 0 ? '00' + preS : (s < 10 ? '0':'') + sPreS
            : sPreS;

    return ( dd + hh + mm + ss ).toString();
}

function VideoTitleA( elem , state ){
	//VideoTitleA("div.thumb .title a",'on');
	$( elem ).each(function(){
		var $this    = $(this),
			strTitle = $this.attr('title'),
			strText  = $this.attr('data-text'),
			strHtml  = $this.text();
		state === 'on' ? $this.text(strTitle).attr('data-text',strHtml) : $this.text(strText);
	});
}
/**
 * @return {string}
 */
function MultiString( f ){
	return f.toString().split('\n').slice(1, -1).join('\n');
}
function wrapWithTag( tag, text, selection ){
	var thisAttr = selection != undefined && selection.startsWith('.') ? 'class' : selection.startsWith('#') && 'id',
		thisTag = $('<' + tag + '/>', { text: text });
	return thisAttr.length ? thisTag.attr( thisAttr, selection.splice( 1 ) ) : thisTag;
}

function clean( node ) {
	/*
	 So to clean those unwanted nodes from inside the <body> element, you would simply do this:

	 clean(document.body);
	 Alternatively, to clean the entire document, you could do this:

	 clean(document);
	 */
	for( var n = 0; n < node.childNodes.length; n ++ ){
		var child = node.childNodes[ n ];
		( child.nodeType === 8 || ( child.nodeType === 3 && !/\S/.test( child.nodeValue ) ) )
			? (
			node.removeChild( child ),
				n --
		)
			: child.nodeType === 1 && clean( child );
	}
}
function commentsCleaner( array ){
	array = array === undefined ? [ '*', document, 'html' ] : array;

	// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
	// http://stackoverflow.com/a/2364760
	$.each( array, function( i, e ) {
		$( e ).contents().each( function() {
			this.nodeType === Node.COMMENT_NODE  && $( this ).remove(); }); });
}
function clearSelection() {
    var sel;
    if ( (sel = document.selection) && sel.empty ) {
        sel.empty();
    } else {
        if (window.getSelection) {
            window.getSelection().removeAllRanges();
        }
        var activeEl = document.activeElement;
        if (activeEl) {
            var tagName = activeEl.nodeName.toLowerCase();
            if ( tagName == "textarea" ||
                (tagName == "input" && activeEl.type == "text") ) {
                // Collapse the selection to the end
                activeEl.selectionStart = activeEl.selectionEnd;
            }
        }
    }
}
function newUrlNoReload( title, url ){
	window.history.pushState("string", title, url );
}
function inURLo( search, exact ){
	exact = exact || false;
	var winLoc = window.location.href;
	return exact ? winLoc === search : winLoc.search( search ) !== -1;
}
function inURLe( search, exact ){
	exact = exact || false;
	var inArr,
		winLoc = window.location.href,
		tiA = function( search ){
			inArr = false;
			$.each( search, function(i,e){
				if( winLoc.search( search ) !== -1 ){
					inArr = true;
					return false;
				}
			});
			return inArr;
		};
	return exact ? winLoc === search : $.isArray( search ) ? tiA( search ) : winLoc.search( search ) !== -1;
}
function inURL( search, exact ){

	var isAll,isNone,inArr,checkInArr,
		winLoc = window.location.href,
		countAll = 0, countNone = 0;

	exact = exact || false;
	isAll = exact === 'all';
	isNone = exact === 'none';
	exact = isNone || isAll ? false : exact;

	checkInArr = function( search, all ){
		inArr = false;
		$.each( search, function( i, e ){
            if(winLoc.search( e ) !== -1 || winLoc.search( e ) === -1){
                inArr = true;
                if( all ) winLoc.search( e ) !== -1 ? countAll++ : winLoc.search( e ) === -1 && countNone++;
                else return false;
            }
/*
            if( winLoc.search( e ) !== -1 ){
				inArr = true;
				if( all ) countAll++; else return false;
			}
			else if( winLoc.search( e ) === -1 ){
				inArr = true;
				if( all ) countNone++; else return false;
			}
*/
		});
		return all ? inArr && ( search.length === countAll || search.length === countNone ) : inArr;
	};

	return 	exact
		? winLoc === search
		: $.isArray( search )
			? checkInArr( search, isAll || isNone )
			: isNone
				? winLoc.search( search ) === -1
				: winLoc.search( search ) !== -1;
}
function loadDoc( href ){
	$( location ).attr('href', href );
}

function isPrimitiveType( value ){
	// will return true if the value is a primitive value
	switch ( typeof value ) {
		case 'string': case 'number': case 'boolean': case 'undefined': {
		return true;
	}
		case 'object': {
			return !value;
		}
	}
	return false;
}
function checkDividedIsInteger( num, div ){
	return ( num % div === 0 );
}
function isEven( value ){
	return ( value % 2 === 0 );
}
function inDom( array, onElement ) {
	var found = false,
		isArrayFN = function(){
			$.each( array, function( i, value ){

				value = onElement !== undefined
					? onElement + value
					: value;

				if( $( value ).length ){
					found = true;
					return false;
				}
			});
		};

	$.isArray( array )
		? isArrayFN()
		: $( array ).length && ( found = true );

	/**
	 * @return {boolean}
	 */
	return found;
}
function isHTMLObject( obj ){
	obj = typeof obj == 'string' ? obj : $( obj );
	return obj.length
		? !!~( obj instanceof HTMLElement || obj[0] instanceof HTMLElement)
		: !!( obj && ( obj.nodeName || ( obj.prop && obj.attr && obj.find ) ) );
}

function isFunction( functionToCheck ){
	var getType = {};
	return functionToCheck && getType.toString.call( functionToCheck ) === '[object Function]';
}
function isNumeric( value ){
	return /^\d+$/.test( value );
}
function is_Numeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

function parse_Boolean( Boolean, onNull, nullIs ) {
    var falsely = /^(?:f(?:alse)?|no?|off?|n?|failure?|sudumoo?|0+)$/i,
        truely = /^(?:t(?:rue)?|yes?|on?|y?|success?|yabbdoo?|1+)$/i;

    //noinspection JSUnresolvedVariable
    return truely.test( Boolean ) === !!Boolean ? true : falsely.test( Boolean ) === !!Boolean ? false
			: !isNaN(parseFloat( Boolean )) && isFinite( Boolean ) ? true
				: ( onNull || false ) ? bool == null ? nullIs : false : null;
}
function parseBoolean( Boolean , Type ) {
	//    http://stackoverflow.com/a/24744599
	Type = Type || false;
	var falsely = /^(?:f(?:alse)?|no?|0+)$/i,
		truely = /^(?:t(?:rue)?|yes?|1+)$/i;
	return Type ? !truely.test( Boolean ) && !!Boolean : !falsely.test( Boolean ) && !!Boolean;
}
function parseBooleanStyle( str, onNull, nullIs ){
    onNull = onNull || false;
    var bool, $this = this.toString().toLowerCase();
    switch( $this ){
        case 'true':
        case '1':
        case 'on':
        case 'y':
        case 'yes':
            bool = true;
            break;
        case 'false':
        case '0':
        case 'off':
        case 'n':
        case 'no':
            bool = false;
            break;
        default:
            bool = typeof bool === 'boolean' ? bool : null;
            break;
    }
    return ( onNull || false ) && ( bool == null && nullIs || false ) || parseInt( $this ) !== 0 || bool;
}

/**
 * Search for correct object in array and return it.
 * @function getObjKeyVal
 * @param {Array} array - The array you wanna search trough
 * @param {string} findKey - The key to search for
 * @param {string} exactValue - Check correct key is found
 * @param {string} [getObjKeyVal]  - get a key value in that object
 */
function getObjKeyVal( array, findKey, exactValue, getObjKeyVal ){
    var obj, node;
    for( node in array )
        for( findKey in obj = array[ node ] )
            if( obj.hasOwnProperty( findKey ) && obj[ findKey ] == exactValue )
                return getObjKeyVal || false ? obj[ getObjKeyVal ] : obj;
    return false;
}
/**
 * Search for all matched objects in array and return those.
 * @function getAllMatchedObj
 * @param {Array} array - The array you wanna search trough
 * @param {string} findKey - The key to search for
 * @param {string} exactValue - Check correct key found
 */
function getAllMatchedObj( array, findKey, exactValue ){
    var newArr = [], obj, node;
    for( node in array )
        for( findKey in obj = array[ node ] )
            if( obj.hasOwnProperty( findKey ) && obj[ findKey ] == exactValue ) newArr.push( obj );
    return newArr;
}
/**
 * Search for all matched objects in array and return those.
 * @function getAllMatchedObjByObject
 * @param {Array} mainArray - The array you wanna search trough
 * @param {Array} filterObject - Is one array object to match object in main array,
 * this object has multiple keys one each for a search criteria example "{ joined:'2012', rank:5, gender:'female' }"
 */
function getAllMatchedObjByObject( mainArray, filterObject ){
    var newArr = [], objectCount = 0, mainLen = mainArray.length, mainObj, filterLen;
    for ( objectCount; objectCount != mainLen; objectCount++ ){

        mainObj = mainArray[ objectCount ];
        filterLen = Object.keys( filterObject ).length;

        Object.keys( filterObject ).forEach( function( filterKey, index ){
            mainObj.hasOwnProperty( filterKey ) && mainObj[ filterKey ] == filterObject[ filterKey ] && ++index === filterLen && newArr.push( mainObj );
        });
    }
    return newArr;
}
/**
 * Do something with an array then return it
 * @function arrayDo
 * @param {string} what - what function is going to be used on an array
 * @param {(string|string[])} array - The array you wanna search trough
 * @param {string} findKey - The key to search for
 * @param {string} [keyValue] - Check correct key found
 * @param {string} [getKeyVal] - Get a key value from correct object found, or add in conjunction with newVal
 * @param {string} [newVal] - Set a new Value to a key
 */
function arrayDo( what, array, findKey, keyValue, getKeyVal, newVal) {

    what = what === 'newKey' ? 'updateKey' : what;
	/*
		index = array.findIndex( function( obj ){
	 		return obj.hasOwnProperty( findKey ) && obj[ findKey ] === keyValue;
	 	})
	*/
    var index = -1, i=0, len = array.length;

  //  if( !!~$.inArray( doWhat, ['allMatched','add','sort'] ) === true )
        for ( i; i != len ; i++ ) {
            var obj = array[ i ];
            if( obj.hasOwnProperty( findKey ) && obj[ findKey ] === keyValue ){
            	c.i('inArray',obj[ findKey ]);
                index = i;
                break;
            }
        }

    var object = array[ index ],
        doWhat = {
            find        : function(){
                array = object;
            },
            remove      : function(){
                index !== -1 && array.splice( index, 1 );
            },
            keyVal      : function(){
                var obj = object[ getKeyVal ];
                array = obj !== undefined ? obj : '';
            },
            updateKey   : function(){ //newKey
            /*    c.i('updateKey array ', JSON.stringify( array ) )
                c.i('updateKey index ', index )
                c.i('updateKey Value ', keyValue)
                c.i('updateKey newVal', newVal )
                c.i('getKeyVal before', array[ index ] )
                c.i('updateKey', '____________________')
            */
                index !== -1 && ( array[ index ][ getKeyVal ] = newVal );
            //    c.i('getKeyVal after ', array[ index ] )
            },
            deleteKey   : function(){
                index !== -1 && delete array[ index ][ getKeyVal ];
            },
            add         : function(){
                array.push( newVal );
            },
            sort        : function(){
                array.sort( sortBy( findKey, keyValue ) );
            },
			allMatched  : function(){
                var newArr = [], i = 0,
                    secondMatch = ( getKeyVal !== undefined && newVal !== undefined );

                for ( i; i != len; i++ ) {
                    var obj = array[ i ];
                    ( obj.hasOwnProperty( findKey ) && obj[ findKey ] === keyValue )
                        && ( secondMatch ? ( obj.hasOwnProperty( getKeyVal ) && obj[ getKeyVal ] === newVal ) : true )
                            && newArr.push( obj );
                }

                array = newArr;
            }
        };

    doWhat[ what ]();
    return array;
}

function getsComputedStyle( style, elem ) {
	elem = elem || 'body';
	return window.getComputedStyle( document[ elem ] )[ style ] !== undefined;
}
function isPropertySupported( property, elem ){
	elem = elem || 'body';
	return property in document[ elem ].style;
}
function cssPropertyValueSupported( prop, value, elem ) {
	//cssPropertyValueSupported('width', '1px');
	elem = elem || 'div';
	var d = document.createElement( elem );
	d.style[ prop ] = value;
	return d.style[ prop ] === value;
}

var cssSupports = (function(){
	// http://code.tutsplus.com/tutorials/quick-tip-detect-css3-support-in-browsers-with-javascript--net-16444
	/*
	 if ( supports('textShadow') ) {
	 document.documentElement.className += ' textShadow';
	 }
	 */
	var div = document.createElement('div'),
		vendors = 'Khtml Ms O Moz Webkit'.split(' '),
		len = vendors.length;

	return function(prop) {
		if ( prop in div.style ) return true;

		prop = prop.replace(/^[a-z]/, function(val) {
			return val.toUpperCase();
		});

		while(len--) {
			if ( vendors[len] + prop in div.style ) {
				// browser supports box-shadow. Do what you need.
				// Or use a bang (!) to test if the browser doesn't.
				return true;
			}
		}
		return false;
	};
})();

function toggleClassState( config, Class, state, elem ){
	config === undefined ? window.config = {} : config;
	config = config || ( window.config = {} );

	config[ Class ] = typeof state === 'string' ? !config[ Class ] : state;
	$( elem || 'html' )[ config[ Class ] ? 'addClass' : 'removeClass' ]( Class );
	$( elem || 'html' ).attr('class');
}
function filterClick( e, $this ){
	return e.which == 1 && e.target == $this;
}
function dispatchEventResize() {
	//noinspection JSClosureCompilerSyntax,JSUnresolvedFunction
	window.dispatchEvent(new Event('resize'));
}

/**
 * @return {string}
 */
function Undefined( check, replace ){
	return check === undefined ? replace.toString() : check.toString();
}
function checkJqueryUI( maxCount, timeout, module ){
    //noinspection JSUnresolvedVariable
    if( typeof jQuery.ui != 'undefined' ){
        //noinspection JSUnresolvedVariable
        return module != undefined ? typeof jQuery.ui[ module ] != 'undefined' : true; }
    else
        setTimeout(function(){
            if( counter.setGet('chkJQ') > maxCount ){
            	counter.del('chkJQ');
            	return false;
            }
            else checkJqueryUI( maxCount, timeout );
        }, timeout );
}

function getGlobal(){
	return (function(){
		return this;
	})();
}
function GM_lister( remove, item ){
	var keys = GM_listValues();
	for (var i = 0, key = null; key = keys[i]; i++) {
		GM_listValues()[i] !== undefined && (
			c.i('GM_ListItem: ' + GM_listValues()[i] + ':', GM_getValue(key)),
			( ( item !== undefined && GM_listValues()[i].inElem( item ) ) || item === undefined )
			&& ( remove === true || remove === 'yes' || remove === 1 ) && GM_deleteValue(key));
	}
}

function roundFloat( num, dec ){
	var d = 1;
	for ( var i=0; i<dec; i++ ){
		d += "0";
	}
	return Math.round(num * d) / d;
}
function randomFloatBetween( min, max, dec ){
	dec = typeof( dec ) == 'undefined' ? 2 : dec;
	return parseFloat( Math.min( min + ( Math.random() * ( max - min ) ), max ).toFixed( dec ) );
}
function random( max ) {
	var min = 1,
		rand = function(){
			return Math.floor( Math.random() * ( max - min + 1 ) + min );
		},
		num1 = rand(),
		num2 = rand();

	return ( num1 > num2 ? num2/num1 : num1/num2 ) * max;
}
function roundNearPeace( number, peaces, dec ) {
	return ( Math.round( number * peaces ) / peaces ).toFixed( dec );
}

var w = window,
	glob = w,
	$w = $( w ),
	$l = $( location ),
	locDoc = window.location.href,
	d = document,
	$d = $( d ),

	c = {
		defaultState: 3,
		cute : function( type, msg, color ) {
			color = color || "black";
			var newColor, bgc = "White";
			switch ( color ) {
				case "success":  newColor = "Green";      bgc = "LimeGreen";       break;
				case "info":     newColor = "DodgerBlue"; bgc = "Turquoise";       break;
				case "error":    newColor = "Red";        bgc = "Black";           break;
				case "start":    newColor = "OliveDrab";  bgc = "PaleGreen";       break;
				case "warning":  newColor = "Tomato";     bgc = "Black";           break;
				case "end":      newColor = "Orchid";     bgc = "MediumVioletRed"; break;
				default: //noinspection SillyAssignmentJS
					newColor = color;
			}

			typeof msg == "object" ?
				window.console[ type ]( msg )
				: typeof color == "object" ? (
				window.console[ type ]("%c" + msg, "color: PowderBlue;font-weight:bold; background-color: RoyalBlue;"),
					window.console[ type ]( newColor )
			) :
				window.console[ type ]("%c" + msg, "color:" + newColor + "; background-color: " + bgc + ";");
		},
		show: function( showThis, type ){
			var State = GM_getValue( type + 'StateValue' ) || this.defaultState;
			return showThis !== 0 && State !== 0 && State === ( showThis || State ) || State === 'all';
		},
		pre: function( type, name, fn, line, color ){
			line = line == undefined ? '' : line + ': ';
			/**
			 * @return {string}
			 */
			var Fn = function(){ return fn !== undefined ? fn : ''; };
			typeof fn == "object"
				? window.console[ type ]( name, Fn() )
				: c.cute( type, line + name + ': ' + Fn(), color );
		},
		type: function( type, name, fn, line, color, showThis ){
			this.show( showThis, type ) && this.pre( type, name, fn, line, color );
		},
		l: function( name, fn, line, color, showThis ){
			this.type( 'log', name, fn, line, color, showThis );
		},
		h: function( name, fn, line, color, showThis ){
			this.type( 'handled', name, fn, line, color, showThis );
		},
		//c.l('name', 'fn'=='fn', 'line', 'blue')
		i: function( name, fn, line, color, showThis ){
			this.type( 'info', name, fn, line, color, showThis );
		},
		d: function( name, fn, line, color, showThis ){
			this.type( 'debug', name, fn, line, color, showThis );
		}
	},
	localStorageObj = {
		name        : 'setName', //important
		localArray  : function(){
			return window.localStorage.getItem( this.name );
		},
		getArray    : function(){
			return this.localArray() === null ? [] : JSON.parse( this.localArray() );
		},
        removeArray : function(){
            window.localStorage.removeItem( this.name );
        },
		stringify   : function( array ){
			c.i('[stringify]', JSON.stringify(array) );
			window.localStorage.setItem( this.name, JSON.stringify(array) );
		},
		check       : function( key, value ){
			var array = this.getArray();
			return array.grepArrayObj('find', key, value );
		},
		viewConsole : function( json ){
			var array = this.getArray();
			// $.toJSON() --- https://github.com/Krinkle/jquery-json/blob/master/src/jquery.json.js
			// array.join('\n');
			c.d( this.name, array ); //debug mode important to make this work
			c.i( this.name, json ? isFunction( $.toJSON() ) ? $.toJSON( array ) : JSON.stringify( array ) : array.toSource() );
		},
		addTo       : function() {

		},
		add         : function( key, value, obj ){
			var array = this.getArray(),
				inA = array.toString();
			c.i('[check array 1]', inA );
			this.check( array, key, value )
			|| (
				array.push( obj ),
					c.i('[Added Object into array]', obj ),
					c.i('[check array 2]', array )
			);
			this.stringify( array );
		},
		remove      : function( key, value, obj ){
			var array = this.getArray();
			this.check( array, key, value )
			&& (
				array = array.grepArrayObj('remove', key, value ),
					c.i('[Removed Object from array]', obj )
			);
			this.stringify( array );
		}
	},
	booleanTimer = {
		timers  : [],
		start   : function( name, ms ){
			var that = this, value = name,
				stop = setTimeout(function(){
					that.stop( value );
				}, ms );
			this.timers.push( { 'name': value, stop:stop } );
			//setTimeout(function(){that.stop( value );}, ms );
		},
		check   : function( value ){
			return this.timers.grepArrayObj( 'find', 'name', value ) !== undefined;
		},
		stop    : function( value ){
			this.timers = this.timers.grepArrayObj( 'remove', 'name', value );
		}
	},
	advTimer = {
		timers  : [],
		start   : function( name, ms ){
			var that = this, value = name,
				stop = setTimeout(function(){
					that.stop( value );
				}, ms );
			//noinspection JSUnresolvedVariable
			this.timers.push({ 'name':value, start:window.performance.now(), timeout:ms, stop:stop });
		},
		check   : function( value ){
			var findObj = this.timers.grepArrayObj( 'find', 'name', value );
			//noinspection JSUnresolvedVariable
			return findObj !== undefined
				? findObj.timeout - ( window.performance.now() - findObj.start )
				: false;
		},
		stop    : function( value ){
			this.timers = this.timers.grepArrayObj( 'remove', 'name', value );
		}
	},
	lap     = {
		data : {},
		tid  : function(){
			//noinspection JSUnresolvedVariable
			return performance.now();
		},
		set  : function(name){
			this.data[name] = this.tid();
		},
		get  : function(name){
			return this.tid() - this.data[name];
		},
		end  : function(name){
			this.print(name);
			this.del(name);
		},
		del  : function(name){
			delete this.data[name];
		},
		print: function(name){
			var get = this.get( name );
			c.i( 'Lap: ' + name,  isNaN( get ) ? 'There is no such a name ' : get + 'ms' );
		}
	},
	timer   = {
		ms  : 0,
		set : function(ms){
			var that = this;
			this.ms = ms;
			setTimeout(function(){
				that.ms = 0;
			}, ms );
		}
	},
    counter = {
        data    : {},
        setGet	: function(name, decrease, reset) {

            this.exists(name) && ( reset || false )
				&& this.set(name,0);

			this.exists(name)
				? this[ ( decrease || false ) ? 'decrease' : 'increase' ](name)
				: this.set(name);

            return this.get(name);
        },
        set     : function(name, start, base){
            this.data[name] = start || 0;
            this.data[name+'Default'] = base || 1;
        },
        get     : function(name){
            return this.data[name];
        },
        is      : function(name, count){
            return this.data[name] === count;
        },
        lessThen: function(name, count, equal ){
            return ( equal || false ) ? this.data[name] <= count : this.data[name] < count;
        },
        moreThen: function(name, count, equal){
            return ( equal || false ) ? this.data[name] >= count : this.data[name] > count;
        },
        del     : function(name){
            delete this.data[name];
        },
        exists  : function(name) {
            return this.data[name] !== undefined;
        },
        plus    : function(name, num){
            this.exists(name) || this.set(name);
            this.data[name] = this.data[name] + ( num === undefined ? this.data[name+'Default'] : num );
        },
        minus   : function(name, num){
            this.exists(name) || this.set(name);
            this.data[name] = this.data[name] - ( num === undefined ? this.data[name+'Default'] : num );
        },
        increase: function (name, num) {
            this.plus(name, num);
        },
        decrease: function (name, num) {
            this.minus(name, num);
        },
        '+'     : function (name, num) {
            this.plus(name, num);
        },
        '-'     : function (name, num) {
            this.minus(name, num);
        },
        up      : function (name, num) {
            this.plus(name, num);
        },
        down    : function (name, num) {
            this.minus(name, num);
        },
        add     : function (name, num) {
            this.plus(name, num);
        },
        subtract: function (name, num) {
            this.minus(name, num);
        },
        withdraw: function (name, num) {
            this.minus(name, num);
        }
    },
	g       = {
		locDoc  : window.location.href,
		ms      : 0,
		timer   : function(ms){
			g.ms = ms;
			setTimeout(function(){ g.ms = 0; }, ms );
		},

		GM      : {
			engine : function( mode, val, range ){
				switch (mode){
					case 'set':     GM_setValue( val.name, val.default );
						break;
					case 'get':     range ? config[ val.name ] = GM_getValue( val.name ):
						ui.config[ val.name ] = GM_getValue( val.name );
						break;
					case 'del':     GM_deleteValue( val.name );
				}
			},
			manager : function( mode, array, range ){
				$.each( array, function( i, val ){ this.engine( mode, val, range === undefined ); });
				mode === 'del' && ( GM_deleteValue( 'firstRun' ), GM_deleteValue( 'yourVer' ) );
			}
		}
	},

	testPerformance = function( name, fn, testCycles ) {
		lap.set( name );
		var i = 0;
		for ( i ; i < testCycles; i++ ){
			fn();
		}
		lap.end( name );
	},
	perf = function (testName, fn) {
		var startTime = new Date().getTime();
		fn();
		var endTime = new Date().getTime();
		console.log( testName + ": " + ( endTime - startTime ) + "ms" );
	};

$(document).on('click','*',function(e){
	this == e.target && c.i('target:', $(this)[0] );
});

c.i('my Function Library ÖÖö');
/*
 isScrolledIntoView = (elem) ->
 docViewTop = $(window).scrollTop()
 docViewBottom = docViewTop + $(window).height()
 elemTop = $(elem).offset().top
 elemBottom = elemTop + $(elem).height()

 (elemBottom - 200 < docViewBottom) and (elemBottom + $(elem).height() > docViewBottom )
 */