﻿// Randomizer - Random string generator plugin for jQuery.
// 
// Randomizer has 2 Modes:
// 		1. Randomizer application. 
// 			1. Execute. $("query").randomizer([options]);
// 		2. Miniplugins. Similar to jQuery's plugins.
// 			1. Find elements. $("query")
// 			2. Execute. $("query").randomizer("miniplugin's name", [options]);
// 	
// 	@author: 		Toni Helminen <toni.helminen@gmail.com>
// 	@license: 		GPLv3 <http://gplv3.fsf.org/>
// 	@copyright:		All rights reserved
// 	@tested-with: 	IE 7.0.5730.11, Opera/10.00 (Windows NT 5.1; U; en) Presto/2.2.0, Opera/9.62 (Windows NT 5.1; U; en) Presto/2.1.1, 
// 					Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4
(function($) {
	var r = {};
	function log() {
		try {
			window.console.log(arguments);
		} catch(e) {}
	}
	if (!window.log) {
		window.log = log;
	}
	var utils = {
		randomizer: {
			ukey: 'pwbth' + new Date().getYear(),
			highestZIndex: 100,
			apps: {}
		},
		uid: function() {
			arguments.callee.uid = arguments.callee.uid || 1;
			return arguments.callee.uid++;
		},
		usid: function(string) {
			return (string || 'id') + utils.uid();
		},
		uidlize: function(e) {
			e[utils.randomizer.ukey] = e[utils.randomizer.ukey] || utils.uid();
			return e;
		},
		getuid: function(e) {
			return utils.uidlize(e)[utils.randomizer.ukey];
		}
	};
	r.MyDraggable = function(query, options) {
		this.options = options || {};
		this.query = query;
	};
	r.MyFx = function(elements, options) {
		this.elements = elements;
		this.options = options || {};
	};
	r.App = function(options) {
		this.startTime = new Date().getTime();
		this.options = options || {};
		this.init();
	};
	r.Miniplugins = function(element, options) {
		this.options = options;
		this.element = element;
	};
	r.myString = {
		trim: function(s, allAround) {
			return !! allAround ? s.replace(/\s/g, '') : s.replace(/^\s+|\s+$/g, '');
		},
		pickCharRandomly: function(s) {
			var rnum = Math.floor(Math.random() * s.length);
			return {
				pick: s.charAt(rnum),
				index: rnum
			};
		},
		removeCharFromString: function(s, i) {
			var a = this.arraylize(s);
			a.splice(i, 1);
			return r.myArray.stringlize(a);
		},
		hasStringOtherString: function(s1, s2) {
			for (var i = 0, l1 = s1.length; i < l1; i++) {
				for (var j = 0, l2 = s2.length; j < l2; j++) {
					if (s1.charAt(i) === s2.charAt(j)) {
						return true;
					}
				}
			}
			return false;
		},
		arraylize: function(s) {
			var array = [];
			for (var i = 0, l = s.length; i < l; i++) {
				array.push(s.charAt(i));
			}
			return array;
		},
		shuffle: function(s) {
			var ret = '';
			var tmp = s;
			for (var i = 0, l = s.length; i < l; i++) {
				var tmp2 = r.myString.pickCharRandomly(tmp);
				ret += tmp2.pick;
				tmp = tmp.slice(0, tmp2.index) + tmp.slice(tmp2.index + 1, tmp.length);
			}
			return ret;
		},
		uniques: function(s) {
			var ret = '';
			for1: for (var i = 0, l = s.length; i < l; i++) {
				for (var j = 0, l2 = ret.length; j < l2; j++) {
					if (s.charAt(i) === ret.charAt(j)) {
						continue for1;
					}
				}
				ret += s.charAt(i);
			}
			return r.myString.trim(ret, true);
		},
		toggleString: function(s1, s2) {
			for (var i = 0, l = s1.length; i < l; i++) {
				for (var j = 0, l2 = s2.length; j < l2; j++) {
					if (s1.charAt(i) === s2.charAt(j)) {
						return r.myString.removeString(s1, s2);
					}
				}
			}
			return r.myString.addString(s1, s2);
		},
		addString: function(s1, s2) {
			return r.myString.uniques(r.myString.trim(s1 + s2, true), true);
		},
		removeString: function(s1, s2) {
			var ret = '';
			for1: for (var i = 0, l = s1.length; i < l; i++) {
				for (var j = 0, l2 = s2.length; j < l2; j++) {
					if (s1.charAt(i) === s2.charAt(j)) {
						continue for1;
					}
				}
				ret += s1.charAt(i);
			}
			return r.myString.trim(ret, true);
		}
	};
	r.MyDraggable.prototype = {
		query: {},
		options: {},
		exec: function() {
			var options = this.options;
			return $(this.query).each(function() {
				var cthis = this;
				var draggableFn;
				if ($(this).draggable) {
					$(this).draggable(options);
				} else { (function() {
						return options.handle ? $(cthis).find(options.handle) : $(cthis);
					})().mousedown(function(event) {
						var last = event;
						$(this).parent().css('opacity', options.opacity || 0.8);
						$(document).mousemove((draggableFn = function(event) {
							var left = window.parseInt($(cthis).css('left')) + (event.clientX - last.clientX);
							var top = window.parseInt($(cthis).css('top')) + (event.clientY - last.clientY);
							if (left) {
								$(cthis).css('left', left);
							}
							if (top) {
								$(cthis).css('top', top);
							}
							last = event;
						}));
					}).mouseup(function() {
						$(document).unbind('mousemove', draggableFn);
						//$(document).unbind('mousemove'); // All mousemove off, 100% sure but removes all the others too...
						$(this).parent().css('opacity', 1);
					});
				}
			});
		}
	};
	r.MyFx.origStore = {}; // Static hash. This is the solution, I don't like it but it works...
	r.MyFx.prototype = {
		elements: {},
		options: {},
		// Creates some effect to indicate user of change-event.
		backgroundColorFun: function() {
			var cthis = this;
			var o = this.options;
			return this.elements.each(function() {
				if (r.MyFx.origStore[utils.getuid(this)]) {
					r.MyFx.origStore[utils.getuid(this)]();
				}
				var cthis2 = this,
				orig = $(this).css('backgroundColor');
				$(this).css('backgroundColor', o.color || '#FF7400');
				window.setTimeout((r.MyFx.origStore[utils.getuid(this)] = function() {
					$(cthis2).css('backgroundColor', orig);
				}), o.speed);
			});
		}
	};
	r.App.prototype = {
		id: null,
		ids: {},
		appHtml: '',
		footer: '',
		startTime: 0,
		options: {},
		typeSafe: true,
		init: function() {
			this.id = this.generateIds();
			this.appHtml = this.createApp();
			return this;
		},
		// Creates app
		createApp: function() {
			var id = this.id;
			var o = this.options;
			var html = '<div class="randomizer app ' + o.theme + '" id="' + id.div.app + '">';
			html += '<div class="header">';
			html += '<div class="toggle-container">';
			html += '<div id="' + id.div.resizeHandle + '" class="resize-handle">' + o.i18nResizeToggle + '</div>';
			html += '<div id="' + id.div.closeHandle + '" class="close-handle">' + o.i18nCloseToggle + '</div>';
			html += '</div>';
			html += o.header;
			html += '</div>';
			html += '<div class="body">';
			html += '<div><input class="randomString" id="' + id.input.randomString + '" type="text" size=50" /></div>';
			if (o.blockStrengthBar) {
				html += '<div><div class="strength-bar"><div id="' + id.div.strengthBar + '"></div></div></div>';
			}
			if (o.blockFeedChars) {
				html += '<div class="bottom-separator feed-chars">';
				html += '<input id="' + id.input.feedChars + '" type="text" size=50" value="' + o.feedChars + '" /></div>';
			}
			html += '<a href="#" id="' + id.link.settings + '" class="settings">';
			html += o.i18nSettings + '</a>' + '<div class="buttons" id="' + id.div.buttons + '">';
			var classy;
			for (var i = 0, l = o.dynamicBlocks.length; i < l; i++) {
				o.dynamicBlocks[i].id = utils.usid();
				html += '<div class="' + o.dynamicBlocks[i].name + '"><button id="' + o.dynamicBlocks[i].id + '">';
				classy = r.myString.hasStringOtherString(o.dynamicBlocks[i].chars, o.feedChars) ? '': 'class="line-through"';
				html += (o.dynamicBlocks[i].button || o.i18nToggle) + '</button> <span ' + classy + '>' + o.dynamicBlocks[i].chars + '</span></div>';
			}
			if (o.blockTypeSafe) {
				html += '<div class="type-safe">';
				classy = r.myString.hasStringOtherString(o.charsTypeSafe, o.feedChars) ? '': 'class="line-through"';
				html += '<button id="' + id.button.typeSafe + '" >' + o.i18nToggle + '</button> <span ' + classy + '>' + o.charsTypeSafe + '</span></div>';
			}
			if (o.rndStrLength) {
				html += '<div class="plus-and-minus">';
				html += '<button id="' + id.button.minus + '">-</button>';
				html += '<button id="' + id.button.plus + '">+</button>';
				html += '<input class="length" " id="' + id.input.rndStrLength;
				html += '"type="text" size="10" value="' + o.rndStrLength + '" />';
				html += '</div>';
			}
			html += '</div>';
			html += '<div class="top-separator generate">';
			html += '<button id="' + id.button.generate + '">' + o.i18nGenerate + '</button>';
			if (o.blockReset) {
				html += '<button id="' + id.button.reset + '">' + o.i18nReset + '</button>';
			}
			html += '</div></div>';
			if (o.footer) {
				html += '<div class="footer" id="' + id.div.footer + '">' + o.footer + '</div>';
			}
			html += '</div>';
			return html;
		},
		generateIds: function() {
			var id = {
				div: ['app', 'resizeHandle', 'closeHandle', 'strengthBar', 'buttons', 'footer'],
				input: ['randomString', 'rndStrLength', 'feedChars'],
				button: ['minus', 'plus', 'reset', 'generate', 'typeSafe'],
				link: ['settings']
			};
			for (var i in id) {
				if (id) {
					var obj = {};
					for (var j = 0, l = id[i].length; j < l; j++) {
						var unid = utils.usid();
						if (unid) {
							obj[id[i][j]] = unid;
						}
					}
					id[i] = obj;
				}
			}
			return id;
		},
		makedraggable: function() {
			var draggable = new r.MyDraggable('#' + this.id.div.app, {
				handle: 'div.header',
				opacity: this.options.fxAppDraggedOpacity
			});
			draggable.exec();
			return this;
		},
		createFooter: function() {
			var startTime = this.startTime;
			var id = this.id;
			var footer = {
				substitutes: [{
					re: /\{VERSION\}/g,
					subs: function() {
						return $.fn.randomizer.version;
					}
				},
				// Though JavaScript's timer is too inaccurate for this...
				{
					re: /\{TIME\}/g,
					subs: function() {
						return ((new Date().getTime() - startTime) / 1000) + 's';
					}
				},
				{
					re: /\{NAME\}/g,
					subs: function() {
						return 'Randomizer';
					}
				},
				{
					re: /\{COPYRIGHT\}/g,
					subs: function() {
						return ' written by Toni Helminen';
					}
				}]
			};
			// Is this really useful?
			for (var i = 0, l = footer.substitutes.length; i < l; i++) {
				$('#' + id.div.footer).text(
				$('#' + id.div.footer).text().replace(footer.substitutes[i].re, footer.substitutes[i].subs));
			}
			return this;
		},
		createEvents: function() {
			var id = this.id;
			var o = this.options;
			var app = this;
			function triggerGenerateClick() {
				$('#' + id.button.generate).trigger('click');
			}
			function onUpdateTriggerGenerateClick() {
				if (o.updateFieldsOnEveryChange) {
					triggerGenerateClick();
				}
			}
			function DynamicEvent(callback, chars, dynamicsId) {
				this.exec = function() {
					$('#' + dynamicsId).click(function() {
						$('#' + id.input.feedChars).val(r.myString.toggleString($('#' + id.input.feedChars).val(), chars));
						onUpdateTriggerGenerateClick();
						callback.call(this);
						$(this).next('span').toggleClass('line-through');
						return false;
					});
				};
			}
			// Events, id is used a lot to ensure 100% compliance and speed.
			// All events executes their callbacks and triggers generate-click if sets true.
			for (var i = 0, l = o.dynamicBlocks.length; i < l; i++) {
				var callback = o.dynamicBlocks[i].callback;
				var chars = o.dynamicBlocks[i].chars;
				var dynamicsId = o.dynamicBlocks[i].id;
				var dynamicEvent = new DynamicEvent(callback, chars, dynamicsId);
				dynamicEvent.exec();
			}
			$('#' + id.button.plus).click(function() {
				$('#' + id.input.rndStrLength).val(window.parseInt($('#' + id.input.rndStrLength).val()) + 1);
				onUpdateTriggerGenerateClick();
				o.callbackPlus.call(this);
				return false;
			});
			$('#' + id.button.minus).click(function() {
				$('#' + id.input.rndStrLength).val(window.parseInt($('#' + id.input.rndStrLength).val()) - 1);
				onUpdateTriggerGenerateClick();
				o.callbackMinus.call(this);
				return false;
			});
			$('#' + id.input.randomString).click(function() {
				this.select();
				o.callbackRandomString.call(this);
				return false;
			});
			$('#' + id.input.feedChars).bind('change',
			function() {
				this.value = r.myString.uniques(this.value);
				onUpdateTriggerGenerateClick();
			});
			$('#' + id.button.reset).click(function() {
				if (o.updateFields) {
					for (var i = 0, tmp = $(o.updateFields), l = tmp.length; i < l; i++) {
						$(tmp[i]).val('');
					}
				}
				$('#' + id.input.randomString).val('');
				o.callbackReset.call(this);
				return false;
			});
			$('#' + id.button.typeSafe).click(function() {
				app.typeSafe = !app.typeSafe;
				$(this).next('span').toggleClass('line-through');
				o.callbackTypeSafe.call(this);
				onUpdateTriggerGenerateClick();
				return false;
			});
			$('#' + id.button.generate).click(function() {
				var feedChars2 = $('#' + id.input.feedChars).val();
				if (app.typeSafe) {
					feedChars2 = r.myString.removeString(feedChars2, o.charsTypeSafe);
				}
				var psw = o.callbackHandleRandomString(r.myRandomString.generateBestRandomString(feedChars2, $('#' + id.input.rndStrLength).val()));

				$('#' + id.input.randomString).val(psw);
				$('#' + id.div.strengthBar).css('width', o.callbackStrength(psw, r.myRandomString.getRandomStringEnthropy(psw)) + '%');
				if (o.updateFields) {
					o.fxInputExecEffects($(o.updateFields).val(psw).trigger('change'), {
						speed: o.fxInputSpeed,
						color: o.fxInputColor
					});
				}
				return false;
			});
			$('#' + id.div.resizeHandle).click(function() {
				$('#' + id.div.app + ' div.body').slideToggle(function() {
					o.callbackResize.call(this);
				});
				return false;
			});
			$('#' + id.div.closeHandle).click(function() {
				$('#' + id.div.app).fadeOut(function() {
					$(this).remove();
					o.callbackClose.call(this);
				});
				return false;
			});
			$('#' + id.link.settings).click(function() {
				$('#' + id.div.buttons).slideToggle();
				o.callbackSettings.call(this);
				return false;
			});
			$('#' + id.div.app).find('div.header').click(function() {
				$(this).parent().css('zIndex', utils.randomizer.highestZIndex++);
				return false;
			}).end().css('zIndex', utils.randomizer.highestZIndex++) // Place app on top.
			.css(o.cssApp).slideToggle();
			return this;
		}
	};
	r.Miniplugins.prototype = {
		options: {},
		element: null,
		// Fireup element
		createStrengthBar: function() {
			var o = this.options;
			var cthis = this.element;
			var id = utils.usid();
			var fn = function() {
				$('#' + id).css('width', o.callbackStrength($(cthis).val(), r.myRandomString.getRandomStringEnthropy($(cthis).val())) + '%');
				return false;
			};
			$(this.element).keyup(fn).change(fn).after('<div class="randomizer strength-bar ' + o.theme + '"><div id="' + id + '"></div></div>');
			$('#' + id).css(o.cssStrengthBar);
			return $(this.element);
		},
		createEqualBar: function($jQuery) {
			var o = this.options;
			var cthis = this.element;
			var id = utils.usid();
			var fn = function() {
				var isEqual = true;
				var val = $(cthis).val();
				var cthis2 = this;
				// Assume everything is same
				$jQuery.each(function() {
					$(this).siblings('div.randomizer.equals').addClass('ok');
					if (this != cthis2 && $(this).val() != val) {
						isEqual = false;
					}
				});
				// It wasn't so remove ok-class of all.
				if (!isEqual) {
					$jQuery.each(function() {
						$(this).siblings('div.randomizer.equals').removeClass('ok');
					});
				}
				return $(this);
			};
			$(this.element).keyup(fn).change(fn).after('<div id="' + id + '" class="randomizer equals ok ' + o.theme + '"></div>');
			$('#' + id).css(o.cssEqualBar);
			return $(this.element);
		},
		createLink: function() {
			var o = this.options;
			if ( !! !$(this.element).css('id')) {
				$(this.element).css('id', utils.usid());
			}
			o.updateFields += ',#' + this.element.id;
			var id = utils.usid();
			$(this.element).after('<a href="#" id="' + id + '">' + o.i18nCreateRandomString + '</a>');
			$('#' + id).randomizer(o).css(o.cssLink);
			return $(this.element);
		},
		createButton: function() {
			var o = this.options;
			return $(this.element).click(function() {
				var psw = o.callbackHandleRandomString(
				r.myRandomString.generateBestRandomString(o.feedChars, o.rndStrLength));
				o.fxInputExecEffects($(o.updateFields).val(psw), {
					speed: o.fxInputSpeed,
					color: o.fxInputColor
				});
				return false;
			});
		},
		createSimple: function(caller) {
			var o = this.options;
			if (caller[0] !== this.element) {
				return $(this.element); // Singleton
			}
			var cthis = this.element;
			var id = utils.usid();
			$(this.element).after('<a href="#" id="' + id + '" class="randomizer equals ' + o.theme + '">' + o.i18nCreateRandomString + '</a>');
			$('#' + id).click(function() {
				var psw = o.callbackHandleRandomString(
				r.myRandomString.generateBestRandomString(o.feedChars, o.rndStrLength));
				var cthis2 = this;
				if (o.updateFields) {
					o.fxInputExecEffects($(o.updateFields).val(psw), {
						speed: o.fxInputSpeed,
						color: o.fxInputColor
					});
				}
				o.fxInputExecEffects($(cthis).val(psw).trigger('change'), {
					speed: o.fxInputSpeed,
					color: o.fxInputColor
				});
				return false;
			}).css(o.cssSimple);
			return $(this.element);
		}
	};
	r.myArray = {
		stringlize: function(a) {
			var s = '';
			for (var i = 0, l = a.length; i < l; i++) {
				s += a[i];
			}
			return s;
		}
	};
	r.myRandomString = {
		getRandomStringEnthropy: function(s) {
			var lowest = 9999;
			var highest = 0;
			var n = 0;
			var tmp = 0;
			for (var i = 0, l = s.length; i < l; i++) {
				tmp = s.charCodeAt(i);
				if (tmp > highest) {
					highest = tmp;
				}
				if (tmp < lowest) {
					lowest = tmp;
				}
			}
			n = Math.abs(highest - lowest);
			// The formula in use H=L*log2N
			// bits/char by getting 2^n by base changing 10's logarithms. 
			//		Means that if we divide any number with its base's exponent in 10'system, 
			//		we get the result from divide of those exponents.
			var enthropy = Math.log(n) / Math.log(2);
			var h = window.parseInt(s.length * enthropy);
			h = isNaN(h) ? 0 : h;
			h = h < 0 ? 0 : h;
			return h > 100 ? 100 : h;
		},
		generateBestRandomString: function(s, length) {
			var ret;
			var strongest = -1;
			for (var i = 0; i < 10; i++) {
				var tmp = this.getRandomString(s, length);
				var tmp2 = this.getRandomStringEnthropy(tmp);
				if (tmp2 > strongest) {
					ret = tmp;
					strongest = tmp2;
				}
			}
			return ret;
		},
		getRandomString: function(s, length) {
			var string2 = s;
			var ret = '';
			for (var i = 0; i < length; i++) {
				if (string2.length === 0) {
					string2 = s;
				}
				var tmp2 = r.myString.pickCharRandomly(string2);
				string2 = r.myString.removeCharFromString(string2, tmp2.index);
				ret += tmp2.pick;
			}
			return r.myString.trim(ret, true);
		}
	};

	$.fn.randomizer = function(options, extra) {
		var caller = this; // For some miniplugins who want to handle all the found element by itself.
		var useMiniPlugins = typeof options == 'string';
		var opts = $.extend({},
		$.fn.randomizer.defaults, useMiniPlugins ? extra: options);
		// Iterates elements whose user wants to make act as links for Apps or target for miniplugins
		return this.each(function() {
			var cthis = utils.uidlize(this);
			var o = $.meta ? $.extend({},
			opts, $(this).data()) : opts;
			// Adds "create" on front of the miniplugin's name and camelized it at the same time.
			if (useMiniPlugins) {
				return new r.Miniplugins(this, o)['create' + options[0].toUpperCase() + options.replace(/^\w/, '')](caller);
			}
			$(this).click(function() {
				if (utils.randomizer.apps[utils.getuid(this)]) {
					return false; // Singleton, we don't need millions of apps to do the same thing, don't we?
				}
				// Make it clean cache afterwards this way, since I don't want App to be tide up to just element. 
				// 		Since anything can trigger randomizer!
				var proxyCloseEvent = o.callbackClose;
				o.callbackClose = function() {
					proxyCloseEvent.call(this);
					utils.randomizer.apps[utils.getuid(cthis)] = null;
				};
				var app = new r.App(o);
				$(this).after(app.appHtml);
				app.createEvents().createFooter().makedraggable();
				utils.randomizer.apps[utils.getuid(this)] = app.id.div.app;
				return false;
			});
			return false;
		});
	};
	// EXPOSE
	r.utils = utils;
	$.fn.randomizer.r = r;

	// Public options
	$.fn.randomizer.version = '2.8';
	$.fn.randomizer.defaults = {
		updateFields: '',
		// jQuery query. Fields where random string will be attached, usually password, password-confirm.
		theme: '',
		// Theme for the app and miniplugins
		updateFieldsOnEveryChange: true,
		// When feedchars has changed, triggers generate-click.
		header: 'Randomizer v. ' + $.fn.randomizer.version,
		// Header
		footer: '',
		// Footer
		//feedChars: 'abcdefghjkmnpqrstu23456789_-ABCDEFGHJKMNPQRSTU',
		feedChars: '23456789abcdefghjkmnpqrstuABCDEFGHJKMNPQRSTU',
		rndStrLength: 15,
		i18nUpdated: 'Updated',
		i18nSettings: 'Settings',
		i18nReset: 'Reset',
		i18nGenerate: 'Generate',
		i18nToggle: 'Toggle',
		i18nCreateRandomString: 'Сгенерировать пароль',
		i18nResizeToggle: '_',
		i18nCloseToggle: 'x',
		blockStrengthBar: true,
		blockFeedChars: true,
		blockTypeSafe: true,
		blockRndStrLength: true,
		// Reset is kinda useless? I might drop it out completely in the future.
		// These you have to handle manually!
		blockReset: false,
		dynamicBlocks: [{
			name: 'smallAlphas',
			callback: function() {
				return;
			},
			chars: 'abcdefghijklmnopqrstuvwxyz'
		},
		{
			name: 'bigAlphas',
			callback: function() {
				return;
			},
			chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
		},
		{
			name: 'numbs',
			callback: function() {
				return;
			},
			chars: '0123456789'
		},
		{
			name: 'symbols',
			callback: function() {
				return;
			},
			chars: '!_-*.,%+=:;|'
		}],
		cssApp: {},
		cssEqualBar: {},
		cssSimple: {},
		cssLink: {},
		cssStrengthBar: {},
		// Callback for randomstring, you can ie. add prefix here.
		callbackHandleRandomString: function(s) {
			return s;
		},
		// Callback for randomstring's strength, you can ie. penalize here.
		callbackStrength: function(s, n) {
			return n;
		},
		callbackClose: function() {
			return;
		},
		callbackResize: function() {
			return;
		},
		callbackReset: function() {
			return;
		},
		callbackPlus: function() {
			return;
		},
		callbackMinus: function() {
			return;
		},
		callbackSettings: function() {
			return;
		},
		callbackTypeSafe: function() {
			return;
		},
		callbackRandomString: function() {
			return;
		},
		fxInputExecEffects: function(query, o) {
			var fx = new r.MyFx(typeof query == 'string' ? $(query) : query, o);
			fx.backgroundColorFun();
		},
		// Color hex-code
		fxInputColor: '#FF7400',
		// Speed in ms	
		fxInputSpeed: 300,
		// Opacity while dragged
		fxAppDraggedOpacity: 0.8,
		charsTypeSafe: 'liI0Oo1|'
	};
})(window.jQuery);