HEX
Server: nginx/1.27.1
System: Linux in-4 5.15.0-131-generic #141-Ubuntu SMP Fri Jan 10 21:18:28 UTC 2025 x86_64
User: ilikadirect (1186)
PHP: 7.4.33
Disabled: exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source
Upload Files
File: /storage/v6964/testingff/public_html/fdfctr/wp-content/plugins/tablepress/admin/js/jsuites.js
/**
 * (c) jSuites Javascript Web Components
 *
 * Website: https://jsuites.net
 * Description: Create amazing web based applications.
 *
 * MIT License
 *
 */
;(function (global, factory) {
	// TablePress: Comment out next to lines to force creation of a global jSuites object.
	// typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	// typeof define === 'function' && define.amd ? define(factory) :
	global.jSuites = factory();
}(window, (function () { // TablePress: Use `window` instead of `this` to get a global jSuites object.

	'use strict';

var jSuites = {};

var Version = '4.17.5';

var Events = function() {

	document.jsuitesComponents = [];

	var find = function(DOMElement, component) {
		if (DOMElement[component.type] && DOMElement[component.type] == component) {
			return true;
		}
		if (DOMElement.component && DOMElement.component == component) {
			return true;
		}
		if (DOMElement.parentNode) {
			return find(DOMElement.parentNode, component);
		}
		return false;
	}

	var isOpened = function(e) {
		if (document.jsuitesComponents && document.jsuitesComponents.length > 0) {
			for (var i = 0; i < document.jsuitesComponents.length; i++) {
				if (document.jsuitesComponents[i] && ! find(e, document.jsuitesComponents[i])) {
					document.jsuitesComponents[i].close();
				}
			}
		}
	}

	// Width of the border
	var cornerSize = 15;

	// Current element
	var element = null;

	// Controllers
	var editorAction = false;

	// Event state
	var state = {
		x: null,
		y: null,
	}

	// Tooltip element
	var tooltip = document.createElement('div')
	tooltip.classList.add('jtooltip');

	// Events
	var mouseDown = function(e) {
		// Check if this is the floating
		var item = jSuites.findElement(e.target, 'jpanel');
		// Jfloating found
		if (item && ! item.classList.contains("readonly")) {
			// Add focus to the chart container
			item.focus();
			// Keep the tracking information
			var rect = e.target.getBoundingClientRect();
			editorAction = {
				e: item,
				x: e.clientX,
				y: e.clientY,
				w: rect.width,
				h: rect.height,
				d: item.style.cursor,
				resizing: item.style.cursor ? true : false,
				actioned: false,
			}

			// Make sure width and height styling is OK
			if (! item.style.width) {
				item.style.width = rect.width + 'px';
			}

			if (! item.style.height) {
				item.style.height = rect.height + 'px';
			}

			// Remove any selection from the page
			var s = window.getSelection();
			if (s.rangeCount) {
				for (var i = 0; i < s.rangeCount; i++) {
					s.removeRange(s.getRangeAt(i));
				}
			}

			e.preventDefault();
			e.stopPropagation();
		} else {
			// No floating action found
			editorAction = false;
		}

		// Verify current components tracking
		if (e.changedTouches && e.changedTouches[0]) {
			var x = e.changedTouches[0].clientX;
			var y = e.changedTouches[0].clientY;
		} else {
			var x = e.clientX;
			var y = e.clientY;
		}

		// Which component I am clicking
		var path = e.path || (e.composedPath && e.composedPath());

		// If path available get the first element in the chain
		if (path) {
			element = path[0];
		} else {
			// Try to guess using the coordinates
			if (e.target && e.target.shadowRoot) {
				var d = e.target.shadowRoot;
			} else {
				var d = document;
			}
			// Get the first target element
			element = d.elementFromPoint(x, y);
		}

		isOpened(element);
	}

	var mouseUp = function(e) {
		if (editorAction && editorAction.e) {
			if (typeof(editorAction.e.refresh) == 'function' && state.actioned) {
				editorAction.e.refresh();
			}
			editorAction.e.style.cursor = '';
		}

		// Reset
		state = {
			x: null,
			y: null,
		}

		editorAction = false;
	}

	var mouseMove = function(e) {
		if (editorAction) {
			var x = e.clientX || e.pageX;
			var y = e.clientY || e.pageY;

			// Action on going
			if (! editorAction.resizing) {
				if (state.x == null && state.y == null) {
					state.x = x;
					state.y = y;
				}

				var dx = x - state.x;
				var dy = y - state.y;
				var top = editorAction.e.offsetTop + dy;
				var left = editorAction.e.offsetLeft + dx;

				// Update position
				editorAction.e.style.top = top + 'px';
				editorAction.e.style.left = left + 'px';
				editorAction.e.style.cursor = "move";

				state.x = x;
				state.y = y;


				// Update element
				if (typeof(editorAction.e.refresh) == 'function') {
					state.actioned = true;
					editorAction.e.refresh('position', top, left);
				}
			} else {
				var width = null;
				var height = null;

				if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
					// Update width
					width = editorAction.w + (x - editorAction.x);
					editorAction.e.style.width = width + 'px';

					// Update Height
					if (e.shiftKey) {
						var newHeight = (x - editorAction.x) * (editorAction.h / editorAction.w);
						height = editorAction.h + newHeight;
						editorAction.e.style.height = height + 'px';
					} else {
						var newHeight = false;
					}
				}

				if (! newHeight) {
					if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
						height = editorAction.h + (y - editorAction.y);
						editorAction.e.style.height = height + 'px';
					}
				}

				// Update element
				if (typeof(editorAction.e.refresh) == 'function') {
					state.actioned = true;
					editorAction.e.refresh('dimensions', width, height);
				}
			}
		} else {
			// Resizing action
			var item = jSuites.findElement(e.target, 'jpanel');
			// Found eligible component
			if (item) {
				if (item.getAttribute('tabindex')) {
					var rect = item.getBoundingClientRect();
					if (e.clientY - rect.top < cornerSize) {
						if (rect.width - (e.clientX - rect.left) < cornerSize) {
							item.style.cursor = 'ne-resize';
						} else if (e.clientX - rect.left < cornerSize) {
							item.style.cursor = 'nw-resize';
						} else {
							item.style.cursor = 'n-resize';
						}
					} else if (rect.height - (e.clientY - rect.top) < cornerSize) {
						if (rect.width - (e.clientX - rect.left) < cornerSize) {
							item.style.cursor = 'se-resize';
						} else if (e.clientX - rect.left < cornerSize) {
							item.style.cursor = 'sw-resize';
						} else {
							item.style.cursor = 's-resize';
						}
					} else if (rect.width - (e.clientX - rect.left) < cornerSize) {
						item.style.cursor = 'e-resize';
					} else if (e.clientX - rect.left < cornerSize) {
						item.style.cursor = 'w-resize';
					} else {
						item.style.cursor = '';
					}
				}
			}
		}
	}

	var mouseOver = function(e) {
		var message = e.target.getAttribute('data-tooltip');
		if (message) {
			// Instructions
			tooltip.innerText = message;

			// Position
			if (e.changedTouches && e.changedTouches[0]) {
				var x = e.changedTouches[0].clientX;
				var y = e.changedTouches[0].clientY;
			} else {
				var x = e.clientX;
				var y = e.clientY;
			}

			tooltip.style.top = y + 'px';
			tooltip.style.left = x + 'px';
			document.body.appendChild(tooltip);
		} else if (tooltip.innerText) {
			tooltip.innerText = '';
			document.body.removeChild(tooltip);
		}
	}

	var dblClick = function(e) {
		var item = jSuites.findElement(e.target, 'jpanel');
		if (item && typeof(item.dblclick) == 'function') {
			// Create edition
			item.dblclick(e);
		}
	}

	var contextMenu = function(e) {
		var item = document.activeElement;
		if (item && typeof(item.contextmenu) == 'function') {
			// Create edition
			item.contextmenu(e);

			e.preventDefault();
			e.stopImmediatePropagation();
		} else {
			// Search for possible context menus
			item = jSuites.findElement(e.target, function(o) {
				return o.tagName && o.getAttribute('aria-contextmenu-id');
			});

			if (item) {
				var o = document.querySelector('#' + item);
				if (! o) {
					console.error('JSUITES: contextmenu id not found: ' + item);
				} else {
					o.contextmenu.open(e);
					e.preventDefault();
					e.stopImmediatePropagation();
				}
			}
		}
	}

	var keyDown = function(e) {
		var item = document.activeElement;
		if (item) {
			if (e.key == "Delete" && typeof(item.delete) == 'function') {
				item.delete();
				e.preventDefault();
				e.stopImmediatePropagation();
			}
		}

		if (document.jsuitesComponents && document.jsuitesComponents.length) {
			if (item = document.jsuitesComponents[document.jsuitesComponents.length - 1]) {
				if (e.key == "Escape" && typeof(item.isOpened) == 'function' && typeof(item.close) == 'function') {
					if (item.isOpened()) {
						item.close();
						e.preventDefault();
						e.stopImmediatePropagation();
					}
				}
			}
		}
	}

	document.addEventListener('mouseup', mouseUp);
	document.addEventListener("mousedown", mouseDown);
	document.addEventListener('mousemove', mouseMove);
	document.addEventListener('mouseover', mouseOver);
	document.addEventListener('dblclick', dblClick);
	document.addEventListener('keydown', keyDown);
	document.addEventListener('contextmenu', contextMenu);
}

/**
 * Global jsuites event
 */
if (typeof(document) !== "undefined" && ! document.jsuitesComponents) {
	Events();
}

jSuites.version = Version;

jSuites.setExtensions = function(o) {
	if (typeof(o) == 'object') {
		var k = Object.keys(o);
		for (var i = 0; i < k.length; i++) {
			jSuites[k[i]] = o[k[i]];
		}
	}
}

jSuites.tracking = function(component, state) {
	if (state == true) {
		document.jsuitesComponents = document.jsuitesComponents.filter(function(v) {
			return v !== null;
		});

		// Start after all events
		setTimeout(function() {
			document.jsuitesComponents.push(component);
		}, 0);

	} else {
		var index = document.jsuitesComponents.indexOf(component);
		if (index >= 0) {
			document.jsuitesComponents.splice(index, 1);
		}
	}
}

/**
 * Get or set a property from a JSON from a string.
 */
jSuites.path = function(str, val) {
	str = str.split('.');
	if (str.length) {
		var o = this;
		var p = null;
		while (str.length > 1) {
			// Get the property
			p = str.shift();
			// Check if the property exists
			if (o.hasOwnProperty(p)) {
				o = o[p];
			} else {
				// Property does not exists
				if (val === undefined) {
					return undefined;
				} else {
					// Create the property
					o[p] = {};
					// Next property
					o = o[p];
				}
			}
		}
		// Get the property
		p = str.shift();
		// Set or get the value
		if (val !== undefined) {
			o[p] = val;
			// Success
			return true;
		} else {
			// Return the value
			if (o) {
				return o[p];
			}
		}
	}
	// Something went wrong
	return false;
}

// Update dictionary
jSuites.setDictionary = function(d) {
	if (! document.dictionary) {
		document.dictionary = {}
	}
	// Replace the key into the dictionary and append the new ones
	var k = Object.keys(d);
	for (var i = 0; i < k.length; i++) {
		document.dictionary[k[i]] = d[k[i]];
	}

	// Translations
	var t = null;
	for (var i = 0; i < jSuites.calendar.weekdays.length; i++) {
		t =  jSuites.translate(jSuites.calendar.weekdays[i]);
		if (jSuites.calendar.weekdays[i]) {
			jSuites.calendar.weekdays[i] = t;
			jSuites.calendar.weekdaysShort[i] = t.substr(0,3);
		}
	}
	for (var i = 0; i < jSuites.calendar.months.length; i++) {
		t = jSuites.translate(jSuites.calendar.months[i]);
		if (t) {
			jSuites.calendar.months[i] = t;
			jSuites.calendar.monthsShort[i] = t.substr(0,3);
		}
	}
}

// Translate
jSuites.translate = function(t) {
	if (typeof(document) !== "undefined" && document.dictionary) {
		return document.dictionary[t] || t;
	 } else {
		return t;
	 }
}

jSuites.ajax = (function(options, complete) {
	if (Array.isArray(options)) {
		// Create multiple request controller
		var multiple = {
			instance: [],
			complete: complete,
		}

		if (options.length > 0) {
			for (var i = 0; i < options.length; i++) {
				options[i].multiple = multiple;
				multiple.instance.push(jSuites.ajax(options[i]));
			}
		}

		return multiple;
	}

	if (! options.data) {
		options.data = {};
	}

	if (options.type) {
		options.method = options.type;
	}

	// Default method
	if (! options.method) {
		options.method = 'GET';
	}

	// Default type
	if (! options.dataType) {
		options.dataType = 'json';
	}

	if (options.data) {
		// Parse object to variables format
		var parseData = function (value, key) {
			var vars = [];
			if (value) {
				var keys = Object.keys(value);
				if (keys.length) {
					for (var i = 0; i < keys.length; i++) {
						if (key) {
							var k = key + '[' + keys[i] + ']';
						} else {
							var k = keys[i];
						}

						if (value[k] instanceof FileList) {
							vars[k] = value[keys[i]];
						} else if (value[keys[i]] === null || value[keys[i]] === undefined) {
							vars[k] = '';
						} else if (typeof(value[keys[i]]) == 'object') {
							var r = parseData(value[keys[i]], k);
							var o = Object.keys(r);
							for (var j = 0; j < o.length; j++) {
								vars[o[j]] = r[o[j]];
							}
						} else {
							vars[k] = value[keys[i]];
						}
					}
				}
			}

			return vars;
		}

		var d = parseData(options.data);
		var k = Object.keys(d);

		// Data form
		if (options.method == 'GET') {
			if (k.length) {
				var data = [];
				for (var i = 0; i < k.length; i++) {
					data.push(k[i] + '=' + encodeURIComponent(d[k[i]]));
				}

				if (options.url.indexOf('?') < 0) {
					options.url += '?';
				}
				options.url += data.join('&');
			}
		} else {
			var data = new FormData();
			for (var i = 0; i < k.length; i++) {
				if (d[k[i]] instanceof FileList) {
					if (d[k[i]].length) {
						for (var j = 0; j < d[k[i]].length; j++) {
							data.append(k[i], d[k[i]][j], d[k[i]][j].name);
						}
					}
				} else {
					data.append(k[i], d[k[i]]);
				}
			}
		}
	}

	var httpRequest = new XMLHttpRequest();
	httpRequest.open(options.method, options.url, true);
	httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

	// Content type
	if (options.contentType) {
		httpRequest.setRequestHeader('Content-Type', options.contentType);
	}

	// Headers
	if (options.method == 'POST') {
		httpRequest.setRequestHeader('Accept', 'application/json');
	} else {
		if (options.dataType == 'blob') {
			httpRequest.responseType = "blob";
		} else {
			if (! options.contentType) {
				if (options.dataType == 'json') {
					httpRequest.setRequestHeader('Content-Type', 'text/json');
				} else if (options.dataType == 'html') {
					httpRequest.setRequestHeader('Content-Type', 'text/html');
				}
			}
		}
	}

	// No cache
	if (options.cache != true) {
		httpRequest.setRequestHeader('pragma', 'no-cache');
		httpRequest.setRequestHeader('cache-control', 'no-cache');
	}

	// Authentication
	if (options.withCredentials == true) {
		httpRequest.withCredentials = true
	}

	// Before send
	if (typeof(options.beforeSend) == 'function') {
		options.beforeSend(httpRequest);
	}

	// Before send
	if (typeof(jSuites.ajax.beforeSend) == 'function') {
		jSuites.ajax.beforeSend(httpRequest);
	}

	if (document.ajax && typeof(document.ajax.beforeSend) == 'function') {
		document.ajax.beforeSend(httpRequest);
	}

	httpRequest.onload = function() {
		if (httpRequest.status === 200) {
			if (options.dataType == 'json') {
				try {
					var result = JSON.parse(httpRequest.responseText);

					if (options.success && typeof(options.success) == 'function') {
						options.success(result);
					}
				} catch(err) {
					if (options.error && typeof(options.error) == 'function') {
						options.error(err, result);
					}
				}
			} else {
				if (options.dataType == 'blob') {
					var result = httpRequest.response;
				} else {
					var result = httpRequest.responseText;
				}

				if (options.success && typeof(options.success) == 'function') {
					options.success(result);
				}
			}
		} else {
			if (options.error && typeof(options.error) == 'function') {
				options.error(httpRequest.responseText, httpRequest.status);
			}
		}

		// Global queue
		if (jSuites.ajax.queue && jSuites.ajax.queue.length > 0) {
			jSuites.ajax.send(jSuites.ajax.queue.shift());
		}

		// Global complete method
		if (jSuites.ajax.requests && jSuites.ajax.requests.length) {
			// Get index of this request in the container
			var index = jSuites.ajax.requests.indexOf(httpRequest);
			// Remove from the ajax requests container
			jSuites.ajax.requests.splice(index, 1);
			// Deprected: Last one?
			if (! jSuites.ajax.requests.length) {
				// Object event
				if (options.complete && typeof(options.complete) == 'function') {
					options.complete(result);
				}
			}
			// Group requests
			if (options.group) {
				if (jSuites.ajax.oncomplete && typeof(jSuites.ajax.oncomplete[options.group]) == 'function') {
					if (! jSuites.ajax.pending(options.group)) {
						jSuites.ajax.oncomplete[options.group]();
						jSuites.ajax.oncomplete[options.group] = null;
					}
				}
			}
			// Multiple requests controller
			if (options.multiple && options.multiple.instance) {
				// Get index of this request in the container
				var index = options.multiple.instance.indexOf(httpRequest);
				// Remove from the ajax requests container
				options.multiple.instance.splice(index, 1);
				// If this is the last one call method complete
				if (! options.multiple.instance.length) {
					if (options.multiple.complete && typeof(options.multiple.complete) == 'function') {
						options.multiple.complete(result);
					}
				}
			}
		}
	}

	// Keep the options
	httpRequest.options = options;
	// Data
	httpRequest.data = data;

	// Queue
	if (options.queue == true && jSuites.ajax.requests.length > 0) {
		jSuites.ajax.queue.push(httpRequest);
	} else {
		jSuites.ajax.send(httpRequest)
	}

	return httpRequest;
});

jSuites.ajax.send = function(httpRequest) {
	if (httpRequest.data) {
		if (Array.isArray(httpRequest.data)) {
			httpRequest.send(httpRequest.data.join('&'));
		} else {
			httpRequest.send(httpRequest.data);
		}
	} else {
		httpRequest.send();
	}

	jSuites.ajax.requests.push(httpRequest);
}

jSuites.ajax.exists = function(url, __callback) {
	var http = new XMLHttpRequest();
	http.open('HEAD', url, false);
	http.send();
	if (http.status) {
		__callback(http.status);
	}
}

jSuites.ajax.pending = function(group) {
	var n = 0;
	var o = jSuites.ajax.requests;
	if (o && o.length) {
		for (var i = 0; i < o.length; i++) {
			if (! group || group == o[i].options.group) {
				n++
			}
		}
	}
	return n;
}

jSuites.ajax.oncomplete = {};
jSuites.ajax.requests = [];
jSuites.ajax.queue = [];

jSuites.animation = {};

jSuites.animation.slideLeft = function(element, direction, done) {
	if (direction == true) {
		element.classList.add('slide-left-in');
		setTimeout(function() {
			element.classList.remove('slide-left-in');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	} else {
		element.classList.add('slide-left-out');
		setTimeout(function() {
			element.classList.remove('slide-left-out');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	}
}

jSuites.animation.slideRight = function(element, direction, done) {
	if (direction == true) {
		element.classList.add('slide-right-in');
		setTimeout(function() {
			element.classList.remove('slide-right-in');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	} else {
		element.classList.add('slide-right-out');
		setTimeout(function() {
			element.classList.remove('slide-right-out');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	}
}

jSuites.animation.slideTop = function(element, direction, done) {
	if (direction == true) {
		element.classList.add('slide-top-in');
		setTimeout(function() {
			element.classList.remove('slide-top-in');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	} else {
		element.classList.add('slide-top-out');
		setTimeout(function() {
			element.classList.remove('slide-top-out');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	}
}

jSuites.animation.slideBottom = function(element, direction, done) {
	if (direction == true) {
		element.classList.add('slide-bottom-in');
		setTimeout(function() {
			element.classList.remove('slide-bottom-in');
			if (typeof(done) == 'function') {
				done();
			}
		}, 400);
	} else {
		element.classList.add('slide-bottom-out');
		setTimeout(function() {
			element.classList.remove('slide-bottom-out');
			if (typeof(done) == 'function') {
				done();
			}
		}, 100);
	}
}

jSuites.animation.fadeIn = function(element, done) {
	element.style.display = '';
	element.classList.add('fade-in');
	setTimeout(function() {
		element.classList.remove('fade-in');
		if (typeof(done) == 'function') {
			done();
		}
	}, 2000);
}

jSuites.animation.fadeOut = function(element, done) {
	element.classList.add('fade-out');
	setTimeout(function() {
		element.style.display = 'none';
		element.classList.remove('fade-out');
		if (typeof(done) == 'function') {
			done();
		}
	}, 1000);
}

jSuites.calendar = (function(el, options) {
	// Already created, update options
	if (el.calendar) {
		return el.calendar.setOptions(options, true);
	}

	// New instance
	var obj = { type:'calendar' };
	obj.options = {};

	// Date
	obj.date = null;

	/**
	 * Update options
	 */
	obj.setOptions = function(options, reset) {
		// Default configuration
		var defaults = {
			// Render type: [ default | year-month-picker ]
			type: 'default',
			// Restrictions
			validRange: null,
			// Starting weekday - 0 for sunday, 6 for saturday
			startingDay: null,
			// Date format
			format: 'DD/MM/YYYY',
			// Allow keyboard date entry
			readonly: true,
			// Today is default
			today: false,
			// Show timepicker
			time: false,
			// Show the reset button
			resetButton: true,
			// Placeholder
			placeholder: '',
			// Translations can be done here
			months: jSuites.calendar.monthsShort,
			monthsFull: jSuites.calendar.months,
			weekdays: jSuites.calendar.weekdays,
			textDone: jSuites.translate('Done'),
			textReset: jSuites.translate('Reset'),
			textUpdate: jSuites.translate('Update'),
			// Value
			value: null,
			// Fullscreen (this is automatic set for screensize < 800)
			fullscreen: false,
			// Create the calendar closed as default
			opened: false,
			// Events
			onopen: null,
			onclose: null,
			onchange: null,
			onupdate: null,
			// Internal mode controller
			mode: null,
			position: null,
			// Data type
			dataType: null,
			// Controls
			controls: true,
		}

		// Loop through our object
		for (var property in defaults) {
			if (options && options.hasOwnProperty(property)) {
				obj.options[property] = options[property];
			} else {
				if (typeof(obj.options[property]) == 'undefined' || reset === true) {
					obj.options[property] = defaults[property];
				}
			}
		}

		// Reset button
		if (obj.options.resetButton == false) {
			calendarReset.style.display = 'none';
		} else {
			calendarReset.style.display = '';
		}

		// Readonly
		if (obj.options.readonly) {
			el.setAttribute('readonly', 'readonly');
		} else {
			el.removeAttribute('readonly');
		}

		// Placeholder
		if (obj.options.placeholder) {
			el.setAttribute('placeholder', obj.options.placeholder);
		} else {
			el.removeAttribute('placeholder');
		}

		if (jSuites.isNumeric(obj.options.value) && obj.options.value > 0) {
			obj.options.value = jSuites.calendar.numToDate(obj.options.value);
			// Data type numeric
			obj.options.dataType = 'numeric';
		}

		// Texts
		calendarReset.innerHTML = obj.options.textReset;
		calendarConfirm.innerHTML = obj.options.textDone;
		calendarControlsUpdateButton.innerHTML = obj.options.textUpdate;

		// Define mask
		el.setAttribute('data-mask', obj.options.format.toLowerCase());

		// Value
		if (! obj.options.value && obj.options.today) {
			var value = jSuites.calendar.now();
		} else {
			var value = obj.options.value;
		}

		// Set internal date
		if (value) {
			// Force the update
			obj.options.value = null;
			// New value
			obj.setValue(value);
		}

		return obj;
	}

	/**
	 * Open the calendar
	 */
	obj.open = function (value) {
		if (! calendar.classList.contains('jcalendar-focus')) {
			if (! calendar.classList.contains('jcalendar-inline')) {
				// Current
				jSuites.calendar.current = obj;
				// Start tracking
				jSuites.tracking(obj, true);
				// Create the days
				obj.getDays();
				// Render months
				if (obj.options.type == 'year-month-picker') {
					obj.getMonths();
				}
				// Get time
				if (obj.options.time) {
					calendarSelectHour.value = obj.date[3];
					calendarSelectMin.value = obj.date[4];
				}

				// Show calendar
				calendar.classList.add('jcalendar-focus');

				// Get the position of the corner helper
				if (jSuites.getWindowWidth() < 800 || obj.options.fullscreen) {
					calendar.classList.add('jcalendar-fullsize');
					// Animation
					jSuites.animation.slideBottom(calendarContent, 1);
				} else {
					calendar.classList.remove('jcalendar-fullsize');

					var rect = el.getBoundingClientRect();
					var rectContent = calendarContent.getBoundingClientRect();

					if (obj.options.position) {
						calendarContainer.style.position = 'fixed';
						if (window.innerHeight < rect.bottom + rectContent.height) {
							calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px';
						} else {
							calendarContainer.style.top = (rect.top + rect.height + 2) + 'px';
						}
						calendarContainer.style.left = rect.left + 'px';
					} else {
						if (window.innerHeight < rect.bottom + rectContent.height) {
							var d = -1 * (rect.height + rectContent.height + 2);
							if (d + rect.top < 0) {
								d = -1 * (rect.top + rect.height);
							}
							calendarContainer.style.top = d + 'px';
						} else {
							calendarContainer.style.top = 2 + 'px';
						}

						if (window.innerWidth < rect.left + rectContent.width) {
							var d = window.innerWidth - (rect.left + rectContent.width + 20);
							calendarContainer.style.left = d + 'px';
						} else {
							calendarContainer.style.left = '0px';
						}
					}
				}

				// Events
				if (typeof(obj.options.onopen) == 'function') {
					obj.options.onopen(el);
				}
			}
		}
	}

	obj.close = function (ignoreEvents, update) {
		if (calendar.classList.contains('jcalendar-focus')) {
			if (update !== false) {
				var element = calendar.querySelector('.jcalendar-selected');

				if (typeof(update) == 'string') {
					var value = update;
				} else if (! element || element.classList.contains('jcalendar-disabled')) {
					var value = obj.options.value
				} else {
					var value = obj.getValue();
				}

				obj.setValue(value);
			}

			// Events
			if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
				obj.options.onclose(el);
			}
			// Hide
			calendar.classList.remove('jcalendar-focus');
			// Stop tracking
			jSuites.tracking(obj, false);
			// Current
			jSuites.calendar.current = null;
		}

		return obj.options.value;
	}

	obj.prev = function() {
		// Check if the visualization is the days picker or years picker
		if (obj.options.mode == 'years') {
			obj.date[0] = obj.date[0] - 12;

			// Update picker table of days
			obj.getYears();
		} else if (obj.options.mode == 'months') {
			obj.date[0] = parseInt(obj.date[0]) - 1;
			// Update picker table of months
			obj.getMonths();
		} else {
			// Go to the previous month
			if (obj.date[1] < 2) {
				obj.date[0] = obj.date[0] - 1;
				obj.date[1] = 12;
			} else {
				obj.date[1] = obj.date[1] - 1;
			}

			// Update picker table of days
			obj.getDays();
		}
	}

	obj.next = function() {
		// Check if the visualization is the days picker or years picker
		if (obj.options.mode == 'years') {
			obj.date[0] = parseInt(obj.date[0]) + 12;

			// Update picker table of days
			obj.getYears();
		} else if (obj.options.mode == 'months') {
			obj.date[0] = parseInt(obj.date[0]) + 1;
			// Update picker table of months
			obj.getMonths();
		} else {
			// Go to the previous month
			if (obj.date[1] > 11) {
				obj.date[0] = parseInt(obj.date[0]) + 1;
				obj.date[1] = 1;
			} else {
				obj.date[1] = parseInt(obj.date[1]) + 1;
			}

			// Update picker table of days
			obj.getDays();
		}
	}

	/**
	 * Set today
	 */
	obj.setToday = function() {
		// Today
		var value = new Date().toISOString().substr(0, 10);
		// Change value
		obj.setValue(value);
		// Value
		return value;
	}

	obj.setValue = function(val) {
		if (! val) {
			val = '' + val;
		}
		// Values
		var newValue = val;
		var oldValue = obj.options.value;

		if (oldValue != newValue) {
			// Set label
			if (! newValue) {
				obj.date = null;
				var val = '';
				el.classList.remove('jcalendar_warning');
				el.title = '';
			} else {
				var value = obj.setLabel(newValue, obj.options);
				var date = newValue.split(' ');
				if (! date[1]) {
					date[1] = '00:00:00';
				}
				var time = date[1].split(':')
				var date = date[0].split('-');
				var y = parseInt(date[0]);
				var m = parseInt(date[1]);
				var d = parseInt(date[2]);
				var h = parseInt(time[0]);
				var i = parseInt(time[1]);
				obj.date = [ y, m, d, h, i, 0 ];
				var val = obj.setLabel(newValue, obj.options);

				// Current selection day
				var current = jSuites.calendar.now(new Date(y, m-1, d), true);

				// Available ranges
				if (obj.options.validRange) {
					if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) {
						var test1 = true;
					} else {
						var test1 = false;
					}

					if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) {
						var test2 = true;
					} else {
						var test2 = false;
					}

					if (! (test1 && test2)) {
						el.classList.add('jcalendar_warning');
						el.title = jSuites.translate('Date outside the valid range');
					} else {
						el.classList.remove('jcalendar_warning');
						el.title = '';
					}
				} else {
					el.classList.remove('jcalendar_warning');
					el.title = '';
				}
			}

			// New value
			obj.options.value = newValue;

			if (typeof(obj.options.onchange) ==  'function') {
				obj.options.onchange(el, newValue, oldValue);
			}

			// Lemonade JS
			if (el.value != val) {
				el.value = val;
				if (typeof(el.oninput) == 'function') {
					el.oninput({
						type: 'input',
						target: el,
						value: el.value
					});
				}
			}
		}

		obj.getDays();
		// Render months
		if (obj.options.type == 'year-month-picker') {
			obj.getMonths();
		}
	}

	obj.getValue = function() {
		if (obj.date) {
			if (obj.options.time) {
				return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(obj.date[3]) + ':' + jSuites.two(obj.date[4]) + ':' + jSuites.two(0);
			} else {
				return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(0) + ':' + jSuites.two(0) + ':' + jSuites.two(0);
			}
		} else {
			return "";
		}
	}

	/**
	 *  Calendar
	 */
	obj.update = function(element, v) {
		if (element.classList.contains('jcalendar-disabled')) {
			// Do nothing
		} else {
			var elements = calendar.querySelector('.jcalendar-selected');
			if (elements) {
				elements.classList.remove('jcalendar-selected');
			}
			element.classList.add('jcalendar-selected');

			if (element.classList.contains('jcalendar-set-month')) {
				obj.date[1] = v;
				obj.date[2] = 1; // first day of the month
			} else {
				obj.date[2] = element.innerText;
			}

			if (! obj.options.time) {
				obj.close();
			} else {
				obj.date[3] = calendarSelectHour.value;
				obj.date[4] = calendarSelectMin.value;
			}
		}

		// Update
		updateActions();
	}

	/**
	 * Set to blank
	 */
	obj.reset = function() {
		// Close calendar
		obj.setValue('');
		obj.date = null;
		obj.close(false, false);
	}

	/**
	 * Get calendar days
	 */
	obj.getDays = function() {
		// Mode
		obj.options.mode = 'days';

		// Setting current values in case of NULLs
		var date = new Date();

		// Current selection
		var year = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
		var month = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : parseInt(date.getMonth()) + 1;
		var day = obj.date && jSuites.isNumeric(obj.date[2]) ? obj.date[2] : parseInt(date.getDate());
		var hour = obj.date && jSuites.isNumeric(obj.date[3]) ? obj.date[3] : parseInt(date.getHours());
		var min = obj.date && jSuites.isNumeric(obj.date[4]) ? obj.date[4] : parseInt(date.getMinutes());

		// Selection container
		obj.date = [ year, month, day, hour, min, 0 ];

		// Update title
		calendarLabelYear.innerHTML = year;
		calendarLabelMonth.innerHTML = obj.options.months[month - 1];

		// Current month and Year
		var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false;
		var currentDay = date.getDate();

		// Number of days in the month
		var date = new Date(year, month, 0, 0, 0);
		var numberOfDays = date.getDate();

		// First day
		var date = new Date(year, month-1, 0, 0, 0);
		var firstDay = date.getDay() + 1;

		// Index value
		var index = obj.options.startingDay || 0;

		// First of day relative to the starting calendar weekday
		firstDay = firstDay - index;

		// Reset table
		calendarBody.innerHTML = '';

		// Weekdays Row
		var row = document.createElement('tr');
		row.setAttribute('align', 'center');
		calendarBody.appendChild(row);

		// Create weekdays row
		for (var i = 0; i < 7; i++) {
			var cell = document.createElement('td');
			cell.classList.add('jcalendar-weekday')
			cell.innerHTML = obj.options.weekdays[index].substr(0,1);
			row.appendChild(cell);
			// Next week day
			index++;
			// Restart index
			if (index > 6) {
				index = 0;
			}
		}

		// Index of days
		var index = 0;
		var d = 0;

		// Calendar table
		for (var j = 0; j < 6; j++) {
			// Reset cells container
			var row = document.createElement('tr');
			row.setAttribute('align', 'center');
			row.style.height = '34px';

			// Create cells
			for (var i = 0; i < 7; i++) {
				// Create cell
				var cell = document.createElement('td');
				cell.classList.add('jcalendar-set-day');

				if (index >= firstDay && index < (firstDay + numberOfDays)) {
					// Day cell
					d++;
					cell.innerHTML = d;

					// Selected
					if (d == day) {
						cell.classList.add('jcalendar-selected');
					}

					// Current selection day is today
					if (isCurrentMonthAndYear && currentDay == d) {
						cell.style.fontWeight = 'bold';
					}

					// Current selection day
					var current = jSuites.calendar.now(new Date(year, month-1, d), true);

					// Available ranges
					if (obj.options.validRange) {
						if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) {
							var test1 = true;
						} else {
							var test1 = false;
						}

						if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) {
							var test2 = true;
						} else {
							var test2 = false;
						}

						if (! (test1 && test2)) {
							cell.classList.add('jcalendar-disabled');
						}
					}
				}
				// Day cell
				row.appendChild(cell);
				// Index
				index++;
			}

			// Add cell to the calendar body
			calendarBody.appendChild(row);
		}

		// Show time controls
		if (obj.options.time) {
			calendarControlsTime.style.display = '';
		} else {
			calendarControlsTime.style.display = 'none';
		}

		// Update
		updateActions();
	}

	obj.getMonths = function() {
		// Mode
		obj.options.mode = 'months';

		// Loading month labels
		var months = obj.options.months;

		// Value
		var value = obj.options.value;

		// Current date
		var date = new Date();
		var currentYear = parseInt(date.getFullYear());
		var currentMonth = parseInt(date.getMonth()) + 1;
		var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : currentYear;
		var selectedMonth = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : currentMonth;

		// Update title
		calendarLabelYear.innerHTML = obj.date[0];
		calendarLabelMonth.innerHTML = months[selectedMonth-1];

		// Table
		var table = document.createElement('table');
		table.setAttribute('width', '100%');

		// Row
		var row = null;

		// Calendar table
		for (var i = 0; i < 12; i++) {
			if (! (i % 4)) {
				// Reset cells container
				var row = document.createElement('tr');
				row.setAttribute('align', 'center');
				table.appendChild(row);
			}

			// Create cell
			var cell = document.createElement('td');
			cell.classList.add('jcalendar-set-month');
			cell.setAttribute('data-value', i+1);
			cell.innerText = months[i];

			if (obj.options.validRange) {
				var current = selectedYear + '-' + jSuites.two(i+1);
				if (! obj.options.validRange[0] || current >= obj.options.validRange[0].substr(0,7)) {
					var test1 = true;
				} else {
					var test1 = false;
				}

				if (! obj.options.validRange[1] || current <= obj.options.validRange[1].substr(0,7)) {
					var test2 = true;
				} else {
					var test2 = false;
				}

				if (! (test1 && test2)) {
					cell.classList.add('jcalendar-disabled');
				}
			}

			if (i+1 == selectedMonth) {
				cell.classList.add('jcalendar-selected');
			}

			if (currentYear == selectedYear && i+1 == currentMonth) {
				cell.style.fontWeight = 'bold';
			}

			row.appendChild(cell);
		}

		calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
		calendarBody.children[0].children[0].appendChild(table);

		// Update
		updateActions();
	}

	obj.getYears = function() {
		// Mode
		obj.options.mode = 'years';

		// Current date
		var date = new Date();
		var currentYear = date.getFullYear();
		var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());

		// Array of years
		var y = [];
		for (var i = 0; i < 25; i++) {
			y[i] = parseInt(obj.date[0]) + (i - 12);
		}

		// Assembling the year tables
		var table = document.createElement('table');
		table.setAttribute('width', '100%');

		for (var i = 0; i < 25; i++) {
			if (! (i % 5)) {
				// Reset cells container
				var row = document.createElement('tr');
				row.setAttribute('align', 'center');
				table.appendChild(row);
			}

			// Create cell
			var cell = document.createElement('td');
			cell.classList.add('jcalendar-set-year');
			cell.innerText = y[i];

			if (selectedYear == y[i]) {
				cell.classList.add('jcalendar-selected');
			}

			if (currentYear == y[i]) {
				cell.style.fontWeight = 'bold';
			}

			row.appendChild(cell);
		}

		calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
		calendarBody.firstChild.firstChild.appendChild(table);

		// Update
		updateActions();
	}

	obj.setLabel = function(value, mixed) {
		return jSuites.calendar.getDateString(value, mixed);
	}

	obj.fromFormatted = function (value, format) {
		return jSuites.calendar.extractDateFromString(value, format);
	}

	var mouseUpControls = function(e) {
		var element = jSuites.findElement(e.target, 'jcalendar-container');
		if (element) {
			var action = e.target.className;

			// Object id
			if (action == 'jcalendar-prev') {
				obj.prev();
			} else if (action == 'jcalendar-next') {
				obj.next();
			} else if (action == 'jcalendar-month') {
				obj.getMonths();
			} else if (action == 'jcalendar-year') {
				obj.getYears();
			} else if (action == 'jcalendar-set-year') {
				obj.date[0] = e.target.innerText;
				if (obj.options.type == 'year-month-picker') {
					obj.getMonths();
				} else {
					obj.getDays();
				}
			} else if (e.target.classList.contains('jcalendar-set-month')) {
				var month = parseInt(e.target.getAttribute('data-value'));
				if (obj.options.type == 'year-month-picker') {
					obj.update(e.target, month);
				} else {
					obj.date[1] = month;
					obj.getDays();
				}
			} else if (action == 'jcalendar-confirm' || action == 'jcalendar-update' || action == 'jcalendar-close') {
				obj.close();
			} else if (action == 'jcalendar-backdrop') {
				obj.close(false, false);
			} else if (action == 'jcalendar-reset') {
				obj.reset();
			} else if (e.target.classList.contains('jcalendar-set-day') && e.target.innerText) {
				obj.update(e.target);
			}
		} else {
			obj.close();
		}
	}

	var keyUpControls = function(e) {
		if (e.target.value && e.target.value.length > 3) {
			var test = jSuites.calendar.extractDateFromString(e.target.value, obj.options.format);
			if (test) {
				obj.setValue(test);
			}
		}
	}

	// Update actions button
	var updateActions = function() {
		var currentDay = calendar.querySelector('.jcalendar-selected');

		if (currentDay && currentDay.classList.contains('jcalendar-disabled')) {
			calendarControlsUpdateButton.setAttribute('disabled', 'disabled');
			calendarSelectHour.setAttribute('disabled', 'disabled');
			calendarSelectMin.setAttribute('disabled', 'disabled');
		} else {
			calendarControlsUpdateButton.removeAttribute('disabled');
			calendarSelectHour.removeAttribute('disabled');
			calendarSelectMin.removeAttribute('disabled');
		}

		// Event
		if (typeof(obj.options.onupdate) == 'function') {
			obj.options.onupdate(el, obj.getValue());
		}
	}

	var calendar = null;
	var calendarReset = null;
	var calendarConfirm = null;
	var calendarContainer = null;
	var calendarContent = null;
	var calendarLabelYear = null;
	var calendarLabelMonth = null;
	var calendarTable = null;
	var calendarBody = null;

	var calendarControls = null;
	var calendarControlsTime = null;
	var calendarControlsUpdate = null;
	var calendarControlsUpdateButton = null;
	var calendarSelectHour = null;
	var calendarSelectMin = null;

	var init = function() {
		// Get value from initial element if that is an input
		if (el.tagName == 'INPUT' && el.value) {
			options.value = el.value;
		}

		// Calendar DOM elements
		calendarReset = document.createElement('div');
		calendarReset.className = 'jcalendar-reset';

		calendarConfirm = document.createElement('div');
		calendarConfirm.className = 'jcalendar-confirm';

		calendarControls = document.createElement('div');
		calendarControls.className = 'jcalendar-controls'
		calendarControls.style.borderBottom = '1px solid #ddd';
		calendarControls.appendChild(calendarReset);
		calendarControls.appendChild(calendarConfirm);

		calendarContainer = document.createElement('div');
		calendarContainer.className = 'jcalendar-container';
		calendarContent = document.createElement('div');
		calendarContent.className = 'jcalendar-content';
		calendarContainer.appendChild(calendarContent);

		// Main element
		if (el.tagName == 'DIV') {
			calendar = el;
			calendar.classList.add('jcalendar-inline');
		} else {
			// Add controls to the screen
			calendarContent.appendChild(calendarControls);

			calendar = document.createElement('div');
			calendar.className = 'jcalendar';
		}
		calendar.classList.add('jcalendar-container');
		calendar.appendChild(calendarContainer);

		// Table container
		var calendarTableContainer = document.createElement('div');
		calendarTableContainer.className = 'jcalendar-table';
		calendarContent.appendChild(calendarTableContainer);

		// Previous button
		var calendarHeaderPrev = document.createElement('td');
		calendarHeaderPrev.setAttribute('colspan', '2');
		calendarHeaderPrev.className = 'jcalendar-prev';

		// Header with year and month
		calendarLabelYear = document.createElement('span');
		calendarLabelYear.className = 'jcalendar-year';
		calendarLabelMonth = document.createElement('span');
		calendarLabelMonth.className = 'jcalendar-month';

		var calendarHeaderTitle = document.createElement('td');
		calendarHeaderTitle.className = 'jcalendar-header';
		calendarHeaderTitle.setAttribute('colspan', '3');
		calendarHeaderTitle.appendChild(calendarLabelMonth);
		calendarHeaderTitle.appendChild(calendarLabelYear);

		var calendarHeaderNext = document.createElement('td');
		calendarHeaderNext.setAttribute('colspan', '2');
		calendarHeaderNext.className = 'jcalendar-next';

		var calendarHeader = document.createElement('thead');
		var calendarHeaderRow = document.createElement('tr');
		calendarHeaderRow.appendChild(calendarHeaderPrev);
		calendarHeaderRow.appendChild(calendarHeaderTitle);
		calendarHeaderRow.appendChild(calendarHeaderNext);
		calendarHeader.appendChild(calendarHeaderRow);

		calendarTable = document.createElement('table');
		calendarBody = document.createElement('tbody');
		calendarTable.setAttribute('cellpadding', '0');
		calendarTable.setAttribute('cellspacing', '0');
		calendarTable.appendChild(calendarHeader);
		calendarTable.appendChild(calendarBody);
		calendarTableContainer.appendChild(calendarTable);

		calendarSelectHour = document.createElement('select');
		calendarSelectHour.className = 'jcalendar-select';
		calendarSelectHour.onchange = function() {
			obj.date[3] = this.value;

			// Event
			if (typeof(obj.options.onupdate) == 'function') {
				obj.options.onupdate(el, obj.getValue());
			}
		}

		for (var i = 0; i < 24; i++) {
			var element = document.createElement('option');
			element.value = i;
			element.innerHTML = jSuites.two(i);
			calendarSelectHour.appendChild(element);
		}

		calendarSelectMin = document.createElement('select');
		calendarSelectMin.className = 'jcalendar-select';
		calendarSelectMin.onchange = function() {
			obj.date[4] = this.value;

			// Event
			if (typeof(obj.options.onupdate) == 'function') {
				obj.options.onupdate(el, obj.getValue());
			}
		}

		for (var i = 0; i < 60; i++) {
			var element = document.createElement('option');
			element.value = i;
			element.innerHTML = jSuites.two(i);
			calendarSelectMin.appendChild(element);
		}

		// Footer controls
		var calendarControlsFooter = document.createElement('div');
		calendarControlsFooter.className = 'jcalendar-controls';

		calendarControlsTime = document.createElement('div');
		calendarControlsTime.className = 'jcalendar-time';
		calendarControlsTime.style.maxWidth = '140px';
		calendarControlsTime.appendChild(calendarSelectHour);
		calendarControlsTime.appendChild(calendarSelectMin);

		calendarControlsUpdateButton = document.createElement('button');
		calendarControlsUpdateButton.setAttribute('type', 'button');
		calendarControlsUpdateButton.className = 'jcalendar-update';

		calendarControlsUpdate = document.createElement('div');
		calendarControlsUpdate.style.flexGrow = '10';
		calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
		calendarControlsFooter.appendChild(calendarControlsTime);

		// Only show the update button for input elements
		if (el.tagName == 'INPUT') {
			calendarControlsFooter.appendChild(calendarControlsUpdate);
		}

		calendarContent.appendChild(calendarControlsFooter);

		var calendarBackdrop = document.createElement('div');
		calendarBackdrop.className = 'jcalendar-backdrop';
		calendar.appendChild(calendarBackdrop);

		// Handle events
		el.addEventListener("keyup", keyUpControls);

		// Add global events
		calendar.addEventListener("swipeleft", function(e) {
			jSuites.animation.slideLeft(calendarTable, 0, function() {
				obj.next();
				jSuites.animation.slideRight(calendarTable, 1);
			});
			e.preventDefault();
			e.stopPropagation();
		});

		calendar.addEventListener("swiperight", function(e) {
			jSuites.animation.slideRight(calendarTable, 0, function() {
				obj.prev();
				jSuites.animation.slideLeft(calendarTable, 1);
			});
			e.preventDefault();
			e.stopPropagation();
		});

		if ('ontouchend' in document.documentElement === true) {
			calendar.addEventListener("touchend", mouseUpControls);
			el.addEventListener("touchend", obj.open);
		} else {
			calendar.addEventListener("mouseup", mouseUpControls);
			el.addEventListener("mouseup", obj.open);
		}

		// Global controls
		if (! jSuites.calendar.hasEvents) {
			// Execute only one time
			jSuites.calendar.hasEvents = true;
			// Enter and Esc
			document.addEventListener("keydown", jSuites.calendar.keydown);
		}

		// Set configuration
		obj.setOptions(options);

		// Append element to the DOM
		if (el.tagName == 'INPUT') {
			el.parentNode.insertBefore(calendar, el.nextSibling);
			// Add properties
			el.setAttribute('autocomplete', 'off');
			// Element
			el.classList.add('jcalendar-input');
			// Value
			el.value = obj.setLabel(obj.getValue(), obj.options);
		} else {
			// Get days
			obj.getDays();
			// Hour
			if (obj.options.time) {
				calendarSelectHour.value = obj.date[3];
				calendarSelectMin.value = obj.date[4];
			}
		}

		// Default opened
		if (obj.options.opened == true) {
			obj.open();
		}

		// Controls
		if (obj.options.controls == false) {
			calendarContainer.classList.add('jcalendar-hide-controls');
		}

		// Change method
		el.change = obj.setValue;

		// Global generic value handler
		el.val = function(val) {
			if (val === undefined) {
				return obj.getValue();
			} else {
				obj.setValue(val);
			}
		}

		// Keep object available from the node
		el.calendar = calendar.calendar = obj;
	}

	init();

	return obj;
});

jSuites.calendar.keydown = function(e) {
	var calendar = null;
	if (calendar = jSuites.calendar.current) {
		if (e.which == 13) {
			// ENTER
			calendar.close(false, true);
		} else if (e.which == 27) {
			// ESC
			calendar.close(false, false);
		}
	}
}

jSuites.calendar.prettify = function(d, texts) {
	if (! texts) {
		var texts = {
			justNow: 'Just now',
			xMinutesAgo: '{0}m ago',
			xHoursAgo: '{0}h ago',
			xDaysAgo: '{0}d ago',
			xWeeksAgo: '{0}w ago',
			xMonthsAgo: '{0} mon ago',
			xYearsAgo: '{0}y ago',
		}
	}

	var d1 = new Date();
	var d2 = new Date(d);
	var total = parseInt((d1 - d2) / 1000 / 60);

	String.prototype.format = function(o) {
		return this.replace('{0}', o);
	}

	if (total == 0) {
		var text = texts.justNow;
	} else if (total < 90) {
		var text = texts.xMinutesAgo.format(total);
	} else if (total < 1440) { // One day
		var text = texts.xHoursAgo.format(Math.round(total/60));
	} else if (total < 20160) { // 14 days
		var text = texts.xDaysAgo.format(Math.round(total / 1440));
	} else if (total < 43200) { // 30 days
		var text = texts.xWeeksAgo.format(Math.round(total / 10080));
	} else if (total < 1036800) { // 24 months
		var text = texts.xMonthsAgo.format(Math.round(total / 43200));
	} else { // 24 months+
		var text = texts.xYearsAgo.format(Math.round(total / 525600));
	}

	return text;
}

jSuites.calendar.prettifyAll = function() {
	var elements = document.querySelectorAll('.prettydate');
	for (var i = 0; i < elements.length; i++) {
		if (elements[i].getAttribute('data-date')) {
			elements[i].innerHTML = jSuites.calendar.prettify(elements[i].getAttribute('data-date'));
		} else {
			if (elements[i].innerHTML) {
				elements[i].setAttribute('title', elements[i].innerHTML);
				elements[i].setAttribute('data-date', elements[i].innerHTML);
				elements[i].innerHTML = jSuites.calendar.prettify(elements[i].innerHTML);
			}
		}
	}
}

jSuites.calendar.now = function(date, dateOnly) {
	if (Array.isArray(date)) {
		var y = date[0];
		var m = date[1];
		var d = date[2];
		var h = date[3];
		var i = date[4];
		var s = date[5];
	} else {
		if (! date) {
			var date = new Date();
		}
		var y = date.getFullYear();
		var m = date.getMonth() + 1;
		var d = date.getDate();
		var h = date.getHours();
		var i = date.getMinutes();
		var s = date.getSeconds();
	}

	if (dateOnly == true) {
		return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d);
	} else {
		return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d) + ' ' + jSuites.two(h) + ':' + jSuites.two(i) + ':' + jSuites.two(s);
	}
}

jSuites.calendar.toArray = function(value) {
	var date = value.split(((value.indexOf('T') !== -1) ? 'T' : ' '));
	var time = date[1];
	var date = date[0].split('-');
	var y = parseInt(date[0]);
	var m = parseInt(date[1]);
	var d = parseInt(date[2]);

	if (time) {
		var time = time.split(':');
		var h = parseInt(time[0]);
		var i = parseInt(time[1]);
	} else {
		var h = 0;
		var i = 0;
	}
	return [ y, m, d, h, i, 0 ];
}

// Helper to extract date from a string
jSuites.calendar.extractDateFromString = function(date, format) {
	var o = jSuites.mask(date, { mask: format }, true);

	// Check if in format Excel (Need difference with format date or type detected is numeric)
	if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
		var d = new Date(Math.round((date - 25569)*86400*1000));
		return d.getFullYear() + "-" + jSuites.two(d.getMonth()) + "-" + jSuites.two(d.getDate()) + ' 00:00:00';
	}

	var complete = false;

	if (o.values.length === o.tokens.length && o.values[o.values.length-1].length >= o.tokens[o.tokens.length-1].length) {
		complete = true;
	}

	if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
		if (! o.date[2]) {
			o.date[2] = 1;
		}

		return o.date[0] + '-' + jSuites.two(o.date[1]) + '-' + jSuites.two(o.date[2]) + ' ' + jSuites.two(o.date[3]) + ':' + jSuites.two(o.date[4])+ ':' + jSuites.two(o.date[5]);
	}

	return '';
}

var excelInitialTime = Date.UTC(1900, 0, 0);
var excelLeapYearBug = Date.UTC(1900, 1, 29);
var millisecondsPerDay = 86400000;

/**
 * Date to number
 */
jSuites.calendar.dateToNum = function(jsDate) {
	if (typeof(jsDate) === 'string') {
		jsDate = new Date(jsDate + '  GMT+0');
	}
	var jsDateInMilliseconds = jsDate.getTime();

	if (jsDateInMilliseconds >= excelLeapYearBug) {
		jsDateInMilliseconds += millisecondsPerDay;
	}

	jsDateInMilliseconds -= excelInitialTime;

	return jsDateInMilliseconds / millisecondsPerDay;
}

/**
 * Number to date
 */
// !IMPORTANT!
// Excel incorrectly considers 1900 to be a leap year
jSuites.calendar.numToDate = function(excelSerialNumber) {
	var jsDateInMilliseconds = excelInitialTime + excelSerialNumber * millisecondsPerDay;

	if (jsDateInMilliseconds >= excelLeapYearBug) {
		jsDateInMilliseconds -= millisecondsPerDay;
	}

	const d = new Date(jsDateInMilliseconds);

	var date = [
		d.getUTCFullYear(),
		d.getUTCMonth()+1,
		d.getUTCDate(),
		d.getUTCHours(),
		d.getUTCMinutes(),
		d.getUTCSeconds(),
	];

	return jSuites.calendar.now(date);
}

// Helper to convert date into string
jSuites.calendar.getDateString = function(value, options) {
	if (! options) {
		var options = {};
	}

	// Labels
	if (options && typeof(options) == 'object') {
		var format = options.format;
	} else {
		var format = options;
	}

	if (! format) {
		format = 'YYYY-MM-DD';
	}

	// Convert to number of hours
	if (format.indexOf('[h]') >= 0) {
		var result = 0;
		if (value && jSuites.isNumeric(value)) {
			result = parseFloat(24 * Number(value));
			if (format.indexOf('mm') >= 0) {
				var h = (''+result).split('.');
				if (h[1]) {
					var d = 60 * parseFloat('0.' + h[1])
					d = parseFloat(d.toFixed(2));
				} else {
					var d = 0;
				}
				result = parseInt(h[0]) + ':' + jSuites.two(d);
			}
		}
		return result;
	}

	// Date instance
	if (value instanceof Date) {
		value = jSuites.calendar.now(value);
	} else if (value && jSuites.isNumeric(value)) {
		value = jSuites.calendar.numToDate(value);
	}

	// Tokens
	var tokens = [ 'DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.' ];

	// Expression to extract all tokens from the string
	var e = new RegExp(tokens.join('|'), 'gi');
	// Extract
	var t = format.match(e);

	// Compatibility with excel
	for (var i = 0; i < t.length; i++) {
		if (t[i].toUpperCase() == 'MM') {
			// Not a month, correct to minutes
			if (t[i-1] && t[i-1].toUpperCase().indexOf('H') >= 0) {
				t[i] = 'mi';
			} else if (t[i-2] && t[i-2].toUpperCase().indexOf('H') >= 0) {
				t[i] = 'mi';
			} else if (t[i+1] && t[i+1].toUpperCase().indexOf('S') >= 0) {
				t[i] = 'mi';
			} else if (t[i+2] && t[i+2].toUpperCase().indexOf('S') >= 0) {
				t[i] = 'mi';
			}
		}
	}

	// Object
	var o = {
		tokens: t
	}

	// Value
	if (value) {
		var d = ''+value;
		var splitStr = (d.indexOf('T') !== -1) ? 'T' : ' ';
		d = d.split(splitStr);

		var h = 0;
		var m = 0;
		var s = 0;

		if (d[1]) {
			h = d[1].split(':');
			m = h[1] ? h[1] : 0;
			s = h[2] ? h[2] : 0;
			h = h[0] ? h[0] : 0;
		}

		d = d[0].split('-');

		if (d[0] && d[1] && d[2] && d[0] > 0 && d[1] > 0 && d[1] < 13 && d[2] > 0 && d[2] < 32) {

			// Data
			o.data = [ d[0], d[1], d[2], h, m, s ];

			// Value
			o.value = [];

			// Calendar instance
			var calendar = new Date(o.data[0], o.data[1]-1, o.data[2], o.data[3], o.data[4], o.data[5]);

			// Get method
			var get = function(i) {
				// Token
				var t = this.tokens[i];
				// Case token
				var s = t.toUpperCase();
				var v = null;

				if (s === 'YYYY') {
					v = this.data[0];
				} else if (s === 'YYY') {
					v = this.data[0].substring(1,4);
				} else if (s === 'YY') {
					v = this.data[0].substring(2,4);
				} else if (s === 'Y') {
					v = this.data[0].substring(3,4);
				} else if (t === 'MON') {
					v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toUpperCase();
				} else if (t === 'mon') {
					v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toLowerCase();
				} else if (t === 'MONTH') {
					v = jSuites.calendar.months[calendar.getMonth()].toUpperCase();
				} else if (t === 'month') {
					v = jSuites.calendar.months[calendar.getMonth()].toLowerCase();
				} else if (s === 'MMMMM') {
					v = jSuites.calendar.months[calendar.getMonth()].substr(0, 1);
				} else if (s === 'MMMM' || t === 'Month') {
					v = jSuites.calendar.months[calendar.getMonth()];
				} else if (s === 'MMM' || t == 'Mon') {
					v = jSuites.calendar.months[calendar.getMonth()].substr(0,3);
				} else if (s === 'MM') {
					v = jSuites.two(this.data[1]);
				} else if (s === 'M') {
					v = calendar.getMonth()+1;
				} else if (t === 'DAY') {
					v = jSuites.calendar.weekdays[calendar.getDay()].toUpperCase();
				} else if (t === 'day') {
					v = jSuites.calendar.weekdays[calendar.getDay()].toLowerCase();
				} else if (s === 'DDDD' || t == 'Day') {
					v = jSuites.calendar.weekdays[calendar.getDay()];
				} else if (s === 'DDD') {
					v = jSuites.calendar.weekdays[calendar.getDay()].substr(0,3);
				} else if (s === 'DD') {
					v = jSuites.two(this.data[2]);
				} else if (s === 'D') {
					v = this.data[2];
				} else if (s === 'Q') {
					v = Math.floor((calendar.getMonth() + 3) / 3);
				} else if (s === 'HH24' || s === 'HH') {
					v = jSuites.two(this.data[3]);
				} else if (s === 'HH12') {
					if (this.data[3] > 12) {
						v = jSuites.two(this.data[3] - 12);
					} else {
						v = jSuites.two(this.data[3]);
					}
				} else if (s === 'H') {
					v = this.data[3];
				} else if (s === 'MI') {
					v = jSuites.two(this.data[4]);
				} else if (s === 'SS') {
					v = jSuites.two(this.data[5]);
				} else if (s === 'MS') {
					v = calendar.getMilliseconds();
				} else if (s === 'AM/PM') {
					if (this.data[3] >= 12) {
						v = 'PM';
					} else {
						v = 'AM';
					}
				} else if (s === 'WD') {
					v = jSuites.calendar.weekdays[calendar.getDay()];
				}

				if (v === null) {
					this.value[i] = this.tokens[i];
				} else {
					this.value[i] = v;
				}
			}

			for (var i = 0; i < o.tokens.length; i++) {
				get.call(o, i);
			}
			// Put pieces together
			value = o.value.join('');
		} else {
			value = '';
		}
	}

	return value;
}

// Jsuites calendar labels
jSuites.calendar.weekdays = [ 'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday' ];
jSuites.calendar.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
jSuites.calendar.weekdaysShort = [ 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' ];
jSuites.calendar.monthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];


jSuites.color = (function(el, options) {
	// Already created, update options
	if (el.color) {
		return el.color.setOptions(options, true);
	}

	// New instance
	var obj = { type: 'color' };
	obj.options = {};

	var container = null;
	var backdrop = null;
	var content = null;
	var resetButton = null;
	var closeButton = null;
	var tabs = null;
	var jsuitesTabs = null;

	/**
	 * Update options
	 */
	obj.setOptions = function(options, reset) {
		/**
		 * @typedef {Object} defaults
		 * @property {(string|Array)} value - Initial value of the compontent
		 * @property {string} placeholder - The default instruction text on the element
		 * @property {requestCallback} onchange - Method to be execute after any changes on the element
		 * @property {requestCallback} onclose - Method to be execute when the element is closed
		 * @property {string} doneLabel - Label for button done
		 * @property {string} resetLabel - Label for button reset
		 * @property {string} resetValue - Value for button reset
		 * @property {Bool} showResetButton - Active or note for button reset - default false
		 */
		var defaults = {
			placeholder: '',
			value: null,
			onopen: null,
			onclose: null,
			onchange: null,
			closeOnChange: true,
			palette: null,
			position: null,
			doneLabel: 'Done',
			resetLabel: 'Reset',
			fullscreen: false,
			opened: false,
		}

		if (! options) {
			options = {};
		}

		if (options && ! options.palette) {
			// Default pallete
			options.palette = jSuites.palette();
		}

		// Loop through our object
		for (var property in defaults) {
			if (options && options.hasOwnProperty(property)) {
				obj.options[property] = options[property];
			} else {
				if (typeof(obj.options[property]) == 'undefined' || reset === true) {
					obj.options[property] = defaults[property];
				}
			}
		}

		// Update the text of the controls, if they have already been created
		if (resetButton) {
			resetButton.innerHTML = obj.options.resetLabel;
		}
		if (closeButton) {
			closeButton.innerHTML = obj.options.doneLabel;
		}

		// Update the pallete
		if (obj.options.palette && jsuitesTabs) {
			jsuitesTabs.updateContent(0, table());
		}

		// Value
		if (typeof obj.options.value === 'string') {
			el.value = obj.options.value;
			if (el.tagName === 'INPUT') {
				el.style.color = el.value;
				el.style.backgroundColor = el.value;
			}
		}

		// Placeholder
		if (obj.options.placeholder) {
			el.setAttribute('placeholder', obj.options.placeholder);
		} else {
			if (el.getAttribute('placeholder')) {
				el.removeAttribute('placeholder');
			}
		}

		return obj;
	}

	obj.select = function(color) {
		// Remove current selected mark
		var selected = container.querySelector('.jcolor-selected');
		if (selected) {
			selected.classList.remove('jcolor-selected');
		}

		// Mark cell as selected
		if (obj.values[color]) {
			obj.values[color].classList.add('jcolor-selected');
		}

		obj.options.value = color;
	}

	/**
	 * Open color pallete
	 */
	obj.open = function() {
		if (! container.classList.contains('jcolor-focus')) {
			// Start tracking
			jSuites.tracking(obj, true);

			// Show color picker
			container.classList.add('jcolor-focus');

			// Select current color
			if (obj.options.value) {
				obj.select(obj.options.value);
			}

			// Reset margin
			content.style.marginTop = '';
			content.style.marginLeft = '';

			var rectContent = content.getBoundingClientRect();
			var availableWidth = jSuites.getWindowWidth();
			var availableHeight = jSuites.getWindowHeight();

			if (availableWidth < 800 || obj.options.fullscreen == true) {
				content.classList.add('jcolor-fullscreen');
				jSuites.animation.slideBottom(content, 1);
				backdrop.style.display = 'block';
			} else {
				if (content.classList.contains('jcolor-fullscreen')) {
					content.classList.remove('jcolor-fullscreen');
					backdrop.style.display = '';
				}

				if (obj.options.position) {
					content.style.position = 'fixed';
				} else {
					content.style.position = '';
				}

				if (rectContent.left + rectContent.width > availableWidth) {
					content.style.marginLeft = -1 * (rectContent.left + rectContent.width - (availableWidth - 20)) + 'px';
				}
				if (rectContent.top + rectContent.height > availableHeight) {
					content.style.marginTop = -1 * (rectContent.top + rectContent.height - (availableHeight - 20)) + 'px';
				}
			}


			if (typeof(obj.options.onopen) == 'function') {
				obj.options.onopen(el);
			}

			jsuitesTabs.setBorder(jsuitesTabs.getActive());

			// Update sliders
			if (obj.options.value) {
				var rgb = HexToRgb(obj.options.value);

				rgbInputs.forEach(function(rgbInput, index) {
					rgbInput.value = rgb[index];
					rgbInput.dispatchEvent(new Event('input'));
				});
			}
		}
	}

	/**
	 * Close color pallete
	 */
	obj.close = function(ignoreEvents) {
		if (container.classList.contains('jcolor-focus')) {
			// Remove focus
			container.classList.remove('jcolor-focus');
			// Make sure backdrop is hidden
			backdrop.style.display = '';
			// Call related events
			if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
				obj.options.onclose(el);
			}
			// Stop  the object
			jSuites.tracking(obj, false);
		}

		return obj.options.value;
	}

	/**
	 * Set value
	 */
	obj.setValue = function(color) {
		if (! color) {
			color = '';
		}

		if (color != obj.options.value) {
			obj.options.value = color;
			slidersResult = color;

			// Remove current selecded mark
			obj.select(color);

			// Onchange
			if (typeof(obj.options.onchange) == 'function') {
				obj.options.onchange(el, color);
			}

			// Changes
			if (el.value != obj.options.value) {
				// Set input value
				el.value = obj.options.value;
				if (el.tagName === 'INPUT') {
					el.style.color = el.value;
					el.style.backgroundColor = el.value;
				}

				// Element onchange native
				if (typeof(el.oninput) == 'function') {
					el.oninput({
						type: 'input',
						target: el,
						value: el.value
					});
				}
			}

			if (obj.options.closeOnChange == true) {
				obj.close();
			}
		}
	}

	/**
	 * Get value
	 */
	obj.getValue = function() {
		return obj.options.value;
	}

	var backdropClickControl = false;

	// Converts a number in decimal to hexadecimal
	var decToHex = function(num) {
		var hex = num.toString(16);
		return hex.length === 1 ? "0" + hex : hex;
	}

	// Converts a color in rgb to hexadecimal
	var rgbToHex = function(r, g, b) {
		return "#" + decToHex(r) + decToHex(g) + decToHex(b);
	}

	// Converts a number in hexadecimal to decimal
	var hexToDec = function(hex) {
		return parseInt('0x' + hex);
	}

	// Converts a color in hexadecimal to rgb
	var HexToRgb = function(hex) {
		return [hexToDec(hex.substr(1, 2)), hexToDec(hex.substr(3, 2)), hexToDec(hex.substr(5, 2))]
	}

	var table = function() {
		// Content of the first tab
		var tableContainer = document.createElement('div');
		tableContainer.className = 'jcolor-grid';

		// Cells
		obj.values = [];

		// Table pallete
		var t = document.createElement('table');
		t.setAttribute('cellpadding', '7');
		t.setAttribute('cellspacing', '0');

		for (var j = 0; j < obj.options.palette.length; j++) {
			var tr = document.createElement('tr');
			for (var i = 0; i < obj.options.palette[j].length; i++) {
				var td = document.createElement('td');
				var color = obj.options.palette[j][i];
				if (color.length < 7 && color.substr(0,1) !== '#') {
					color = '#' + color;
				}
				td.style.backgroundColor = color;
				td.setAttribute('data-value', color);
				td.innerHTML = '';
				tr.appendChild(td);

				// Selected color
				if (obj.options.value == color) {
					td.classList.add('jcolor-selected');
				}

				// Possible values
				obj.values[color] = td;
			}
			t.appendChild(tr);
		}

		// Append to the table
		tableContainer.appendChild(t);

		return tableContainer;
	}

	// Canvas where the image will be rendered
	var canvas = document.createElement('canvas');
	canvas.width = 200;
	canvas.height = 160;
	var context = canvas.getContext("2d");

	var resizeCanvas = function() {
		// Specifications necessary to correctly obtain colors later in certain positions
		var m = tabs.firstChild.getBoundingClientRect();
		canvas.width = m.width - 14;
		gradient()
	}

	var gradient = function() {
		var g = context.createLinearGradient(0, 0, canvas.width, 0);
		// Create color gradient
		g.addColorStop(0,    "rgb(255,0,0)");
		g.addColorStop(0.15, "rgb(255,0,255)");
		g.addColorStop(0.33, "rgb(0,0,255)");
		g.addColorStop(0.49, "rgb(0,255,255)");
		g.addColorStop(0.67, "rgb(0,255,0)");
		g.addColorStop(0.84, "rgb(255,255,0)");
		g.addColorStop(1,    "rgb(255,0,0)");
		context.fillStyle = g;
		context.fillRect(0, 0, canvas.width, canvas.height);
		g = context.createLinearGradient(0, 0, 0, canvas.height);
		g.addColorStop(0,   "rgba(255,255,255,1)");
		g.addColorStop(0.5, "rgba(255,255,255,0)");
		g.addColorStop(0.5, "rgba(0,0,0,0)");
		g.addColorStop(1,   "rgba(0,0,0,1)");
		context.fillStyle = g;
		context.fillRect(0, 0, canvas.width, canvas.height);
	}

	var hsl = function() {
		var element = document.createElement('div');
		element.className = "jcolor-hsl";

		var point = document.createElement('div');
		point.className = 'jcolor-point';

		var div = document.createElement('div');
		div.appendChild(canvas);
		div.appendChild(point);
		element.appendChild(div);

		// Moves the marquee point to the specified position
		var update = function(buttons, x, y) {
			if (buttons === 1) {
				var rect = element.getBoundingClientRect();
				var left = x - rect.left;
				var top = y - rect.top;
				if (left < 0) {
					left = 0;
				}
				if (top < 0) {
					top = 0;
				}
				if (left > rect.width) {
					left = rect.width;
				}
				if (top > rect.height) {
					top = rect.height;
				}
				point.style.left = left + 'px';
				point.style.top = top + 'px';
				var pixel = context.getImageData(left, top, 1, 1).data;
				slidersResult = rgbToHex(pixel[0], pixel[1], pixel[2]);
			}
		}

		// Applies the point's motion function to the div that contains it
		element.addEventListener('mousedown', function(e) {
			update(e.buttons, e.clientX, e.clientY);
		});

		element.addEventListener('mousemove', function(e) {
			update(e.buttons, e.clientX, e.clientY);
		});

		element.addEventListener('touchmove', function(e) {
			update(1, e.changedTouches[0].clientX, e.changedTouches[0].clientY);
		});

		return element;
	}

	var slidersResult = '';

	var rgbInputs = [];

	var changeInputColors = function() {
		if (slidersResult !== '') {
			for (var j = 0; j < rgbInputs.length; j++) {
				var currentColor = HexToRgb(slidersResult);

				currentColor[j] = 0;

				var newGradient = 'linear-gradient(90deg, rgb(';
				newGradient += currentColor.join(', ');
				newGradient += '), rgb(';

				currentColor[j] = 255;

				newGradient += currentColor.join(', ');
				newGradient += '))';

				rgbInputs[j].style.backgroundImage = newGradient;
			}
		}
	}

	var sliders = function() {
		// Content of the third tab
		var slidersElement = document.createElement('div');
		slidersElement.className = 'jcolor-sliders';

		var slidersBody = document.createElement('div');

		// Creates a range-type input with the specified name
		var createSliderInput = function(name) {
			var inputContainer = document.createElement('div');
			inputContainer.className = 'jcolor-sliders-input-container';

			var label = document.createElement('label');
			label.innerText = name;

			var subContainer = document.createElement('div');
			subContainer.className = 'jcolor-sliders-input-subcontainer';

			var input = document.createElement('input');
			input.type = 'range';
			input.min = 0;
			input.max = 255;
			input.value = 0;

			inputContainer.appendChild(label);
			subContainer.appendChild(input);

			var value = document.createElement('div');
			value.innerText = input.value;

			input.addEventListener('input', function() {
				value.innerText = input.value;
			});

			subContainer.appendChild(value);
			inputContainer.appendChild(subContainer);

			slidersBody.appendChild(inputContainer);

			return input;
		}

		// Creates red, green and blue inputs
		rgbInputs = [
			createSliderInput('Red'),
			createSliderInput('Green'),
			createSliderInput('Blue'),
		];

		slidersElement.appendChild(slidersBody);

		// Element that prints the current color
		var slidersResultColor = document.createElement('div');
		slidersResultColor.className = 'jcolor-sliders-final-color';

		var resultElement = document.createElement('div');
		resultElement.style.visibility = 'hidden';
		resultElement.innerText = 'a';
		slidersResultColor.appendChild(resultElement)

		// Update the element that prints the current color
		var updateResult = function() {
			var resultColor = rgbToHex(parseInt(rgbInputs[0].value), parseInt(rgbInputs[1].value), parseInt(rgbInputs[2].value));

			resultElement.innerText = resultColor;
			resultElement.style.color = resultColor;
			resultElement.style.removeProperty('visibility');

			slidersResult = resultColor;
		}

		// Apply the update function to color inputs
		rgbInputs.forEach(function(rgbInput) {
			rgbInput.addEventListener('input', function() {
				updateResult();
				changeInputColors();
			});
		});

		slidersElement.appendChild(slidersResultColor);

		return slidersElement;
	}

	var init = function() {
		// Initial options
		obj.setOptions(options);

		// Add a proper input tag when the element is an input
		if (el.tagName == 'INPUT') {
			el.classList.add('jcolor-input');
			el.readOnly = true;
		}

		// Table container
		container = document.createElement('div');
		container.className = 'jcolor';

		// Table container
		backdrop = document.createElement('div');
		backdrop.className = 'jcolor-backdrop';
		container.appendChild(backdrop);

		// Content
		content = document.createElement('div');
		content.className = 'jcolor-content';

		// Controls
		var controls = document.createElement('div');
		controls.className = 'jcolor-controls';
		content.appendChild(controls);

		// Reset button
		resetButton  = document.createElement('div');
		resetButton.className = 'jcolor-reset';
		resetButton.innerHTML = obj.options.resetLabel;
		controls.appendChild(resetButton);

		// Close button
		closeButton  = document.createElement('div');
		closeButton.className = 'jcolor-close';
		closeButton.innerHTML = obj.options.doneLabel;
		controls.appendChild(closeButton);

		// Element that will be used to create the tabs
		tabs = document.createElement('div');
		content.appendChild(tabs);

		// Starts the jSuites tabs component
		jsuitesTabs = jSuites.tabs(tabs, {
			animation: true,
			data: [
				{
					title: 'Grid',
					contentElement: table(),
				},
				{
					title: 'Spectrum',
					contentElement: hsl(),
				},
				{
					title: 'Sliders',
					contentElement: sliders(),
				}
			],
			onchange: function(element, instance, index) {
				if (index === 1) {
					resizeCanvas();
				} else {
					var color = slidersResult !== '' ? slidersResult : obj.getValue();

					if (index === 2 && color) {
						var rgb = HexToRgb(color);

						rgbInputs.forEach(function(rgbInput, index) {
							rgbInput.value = rgb[index];
							rgbInput.dispatchEvent(new Event('input'));
						});
					}
				}
			},
			palette: 'modern',
		});

		container.appendChild(content);

		// Insert picker after the element
		if (el.tagName == 'INPUT') {
			el.parentNode.insertBefore(container, el.nextSibling);
		} else {
			el.appendChild(container);
		}

		container.addEventListener("click", function(e) {
			if (e.target.tagName == 'TD') {
				var value = e.target.getAttribute('data-value');
				if (value) {
					obj.setValue(value);
				}
			} else if (e.target.classList.contains('jcolor-reset')) {
				obj.setValue('');
				obj.close();
			} else if (e.target.classList.contains('jcolor-close')) {
				if (jsuitesTabs.getActive() > 0) {
					obj.setValue(slidersResult);
				}
				obj.close();
			} else if (e.target.classList.contains('jcolor-backdrop')) {
				obj.close();
			} else {
				obj.open();
			}
		});

		/**
		 * If element is focus open the picker
		 */
		el.addEventListener("mouseup", function(e) {
			obj.open();
		});

		// If the picker is open on the spectrum tab, it changes the canvas size when the window size is changed
		window.addEventListener('resize', function() {
			if (container.classList.contains('jcolor-focus') && jsuitesTabs.getActive() == 1) {
				resizeCanvas();
			}
		});

		// Default opened
		if (obj.options.opened == true) {
			obj.open();
		}

		// Change
		el.change = obj.setValue;

		// Global generic value handler
		el.val = function(val) {
			if (val === undefined) {
				return obj.getValue();
			} else {
				obj.setValue(val);
			}
		}

		// Keep object available from the node
		el.color = obj;

		// Container shortcut
		container.color = obj;
	}

	obj.toHex = function(rgb) {
		var hex = function(x) {
			return ("0" + parseInt(x).toString(16)).slice(-2);
		}
		if (rgb) {
			if (/^#[0-9A-F]{6}$/i.test(rgb)) {
				return rgb;
			} else {
				rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
				if (rgb && rgb.length) {
					return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
				} else {
					return "";
				}
			}
		}
	}

	init();

	return obj;
});



jSuites.contextmenu = (function(el, options) {
	// New instance
	var obj = { type:'contextmenu'};
	obj.options = {};

	// Default configuration
	var defaults = {
		items: null,
		onclick: null,
	};

	// Loop through our object
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	// Class definition
	el.classList.add('jcontextmenu');

	/**
	 * Open contextmenu
	 */
	obj.open = function(e, items) {
		if (items) {
			// Update content
			obj.options.items = items;
			// Create items
			obj.create(items);
		}

		// Close current contextmenu
		if (jSuites.contextmenu.current) {
			jSuites.contextmenu.current.close();
		}

		// Add to the opened components monitor
		jSuites.tracking(obj, true);

		// Show context menu
		el.classList.add('jcontextmenu-focus');

		// Current
		jSuites.contextmenu.current = obj;

		// Coordinates
		if ((obj.options.items && obj.options.items.length > 0) || el.children.length) {
			if (e.target) {
				if (e.changedTouches && e.changedTouches[0]) {
					x = e.changedTouches[0].clientX;
					y = e.changedTouches[0].clientY;
				} else {
					var x = e.clientX;
					var y = e.clientY;
				}
			} else {
				var x = e.x;
				var y = e.y;
			}

			var rect = el.getBoundingClientRect();

			if (window.innerHeight < y + rect.height) {
				var h = y - rect.height;
				if (h < 0) {
					h = 0;
				}
				el.style.top = h + 'px';
			} else {
				el.style.top = y + 'px';
			}

			if (window.innerWidth < x + rect.width) {
				if (x - rect.width > 0) {
					el.style.left = (x - rect.width) + 'px';
				} else {
					el.style.left = '10px';
				}
			} else {
				el.style.left = x + 'px';
			}
		}
	}

	obj.isOpened = function() {
		return el.classList.contains('jcontextmenu-focus') ? true : false;
	}

	/**
	 * Close menu
	 */
	obj.close = function() {
		if (el.classList.contains('jcontextmenu-focus')) {
			el.classList.remove('jcontextmenu-focus');
		}
		jSuites.tracking(obj, false);
	}

	/**
	 * Create items based on the declared objectd
	 * @param {object} items - List of object
	 */
	obj.create = function(items) {
		// Update content
		el.innerHTML = '';

		// Add header contextmenu
		var itemHeader = createHeader();
		el.appendChild(itemHeader);

		// Append items
		for (var i = 0; i < items.length; i++) {
			var itemContainer = createItemElement(items[i]);
			el.appendChild(itemContainer);
		}
	}

	/**
	 * createHeader for context menu
	 * @private
	 * @returns {HTMLElement}
	 */
	function createHeader() {
		var header = document.createElement('div');
		header.classList.add("header");
		header.addEventListener("click", function(e) {
			e.preventDefault();
			e.stopPropagation();
		});
		var title = document.createElement('a');
		title.classList.add("title");
		title.innerHTML = jSuites.translate("Menu");

		header.appendChild(title);

		var closeButton = document.createElement('a');
		closeButton.classList.add("close");
		closeButton.innerHTML = jSuites.translate("close");
		closeButton.addEventListener("click", function(e) {
			obj.close();
		});

		header.appendChild(closeButton);

		return header;
	}

	/**
	 * Private function for create a new Item element
	 * @param {type} item
	 * @returns {jsuitesL#15.jSuites.contextmenu.createItemElement.itemContainer}
	 */
	function createItemElement(item) {
		if (item.type && (item.type == 'line' || item.type == 'divisor')) {
			var itemContainer = document.createElement('hr');
		} else {
			var itemContainer = document.createElement('div');
			var itemText = document.createElement('a');
			itemText.innerHTML = item.title;

			if (item.tooltip) {
				itemContainer.setAttribute('title', item.tooltip);
			}

			if (item.icon) {
				itemContainer.setAttribute('data-icon', item.icon);
			}

			if (item.id) {
				itemContainer.id = item.id;
			}

			if (item.disabled) {
				itemContainer.className = 'jcontextmenu-disabled';
			} else if (item.onclick) {
				itemContainer.method = item.onclick;
				itemContainer.addEventListener("mousedown", function(e) {
					e.preventDefault();
				});
				itemContainer.addEventListener("mouseup", function(e) {
					// Execute method
					this.method(this, e);
				});
			}
			itemContainer.appendChild(itemText);

			if (item.submenu) {
				var itemIconSubmenu = document.createElement('span');
				itemIconSubmenu.innerHTML = "&#9658;";
				itemContainer.appendChild(itemIconSubmenu);
				itemContainer.classList.add('jcontexthassubmenu');
				var el_submenu = document.createElement('div');
				// Class definition
				el_submenu.classList.add('jcontextmenu');
				// Focusable
				el_submenu.setAttribute('tabindex', '900');

				// Append items
				var submenu = item.submenu;
				for (var i = 0; i < submenu.length; i++) {
					var itemContainerSubMenu = createItemElement(submenu[i]);
					el_submenu.appendChild(itemContainerSubMenu);
				}

				itemContainer.appendChild(el_submenu);
			} else if (item.shortcut) {
				var itemShortCut = document.createElement('span');
				itemShortCut.innerHTML = item.shortcut;
				itemContainer.appendChild(itemShortCut);
			}
		}
		return itemContainer;
	}

	if (typeof(obj.options.onclick) == 'function') {
		el.addEventListener('click', function(e) {
			obj.options.onclick(obj, e);
		});
	}

	// Create items
	if (obj.options.items) {
		obj.create(obj.options.items);
	}

	window.addEventListener("mousewheel", function() {
		obj.close();
	});

	el.contextmenu = obj;

	return obj;
});


jSuites.dropdown = (function(el, options) {
	// Already created, update options
	if (el.dropdown) {
		return el.dropdown.setOptions(options, true);
	}

	// New instance
	var obj = { type: 'dropdown' };
	obj.options = {};

	// Success
	var success = function(data, val) {
		// Set data
		if (data && data.length) {
			// Sort
			if (obj.options.sortResults !== false) {
				if(typeof obj.options.sortResults == "function") {
					data.sort(obj.options.sortResults);
				} else {
					data.sort(sortData);
				}
			}

			obj.setData(data);
		}

		// Onload method
		if (typeof(obj.options.onload) == 'function') {
			obj.options.onload(el, obj, data, val);
		}

		// Set value
		if (val) {
			applyValue(val);
		}

		// Component value
		if (val === undefined || val === null) {
			obj.options.value = '';
		}
		el.value = obj.options.value;

		// Open dropdown
		if (obj.options.opened == true) {
			obj.open();
		}
	}


	// Default sort
	var sortData = function(itemA, itemB) {
		var testA, testB;
		if(typeof itemA == "string") {
			testA = itemA;
		} else {
			if(itemA.text) {
				testA = itemA.text;
			} else if(itemA.name) {
				testA = itemA.name;
			}
		}

		if(typeof itemB == "string") {
			testB = itemB;
		} else {
			if(itemB.text) {
				testB = itemB.text;
			} else if(itemB.name) {
				testB = itemB.name;
			}
		}

		if (typeof testA == "string" || typeof testB == "string") {
			if (typeof testA != "string") { testA = ""+testA; }
			if (typeof testB != "string") { testB = ""+testB; }
			return testA.localeCompare(testB);
		} else {
			return testA - testB;
		}
	}

	/**
	 * Reset the options for the dropdown
	 */
	var resetValue = function() {
		// Reset value container
		obj.value = {};
		// Remove selected
		for (var i = 0; i < obj.items.length; i++) {
			if (obj.items[i].selected == true) {
				if (obj.items[i].element) {
					obj.items[i].element.classList.remove('jdropdown-selected')
				}
				obj.items[i].selected = null;
			}
		}
		// Reset options
		obj.options.value = '';
		// Reset value
		el.value = '';
	}

	/**
	 * Apply values to the dropdown
	 */
	var applyValue = function(values) {
		// Reset the current values
		resetValue();

		// Read values
		if (values !== null) {
			if (! values) {
				if (typeof(obj.value['']) !== 'undefined') {
					obj.value[''] = '';
				}
			} else {
				if (! Array.isArray(values)) {
					values = ('' + values).split(';');
				}
				for (var i = 0; i < values.length; i++) {
					obj.value[values[i]] = '';
				}
			}
		}

		// Update the DOM
		for (var i = 0; i < obj.items.length; i++) {
			if (typeof(obj.value[Value(i)]) !== 'undefined') {
				if (obj.items[i].element) {
					obj.items[i].element.classList.add('jdropdown-selected')
				}
				obj.items[i].selected = true;

				// Keep label
				obj.value[Value(i)] = Text(i);
			}
		}

		// Global value
		obj.options.value = Object.keys(obj.value).join(';');

		// Update labels
		obj.header.value = obj.getText();
	}

	// Get the value of one item
	var Value = function(k, v) {
		// Legacy purposes
		if (! obj.options.format) {
			var property = 'value';
		} else {
			var property = 'id';
		}

		if (obj.items[k]) {
			if (v !== undefined) {
				return obj.items[k].data[property] = v;
			} else {
				return obj.items[k].data[property];
			}
		}

		return '';
	}

	// Get the label of one item
	var Text = function(k, v) {
		// Legacy purposes
		if (! obj.options.format) {
			var property = 'text';
		} else {
			var property = 'name';
		}

		if (obj.items[k]) {
			if (v !== undefined) {
				return obj.items[k].data[property] = v;
			} else {
				return obj.items[k].data[property];
			}
		}

		return '';
	}

	var getValue = function() {
		return Object.keys(obj.value);
	}

	var getText = function() {
		var data = [];
		var k = Object.keys(obj.value);
		for (var i = 0; i < k.length; i++) {
			data.push(obj.value[k[i]]);
		}
		return data;
	}

	obj.setOptions = function(options, reset) {
		if (! options) {
			options = {};
		}

		// Default configuration
		var defaults = {
			url: null,
			data: [],
			format: 0,
			multiple: false,
			autocomplete: false,
			remoteSearch: false,
			lazyLoading: false,
			type: null,
			width: null,
			maxWidth: null,
			opened: false,
			value: null,
			placeholder: '',
			newOptions: false,
			position: false,
			onchange: null,
			onload: null,
			onopen: null,
			onclose: null,
			onfocus: null,
			onblur: null,
			oninsert: null,
			onbeforeinsert: null,
			sortResults: false,
			autofocus: false,
		}

		// Loop through our object
		for (var property in defaults) {
			if (options && options.hasOwnProperty(property)) {
				obj.options[property] = options[property];
			} else {
				if (typeof(obj.options[property]) == 'undefined' || reset === true) {
					obj.options[property] = defaults[property];
				}
			}
		}

		// Force autocomplete search
		if (obj.options.remoteSearch == true || obj.options.type === 'searchbar') {
			obj.options.autocomplete = true;
		}

		// New options
		if (obj.options.newOptions == true) {
			obj.header.classList.add('jdropdown-add');
		} else {
			obj.header.classList.remove('jdropdown-add');
		}

		// Autocomplete
		if (obj.options.autocomplete == true) {
			obj.header.removeAttribute('readonly');
		} else {
			obj.header.setAttribute('readonly', 'readonly');
		}

		// Place holder
		if (obj.options.placeholder) {
			obj.header.setAttribute('placeholder', obj.options.placeholder);
		} else {
			obj.header.removeAttribute('placeholder');
		}

		// Remove specific dropdown typing to add again
		el.classList.remove('jdropdown-searchbar');
		el.classList.remove('jdropdown-picker');
		el.classList.remove('jdropdown-list');

		if (obj.options.type == 'searchbar') {
			el.classList.add('jdropdown-searchbar');
		} else if (obj.options.type == 'list') {
			el.classList.add('jdropdown-list');
		} else if (obj.options.type == 'picker') {
			el.classList.add('jdropdown-picker');
		} else {
			if (jSuites.getWindowWidth() < 800) {
				if (obj.options.autocomplete) {
					el.classList.add('jdropdown-searchbar');
					obj.options.type = 'searchbar';
				} else {
					el.classList.add('jdropdown-picker');
					obj.options.type = 'picker';
				}
			} else {
				if (obj.options.width) {
					el.style.width = obj.options.width;
					el.style.minWidth = obj.options.width;
				} else {
					el.style.removeProperty('width');
					el.style.removeProperty('min-width');
				}

				el.classList.add('jdropdown-default');
				obj.options.type = 'default';
			}
		}

		// Close button
		if (obj.options.type == 'searchbar') {
			containerHeader.appendChild(closeButton);
		} else {
			container.insertBefore(closeButton, container.firstChild);
		}

		// Load the content
		if (obj.options.url && ! options.data) {
			jSuites.ajax({
				url: obj.options.url,
				method: 'GET',
				dataType: 'json',
				success: function(data) {
					if (data) {
						success(data, obj.options.value);
					}
				}
			});
		} else {
			success(obj.options.data, obj.options.value);
		}

		// Return the instance
		return obj;
	}

	// Helpers
	var containerHeader = null;
	var container = null;
	var content = null;
	var closeButton = null;
	var resetButton = null;
	var backdrop = null;

	var keyTimer = null;

	/**
	 * Init dropdown
	 */
	var init = function() {
		// Do not accept null
		if (! options) {
			options = {};
		}

		// If the element is a SELECT tag, create a configuration object
		if (el.tagName == 'SELECT') {
			var ret = jSuites.dropdown.extractFromDom(el, options);
			el = ret.el;
			options = ret.options;
		}

		// Place holder
		if (! options.placeholder && el.getAttribute('placeholder')) {
			options.placeholder = el.getAttribute('placeholder');
		}

		// Value container
		obj.value = {};
		// Containers
		obj.items = [];
		obj.groups = [];
		// Search options
		obj.search = '';
		obj.results = null;

		// Create dropdown
		el.classList.add('jdropdown');

		// Header container
		containerHeader = document.createElement('div');
		containerHeader.className = 'jdropdown-container-header';

		// Header
		obj.header = document.createElement('input');
		obj.header.className = 'jdropdown-header jss_object';
		obj.header.type = 'text';
		obj.header.setAttribute('autocomplete', 'off');
		obj.header.onfocus = function() {
			if (typeof(obj.options.onfocus) == 'function') {
				obj.options.onfocus(el);
			}
		}

		obj.header.onblur = function() {
			if (typeof(obj.options.onblur) == 'function') {
				obj.options.onblur(el);
			}
		}

		obj.header.onkeyup = function(e) {
			if (obj.options.autocomplete == true && ! keyTimer) {
				if (obj.search != obj.header.value.trim()) {
					keyTimer = setTimeout(function() {
						obj.find(obj.header.value.trim());
						keyTimer = null;
					}, 400);
				}

				if (! el.classList.contains('jdropdown-focus')) {
					obj.open();
				}
			} else {
				if (! obj.options.autocomplete) {
					obj.next(e.key);
				}
			}
		}

		// Global controls
		if (! jSuites.dropdown.hasEvents) {
			// Execute only one time
			jSuites.dropdown.hasEvents = true;
			// Enter and Esc
			document.addEventListener("keydown", jSuites.dropdown.keydown);
		}

		// Container
		container = document.createElement('div');
		container.className = 'jdropdown-container';

		// Dropdown content
		content = document.createElement('div');
		content.className = 'jdropdown-content';

		// Close button
		closeButton = document.createElement('div');
		closeButton.className = 'jdropdown-close';
		closeButton.innerHTML = 'Done';

		// Reset button
		resetButton = document.createElement('div');
		resetButton.className = 'jdropdown-reset';
		resetButton.innerHTML = 'x';
		resetButton.onclick = function() {
			obj.reset();
			obj.close();
		}

		// Create backdrop
		backdrop = document.createElement('div');
		backdrop.className = 'jdropdown-backdrop';

		// Append elements
		containerHeader.appendChild(obj.header);

		container.appendChild(content);
		el.appendChild(containerHeader);
		el.appendChild(container);
		el.appendChild(backdrop);

		// Set the otiptions
		obj.setOptions(options);

		if ('ontouchsend' in document.documentElement === true) {
			el.addEventListener('touchsend', jSuites.dropdown.mouseup);
		} else {
			el.addEventListener('mouseup', jSuites.dropdown.mouseup);
		}

		// Lazyloading
		if (obj.options.lazyLoading == true) {
			jSuites.lazyLoading(content, {
				loadUp: obj.loadUp,
				loadDown: obj.loadDown,
			});
		}

		content.onwheel = function(e) {
			e.stopPropagation();
		}

		// Change method
		el.change = obj.setValue;

		// Global generic value handler
		el.val = function(val) {
			if (val === undefined) {
				return obj.getValue(obj.options.multiple ? true : false);
			} else {
				obj.setValue(val);
			}
		}

		// Keep object available from the node
		el.dropdown = obj;
	}

	/**
	 * Get the current remote source of data URL
	 */
	obj.getUrl = function() {
		return obj.options.url;
	}

	/**
	 * Set the new data from a remote source
	 * @param {string} url - url from the remote source
	 * @param {function} callback - callback when the data is loaded
	 */
	obj.setUrl = function(url, callback) {
		obj.options.url = url;

		jSuites.ajax({
			url: obj.options.url,
			method: 'GET',
			dataType: 'json',
			success: function(data) {
				obj.setData(data);
				// Callback
				if (typeof(callback) == 'function') {
					callback(obj);
				}
			}
		});
	}

	/**
	 * Set ID for one item
	 */
	obj.setId = function(item, v) {
		// Legacy purposes
		if (! obj.options.format) {
			var property = 'value';
		} else {
			var property = 'id';
		}

		if (typeof(item) == 'object') {
			item[property] = v;
		} else {
			obj.items[item].data[property] = v;
		}
	}

	/**
	 * Add a new item
	 * @param {string} title - title of the new item
	 * @param {string} id - value/id of the new item
	 */
	obj.add = function(title, id) {
		if (! title) {
			var current = obj.options.autocomplete == true ? obj.header.value : '';
			var title = prompt(jSuites.translate('Add A New Option'), current);
			if (! title) {
				return false;
			}
		}

		// Id
		if (! id) {
		   id = jSuites.guid();
		}

		// Create new item
		if (! obj.options.format) {
			var item = {
				value: id,
				text: title,
			}
		} else {
			var item = {
				id: id,
				name: title,
			}
		}

		// Callback
		if (typeof(obj.options.onbeforeinsert) == 'function') {
			var ret = obj.options.onbeforeinsert(obj, item);
			if (ret === false) {
				return false;
			} else if (ret) {
				item = ret;
			}
		}

		// Add item to the main list
		obj.options.data.push(item);

		// Create DOM
		var newItem = obj.createItem(item);

		// Append DOM to the list
		content.appendChild(newItem.element);

		// Callback
		if (typeof(obj.options.oninsert) == 'function') {
			obj.options.oninsert(obj, item, newItem);
		}

		// Show content
		if (content.style.display == 'none') {
			content.style.display = '';
		}

		// Search?
		if (obj.results) {
			obj.results.push(newItem);
		}

		return item;
	}

	/**
	 * Create a new item
	 */
	obj.createItem = function(data, group, groupName) {
		// Keep the correct source of data
		if (! obj.options.format) {
			if (! data.value && data.id !== undefined) {
				data.value = data.id;
				//delete data.id;
			}
			if (! data.text && data.name !== undefined) {
				data.text = data.name;
				//delete data.name;
			}
		} else {
			if (! data.id && data.value !== undefined) {
				data.id = data.value;
				//delete data.value;
			}
			if (! data.name && data.text !== undefined) {
				data.name = data.text
				//delete data.text;
			}
		}

		// Create item
		var item = {};
		item.element = document.createElement('div');
		item.element.className = 'jdropdown-item';
		item.element.indexValue = obj.items.length;
		item.data = data;

		// Groupd DOM
		if (group) {
			item.group = group;
		}

		// Id
		if (data.id) {
			item.element.setAttribute('id', data.id);
		}

		// Disabled
		if (data.disabled == true) {
			item.element.setAttribute('data-disabled', true);
		}

		// Tooltip
		if (data.tooltip) {
			item.element.setAttribute('title', data.tooltip);
		}

		// Image
		if (data.image) {
			var image = document.createElement('img');
			image.className = 'jdropdown-image';
			image.src = data.image;
			if (! data.title) {
			   image.classList.add('jdropdown-image-small');
			}
			item.element.appendChild(image);
		} else if (data.icon) {
			var icon = document.createElement('span');
			icon.className = "jdropdown-icon material-icons";
			icon.innerText = data.icon;
			if (! data.title) {
			   icon.classList.add('jdropdown-icon-small');
			}
			if (data.color) {
				icon.style.color = data.color;
			}
			item.element.appendChild(icon);
		} else if (data.color) {
			var color = document.createElement('div');
			color.className = 'jdropdown-color';
			color.style.backgroundColor = data.color;
			item.element.appendChild(color);
		}

		// Set content
		if (! obj.options.format) {
			var text = data.text;
		} else {
			var text = data.name;
		}

		var node = document.createElement('div');
		node.className = 'jdropdown-description';
		node.innerHTML = text || '&nbsp;';

		// Title
		if (data.title) {
			var title = document.createElement('div');
			title.className = 'jdropdown-title';
			title.innerText = data.title;
			node.appendChild(title);
		}

		// Set content
		if (! obj.options.format) {
			var val = data.value;
		} else {
			var val = data.id;
		}

		// Value
		if (obj.value[val]) {
			item.element.classList.add('jdropdown-selected');
			item.selected = true;
		}

		// Keep DOM accessible
		obj.items.push(item);

		// Add node to item
		item.element.appendChild(node);

		return item;
	}

	obj.appendData = function(data) {
		// Create elements
		if (data.length) {
			// Helpers
			var items = [];
			var groups = [];

			// Prepare data
			for (var i = 0; i < data.length; i++) {
				// Process groups
				if (data[i].group) {
					if (! groups[data[i].group]) {
						groups[data[i].group] = [];
					}
					groups[data[i].group].push(i);
				} else {
					items.push(i);
				}
			}

			// Number of items counter
			var counter = 0;

			// Groups
			var groupNames = Object.keys(groups);

			// Append groups in case exists
			if (groupNames.length > 0) {
				for (var i = 0; i < groupNames.length; i++) {
					// Group container
					var group = document.createElement('div');
					group.className = 'jdropdown-group';
					// Group name
					var groupName = document.createElement('div');
					groupName.className = 'jdropdown-group-name';
					groupName.innerHTML = groupNames[i];
					// Group arrow
					var groupArrow = document.createElement('i');
					groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down';
					groupName.appendChild(groupArrow);
					// Group items
					var groupContent = document.createElement('div');
					groupContent.className = 'jdropdown-group-items';
					for (var j = 0; j < groups[groupNames[i]].length; j++) {
						var item = obj.createItem(data[groups[groupNames[i]][j]], group, groupNames[i]);

						if (obj.options.lazyLoading == false || counter < 200) {
							groupContent.appendChild(item.element);
							counter++;
						}
					}
					// Group itens
					group.appendChild(groupName);
					group.appendChild(groupContent);
					// Keep group DOM
					obj.groups.push(group);
					// Only add to the screen if children on the group
					if (groupContent.children.length > 0) {
						// Add DOM to the content
						content.appendChild(group);
					}
				}
			}

			if (items.length) {
				for (var i = 0; i < items.length; i++) {
					var item = obj.createItem(data[items[i]]);
					if (obj.options.lazyLoading == false || counter < 200) {
						content.appendChild(item.element);
						counter++;
					}
				}
			}
		}
	}

	obj.setData = function(data) {
		// Reset current value
		resetValue();

		// Make sure the content container is blank
		content.innerHTML = '';

		// Reset
		obj.header.value = '';

		// Reset items and values
		obj.items = [];

		// Prepare data
		if (data && data.length) {
			for (var i = 0; i < data.length; i++) {
				// Compatibility
				if (typeof(data[i]) != 'object') {
					// Correct format
					if (! obj.options.format) {
						data[i] = {
							value: data[i],
							text: data[i]
						}
					} else {
						data[i] = {
							id: data[i],
							name: data[i]
						}
					}
				}
			}

			// Append data
			obj.appendData(data);

			// Update data
			obj.options.data = data;
		} else {
			// Update data
		   obj.options.data = [];
		}

		obj.close();
	}

	obj.getData = function() {
		return obj.options.data;
	}

	/**
	 * Get position of the item
	 */
	obj.getPosition = function(val) {
		for (var i = 0; i < obj.items.length; i++) {
			if (Value(i) == val) {
				return i;
			}
		}
		return false;
	}

	/**
	 * Get dropdown current text
	 */
	obj.getText = function(asArray) {
		// Get value
		var v = getText();
		// Return value
		if (asArray) {
			return v;
		} else {
			return v.join('; ');
		}
	}

	/**
	 * Get dropdown current value
	 */
	obj.getValue = function(asArray) {
		// Get value
		var v = getValue();
		// Return value
		if (asArray) {
			return v;
		} else {
			return v.join(';');
		}
	}

	/**
	 * Change event
	 */
	var change = function(oldValue) {
		// Lemonade JS
		if (el.value != obj.options.value) {
			el.value = obj.options.value;
			if (typeof(el.oninput) == 'function') {
				el.oninput({
					type: 'input',
					target: el,
					value: el.value
				});
			}
		}

		// Events
		if (typeof(obj.options.onchange) == 'function') {
			obj.options.onchange(el, obj, oldValue, obj.options.value);
		}
	}

	/**
	 * Set value
	 */
	obj.setValue = function(newValue) {
		// Current value
		var oldValue = obj.getValue();
		// New value
		if (Array.isArray(newValue)) {
			newValue = newValue.join(';')
		}

		if (oldValue !== newValue) {
			// Set value
			applyValue(newValue);

			// Change
			change(oldValue);
		}
	}

	obj.resetSelected = function() {
		obj.setValue(null);
	}

	obj.selectIndex = function(index, force) {
		// Make sure is a number
		var index = parseInt(index);

		// Only select those existing elements
		if (obj.items && obj.items[index] && (force === true || obj.items[index].data.disabled !== true)) {
			// Reset cursor to a new position
			obj.setCursor(index, false);

			// Behaviour
			if (! obj.options.multiple) {
				// Update value
				if (obj.items[index].selected) {
					obj.setValue(null);
				} else {
					obj.setValue(Value(index));
				}

				// Close component
				obj.close();
			} else {
				// Old value
				var oldValue = obj.options.value;

				// Toggle option
				if (obj.items[index].selected) {
					obj.items[index].element.classList.remove('jdropdown-selected');
					obj.items[index].selected = false;

					delete obj.value[Value(index)];
				} else {
					// Select element
					obj.items[index].element.classList.add('jdropdown-selected');
					obj.items[index].selected = true;

					// Set value
					obj.value[Value(index)] = Text(index);
				}

				// Global value
				obj.options.value = Object.keys(obj.value).join(';');

				// Update labels for multiple dropdown
				if (obj.options.autocomplete == false) {
					obj.header.value = getText().join('; ');
				}

				// Events
				change(oldValue);
			}
		}
	}

	obj.selectItem = function(item) {
		obj.selectIndex(item.indexValue);
	}

	var exists = function(k, result) {
		for (var j = 0; j < result.length; j++) {
			if (! obj.options.format) {
				if (result[j].value == k) {
					return true;
				}
			} else {
				if (result[j].id == k) {
					return true;
				}
			}
		}
		return false;
	}

	obj.find = function(str) {
		if (obj.search == str.trim()) {
			return false;
		}

		// Search term
		obj.search = str;

		// Reset index
		obj.setCursor();

		// Remove nodes from all groups
		if (obj.groups.length) {
			for (var i = 0; i < obj.groups.length; i++) {
				obj.groups[i].lastChild.innerHTML = '';
			}
		}

		// Remove all nodes
		content.innerHTML = '';

		// Remove current items in the remote search
		if (obj.options.remoteSearch == true) {
			// Reset results
			obj.results = null;
			// URL
			var url = obj.options.url + (obj.options.url.indexOf('?') > 0 ? '&' : '?') + 'q=' + str;
			// Remote search
			jSuites.ajax({
				url: url,
				method: 'GET',
				dataType: 'json',
				success: function(result) {
					// Reset items
					obj.items = [];

					// Add the current selected items to the results in case they are not there
					var current = Object.keys(obj.value);
					if (current.length) {
						for (var i = 0; i < current.length; i++) {
							if (! exists(current[i], result)) {
								if (! obj.options.format) {
									result.unshift({ value: current[i], text: obj.value[current[i]] });
								} else {
									result.unshift({ id: current[i], name: obj.value[current[i]] });
								}
							}
						}
					}
					// Append data
					obj.appendData(result);
					// Show or hide results
					if (! result.length) {
						content.style.display = 'none';
					} else {
						content.style.display = '';
					}
				}
			});
		} else {
			// Search terms
			str = new RegExp(str, 'gi');

			// Reset search
			var results = [];

			// Append options
			for (var i = 0; i < obj.items.length; i++) {
				// Item label
				var label = Text(i);
				// Item title
				var title = obj.items[i].data.title || '';
				// Group name
				var groupName = obj.items[i].data.group || '';
				// Synonym
				var synonym = obj.items[i].data.synonym || '';
				if (synonym) {
					synonym = synonym.join(' ');
				}

				if (str == null || obj.items[i].selected == true || label.match(str) || title.match(str) || groupName.match(str) || synonym.match(str)) {
					results.push(obj.items[i]);
				}
			}

			if (! results.length) {
				content.style.display = 'none';

				// Results
				obj.results = null;
			} else {
				content.style.display = '';

				// Results
				obj.results = results;

				// Show 200 items at once
				var number = results.length || 0;

				// Lazyloading
				if (obj.options.lazyLoading == true && number > 200) {
					number = 200;
				}

				for (var i = 0; i < number; i++) {
					if (obj.results[i].group) {
						if (! obj.results[i].group.parentNode) {
							content.appendChild(obj.results[i].group);
						}
						obj.results[i].group.lastChild.appendChild(obj.results[i].element);
					} else {
						content.appendChild(obj.results[i].element);
					}
				}
			}
		}

		// Auto focus
		if (obj.options.autofocus == true) {
			obj.first();
		}
	}

	obj.open = function() {
		// Focus
		if (! el.classList.contains('jdropdown-focus')) {
			// Current dropdown
			jSuites.dropdown.current = obj;

			// Start tracking
			jSuites.tracking(obj, true);

			// Add focus
			el.classList.add('jdropdown-focus');

			// Animation
			if (jSuites.getWindowWidth() < 800) {
				if (obj.options.type == null || obj.options.type == 'picker') {
					jSuites.animation.slideBottom(container, 1);
				}
			}

			// Filter
			if (obj.options.autocomplete == true) {
				obj.header.value = obj.search;
				obj.header.focus();
			}

			// Set cursor for the first or first selected element
			var k = getValue();
			if (k[0]) {
				var cursor = obj.getPosition(k[0]);
				if (cursor !== false) {
					obj.setCursor(cursor);
				}
			}

			// Container Size
			if (! obj.options.type || obj.options.type == 'default') {
				var rect = el.getBoundingClientRect();
				var rectContainer = container.getBoundingClientRect();

				if (obj.options.position) {
					container.style.position = 'fixed';
					if (window.innerHeight < rect.bottom + rectContainer.height) {
						container.style.top = '';
						container.style.bottom = (window.innerHeight - rect.top ) + 1 + 'px';
					} else {
						container.style.top = rect.bottom + 'px';
						container.style.bottom = '';
					}
					container.style.left = rect.left + 'px';
				} else {
					if (window.innerHeight < rect.bottom + rectContainer.height) {
						container.style.top = '';
						container.style.bottom = rect.height + 1 + 'px';
					} else {
						container.style.top = '';
						container.style.bottom = '';
					}
				}

				container.style.minWidth = rect.width + 'px';

				if (obj.options.maxWidth) {
					container.style.maxWidth = obj.options.maxWidth;
				}

				if (! obj.items.length && obj.options.autocomplete == true) {
					content.style.display = 'none';
				} else {
					content.style.display = '';
				}
			}
		}

		// Events
		if (typeof(obj.options.onopen) == 'function') {
			obj.options.onopen(el);
		}
	}

	obj.close = function(ignoreEvents) {
		if (el.classList.contains('jdropdown-focus')) {
			// Update labels
			obj.header.value = obj.getText();
			// Remove cursor
			obj.setCursor();
			// Events
			if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
				obj.options.onclose(el);
			}
			// Blur
			if (obj.header.blur) {
				obj.header.blur();
			}
			// Remove focus
			el.classList.remove('jdropdown-focus');
			// Start tracking
			jSuites.tracking(obj, false);
			// Current dropdown
			jSuites.dropdown.current = null;
		}

		return obj.getValue();
	}

	/**
	 * Set cursor
	 */
	obj.setCursor = function(index, setPosition) {
		// Remove current cursor
		if (obj.currentIndex != null) {
			// Remove visual cursor
			if (obj.items && obj.items[obj.currentIndex]) {
				obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
			}
		}

		if (index == undefined) {
			obj.currentIndex = null;
		} else {
			index = parseInt(index);

			// Cursor only for visible items
			if (obj.items[index].element.parentNode) {
				obj.items[index].element.classList.add('jdropdown-cursor');
				obj.currentIndex = index;

				// Update scroll to the cursor element
				if (setPosition !== false && obj.items[obj.currentIndex].element) {
					var container = content.scrollTop;
					var element = obj.items[obj.currentIndex].element;
					content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
				}
			}
		}
	}

	// Compatibility
	obj.resetCursor = obj.setCursor;
	obj.updateCursor = obj.setCursor;

	/**
	 * Reset cursor and selected items
	 */
	obj.reset = function() {
		// Reset cursor
		obj.setCursor();

		// Reset selected
		obj.setValue(null);
	}

	/**
	 * First available item
	 */
	obj.first = function() {
		if (obj.options.lazyLoading === true) {
			obj.loadFirst();
		}

		var items = content.querySelectorAll('.jdropdown-item');
		if (items.length) {
			var newIndex = items[0].indexValue;
			obj.setCursor(newIndex);
		}
	}

	/**
	 * Last available item
	 */
	obj.last = function() {
		if (obj.options.lazyLoading === true) {
			obj.loadLast();
		}

		var items = content.querySelectorAll('.jdropdown-item');
		if (items.length) {
			var newIndex = items[items.length-1].indexValue;
			obj.setCursor(newIndex);
		}
	}

	obj.next = function(letter) {
		var newIndex = null;

		if (letter) {
			if (letter.length == 1) {
				// Current index
				var current = obj.currentIndex || -1;
				// Letter
				letter = letter.toLowerCase();

				var e = null;
				var l = null;
				var items = content.querySelectorAll('.jdropdown-item');
				if (items.length) {
					for (var i = 0; i < items.length; i++) {
						if (items[i].indexValue > current) {
							if (e = obj.items[items[i].indexValue]) {
								if (l = e.element.innerText[0]) {
									l = l.toLowerCase();
									if (letter == l) {
										newIndex = items[i].indexValue;
										break;
									}
								}
							}
						}
					}
					obj.setCursor(newIndex);
				}
			}
		} else {
			if (obj.currentIndex == undefined || obj.currentIndex == null) {
				obj.first();
			} else {
				var element = obj.items[obj.currentIndex].element;

				var next = element.nextElementSibling;
				if (next) {
					if (next.classList.contains('jdropdown-group')) {
						next = next.lastChild.firstChild;
					}
					newIndex = next.indexValue;
				} else {
					if (element.parentNode.classList.contains('jdropdown-group-items')) {
						if (next = element.parentNode.parentNode.nextElementSibling) {
							if (next.classList.contains('jdropdown-group')) {
								next = next.lastChild.firstChild;
							} else if (next.classList.contains('jdropdown-item')) {
								newIndex = next.indexValue;
							} else {
								next = null;
							}
						}

						if (next) {
							newIndex = next.indexValue;
						}
					}
				}

				if (newIndex !== null) {
					obj.setCursor(newIndex);
				}
			}
		}
	}

	obj.prev = function() {
		var newIndex = null;

		if (obj.currentIndex === null) {
			obj.first();
		} else {
			var element = obj.items[obj.currentIndex].element;

			var prev = element.previousElementSibling;
			if (prev) {
				if (prev.classList.contains('jdropdown-group')) {
					prev = prev.lastChild.lastChild;
				}
				newIndex = prev.indexValue;
			} else {
				if (element.parentNode.classList.contains('jdropdown-group-items')) {
					if (prev = element.parentNode.parentNode.previousElementSibling) {
						if (prev.classList.contains('jdropdown-group')) {
							prev = prev.lastChild.lastChild;
						} else if (prev.classList.contains('jdropdown-item')) {
							newIndex = prev.indexValue;
						} else {
							prev = null
						}
					}

					if (prev) {
						newIndex = prev.indexValue;
					}
				}
			}
		}

		if (newIndex !== null) {
			obj.setCursor(newIndex);
		}
	}

	obj.loadFirst = function() {
		// Search
		if (obj.results) {
			var results = obj.results;
		} else {
			var results = obj.items;
		}

		// Show 200 items at once
		var number = results.length || 0;

		// Lazyloading
		if (obj.options.lazyLoading == true && number > 200) {
			number = 200;
		}

		// Reset container
		content.innerHTML = '';

		// First 200 items
		for (var i = 0; i < number; i++) {
			if (results[i].group) {
				if (! results[i].group.parentNode) {
					content.appendChild(results[i].group);
				}
				results[i].group.lastChild.appendChild(results[i].element);
			} else {
				content.appendChild(results[i].element);
			}
		}

		// Scroll go to the begin
		content.scrollTop = 0;
	}

	obj.loadLast = function() {
		// Search
		if (obj.results) {
			var results = obj.results;
		} else {
			var results = obj.items;
		}

		// Show first page
		var number = results.length;

		// Max 200 items
		if (number > 200) {
			number = number - 200;

			// Reset container
			content.innerHTML = '';

			// First 200 items
			for (var i = number; i < results.length; i++) {
				if (results[i].group) {
					if (! results[i].group.parentNode) {
						content.appendChild(results[i].group);
					}
					results[i].group.lastChild.appendChild(results[i].element);
				} else {
					content.appendChild(results[i].element);
				}
			}

			// Scroll go to the begin
			content.scrollTop = content.scrollHeight;
		}
	}

	obj.loadUp = function() {
		var test = false;

		// Search
		if (obj.results) {
			var results = obj.results;
		} else {
			var results = obj.items;
		}

		var items = content.querySelectorAll('.jdropdown-item');
		var fistItem = items[0].indexValue;
		fistItem = obj.items[fistItem];
		var index = results.indexOf(fistItem) - 1;

		if (index > 0) {
			var number = 0;

			while (index > 0 && results[index] && number < 200) {
				if (results[index].group) {
					if (! results[index].group.parentNode) {
						content.insertBefore(results[index].group, content.firstChild);
					}
					results[index].group.lastChild.insertBefore(results[index].element, results[index].group.lastChild.firstChild);
				} else {
					content.insertBefore(results[index].element, content.firstChild);
				}

				index--;
				number++;
			}

			// New item added
			test = true;
		}

		return test;
	}

	obj.loadDown = function() {
		var test = false;

		// Search
		if (obj.results) {
			var results = obj.results;
		} else {
			var results = obj.items;
		}

		var items = content.querySelectorAll('.jdropdown-item');
		var lastItem = items[items.length-1].indexValue;
		lastItem = obj.items[lastItem];
		var index = results.indexOf(lastItem) + 1;

		if (index < results.length) {
			var number = 0;
			while (index < results.length && results[index] && number < 200) {
				if (results[index].group) {
					if (! results[index].group.parentNode) {
						content.appendChild(results[index].group);
					}
					results[index].group.lastChild.appendChild(results[index].element);
				} else {
					content.appendChild(results[index].element);
				}

				index++;
				number++;
			}

			// New item added
			test = true;
		}

		return test;
	}

	init();

	return obj;
});

jSuites.dropdown.keydown = function(e) {
	var dropdown = null;
	if (dropdown = jSuites.dropdown.current) {
		if (e.which == 13 || e.which == 9) {  // enter or tab
			if (dropdown.header.value && dropdown.currentIndex == null && dropdown.options.newOptions) {
				// if they typed something in, but it matched nothing, and newOptions are allowed, start that flow
				dropdown.add();
			} else {
				// Quick Select/Filter
				if (dropdown.currentIndex == null && dropdown.options.autocomplete == true && dropdown.header.value != "") {
					dropdown.find(dropdown.header.value);
				}
				dropdown.selectIndex(dropdown.currentIndex);
			}
		} else if (e.which == 38) {  // up arrow
			if (dropdown.currentIndex == null) {
				dropdown.first();
			} else if (dropdown.currentIndex > 0) {
				dropdown.prev();
			}
			e.preventDefault();
		} else if (e.which == 40) {  // down arrow
			if (dropdown.currentIndex == null) {
				dropdown.first();
			} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
				dropdown.next();
			}
			e.preventDefault();
		} else if (e.which == 36) {
			dropdown.first();
			if (! e.target.classList.contains('jdropdown-header')) {
				e.preventDefault();
			}
		} else if (e.which == 35) {
			dropdown.last();
			if (! e.target.classList.contains('jdropdown-header')) {
				e.preventDefault();
			}
		} else if (e.which == 27) {
			dropdown.close();
		} else if (e.which == 33) {  // page up
			if (dropdown.currentIndex == null) {
				dropdown.first();
			} else if (dropdown.currentIndex > 0) {
				for (var i = 0; i < 7; i++) {
					dropdown.prev()
				}
			}
			e.preventDefault();
		} else if (e.which == 34) {  // page down
			if (dropdown.currentIndex == null) {
				dropdown.first();
			} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
				for (var i = 0; i < 7; i++) {
					dropdown.next()
				}
			}
			e.preventDefault();
		}
	}
}

jSuites.dropdown.mouseup = function(e) {
	var element = jSuites.findElement(e.target, 'jdropdown');
	if (element) {
		var dropdown = element.dropdown;
		if (e.target.classList.contains('jdropdown-header')) {
			if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) {
				var rect = element.getBoundingClientRect();

				if (e.changedTouches && e.changedTouches[0]) {
					var x = e.changedTouches[0].clientX;
					var y = e.changedTouches[0].clientY;
				} else {
					var x = e.clientX;
					var y = e.clientY;
				}

				if (rect.width - (x - rect.left) < 30) {
					if (e.target.classList.contains('jdropdown-add')) {
						dropdown.add();
					} else {
						dropdown.close();
					}
				} else {
					if (dropdown.options.autocomplete == false) {
						dropdown.close();
					}
				}
			} else {
				dropdown.open();
			}
		} else if (e.target.classList.contains('jdropdown-group-name')) {
			var items = e.target.nextSibling.children;
			if (e.target.nextSibling.style.display != 'none') {
				for (var i = 0; i < items.length; i++) {
					if (items[i].style.display != 'none') {
						dropdown.selectItem(items[i]);
					}
				}
			}
		} else if (e.target.classList.contains('jdropdown-group-arrow')) {
			if (e.target.classList.contains('jdropdown-group-arrow-down')) {
				e.target.classList.remove('jdropdown-group-arrow-down');
				e.target.classList.add('jdropdown-group-arrow-up');
				e.target.parentNode.nextSibling.style.display = 'none';
			} else {
				e.target.classList.remove('jdropdown-group-arrow-up');
				e.target.classList.add('jdropdown-group-arrow-down');
				e.target.parentNode.nextSibling.style.display = '';
			}
		} else if (e.target.classList.contains('jdropdown-item')) {
			dropdown.selectItem(e.target);
		} else if (e.target.classList.contains('jdropdown-image')) {
			dropdown.selectItem(e.target.parentNode);
		} else if (e.target.classList.contains('jdropdown-description')) {
			dropdown.selectItem(e.target.parentNode);
		} else if (e.target.classList.contains('jdropdown-title')) {
			dropdown.selectItem(e.target.parentNode.parentNode);
		} else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
			dropdown.close();
		}
	}
}

jSuites.dropdown.extractFromDom = function(el, options) {
	// Keep reference
	var select = el;
	if (! options) {
		options = {};
	}
	// Prepare configuration
	if (el.getAttribute('multiple') && (! options || options.multiple == undefined)) {
		options.multiple = true;
	}
	if (el.getAttribute('placeholder') && (! options || options.placeholder == undefined)) {
		options.placeholder = el.getAttribute('placeholder');
	}
	if (el.getAttribute('data-autocomplete') && (! options || options.autocomplete == undefined)) {
		options.autocomplete = true;
	}
	if (! options || options.width == undefined) {
		options.width = el.offsetWidth;
	}
	if (el.value && (! options || options.value == undefined)) {
		options.value = el.value;
	}
	if (! options || options.data == undefined) {
		options.data = [];
		for (var j = 0; j < el.children.length; j++) {
			if (el.children[j].tagName == 'OPTGROUP') {
				for (var i = 0; i < el.children[j].children.length; i++) {
					options.data.push({
						value: el.children[j].children[i].value,
						text: el.children[j].children[i].innerHTML,
						group: el.children[j].getAttribute('label'),
					});
				}
			} else {
				options.data.push({
					value: el.children[j].value,
					text: el.children[j].innerHTML,
				});
			}
		}
	}
	if (! options || options.onchange == undefined) {
		options.onchange = function(a,b,c,d) {
			if (options.multiple == true) {
				if (obj.items[b].classList.contains('jdropdown-selected')) {
					select.options[b].setAttribute('selected', 'selected');
				} else {
					select.options[b].removeAttribute('selected');
				}
			} else {
				select.value = d;
			}
		}
	}
	// Create DIV
	var div = document.createElement('div');
	el.parentNode.insertBefore(div, el);
	el.style.display = 'none';
	el = div;

	return { el:el, options:options };
}

jSuites.editor = (function(el, options) {
	// New instance
	var obj = { type:'editor' };
	obj.options = {};

	// Default configuration
	var defaults = {
		// Load data from a remove location
		url: null,
		// Initial HTML content
		value: '',
		// Initial snippet
		snippet: null,
		// Add toolbar
		toolbar: true,
		toolbarOnTop: false,
		// Website parser is to read websites and images from cross domain
		remoteParser: null,
		// Placeholder
		placeholder: null,
		// Parse URL
		filterPaste: true,
		// Accept drop files
		dropZone: true,
		dropAsSnippet: false,
		acceptImages: true,
		acceptFiles: false,
		maxFileSize: 5000000,
		allowImageResize: true,
		// Style
		maxHeight: null,
		height: null,
		focus: false,
		// Events
		onclick: null,
		onfocus: null,
		onblur: null,
		onload: null,
		onkeyup: null,
		onkeydown: null,
		onchange: null,
		extensions: null,
		type: null,
	};

	// Loop through our object
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	// Private controllers
	var editorTimer = null;
	var editorAction = null;
	var files = [];

	// Keep the reference for the container
	obj.el = el;

	if (typeof(obj.options.onclick) == 'function') {
		el.onclick = function(e) {
			obj.options.onclick(el, obj, e);
		}
	}

	// Prepare container
	el.classList.add('jeditor-container');

	// Snippet
	var snippet = document.createElement('div');
	snippet.className = 'jsnippet';
	snippet.setAttribute('contenteditable', false);

	// Toolbar
	var toolbar = document.createElement('div');
	toolbar.className = 'jeditor-toolbar';

	obj.editor = document.createElement('div');
	obj.editor.setAttribute('contenteditable', true);
	obj.editor.setAttribute('spellcheck', false);
	obj.editor.classList.add('jeditor');

	// Placeholder
	if (obj.options.placeholder) {
		obj.editor.setAttribute('data-placeholder', obj.options.placeholder);
	}

	// Max height
	if (obj.options.maxHeight || obj.options.height) {
		obj.editor.style.overflowY = 'auto';

		if (obj.options.maxHeight) {
			obj.editor.style.maxHeight = obj.options.maxHeight;
		}
		if (obj.options.height) {
			obj.editor.style.height = obj.options.height;
		}
	}

	// Set editor initial value
	if (obj.options.url) {
		jSuites.ajax({
			url: obj.options.url,
			dataType: 'html',
			success: function(result) {
				obj.editor.innerHTML = result;

				jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
			}
		})
	} else {
		if (obj.options.value) {
			obj.editor.innerHTML = obj.options.value;
		} else {
			// Create from existing elements
			for (var i = 0; i < el.children.length; i++) {
				obj.editor.appendChild(el.children[i]);
			}
		}
	}

	// Make sure element is empty
	el.innerHTML = '';

	/**
	 * Onchange event controllers
	 */
	var change = function(e) {
		if (typeof(obj.options.onchange) == 'function') {
			obj.options.onchange(el, obj, e);
		}

		// Update value
		obj.options.value = obj.getData();

		// Lemonade JS
		if (el.value != obj.options.value) {
			el.value = obj.options.value;
			if (typeof(el.oninput) == 'function') {
				el.oninput({
					type: 'input',
					target: el,
					value: el.value
				});
			}
		}
	}

	/**
	 * Extract images from a HTML string
	 */
	var extractImageFromHtml = function(html) {
		// Create temp element
		var div = document.createElement('div');
		div.innerHTML = html;

		// Extract images
		var img = div.querySelectorAll('img');

		if (img.length) {
			for (var i = 0; i < img.length; i++) {
				obj.addImage(img[i].src);
			}
		}
	}

	/**
	 * Insert node at caret
	 */
	var insertNodeAtCaret = function(newNode) {
		var sel, range;

		if (window.getSelection) {
			sel = window.getSelection();
			if (sel.rangeCount) {
				range = sel.getRangeAt(0);
				var selectedText = range.toString();
				range.deleteContents();
				range.insertNode(newNode);
				// move the cursor after element
				range.setStartAfter(newNode);
				range.setEndAfter(newNode);
				sel.removeAllRanges();
				sel.addRange(range);
			}
		}
	}

	var updateTotalImages = function() {
		var o = null;
		if (o = snippet.children[0]) {
			// Make sure is a grid
			if (! o.classList.contains('jslider-grid')) {
				o.classList.add('jslider-grid');
			}
			// Quantify of images
			var number = o.children.length;
			// Set the configuration of the grid
			o.setAttribute('data-number', number > 4 ? 4 : number);
			// Total of images inside the grid
			if (number > 4) {
				o.setAttribute('data-total', number - 4);
			} else {
				o.removeAttribute('data-total');
			}
		}
	}

	/**
	 * Append image to the snippet
	 */
	var appendImage = function(image) {
		if (! snippet.innerHTML) {
			obj.appendSnippet({});
		}
		snippet.children[0].appendChild(image);
		updateTotalImages();
	}

	/**
	 * Append snippet
	 * @Param object data
	 */
	obj.appendSnippet = function(data) {
		// Reset snippet
		snippet.innerHTML = '';

		// Attributes
		var a = [ 'image', 'title', 'description', 'host', 'url' ];

		for (var i = 0; i < a.length; i++) {
			var div = document.createElement('div');
			div.className = 'jsnippet-' + a[i];
			div.setAttribute('data-k', a[i]);
			snippet.appendChild(div);
			if (data[a[i]]) {
				if (a[i] == 'image') {
					if (! Array.isArray(data.image)) {
						data.image = [ data.image ];
					}
					for (var j = 0; j < data.image.length; j++) {
						var img = document.createElement('img');
						img.src = data.image[j];
						div.appendChild(img);
					}
				} else {
					div.innerHTML = data[a[i]];
				}
			}
		}

		obj.editor.appendChild(document.createElement('br'));
		obj.editor.appendChild(snippet);
	}

	/**
	 * Set editor value
	 */
	obj.setData = function(o) {
		if (typeof(o) == 'object') {
			obj.editor.innerHTML = o.content;
		} else {
			obj.editor.innerHTML = o;
		}

		if (obj.options.focus) {
			jSuites.editor.setCursor(obj.editor, true);
		}

		// Reset files container
		files = [];
	}

	obj.getFiles = function() {
		var f = obj.editor.querySelectorAll('.jfile');
		var d = [];
		for (var i = 0; i < f.length; i++) {
			if (files[f[i].src]) {
				d.push(files[f[i].src]);
			}
		}
		return d;
	}

	obj.getText = function() {
		return obj.editor.innerText;
	}

	/**
	 * Get editor data
	 */
	obj.getData = function(json) {
		if (! json) {
			var data = obj.editor.innerHTML;
		} else {
			var data = {
				content : '',
			}

			// Get snippet
			if (snippet.innerHTML) {
				var index = 0;
				data.snippet = {};
				for (var i = 0; i < snippet.children.length; i++) {
					// Get key from element
					var key = snippet.children[i].getAttribute('data-k');
					if (key) {
						if (key == 'image') {
							if (! data.snippet.image) {
								data.snippet.image = [];
							}
							// Get all images
							for (var j = 0; j < snippet.children[i].children.length; j++) {
								data.snippet.image.push(snippet.children[i].children[j].getAttribute('src'))
							}
						} else {
							data.snippet[key] = snippet.children[i].innerHTML;
						}
					}
				}
			}

			// Get files
			var f = Object.keys(files);
			if (f.length) {
				data.files = [];
				for (var i = 0; i < f.length; i++) {
					data.files.push(files[f[i]]);
				}
			}

			// Get content
			var d = document.createElement('div');
			d.innerHTML = obj.editor.innerHTML;
			var s = d.querySelector('.jsnippet');
			if (s) {
				s.remove();
			}

			var text = d.innerHTML;
			text = text.replace(/<br>/g, "\n");
			text = text.replace(/<\/div>/g, "<\/div>\n");
			text = text.replace(/<(?:.|\n)*?>/gm, "");
			data.content = text.trim();

			// Process extensions
			processExtensions('getData', data);
		}

		return data;
	}

	// Reset
	obj.reset = function() {
		obj.editor.innerHTML = '';
		snippet.innerHTML = '';
		files = [];
	}

	obj.addPdf = function(data) {
		if (data.result.substr(0,4) != 'data') {
			console.error('Invalid source');
		} else {
			var canvas = document.createElement('canvas');
			canvas.width = 60;
			canvas.height = 60;

			var img = new Image();
			var ctx = canvas.getContext('2d');
			ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

			canvas.toBlob(function(blob) {
				var newImage = document.createElement('img');
				newImage.src = window.URL.createObjectURL(blob);
				newImage.title = data.name;
				newImage.className = 'jfile pdf';

				files[newImage.src] = {
					file: newImage.src,
					extension: 'pdf',
					content: data.result,
				}

				//insertNodeAtCaret(newImage);
				document.execCommand('insertHtml', false, newImage.outerHTML);
			});
		}
	}

	obj.addImage = function(src, asSnippet) {
		if (! src) {
			src = '';
		}

		if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
			console.error('remoteParser not defined in your initialization');
		} else {
			// This is to process cross domain images
			if (src.substr(0,4) == 'data') {
				var extension = src.split(';')
				extension = extension[0].split('/');
				extension = extension[1];
			} else {
				var extension = src.substr(src.lastIndexOf('.') + 1);
				// Work for cross browsers
				src = obj.options.remoteParser + src;
			}

			var img = new Image();

			img.onload = function onload() {
				var canvas = document.createElement('canvas');
				canvas.width = img.width;
				canvas.height = img.height;

				var ctx = canvas.getContext('2d');
				ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

				canvas.toBlob(function(blob) {
					var newImage = document.createElement('img');
					newImage.src = window.URL.createObjectURL(blob);
					newImage.classList.add('jfile');
					newImage.setAttribute('tabindex', '900');
					newImage.setAttribute('width', img.width);
					newImage.setAttribute('height', img.height);
					files[newImage.src] = {
						file: newImage.src,
						extension: extension,
						content: canvas.toDataURL(),
					}

					if (obj.options.dropAsSnippet || asSnippet) {
						appendImage(newImage);
						// Just to understand the attachment is part of a snippet
						files[newImage.src].snippet = true;
					} else {
						//insertNodeAtCaret(newImage);
						document.execCommand('insertHtml', false, newImage.outerHTML);
					}

					change();
				});
			};

			img.src = src;
		}
	}

	obj.addFile = function(files) {
		var reader = [];

		for (var i = 0; i < files.length; i++) {
			if (files[i].size > obj.options.maxFileSize) {
				alert('The file is too big');
			} else {
				// Only PDF or Images
				var type = files[i].type.split('/');

				if (type[0] == 'image') {
					type = 1;
				} else if (type[1] == 'pdf') {
					type = 2;
				} else {
					type = 0;
				}

				if (type) {
					// Create file
					reader[i] = new FileReader();
					reader[i].index = i;
					reader[i].type = type;
					reader[i].name = files[i].name;
					reader[i].date = files[i].lastModified;
					reader[i].size = files[i].size;
					reader[i].addEventListener("load", function (data) {
						// Get result
						if (data.target.type == 2) {
							if (obj.options.acceptFiles == true) {
								obj.addPdf(data.target);
							}
						} else {
							obj.addImage(data.target.result);
						}
					}, false);

					reader[i].readAsDataURL(files[i])
				} else {
					alert('The extension is not allowed');
				}
			}
		}
	}

	// Destroy
	obj.destroy = function() {
		obj.editor.removeEventListener('mouseup', editorMouseUp);
		obj.editor.removeEventListener('mousedown', editorMouseDown);
		obj.editor.removeEventListener('mousemove', editorMouseMove);
		obj.editor.removeEventListener('keyup', editorKeyUp);
		obj.editor.removeEventListener('keydown', editorKeyDown);
		obj.editor.removeEventListener('dragstart', editorDragStart);
		obj.editor.removeEventListener('dragenter', editorDragEnter);
		obj.editor.removeEventListener('dragover', editorDragOver);
		obj.editor.removeEventListener('drop', editorDrop);
		obj.editor.removeEventListener('paste', editorPaste);
		obj.editor.removeEventListener('blur', editorBlur);
		obj.editor.removeEventListener('focus', editorFocus);

		el.editor = null;
		el.classList.remove('jeditor-container');

		toolbar.remove();
		snippet.remove();
		obj.editor.remove();
	}

	obj.upload = function() {
		jSuites.click(obj.file);
	}

	// Elements to be removed
	var remove = [
		HTMLUnknownElement,
		HTMLAudioElement,
		HTMLEmbedElement,
		HTMLIFrameElement,
		HTMLTextAreaElement,
		HTMLInputElement,
		HTMLScriptElement
	];

	// Valid properties
	var validProperty = ['width', 'height', 'align', 'border', 'src', 'tabindex'];

	// Valid CSS attributes
	var validStyle = ['color', 'font-weight', 'font-size', 'background', 'background-color', 'margin'];

	var parse = function(element) {
	   // Remove attributes
	   if (element.attributes && element.attributes.length) {
		   var image = null;
		   var style = null;
		   // Process style attribute
		   var elementStyle = element.getAttribute('style');
		   if (elementStyle) {
			   style = [];
			   var t = elementStyle.split(';');
			   for (var j = 0; j < t.length; j++) {
				   var v = t[j].trim().split(':');
				   if (validStyle.indexOf(v[0].trim()) >= 0) {
					   var k = v.shift();
					   var v = v.join(':');
					   style.push(k + ':' + v);
				   }
			   }
		   }
		   // Process image
		   if (element.tagName.toUpperCase() == 'IMG') {
			   if (! obj.options.acceptImages || ! element.src) {
				   element.parentNode.removeChild(element);
			   } else {
				   // Check if is data
				   element.setAttribute('tabindex', '900');
				   // Check attributes for persistance
				   obj.addImage(element.src);
			   }
		   }
		   // Remove attributes
		   var attr = [];
		   var numAttributes = element.attributes.length - 1;
		   if (numAttributes > 0) {
			   for (var i = numAttributes; i >= 0 ; i--) {
				   attr.push(element.attributes[i].name);
			   }
			   attr.forEach(function(v) {
				   if (validProperty.indexOf(v) == -1) {
					   element.removeAttribute(v);
				   }
			   });
		   }
		   element.style = '';
		   // Add valid style
		   if (style && style.length) {
			   element.setAttribute('style', style.join(';'));
		   }
	   }
	   // Parse children
	   if (element.children.length) {
		   for (var i = 0; i < element.children.length; i++) {
			   parse(element.children[i]);
		   }
	   }

	   if (remove.indexOf(element.constructor) >= 0) {
		   element.remove();
	   }
	}

	var select = function(e) {
		var s = window.getSelection()
		var r = document.createRange();
		r.selectNode(e);
		s.addRange(r)
	}

	var filter = function(data) {
		if (data) {
			data = data.replace(new RegExp('<!--(.*?)-->', 'gsi'), '');
		}
		var parser = new DOMParser();
		var d = parser.parseFromString(data, "text/html");
		parse(d);
		var span = document.createElement('span');
		span.innerHTML = d.firstChild.innerHTML;
		return span;
	}

	var editorPaste = function(e) {
		if (obj.options.filterPaste == true) {
			if (e.clipboardData || e.originalEvent.clipboardData) {
				var html = (e.originalEvent || e).clipboardData.getData('text/html');
				var text = (e.originalEvent || e).clipboardData.getData('text/plain');
				var file = (e.originalEvent || e).clipboardData.files
			} else if (window.clipboardData) {
				var html = window.clipboardData.getData('Html');
				var text = window.clipboardData.getData('Text');
				var file = window.clipboardData.files
			}

			if (file.length) {
				// Paste a image from the clipboard
				obj.addFile(file);
			} else {
				if (! html) {
					html = text.split('\r\n');
					if (! e.target.innerText) {
						html.map(function(v) {
							var d = document.createElement('div');
							d.innerText = v;
							obj.editor.appendChild(d);
						});
					} else {
						html = html.map(function(v) {
							return '<div>' + v + '</div>';
						});
						document.execCommand('insertHtml', false, html.join(''));
					}
				} else {
					var d = filter(html);
					// Paste to the editor
					//insertNodeAtCaret(d);
					document.execCommand('insertHtml', false, d.innerHTML);
				}
			}

			e.preventDefault();
		}
	}

	var editorDragStart = function(e) {
		if (editorAction && editorAction.e) {
			e.preventDefault();
		}
	}

	var editorDragEnter = function(e) {
		if (editorAction || obj.options.dropZone == false) {
			// Do nothing
		} else {
			el.classList.add('jeditor-dragging');
			e.preventDefault();
		}
	}

	var editorDragOver = function(e) {
		if (editorAction || obj.options.dropZone == false) {
			// Do nothing
		} else {
			if (editorTimer) {
				clearTimeout(editorTimer);
			}

			editorTimer = setTimeout(function() {
				el.classList.remove('jeditor-dragging');
			}, 100);
			e.preventDefault();
		}
	}

	var editorDrop = function(e) {
		if (editorAction || obj.options.dropZone == false) {
			// Do nothing
		} else {
			// Position caret on the drop
			var range = null;
			if (document.caretRangeFromPoint) {
				range=document.caretRangeFromPoint(e.clientX, e.clientY);
			} else if (e.rangeParent) {
				range=document.createRange();
				range.setStart(e.rangeParent,e.rangeOffset);
			}
			var sel = window.getSelection();
			sel.removeAllRanges();
			sel.addRange(range);
			sel.anchorNode.parentNode.focus();

			var html = (e.originalEvent || e).dataTransfer.getData('text/html');
			var text = (e.originalEvent || e).dataTransfer.getData('text/plain');
			var file = (e.originalEvent || e).dataTransfer.files;

			if (file.length) {
				obj.addFile(file);
			} else if (text) {
				extractImageFromHtml(html);
			}

			el.classList.remove('jeditor-dragging');
			e.preventDefault();
		}
	}

	var editorBlur = function(e) {
		// Process extensions
		processExtensions('onevent', e);
		// Apply changes
		change(e);
		// Blur
		if (typeof(obj.options.onblur) == 'function') {
			obj.options.onblur(el, obj, e);
		}
	}

	var editorFocus = function(e) {
		// Focus
		if (typeof(obj.options.onfocus) == 'function') {
			obj.options.onfocus(el, obj, e);
		}
	}

	var editorKeyUp = function(e) {
		if (! obj.editor.innerHTML) {
			obj.editor.innerHTML = '<div><br></div>';
		}
		if (typeof(obj.options.onkeyup) == 'function') {
			obj.options.onkeyup(el, obj, e);
		}
	}

	var editorKeyDown = function(e) {
		// Process extensions
		processExtensions('onevent', e);

		if (e.key == 'Delete') {
			if (e.target.tagName == 'IMG') {
				var parent = e.target.parentNode;
				select(e.target);
				if (parent.classList.contains('jsnippet-image')) {
					updateTotalImages();
				}
			}
		}

		if (typeof(obj.options.onkeydown) == 'function') {
			obj.options.onkeydown(el, obj, e);
		}
	}

	var editorMouseUp = function(e) {
		if (editorAction && editorAction.e) {
			editorAction.e.classList.remove('resizing');

			if (editorAction.e.changed == true) {
				var image = editorAction.e.cloneNode()
				image.width = parseInt(editorAction.e.style.width) || editorAction.e.getAttribute('width');
				image.height = parseInt(editorAction.e.style.height) || editorAction.e.getAttribute('height');
				editorAction.e.style.width = '';
				editorAction.e.style.height = '';
				select(editorAction.e);
				document.execCommand('insertHtml', false, image.outerHTML);
			}
		}

		editorAction = false;
	}

	var editorMouseDown = function(e) {
		var close = function(snippet) {
			var rect = snippet.getBoundingClientRect();
			if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) {
				snippet.innerHTML = '';
				snippet.remove();
			}
		}

		if (e.target.tagName == 'IMG') {
			if (e.target.style.cursor) {
				var rect = e.target.getBoundingClientRect();
				editorAction = {
					e: e.target,
					x: e.clientX,
					y: e.clientY,
					w: rect.width,
					h: rect.height,
					d: e.target.style.cursor,
				}

				if (! e.target.getAttribute('width')) {
					e.target.setAttribute('width', rect.width)
				}

				if (! e.target.getAttribute('height')) {
					e.target.setAttribute('height', rect.height)
				}

				var s = window.getSelection();
				if (s.rangeCount) {
					for (var i = 0; i < s.rangeCount; i++) {
						s.removeRange(s.getRangeAt(i));
					}
				}

				e.target.classList.add('resizing');
			} else {
				editorAction = true;
			}
		} else {
			if (e.target.classList.contains('jsnippet')) {
				close(e.target);
			} else if (e.target.parentNode.classList.contains('jsnippet')) {
				close(e.target.parentNode);
			}

			editorAction = true;
		}
	}

	var editorMouseMove = function(e) {
		if (e.target.tagName == 'IMG' && ! e.target.parentNode.classList.contains('jsnippet-image') && obj.options.allowImageResize == true) {
			if (e.target.getAttribute('tabindex')) {
				var rect = e.target.getBoundingClientRect();
				if (e.clientY - rect.top < 5) {
					if (rect.width - (e.clientX - rect.left) < 5) {
						e.target.style.cursor = 'ne-resize';
					} else if (e.clientX - rect.left < 5) {
						e.target.style.cursor = 'nw-resize';
					} else {
						e.target.style.cursor = 'n-resize';
					}
				} else if (rect.height - (e.clientY - rect.top) < 5) {
					if (rect.width - (e.clientX - rect.left) < 5) {
						e.target.style.cursor = 'se-resize';
					} else if (e.clientX - rect.left < 5) {
						e.target.style.cursor = 'sw-resize';
					} else {
						e.target.style.cursor = 's-resize';
					}
				} else if (rect.width - (e.clientX - rect.left) < 5) {
					e.target.style.cursor = 'e-resize';
				} else if (e.clientX - rect.left < 5) {
					e.target.style.cursor = 'w-resize';
				} else {
					e.target.style.cursor = '';
				}
			}
		}

		// Move
		if (e.which == 1 && editorAction && editorAction.d) {
			if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' ||  editorAction.d == 'se-resize') {
				editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x));

				if (e.shiftKey) {
					var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
					editorAction.e.style.height = editorAction.h + newHeight;
				} else {
					var newHeight =  null;
				}
			}

			if (! newHeight) {
				if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
					if (! e.shiftKey) {
						editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y);
					}
				}
			}

			editorAction.e.changed = true;
		}
	}

	var processExtensions = function(method, data) {
		if (obj.options.extensions) {
			var ext = Object.keys(obj.options.extensions);
			if (ext.length) {
				for (var i = 0; i < ext.length; i++)
					if (obj.options.extensions[ext[i]] && typeof(obj.options.extensions[ext[i]][method]) == 'function') {
						obj.options.extensions[ext[i]][method].call(obj, data);
					}
			}
		}
	}

	var loadExtensions = function() {
		if (obj.options.extensions) {
			var ext = Object.keys(obj.options.extensions);
			if (ext.length) {
				for (var i = 0; i < ext.length; i++) {
					if (obj.options.extensions[ext[i]] && typeof (obj.options.extensions[ext[i]]) == 'function') {
						obj.options.extensions[ext[i]] = obj.options.extensions[ext[i]](el, obj);
					}
				}
			}
		}
	}

	document.addEventListener('mouseup', editorMouseUp);
	document.addEventListener('mousemove', editorMouseMove);
	obj.editor.addEventListener('mousedown', editorMouseDown);
	obj.editor.addEventListener('keyup', editorKeyUp);
	obj.editor.addEventListener('keydown', editorKeyDown);
	obj.editor.addEventListener('dragstart', editorDragStart);
	obj.editor.addEventListener('dragenter', editorDragEnter);
	obj.editor.addEventListener('dragover', editorDragOver);
	obj.editor.addEventListener('drop', editorDrop);
	obj.editor.addEventListener('paste', editorPaste);
	obj.editor.addEventListener('focus', editorFocus);
	obj.editor.addEventListener('blur', editorBlur);

	// Append editor to the container
	el.appendChild(obj.editor);
	// Snippet
	if (obj.options.snippet) {
		obj.appendSnippet(obj.options.snippet);
	}

	// Add toolbar
	if (obj.options.toolbar) {
		// Default toolbar configuration
		if (Array.isArray(obj.options.toolbar)) {
			var toolbarOptions = {
				container: true,
				responsive: true,
				items: obj.options.toolbar
			}
		} else if (obj.options.toolbar === true) {
			var toolbarOptions = {
				container: true,
				responsive: true,
				items: [],
			}
		} else {
			var toolbarOptions = obj.options.toolbar;
		}

		// Default items
		if (! (toolbarOptions.items && toolbarOptions.items.length)) {
			toolbarOptions.items = jSuites.editor.getDefaultToolbar(obj);
		}

		if (obj.options.toolbarOnTop) {
			// Add class
			el.classList.add('toolbar-on-top');
			// Append to the DOM
			el.insertBefore(toolbar, el.firstChild);
		} else {
			// Add padding to the editor
			obj.editor.style.padding = '15px';
			// Append to the DOM
			el.appendChild(toolbar);
		}

		// Create toolbar
		jSuites.toolbar(toolbar, toolbarOptions);

		toolbar.addEventListener('click', function() {
			obj.editor.focus();
		})
	}

	// Upload file
	obj.file = document.createElement('input');
	obj.file.style.display = 'none';
	obj.file.type = 'file';
	obj.file.setAttribute('accept', 'image/*');
	obj.file.onchange = function() {
		obj.addFile(this.files);
	}
	el.appendChild(obj.file);

	// Focus to the editor
	if (obj.editor.innerHTML && obj.options.focus) {
		jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
	}

	// Change method
	el.change = obj.setData;

	// Global generic value handler
	el.val = function(val) {
		if (val === undefined) {
			// Data type
			var o = el.getAttribute('data-html') === 'true' ? false : true;
			return obj.getData(o);
		} else {
			obj.setData(val);
		}
	}

	loadExtensions();

	el.editor = obj;

	// Onload
	if (typeof(obj.options.onload) == 'function') {
		obj.options.onload(el, obj, obj.editor);
	}

	return obj;
});

jSuites.editor.setCursor = function(element, first) {
	element.focus();
	document.execCommand('selectAll');
	var sel = window.getSelection();
	var range = sel.getRangeAt(0);
	if (first == true) {
		var node = range.startContainer;
		var size = 0;
	} else {
		var node = range.endContainer;
		var size = node.length;
	}
	range.setStart(node, size);
	range.setEnd(node, size);
	sel.removeAllRanges();
	sel.addRange(range);
}

jSuites.editor.getDefaultToolbar = function(obj) {

	var color = function(a,b,c) {
		if (! c.color) {
			var t = null;
			var colorPicker = jSuites.color(c, {
				onchange: function(o, v) {
					if (c.k === 'color') {
						document.execCommand('foreColor', false, v);
					} else {
						document.execCommand('backColor', false, v);
					}
				}
			});
			c.color.open();
		}
	}

	var items = [];

	items.push({
		content: 'undo',
		onclick: function() {
			document.execCommand('undo');
		}
	});

	items.push({
		content: 'redo',
		onclick: function() {
			document.execCommand('redo');
		}
	});

	items.push({
		type: 'divisor'
	});

	if (obj.options.toolbarOnTop) {
		items.push({
			type: 'select',
			width: '140px',
			options: ['Default', 'Verdana', 'Arial', 'Courier New'],
			render: function (e) {
				return '<span style="font-family:' + e + '">' + e + '</span>';
			},
			onchange: function (a,b,c,d,e) {
				document.execCommand("fontName", false, d);
			}
		});

		items.push({
			type: 'select',
			content: 'format_size',
			options: ['x-small', 'small', 'medium', 'large', 'x-large'],
			render: function (e) {
				return '<span style="font-size:' + e + '">' + e + '</span>';
			},
			onchange: function (a,b,c,d,e) {
				//var html = `<span style="font-size: ${c}">${text}</span>`;
				//document.execCommand('insertHtml', false, html);
				document.execCommand("fontSize", false, parseInt(e)+1);
				//var f = window.getSelection().anchorNode.parentNode
				//f.removeAttribute("size");
				//f.style.fontSize = d;
			}
		});

		items.push({
			type: 'select',
			options: ['format_align_left', 'format_align_center', 'format_align_right', 'format_align_justify'],
			render: function (e) {
				return '<i class="material-icons">' + e + '</i>';
			},
			onchange: function (a,b,c,d,e) {
				var options = ['JustifyLeft','justifyCenter','justifyRight','justifyFull'];
				document.execCommand(options[e]);
			}
		});

		items.push({
			type: 'divisor'
		});

		items.push({
			content: 'format_color_text',
			k: 'color',
			onclick: color,
		});

		items.push({
			content: 'format_color_fill',
			k: 'background-color',
			onclick: color,
		});
	}

	items.push({
		content: 'format_bold',
		onclick: function(a,b,c) {
			document.execCommand('bold');

			if (document.queryCommandState("bold")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		content: 'format_italic',
		onclick: function(a,b,c) {
			document.execCommand('italic');

			if (document.queryCommandState("italic")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		content: 'format_underline',
		onclick: function(a,b,c) {
			document.execCommand('underline');

			if (document.queryCommandState("underline")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		type:'divisor'
	});

	items.push({
		content: 'format_list_bulleted',
		onclick: function(a,b,c) {
			document.execCommand('insertUnorderedList');

			if (document.queryCommandState("insertUnorderedList")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		content: 'format_list_numbered',
		onclick: function(a,b,c) {
			document.execCommand('insertOrderedList');

			if (document.queryCommandState("insertOrderedList")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		content: 'format_indent_increase',
		onclick: function(a,b,c) {
			document.execCommand('indent', true, null);

			if (document.queryCommandState("indent")) {
				c.classList.add('selected');
			} else {
				c.classList.remove('selected');
			}
		}
	});

	items.push({
		content: 'format_indent_decrease',
		onclick: function() {
			document.execCommand('outdent');

			if (document.queryCommandState("outdent")) {
				this.classList.add('selected');
			} else {
				this.classList.remove('selected');
			}
		}
	});

	if (obj.options.toolbarOnTop) {
		items.push({
			type: 'divisor'
		});

		items.push({
			content: 'photo',
			onclick: function () {
				obj.upload();
			}
		});

		items.push({
			type: 'select',
			content: 'table_view',
			columns: 10,
			right: true,
			options: [
				'0x0', '1x0', '2x0', '3x0', '4x0', '5x0', '6x0', '7x0', '8x0', '9x0',
				'0x1', '1x1', '2x1', '3x1', '4x1', '5x1', '6x1', '7x1', '8x1', '9x1',
				'0x2', '1x2', '2x2', '3x2', '4x2', '5x2', '6x2', '7x2', '8x2', '9x2',
				'0x3', '1x3', '2x3', '3x3', '4x3', '5x3', '6x3', '7x3', '8x3', '9x3',
				'0x4', '1x4', '2x4', '3x4', '4x4', '5x4', '6x4', '7x4', '8x4', '9x4',
				'0x5', '1x5', '2x5', '3x5', '4x5', '5x5', '6x5', '7x5', '8x5', '9x5',
				'0x6', '1x6', '2x6', '3x6', '4x6', '5x6', '6x6', '7x6', '8x6', '9x6',
				'0x7', '1x7', '2x7', '3x7', '4x7', '5x7', '6x7', '7x7', '8x7', '9x7',
				'0x8', '1x8', '2x8', '3x8', '4x8', '5x8', '6x8', '7x8', '8x8', '9x8',
				'0x9', '1x9', '2x9', '3x9', '4x9', '5x9', '6x9', '7x9', '8x9', '9x9',
			],
			render: function (e, item) {
				if (item) {
					item.onmouseover = this.onmouseover;
					e = e.split('x');
					item.setAttribute('data-x', e[0]);
					item.setAttribute('data-y', e[1]);
				}
				var element = document.createElement('div');
				item.style.margin = '1px';
				item.style.border = '1px solid #ddd';
				return element;
			},
			onmouseover: function (e) {
				var x = parseInt(e.target.getAttribute('data-x'));
				var y = parseInt(e.target.getAttribute('data-y'));
				for (var i = 0; i < e.target.parentNode.children.length; i++) {
					var element = e.target.parentNode.children[i];
					var ex = parseInt(element.getAttribute('data-x'));
					var ey = parseInt(element.getAttribute('data-y'));
					if (ex <= x && ey <= y) {
						element.style.backgroundColor = '#cae1fc';
						element.style.borderColor = '#2977ff';
					} else {
						element.style.backgroundColor = '';
						element.style.borderColor = '#ddd';
					}
				}
			},
			onchange: function (a, b, c) {
				c = c.split('x');
				var table = document.createElement('table');
				var tbody = document.createElement('tbody');
				for (var y = 0; y <= c[1]; y++) {
					var tr = document.createElement('tr');
					for (var x = 0; x <= c[0]; x++) {
						var td = document.createElement('td');
						td.innerHTML = '';
						tr.appendChild(td);
					}
					tbody.appendChild(tr);
				}
				table.appendChild(tbody);
				table.setAttribute('width', '100%');
				table.setAttribute('cellpadding', '6');
				table.setAttribute('cellspacing', '0');
				document.execCommand('insertHTML', false, table.outerHTML);
			}
		});
	}

	return items;
}


jSuites.focus = function(el) {
	if (el.innerText.length) {
		var range = document.createRange();
		var sel = window.getSelection();
		var node = el.childNodes[el.childNodes.length-1];
		range.setStart(node, node.length)
		range.collapse(true)
		sel.removeAllRanges()
		sel.addRange(range)
		el.scrollLeft = el.scrollWidth;
	}
}

jSuites.isNumeric = (function (num) {
	if (typeof(num) === 'string') {
		num = num.trim();
	}
	return !isNaN(num) && num !== null && num !== '';
});

jSuites.guid = function() {
	return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
		var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
		return v.toString(16);
	});
}

jSuites.getNode = function() {
	var node = document.getSelection().anchorNode;
	if (node) {
		return (node.nodeType == 3 ? node.parentNode : node);
	} else {
		return null;
	}
}
/**
 * Generate hash from a string
 */
jSuites.hash = function(str) {
	var hash = 0, i, chr;

	if (str.length === 0) {
		return hash;
	} else {
		for (i = 0; i < str.length; i++) {
			chr = str.charCodeAt(i);
			if (chr > 32) {
				hash = ((hash << 5) - hash) + chr;
				hash |= 0;
			}
		}
	}
	return hash;
}

/**
 * Generate a random color
 */
jSuites.randomColor = function(h) {
	var lum = -0.25;
	var hex = String('#' + Math.random().toString(16).slice(2, 8).toUpperCase()).replace(/[^0-9a-f]/gi, '');
	if (hex.length < 6) {
		hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
	}
	var rgb = [], c, i;
	for (i = 0; i < 3; i++) {
		c = parseInt(hex.substr(i * 2, 2), 16);
		c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
		rgb.push(("00" + c).substr(c.length));
	}

	// Return hex
	if (h == true) {
		return '#' + jSuites.two(rgb[0].toString(16)) + jSuites.two(rgb[1].toString(16)) + jSuites.two(rgb[2].toString(16));
	}

	return rgb;
}

jSuites.getWindowWidth = function() {
	var w = window,
	d = document,
	e = d.documentElement,
	g = d.getElementsByTagName('body')[0],
	x = w.innerWidth || e.clientWidth || g.clientWidth;
	return x;
}

jSuites.getWindowHeight = function() {
	var w = window,
	d = document,
	e = d.documentElement,
	g = d.getElementsByTagName('body')[0],
	y = w.innerHeight|| e.clientHeight|| g.clientHeight;
	return  y;
}

jSuites.getPosition = function(e) {
	if (e.changedTouches && e.changedTouches[0]) {
		var x = e.changedTouches[0].pageX;
		var y = e.changedTouches[0].pageY;
	} else {
		var x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
		var y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
	}

	return [ x, y ];
}

jSuites.click = function(el) {
	if (el.click) {
		el.click();
	} else {
		var evt = new MouseEvent('click', {
			bubbles: true,
			cancelable: true,
			view: window
		});
		el.dispatchEvent(evt);
	}
}

jSuites.findElement = function(element, condition) {
	var foundElement = false;

	function path (element) {
		if (element && ! foundElement) {
			if (typeof(condition) == 'function') {
				foundElement = condition(element)
			} else if (typeof(condition) == 'string') {
				if (element.classList && element.classList.contains(condition)) {
					foundElement = element;
				}
			}
		}

		if (element.parentNode && ! foundElement) {
			path(element.parentNode);
		}
	}

	path(element);

	return foundElement;
}

// Two digits
jSuites.two = function(value) {
	value = '' + value;
	if (value.length == 1) {
		value = '0' + value;
	}
	return value;
}

jSuites.sha512 = (function(str) {
	function int64(msint_32, lsint_32) {
		this.highOrder = msint_32;
		this.lowOrder = lsint_32;
	}

	var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b),
		new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1),
		new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f),
		new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)];

	var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd),
		new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc),
		new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019),
		new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118),
		new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe),
		new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2),
		new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1),
		new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694),
		new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3),
		new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65),
		new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
		new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5),
		new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210),
		new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4),
		new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725),
		new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70),
		new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
		new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df),
		new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8),
		new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b),
		new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001),
		new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30),
		new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910),
		new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
		new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53),
		new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8),
		new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb),
		new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3),
		new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
		new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec),
		new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9),
		new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b),
		new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207),
		new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178),
		new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6),
		new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b),
		new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
		new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c),
		new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a),
		new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];

	var W = new Array(64);
	var a, b, c, d, e, f, g, h, i, j;
	var T1, T2;
	var charsize = 8;

	function utf8_encode(str) {
		return unescape(encodeURIComponent(str));
	}

	function str2binb(str) {
		var bin = [];
		var mask = (1 << charsize) - 1;
		var len = str.length * charsize;

		for (var i = 0; i < len; i += charsize) {
			bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
		}

		return bin;
	}

	function binb2hex(binarray) {
		var hex_tab = "0123456789abcdef";
		var str = "";
		var length = binarray.length * 4;
		var srcByte;

		for (var i = 0; i < length; i += 1) {
			srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
			str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF);
		}

		return str;
	}

	function safe_add_2(x, y) {
		var lsw, msw, lowOrder, highOrder;

		lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF);
		msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
		lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16);
		msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
		highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		return new int64(highOrder, lowOrder);
	}

	function safe_add_4(a, b, c, d) {
		var lsw, msw, lowOrder, highOrder;

		lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF);
		msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16);
		lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16);
		msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16);
		highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		return new int64(highOrder, lowOrder);
	}

	function safe_add_5(a, b, c, d, e) {
		var lsw, msw, lowOrder, highOrder;

		lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF);
		msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16);
		lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16);
		msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16);
		highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

		return new int64(highOrder, lowOrder);
	}

	function maj(x, y, z) {
		return new int64(
			(x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder),
			(x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder)
		);
	}

	function ch(x, y, z) {
		return new int64(
			(x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
			(x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
		);
	}

	function rotr(x, n) {
		if (n <= 32) {
			return new int64(
			 (x.highOrder >>> n) | (x.lowOrder << (32 - n)),
			 (x.lowOrder >>> n) | (x.highOrder << (32 - n))
			);
		} else {
			return new int64(
			 (x.lowOrder >>> n) | (x.highOrder << (32 - n)),
			 (x.highOrder >>> n) | (x.lowOrder << (32 - n))
			);
		}
	}

	function sigma0(x) {
		var rotr28 = rotr(x, 28);
		var rotr34 = rotr(x, 34);
		var rotr39 = rotr(x, 39);

		return new int64(
			rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
			rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
		);
	}

	function sigma1(x) {
		var rotr14 = rotr(x, 14);
		var rotr18 = rotr(x, 18);
		var rotr41 = rotr(x, 41);

		return new int64(
			rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
			rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
		);
	}

	function gamma0(x) {
		var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7);

		return new int64(
			rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
			rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
		);
	}

	function gamma1(x) {
		var rotr19 = rotr(x, 19);
		var rotr61 = rotr(x, 61);
		var shr6 = shr(x, 6);

		return new int64(
			rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
			rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
		);
	}

	function shr(x, n) {
		if (n <= 32) {
			return new int64(
				x.highOrder >>> n,
				x.lowOrder >>> n | (x.highOrder << (32 - n))
			);
		} else {
			return new int64(
				0,
				x.highOrder << (32 - n)
			);
		}
	}

	var str = utf8_encode(str);
	var strlen = str.length*charsize;
	str = str2binb(str);

	str[strlen >> 5] |= 0x80 << (24 - strlen % 32);
	str[(((strlen + 128) >> 10) << 5) + 31] = strlen;

	for (var i = 0; i < str.length; i += 32) {
		a = H[0];
		b = H[1];
		c = H[2];
		d = H[3];
		e = H[4];
		f = H[5];
		g = H[6];
		h = H[7];

		for (var j = 0; j < 80; j++) {
			if (j < 16) {
				W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]);
			} else {
				W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]);
			}

			T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
			T2 = safe_add_2(sigma0(a), maj(a, b, c));
			h = g;
			g = f;
			f = e;
			e = safe_add_2(d, T1);
			d = c;
			c = b;
			b = a;
			a = safe_add_2(T1, T2);
		}

		H[0] = safe_add_2(a, H[0]);
		H[1] = safe_add_2(b, H[1]);
		H[2] = safe_add_2(c, H[2]);
		H[3] = safe_add_2(d, H[3]);
		H[4] = safe_add_2(e, H[4]);
		H[5] = safe_add_2(f, H[5]);
		H[6] = safe_add_2(g, H[6]);
		H[7] = safe_add_2(h, H[7]);
	}

	var binarray = [];
	for (var i = 0; i < H.length; i++) {
		binarray.push(H[i].highOrder);
		binarray.push(H[i].lowOrder);
	}

	return binb2hex(binarray);
});


jSuites.image = jSuites.upload = (function(el, options) {
	var obj = {};
	obj.options = {};

	// Default configuration
	var defaults = {
		type: 'image',
		extension: '*',
		input: false,
		minWidth: false,
		maxWidth: null,
		maxHeight: null,
		maxJpegSizeBytes: null, // For example, 350Kb would be 350000
		onchange: null,
		multiple: false,
		remoteParser: null,
		text:{
			extensionNotAllowed:'The extension is not allowed',
		}
	};

	// Loop through our object
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	// Multiple
	if (obj.options.multiple == true) {
		el.setAttribute('data-multiple', true);
	}

	// Container
	el.content = [];

	// Upload icon
	el.classList.add('jupload');

	if (obj.options.input == true) {
		el.classList.add('input');
	}

	obj.add = function(data) {
		// Reset container for single files
		if (obj.options.multiple == false) {
			el.content = [];
			el.innerText = '';
		}

		// Append to the element
		if (obj.options.type == 'image') {
			var img = document.createElement('img');
			img.setAttribute('src', data.file);
			img.setAttribute('tabindex', -1);
			if (! el.getAttribute('name')) {
				img.className = 'jfile';
				img.content = data;
			}
			el.appendChild(img);
		} else {
			if (data.name) {
				var name = data.name;
			} else {
				var name = data.file;
			}
			var div = document.createElement('div');
			div.innerText = name || obj.options.type;
			div.classList.add('jupload-item');
			div.setAttribute('tabindex', -1);
			el.appendChild(div);
		}

		if (data.content) {
			data.file = jSuites.guid();
		}

		// Push content
		el.content.push(data);

		// Onchange
		if (typeof(obj.options.onchange) == 'function') {
			obj.options.onchange(el, data);
		}
	}

	obj.addFromFile = function(file) {
		var type = file.type.split('/');
		if (type[0] == obj.options.type) {
			var readFile = new FileReader();
			readFile.addEventListener("load", function (v) {
				var data = {
					file: v.srcElement.result,
					extension: file.name.substr(file.name.lastIndexOf('.') + 1),
					name: file.name,
					size: file.size,
					lastmodified: file.lastModified,
					content: v.srcElement.result,
				}

				obj.add(data);
			});

			readFile.readAsDataURL(file);
		} else {
			alert(obj.options.text.extensionNotAllowed);
		}
	}

	obj.addFromUrl = function(src) {
		if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
			console.error('remoteParser not defined in your initialization');
		} else {
			// This is to process cross domain images
			if (src.substr(0,4) == 'data') {
				var extension = src.split(';')
				extension = extension[0].split('/');
				var type = extension[0].replace('data:','');
				if (type == obj.options.type) {
					var data = {
						file: src,
						name: '',
						extension: extension[1],
						content: src,
					}
					obj.add(data);
				} else {
					alert(obj.options.text.extensionNotAllowed);
				}
			} else {
				var extension = src.substr(src.lastIndexOf('.') + 1);
				// Work for cross browsers
				src = obj.options.remoteParser + src;
				// Get remove content
				jSuites.ajax({
					url: src,
					type: 'GET',
					dataType: 'blob',
					success: function(data) {
						//add(extension[0].replace('data:',''), data);
					}
				})
			}
		}
	}

	var getDataURL = function(canvas, type) {
		var compression = 0.92;
		var lastContentLength = null;
		var content = canvas.toDataURL(type, compression);
		while (obj.options.maxJpegSizeBytes && type === 'image/jpeg' &&
			   content.length > obj.options.maxJpegSizeBytes && content.length !== lastContentLength) {
			// Apply the compression
			compression *= 0.9;
			lastContentLength = content.length;
			content = canvas.toDataURL(type, compression);
		}
		return content;
	}

	var mime = obj.options.type + '/' + obj.options.extension;
	var input = document.createElement('input');
	input.type = 'file';
	input.setAttribute('accept', mime);
	input.onchange = function() {
		for (var i = 0; i < this.files.length; i++) {
			obj.addFromFile(this.files[i]);
		}
	}

	// Allow multiple files
	if (obj.options.multiple == true) {
		input.setAttribute('multiple', true);
	}

	var current = null;

	el.addEventListener("click", function(e) {
		current = null;
		if (! el.children.length || e.target === el) {
			jSuites.click(input);
		} else {
			if (e.target.parentNode == el) {
				current = e.target;
			}
		}
	});

	el.addEventListener("dblclick", function(e) {
		jSuites.click(input);
	});

	el.addEventListener('dragenter', function(e) {
		el.style.border = '1px dashed #000';
	});

	el.addEventListener('dragleave', function(e) {
		el.style.border = '1px solid #eee';
	});

	el.addEventListener('dragstop', function(e) {
		el.style.border = '1px solid #eee';
	});

	el.addEventListener('dragover', function(e) {
		e.preventDefault();
	});

	el.addEventListener('keydown', function(e) {
		if (current && e.which == 46) {
			var index = Array.prototype.indexOf.call(el.children, current);
			if (index >= 0) {
				el.content.splice(index, 1);
				current.remove();
				current = null;
			}
		}
	});

	el.addEventListener('drop', function(e) {
		e.preventDefault();
		e.stopPropagation();

		var html = (e.originalEvent || e).dataTransfer.getData('text/html');
		var file = (e.originalEvent || e).dataTransfer.files;

		if (file.length) {
			for (var i = 0; i < e.dataTransfer.files.length; i++) {
				obj.addFromFile(e.dataTransfer.files[i]);
			}
		} else if (html) {
			if (obj.options.multiple == false) {
				el.innerText = '';
			}

			// Create temp element
			var div = document.createElement('div');
			div.innerHTML = html;

			// Extract images
			var img = div.querySelectorAll('img');

			if (img.length) {
				for (var i = 0; i < img.length; i++) {
					obj.addFromUrl(img[i].src);
				}
			}
		}

		el.style.border = '1px solid #eee';

		return false;
	});

	el.val = function(val) {
		if (val === undefined) {
			return el.content && el.content.length ? el.content : null;
		} else {
			// Reset
			el.innerText = '';
			el.content = [];

			if (val) {
				if (Array.isArray(val)) {
					for (var i = 0; i < val.length; i++) {
						if (typeof(val[i]) == 'string') {
							obj.add({ file: val[i] });
						} else {
							obj.add(val[i]);
						}
					}
				} else if (typeof(val) == 'string') {
					obj.add({ file: val });
				}
			}
		}
	}

	el.upload = el.image = obj;

	return obj;
});

jSuites.image.create = function(data) {
	var img = document.createElement('img');
	img.setAttribute('src', data.file);
	img.className = 'jfile';
	img.setAttribute('tabindex', -1);
	img.content = data;

	return img;
}


jSuites.lazyLoading = (function(el, options) {
	var obj = {}

	// Mandatory options
	if (! options.loadUp || typeof(options.loadUp) != 'function') {
		options.loadUp = function() {
			return false;
		}
	}
	if (! options.loadDown || typeof(options.loadDown) != 'function') {
		options.loadDown = function() {
			return false;
		}
	}
	// Timer ms
	if (! options.timer) {
		options.timer = 100;
	}

	// Timer
	var timeControlLoading = null;

	// Controls
	var scrollControls = function(e) {
		if (timeControlLoading == null) {
			var event = false;
			var scrollTop = el.scrollTop;
			if (el.scrollTop + (el.clientHeight * 2) >= el.scrollHeight) {
				if (options.loadDown()) {
					if (scrollTop == el.scrollTop) {
						el.scrollTop = el.scrollTop - (el.clientHeight);
					}
					event = true;
				}
			} else if (el.scrollTop <= el.clientHeight) {
				if (options.loadUp()) {
					if (scrollTop == el.scrollTop) {
						el.scrollTop = el.scrollTop + (el.clientHeight);
					}
					event = true;
				}
			}

			timeControlLoading = setTimeout(function() {
				timeControlLoading = null;
			}, options.timer);

			if (event) {
				if (typeof(options.onupdate) == 'function') {
					options.onupdate();
				}
			}
		}
	}

	// Onscroll
	el.onscroll = function(e) {
		scrollControls(e);
	}

	el.onwheel = function(e) {
		scrollControls(e);
	}

	return obj;
});

jSuites.loading = (function() {
	var obj = {};

	var loading = null;

	obj.show = function(timeout) {
		if (! loading) {
			loading = document.createElement('div');
			loading.className = 'jloading';
		}
		document.body.appendChild(loading);

		// Max timeout in seconds
		if (timeout > 0) {
			setTimeout(function() {
				obj.hide();
			}, timeout * 1000)
		}
	}

	obj.hide = function() {
		if (loading && loading.parentNode) {
			document.body.removeChild(loading);
		}
	}

	return obj;
})();

jSuites.mask = (function() {
	// Currency
	var tokens = {
		// Text
		text: [ '@' ],
		// Currency tokens
		currency: [ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?', '#' ],
		// Percentage
		percentage: [ '0{1}(.{1}0+)?%' ],
		// Number
		numeric: [ '0{1}(.{1}0+)?' ],
		// Data tokens
		datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'PM', 'AM', 'MI', 'SS', 'MS', 'Y', 'M' ],
		// Other
		general: [ 'A', '0', '[0-9a-zA-Z\$]+', '.']
	}

	var getDate = function() {
		if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
			var m = 0;
			if (this.date[4]) {
				m = parseFloat(this.date[4] / 60);
			}
			var v = parseInt(this.date[3]) + m;
			v /= 24;
		} else if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
			v = jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5])
		} else {
			if (this.date[0] && this.date[1] && ! this.date[2]) {
				this.date[2] = 1;
			}
			v = jSuites.two(this.date[0]) + '-' + jSuites.two(this.date[1]) + '-' + jSuites.two(this.date[2]);

			if (this.date[3] || this.date[4] || this.date[5]) {
				v += ' ' + jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5]);
			}
		}

		return v;
	}

	var extractDate = function() {
		 var v = '';
		 if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
			 if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
				 v = parseInt(this.date[3]);
			 } else {
				 v = parseInt(this.date[3]) % 24;
			 }
			 if (this.date[4]) {
				 v += parseFloat(this.date[4] / 60);
			 }
			 v /= 24;
		 } else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
			 if (this.date[0] && this.date[1] && ! this.date[2]) {
				 this.date[2] = 1;
			 }
			 var t = jSuites.calendar.now(this.date);
			 v = jSuites.calendar.dateToNum(t);
			 if (this.date[4]) {
				 v += parseFloat(this.date[4] / 60);
			 }
		 }
		 return v;
	 }

	var isBlank = function(v) {
		return v === null || v === '' || v === undefined ? true : false;
	}

	var isFormula = function(value) {
		return (''+value).chartAt(0) == '=';
	}

	var isNumeric = function(t) {
		return t === 'currency' || t === 'percentage' || t === 'numeric' ? true : false;
	}
	/**
	 * Get the decimal defined in the mask configuration
	 */
	var getDecimal = function(v) {
		if (v && Number(v) == v) {
			return '.';
		} else {
			if (this.options.decimal) {
				return this.options.decimal;
			} else {
				if (this.locale) {
					var t = Intl.NumberFormat(this.locale).format(1.1);
					return this.options.decimal = t[1];
				} else {
					if (! v) {
						v  = this.mask;
					}
					var e = new RegExp('0{1}(.{1})0+', 'ig');
					var t = e.exec(v);
					if (t && t[1] && t[1].length == 1) {
						// Save decimal
						this.options.decimal = t[1];
						// Return decimal
						return t[1];
					} else {
						// Did not find any decimal last resort the default
						var e = new RegExp('#{1}(.{1})#+', 'ig');
						var t = e.exec(v);
						if (t && t[1] && t[1].length == 1) {
							if (t[1] === ',') {
								this.options.decimal = '.';
							} else {
								this.options.decimal = ',';
							}
						} else {
							this.options.decimal = '1.1'.toLocaleString().substring(1,2);
						}
					}
				}
			}
		}

		if (this.options.decimal) {
			return this.options.decimal;
		} else {
			return null;
		}
	}

	var ParseValue = function(v, decimal) {
		if (v == '') {
			return '';
		}

		// Get decimal
		if (! decimal) {
			decimal = getDecimal.call(this);
		}

		// New value
		v = (''+v).split(decimal);

		// Signal
		var signal = v[0].match(/[-]+/g);
		if (signal && signal.length) {
			signal = true;
		} else {
			signal = false;
		}

		v[0] = v[0].match(/[0-9]+/g);

		if (v[0]) {
			if (signal) {
				v[0].unshift('-');
			}
			v[0] = v[0].join('');
		} else {
			if (signal) {
				v[0] = '-';
			}
		}

		if (v[0] || v[1]) {
			if (v[1] !== undefined) {
				v[1] = v[1].match(/[0-9]+/g);
				if (v[1]) {
					v[1] = v[1].join('');
				} else {
					v[1] = '';
				}
			}
		} else {
			return '';
		}
		return v;
	}

	var FormatValue = function(v, event) {
		if (v == '') {
			return '';
		}
		// Get decimal
		var d = getDecimal.call(this);
		// Convert value
		var o = this.options;
		// Parse value
		v = ParseValue.call(this, v);
		if (v == '') {
			return '';
		}
		// Temporary value
		if (v[0]) {
			var t = parseFloat(v[0] + '.1');
			if (o.style == 'percent') {
				t /= 100;
			}
		} else {
			var t = null;
		}

		if ((v[0] == '-' || v[0] == '-00') && ! v[1] && (event && event.inputType == "deleteContentBackward")) {
			return '';
		}

		var n = new Intl.NumberFormat(this.locale, o).format(t);
		n = n.split(d);
		if (typeof(n[1]) !== 'undefined') {
			var s = n[1].replace(/[0-9]*/g, '');
			if (s) {
				n[2] = s;
			}
		}

		if (v[1] !== undefined) {
			n[1] = d + v[1];
		} else {
			n[1] = '';
		}

		return n.join('');
	}

	var Format = function(e, event) {
		var v = Value.call(e);
		if (! v) {
			return;
		}

		// Get decimal
		var d = getDecimal.call(this);
		var n = FormatValue.call(this, v, event);
		var t = (n.length) - v.length;
		var index = Caret.call(e) + t;
		// Set value and update caret
		Value.call(e, n, index, true);
	}

	var Extract = function(v) {
		// Keep the raw value
		var current = ParseValue.call(this, v);
		if (current) {
			// Negative values
			if (current[0] === '-') {
				current[0] = '-0';
			}
			return parseFloat(current.join('.'));
		}
		return null;
	}

	/**
	 * Caret getter and setter methods
	 */
	var Caret = function(index, adjustNumeric) {
		if (index === undefined) {
			if (this.tagName == 'DIV') {
				var pos = 0;
				var s = window.getSelection();
				if (s) {
					if (s.rangeCount !== 0) {
						var r = s.getRangeAt(0);
						var p = r.cloneRange();
						p.selectNodeContents(this);
						p.setEnd(r.endContainer, r.endOffset);
						pos = p.toString().length;
					}
				}
				return pos;
			} else {
				return this.selectionStart;
			}
		} else {
			// Get the current value
			var n = Value.call(this);

			// Review the position
			if (adjustNumeric) {
				var p = null;
				for (var i = 0; i < n.length; i++) {
					if (n[i].match(/[\-0-9]/g) || n[i] == '.' || n[i] == ',') {
						p = i;
					}
				}

				// If the string has no numbers
				if (p === null) {
					p = n.indexOf(' ');
				}

				if (index >= p) {
					index = p + 1;
				}
			}

			// Do not update caret
			if (index > n.length) {
				index = n.length;
			}

			if (index) {
				// Set caret
				if (this.tagName == 'DIV') {
					var s = window.getSelection();
					var r = document.createRange();

					if (this.childNodes[0]) {
						r.setStart(this.childNodes[0], index);
						s.removeAllRanges();
						s.addRange(r);
					}
				} else {
					this.selectionStart = index;
					this.selectionEnd = index;
				}
			}
		}
	}

	/**
	 * Value getter and setter method
	 */
	var Value = function(v, updateCaret, adjustNumeric) {
		if (this.tagName == 'DIV') {
			if (v === undefined) {
				var v = this.innerText;
				if (this.value && this.value.length > v.length) {
					v = this.value;
				}
				return v;
			} else {
				if (this.innerText !== v) {
					this.innerText = v;

					if (updateCaret) {
						Caret.call(this, updateCaret, adjustNumeric);
					}
				}
			}
		} else {
			if (v === undefined) {
				return this.value;
			} else {
				if (this.value !== v) {
					this.value = v;
					if (updateCaret) {
						Caret.call(this, updateCaret, adjustNumeric);
					}
				}
			}
		}
	}

	// Labels
	var weekDaysFull = jSuites.calendar.weekdays;
	var weekDays = jSuites.calendar.weekdaysShort;
	var monthsFull = jSuites.calendar.months;
	var months = jSuites.calendar.monthsShort;

	var parser = {
		'YEAR': function(v, s) {
			var y = ''+new Date().getFullYear();

			if (typeof(this.values[this.index]) === 'undefined') {
				this.values[this.index] = '';
			}
			if (parseInt(v) >= 0 && parseInt(v) <= 10) {
				if (this.values[this.index].length < s) {
					this.values[this.index] += v;
				}
			}
			if (this.values[this.index].length == s) {
				if (s == 2) {
					var y = y.substr(0,2) + this.values[this.index];
				} else if (s == 3) {
					var y = y.substr(0,1) + this.values[this.index];
				} else if (s == 4) {
					var y = this.values[this.index];
				}
				this.date[0] = y;
				this.index++;
			}
		},
		'YYYY': function(v) {
			parser.YEAR.call(this, v, 4);
		},
		'YYY': function(v) {
			parser.YEAR.call(this, v, 3);
		},
		'YY': function(v) {
			parser.YEAR.call(this, v, 2);
		},
		'FIND': function(v, a) {
			if (isBlank(this.values[this.index])) {
				this.values[this.index] = '';
			}
			if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
				this.values[this.index] += v;
				return;
			}
			var pos = 0;
			var count = 0;
			var value = (this.values[this.index] + v).toLowerCase();
			for (var i = 0; i < a.length; i++) {
				if (a[i].toLowerCase().indexOf(value) == 0) {
					pos = i;
					count++;
				}
			}
			if (count > 1) {
				this.values[this.index] += v;
			} else if (count == 1) {
				// Jump number of chars
				var t = (a[pos].length - this.values[this.index].length) - 1;
				this.position += t;

				this.values[this.index] = a[pos];
				this.index++;
				return pos;
			}
		},
		'MMM': function(v) {
			var ret = parser.FIND.call(this, v, months);
			if (ret !== undefined) {
				this.date[1] = ret + 1;
			}
		},
		'MON': function(v) {
			parser['MMM'].call(this, v);
		},
		'MMMM': function(v) {
			var ret = parser.FIND.call(this, v, monthsFull);
			if (ret !== undefined) {
				this.date[1] = ret + 1;
			}
		},
		'MONTH': function(v) {
			parser['MMMM'].call(this, v);
		},
		'MMMMM': function(v) {
			if (isBlank(this.values[this.index])) {
				this.values[this.index] = '';
			}
			var pos = 0;
			var count = 0;
			var value = (this.values[this.index] + v).toLowerCase();
			for (var i = 0; i < monthsFull.length; i++) {
				if (monthsFull[i][0].toLowerCase().indexOf(value) == 0) {
					this.values[this.index] = monthsFull[i][0];
					this.date[1] = i + 1;
					this.index++;
					break;
				}
			}
		},
		'MM': function(v) {
			if (isBlank(this.values[this.index])) {
				if (parseInt(v) > 1 && parseInt(v) < 10) {
					this.date[1] = this.values[this.index] = '0' + v;
					this.index++;
				} else if (parseInt(v) < 2) {
					this.values[this.index] = v;
				}
			} else {
				if (this.values[this.index] == 1 && parseInt(v) < 3) {
					this.date[1] = this.values[this.index] += v;
					this.index++;
				} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
					this.date[1] = this.values[this.index] += v;
					this.index++;
				}
			}
		},
		'M': function(v) {
			var test = false;
			if (parseInt(v) >= 0 && parseInt(v) < 10) {
				if (isBlank(this.values[this.index])) {
					this.values[this.index] = v;
					if (v > 1) {
						this.date[1] = this.values[this.index];
						this.index++;
					}
				} else {
					if (this.values[this.index] == 1 && parseInt(v) < 3) {
						this.date[1] = this.values[this.index] += v;
						this.index++;
					} else if (this.values[this.index] == 0 && parseInt(v) > 0) {
						this.date[1] = this.values[this.index] += v;
						this.index++;
					} else {
						var test = true;
					}
				}
			} else {
				var test = true;
			}

			// Re-test
			if (test == true) {
				var t = parseInt(this.values[this.index]);
				if (t > 0 && t < 12) {
					this.date[2] = this.values[this.index];
					this.index++;
					// Repeat the character
					this.position--;
				}
			}
		},
		'D': function(v) {
			var test = false;
			if (parseInt(v) >= 0 && parseInt(v) < 10) {
				if (isBlank(this.values[this.index])) {
					this.values[this.index] = v;
					if (parseInt(v) > 3) {
						this.date[2] = this.values[this.index];
						this.index++;
					}
				} else {
					if (this.values[this.index] == 3 && parseInt(v) < 2) {
						this.date[2] = this.values[this.index] += v;
						this.index++;
					} else if (this.values[this.index] == 1 || this.values[this.index] == 2) {
						this.date[2] = this.values[this.index] += v;
						this.index++;
					} else if (this.values[this.index] == 0 && parseInt(v) > 0) {
						this.date[2] = this.values[this.index] += v;
						this.index++;
					} else {
						var test = true;
					}
				}
			} else {
				var test = true;
			}

			// Re-test
			if (test == true) {
				var t = parseInt(this.values[this.index]);
				if (t > 0 && t < 32) {
					this.date[2] = this.values[this.index];
					this.index++;
					// Repeat the character
					this.position--;
				}
			}
		},
		'DD': function(v) {
			if (isBlank(this.values[this.index])) {
				if (parseInt(v) > 3 && parseInt(v) < 10) {
					this.date[2] = this.values[this.index] = '0' + v;
					this.index++;
				} else if (parseInt(v) < 10) {
					this.values[this.index] = v;
				}
			} else {
				if (this.values[this.index] == 3 && parseInt(v) < 2) {
					this.date[2] = this.values[this.index] += v;
					this.index++;
				} else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && parseInt(v) < 10) {
					this.date[2] = this.values[this.index] += v;
					this.index++;
				} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
					this.date[2] = this.values[this.index] += v;
					this.index++;
				}
			}
		},
		'DDD': function(v) {
			parser.FIND.call(this, v, weekDays);
		},
		'DY': function(v) {
			parser['DDD'].call(this, v);
		},
		'DDDD': function(v) {
			parser.FIND.call(this, v, weekDaysFull);
		},
		'DAY': function(v) {
			parser['DDDD'].call(this, v);
		},
		'HH12': function(v, two) {
			if (isBlank(this.values[this.index])) {
				if (parseInt(v) > 1 && parseInt(v) < 10) {
					if (two) {
						v = 0 + v;
					}
					this.date[3] = this.values[this.index] = v;
					this.index++;
				} else if (parseInt(v) < 10) {
					this.values[this.index] = v;
				}
			} else {
				if (this.values[this.index] == 1 && parseInt(v) < 3) {
					this.date[3] = this.values[this.index] += v;
					this.index++;
				} else if (this.values[this.index] < 1 && parseInt(v) < 10) {
					this.date[3] = this.values[this.index] += v;
					this.index++;
				}
			}
		},
		'HH24': function(v, two) {
			var test = false;
			if (parseInt(v) >= 0 && parseInt(v) < 10) {
				if (this.values[this.index] == null || this.values[this.index] == '') {
					if (parseInt(v) > 2 && parseInt(v) < 10) {
						if (two) {
							v = 0 + v;
						}
						this.date[3] = this.values[this.index] = v;
						this.index++;
					} else if (parseInt(v) < 10) {
						this.values[this.index] = v;
					}
				} else {
					if (this.values[this.index] == 2 && parseInt(v) < 4) {
						this.date[3] = this.values[this.index] += v;
						this.index++;
					} else if (this.values[this.index] < 2 && parseInt(v) < 10) {
						this.date[3] = this.values[this.index] += v;
						this.index++;
					}
				}
			}
		},
		'HH': function(v) {
			parser['HH24'].call(this, v, 1);
		},
		'H': function(v) {
			parser['HH24'].call(this, v, 0);
		},
		'\\[H\\]': function(v) {
			if (this.values[this.index] == undefined) {
				this.values[this.index] = '';
			}
			if (v.match(/[0-9]/g)) {
				this.date[3] = this.values[this.index] += v;
			} else {
				if (this.values[this.index].match(/[0-9]/g)) {
					this.date[3] = this.values[this.index];
					this.index++;
					// Repeat the character
					this.position--;
				}
			}
		},
		'N60': function(v, i) {
			if (this.values[this.index] == null || this.values[this.index] == '') {
				if (parseInt(v) > 5 && parseInt(v) < 10) {
					this.date[i] = this.values[this.index] = '0' + v;
					this.index++;
				} else if (parseInt(v) < 10) {
					this.values[this.index] = v;
				}
			} else {
				if (parseInt(v) < 10) {
					this.date[i] = this.values[this.index] += v;
					this.index++;
				 }
			}
		},
		'MI': function(v) {
			parser.N60.call(this, v, 4);
		},
		'SS': function(v) {
			parser.N60.call(this, v, 5);
		},
		'AM/PM': function(v) {
			this.values[this.index] = '';
			if (v) {
				if (this.date[3] > 12) {
					this.values[this.index] = 'PM';
				} else {
					this.values[this.index] = 'AM';
				}
			}
			this.index++;
		},
		'WD': function(v) {
			if (typeof(this.values[this.index]) === 'undefined') {
				this.values[this.index] = '';
			}
			if (parseInt(v) >= 0 && parseInt(v) < 7) {
				this.values[this.index] = v;
			}
			if (this.value[this.index].length == 1) {
				this.index++;
			}
		},
		'0{1}(.{1}0+)?': function(v) {
			// Get decimal
			var decimal = getDecimal.call(this);
			// Negative number
			var neg = false;
			// Create if is blank
			if (isBlank(this.values[this.index])) {
				this.values[this.index] = '';
			} else {
				if (this.values[this.index] == '-') {
					neg = true;
				}
			}
			var current = ParseValue.call(this, this.values[this.index], decimal);
			if (current) {
				this.values[this.index] = current.join(decimal);
			}
			// New entry
			if (parseInt(v) >= 0 && parseInt(v) < 10) {
				// Replace the zero for a number
				if (this.values[this.index] == '0' && v > 0) {
					this.values[this.index] = '';
				} else if (this.values[this.index] == '-0' && v > 0) {
					this.values[this.index] = '-';
				}
				// Don't add up zeros because does not mean anything here
				if ((this.values[this.index] != '0' && this.values[this.index] != '-0') || v == decimal) {
					this.values[this.index] += v;
				}
			} else if (decimal && v == decimal) {
				if (this.values[this.index].indexOf(decimal) == -1) {
					if (! this.values[this.index]) {
						this.values[this.index] = '0';
					}
					this.values[this.index] += v;
				}
			} else if (v == '-') {
				// Negative signed
				neg = true;
			}

			if (neg === true && this.values[this.index][0] !== '-') {
				this.values[this.index] = '-' + this.values[this.index];
			}
		},
		'0{1}(.{1}0+)?%': function(v) {
			parser['0{1}(.{1}0+)?'].call(this, v);

			if (this.values[this.index].match(/[\-0-9]/g)) {
				if (this.values[this.index] && this.values[this.index].indexOf('%') == -1) {
					this.values[this.index] += '%';
				}
			} else {
				this.values[this.index] = '';
			}
		},
		'#(.{1})##0?(.{1}0+)?( ?;(.*)?)?': function(v) {
			// Parse number
			parser['0{1}(.{1}0+)?'].call(this, v);
			// Get decimal
			var decimal = getDecimal.call(this);
			// Get separator
			var separator = this.tokens[this.index].substr(1,1);
			// Negative
			var negative = this.values[this.index][0] === '-' ? true : false;
			// Current value
			var current = ParseValue.call(this, this.values[this.index], decimal);

			// Get main and decimal parts
			if (current !== '') {
				// Format number
				var n = current[0].match(/[0-9]/g);
				if (n) {
					// Format
					n = n.join('');
					var t = [];
					var s = 0;
					for (var j = n.length - 1; j >= 0 ; j--) {
						t.push(n[j]);
						s++;
						if (! (s % 3)) {
							t.push(separator);
						}
					}
					t = t.reverse();
					current[0] = t.join('');
					if (current[0].substr(0,1) == separator) {
						current[0] = current[0].substr(1);
					}
				} else {
					current[0] = '';
				}

				// Value
				this.values[this.index] = current.join(decimal);

				// Negative
				if (negative) {
					this.values[this.index] = '-' + this.values[this.index];
				}
			}
		},
		'0': function(v) {
			if (v.match(/[0-9]/g)) {
				this.values[this.index] = v;
				this.index++;
			}
		},
		'[0-9a-zA-Z$]+': function(v) {
			if (isBlank(this.values[this.index])) {
				this.values[this.index] = '';
			}
			var t = this.tokens[this.index];
			var s = this.values[this.index];
			var i = s.length;

			if (t[i] == v) {
				this.values[this.index] += v;

				if (this.values[this.index] == t) {
					this.index++;
				}
			} else {
				this.values[this.index] = t;
				this.index++;

				if (v.match(/[\-0-9]/g)) {
					// Repeat the character
					this.position--;
				}
			}
		},
		'A': function(v) {
			if (v.match(/[a-zA-Z]/gi)) {
				this.values[this.index] = v;
				this.index++;
			}
		},
		'.': function(v) {
			parser['[0-9a-zA-Z$]+'].call(this, v);
		},
		'@': function(v) {
			if (isBlank(this.values[this.index])) {
				this.values[this.index] = '';
			}
			this.values[this.index] += v;
		}
	}

	/**
	 * Get the tokens in the mask string
	 */
	var getTokens = function(str) {
		if (this.type == 'general') {
			var t = [].concat(tokens.general);
		} else {
			var t = [].concat(tokens.currency, tokens.datetime, tokens.percentage, tokens.numeric, tokens.text, tokens.general);
		}
		// Expression to extract all tokens from the string
		var e = new RegExp(t.join('|'), 'gi');
		// Extract
		return str.match(e);
	}

	/**
	 * Get the method of one given token
	 */
	var getMethod = function(str) {
		if (! this.type) {
			var types = Object.keys(tokens);
		} else if (this.type == 'text') {
			var types = [ 'text' ];
		} else if (this.type == 'general') {
			var types = [ 'general' ];
		} else if (this.type == 'datetime') {
			var types = [ 'numeric', 'datetime', 'general' ];
		} else {
			var types = [ 'currency', 'percentage', 'numeric', 'general' ];
		}

		// Found
		for (var i = 0; i < types.length; i++) {
			var type = types[i];
			for (var j = 0; j < tokens[type].length; j++) {
				var e = new RegExp(tokens[type][j], 'gi');
				var r = str.match(e);
				if (r) {
					return { type: type, method: tokens[type][j] }
				}
			}
		}
	}

	/**
	 * Identify each method for each token
	 */
	var getMethods = function(t) {
		var result = [];
		for (var i = 0; i < t.length; i++) {
			var m = getMethod.call(this, t[i]);
			if (m) {
				result.push(m.method);
			} else {
				result.push(null);
			}
		}

		// Compatibility with excel
		for (var i = 0; i < result.length; i++) {
			if (result[i] == 'MM') {
				// Not a month, correct to minutes
				if (result[i-1] && result[i-1].indexOf('H') >= 0) {
					result[i] = 'MI';
				} else if (result[i-2] && result[i-2].indexOf('H') >= 0) {
					result[i] = 'MI';
				} else if (result[i+1] && result[i+1].indexOf('S') >= 0) {
					result[i] = 'MI';
				} else if (result[i+2] && result[i+2].indexOf('S') >= 0) {
					result[i] = 'MI';
				}
			}
		}

		return result;
	}

	/**
	 * Get the type for one given token
	 */
	var getType = function(str) {
		var m = getMethod.call(this, str);
		if (m) {
			var type = m.type;
		}

		if (type) {
			var numeric = 0;
			// Make sure the correct type
			var t = getTokens.call(this, str);
			for (var i = 0; i < t.length; i++) {
				m = getMethod.call(this, t[i]);
				if (m && isNumeric(m.type)) {
					numeric++;
				}
			}
			if (numeric > 1) {
				type = 'general';
			}
		}

		return type;
	}

	/**
	 * Parse character per character using the detected tokens in the mask
	 */
	var parse = function() {
		// Parser method for this position
		if (typeof(parser[this.methods[this.index]]) == 'function') {
			parser[this.methods[this.index]].call(this, this.value[this.position]);
			this.position++;
		} else {
			this.values[this.index] = this.tokens[this.index];
			this.index++;
		}
	}

	var isFormula = function(value) {
		var v = (''+value)[0];
		return v == '=' ? true : false;
	}

	var toPlainString = function(num) {
		return (''+ +num).replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/,
		  function(a,b,c,d,e) {
			return e < 0
			  ? b + '0.' + Array(1-e-c.length).join(0) + c + d
			  : b + c + d + Array(e-d.length+1).join(0);
		  });
	}

	/**
	 * Mask function
	 * @param {mixed|string} JS input or a string to be parsed
	 * @param {object|string} When the first param is a string, the second is the mask or object with the mask options
	 */
	var obj = function(e, config, returnObject) {
		// Options
		var r = null;
		var t = null;
		var o = {
			// Element
			input: null,
			// Current value
			value: null,
			// Mask options
			options: {},
			// New values for each token found
			values: [],
			// Token position
			index: 0,
			// Character position
			position: 0,
			// Date raw values
			date: [0,0,0,0,0,0],
			// Raw number for the numeric values
			number: 0,
		}

		// This is a JavaScript Event
		if (typeof(e) == 'object') {
			// Element
			o.input = e.target;
			// Current value
			o.value = Value.call(e.target);
			// Current caret position
			o.caret = Caret.call(e.target);
			// Mask
			if (t = e.target.getAttribute('data-mask')) {
				o.mask = t;
			}
			// Type
			if (t = e.target.getAttribute('data-type')) {
				o.type = t;
			}
			// Options
			if (e.target.mask) {
				if (e.target.mask.options) {
					o.options = e.target.mask.options;
				}
				if (e.target.mask.locale) {
					o.locale = e.target.mask.locale;
				}
			} else {
				// Locale
				if (t = e.target.getAttribute('data-locale')) {
					o.locale = t;
					if (o.mask) {
						o.options.style = o.mask;
					}
				}
			}
			// Extra configuration
			if (e.target.attributes && e.target.attributes.length) {
				for (var i = 0; i < e.target.attributes.length; i++) {
					var k = e.target.attributes[i].name;
					var v = e.target.attributes[i].value;
					if (k.substr(0,4) == 'data') {
						o.options[k.substr(5)] = v;
					}
				}
			}
		} else {
			// Options
			if (typeof(config) == 'string') {
				// Mask
				o.mask = config;
			} else {
				// Mask
				var k = Object.keys(config);
				for (var i = 0; i < k.length; i++) {
					o[k[i]] = config[k[i]];
				}
			}

			if (typeof(e) === 'number') {
				// Get decimal
				getDecimal.call(o, o.mask);
				// Replace to the correct decimal
				e = (''+e).replace('.', o.options.decimal);
			}

			// Current
			o.value = e;

			if (o.input) {
				// Value
				Value.call(o.input, e);
				// Focus
				jSuites.focus(o.input);
				// Caret
				o.caret = Caret.call(o.input);
			}
		}

		// Mask detected start the process
		if (! isFormula(o.value) && (o.mask || o.locale)) {
			// Compatibility fixes
			if (o.mask) {
				// Remove []
				o.mask = o.mask.replace(new RegExp(/\[h]/),'|h|');
				o.mask = o.mask.replace(new RegExp(/\[.*?\]/),'');
				o.mask = o.mask.replace(new RegExp(/\|h\|/),'[h]');
				if (o.mask.indexOf(';') !== -1) {
					var t = o.mask.split(';');
					o.mask = t[0];
				}
				// Excel mask TODO: Improve
				if (o.mask.indexOf('##') !== -1) {
					var d = o.mask.split(';');
					if (d[0]) {
						d[0] = d[0].replace('*', '\t');
						d[0] = d[0].replace(new RegExp(/_-/g), ' ');
						d[0] = d[0].replace(new RegExp(/_/g), '');
						d[0] = d[0].replace('##0.###','##0.000');
						d[0] = d[0].replace('##0.##','##0.00');
						d[0] = d[0].replace('##0.#','##0.0');
						d[0] = d[0].replace('##0,###','##0,000');
						d[0] = d[0].replace('##0,##','##0,00');
						d[0] = d[0].replace('##0,#','##0,0');
					}
					o.mask = d[0];
				}
				// Get type
				if (! o.type) {
					o.type = getType.call(o, o.mask);
				}
				// Get tokens
				o.tokens = getTokens.call(o, o.mask);
			}

			// On new input
			if (typeof(e) !== 'object'  || ! e.inputType || ! e.inputType.indexOf('insert') || ! e.inputType.indexOf('delete')) {
				// Start transformation
				if (o.locale) {
					if (o.input) {
						Format.call(o, o.input, e);
					} else {
						var newValue = FormatValue.call(o, o.value);
					}
				} else {
					// Get tokens
					o.methods = getMethods.call(o, o.tokens);
					o.event = e;

					// Go through all tokes
					while (o.position < o.value.length && typeof(o.tokens[o.index]) !== 'undefined') {
						// Get the appropriate parser
						parse.call(o);
					}

					// New value
					var newValue = o.values.join('');

					// Add tokens to the end of string only if string is not empty
					if (isNumeric(o.type) && newValue !== '') {
						// Complement things in the end of the mask
						while (typeof(o.tokens[o.index]) !== 'undefined') {
							var t = getMethod.call(o, o.tokens[o.index]);
							if (t && t.type == 'general') {
								o.values[o.index] = o.tokens[o.index];
							}
							o.index++;
						}

						var adjustNumeric = true;
					} else {
						var adjustNumeric = false;
					}

					// New value
					newValue = o.values.join('');

					// Reset value
					if (o.input) {
						t = newValue.length - o.value.length;
						if (t > 0) {
							var caret = o.caret + t;
						} else {
							var caret = o.caret;
						}
						Value.call(o.input, newValue, caret, adjustNumeric);
					}
				}
			}

			// Update raw data
			if (o.input) {
				var label = null;
				if (isNumeric(o.type)) {
					// Extract the number
					o.number = Extract.call(o, Value.call(o.input));
					// Keep the raw data as a property of the tag
					if (o.type == 'percentage') {
						label = o.number / 100;
					} else {
						label = o.number;
					}
				} else if (o.type == 'datetime') {
					label = getDate.call(o);

					if (o.date[0] && o.date[1] && o.date[2]) {
						o.input.setAttribute('data-completed', true);
					}
				}

				if (label) {
					o.input.setAttribute('data-value', label);
				}
			}

			if (newValue !== undefined) {
				if (returnObject) {
					return o;
				} else {
					return newValue;
				}
			}
		}
	}

	// Get the type of the mask
	obj.getType = getType;

	// Extract the tokens from a mask
	obj.prepare = function(str, o) {
		if (! o) {
			o = {};
		}
		return getTokens.call(o, str);
	}

	/**
	 * Apply the mask to a element (legacy)
	 */
	obj.apply = function(e) {
		var v = Value.call(e.target);
		if (e.key.length == 1) {
			v += e.key;
		}
		Value.call(e.target, obj(v, e.target.getAttribute('data-mask')));
	}

	/**
	 * Legacy support
	 */
	obj.run = function(value, mask, decimal) {
		return obj(value, { mask: mask, decimal: decimal });
	}

	/**
	 * Extract number from masked string
	 */
	obj.extract = function(v, options, returnObject) {
		if (isBlank(v)) {
			return v;
		}
		if (typeof(options) != 'object') {
			return value;
		} else {
			options = Object.assign({}, options);

			if (! options.options) {
				options.options = {};
			}
		}

		// Compatibility
		if (! options.mask && options.format) {
			options.mask = options.format;
		}

		// Remove []
		if (options.mask) {
			if (options.mask.indexOf(';') !== -1) {
				var t = options.mask.split(';');
				options.mask = t[0];
			}
			options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
			options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
			options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
		}

		// Get decimal
		getDecimal.call(options, options.mask);

		var type = null;
		if (options.type == 'percent' || options.options.style == 'percent') {
			type = 'percentage';
		} else if (options.mask) {
			type = getType.call(options, options.mask);
		}

		if (type === 'general') {
			var o = obj(v, options, true);

			value = v;
		} else if (type === 'datetime') {
			if (v instanceof Date) {
				var t = jSuites.calendar.getDateString(value, options.mask);
			}

			var o = obj(v, options, true);

			if (jSuites.isNumeric(v)) {
				value = v;
			} else {
				var value = extractDate.call(o);
			}
		} else {
			var value = Extract.call(options, v);
			// Percentage
			if (type == 'percentage') {
				value /= 100;
			}
			var o = options;
		}

		o.value = value;

		if (! o.type && type) {
			o.type = type;
		}

		if (returnObject) {
			return o;
		} else {
			return value;
		}
	}

	/**
	 * Render
	 */
	obj.render = function(value, options, fullMask) {
		if (isBlank(value)) {
			return value;
		}

		if (typeof(options) != 'object') {
			return value;
		} else {
			options = Object.assign({}, options);

			if (! options.options) {
				options.options = {};
			}
		}

		// Compatibility
		if (! options.mask && options.format) {
			options.mask = options.format;
		}

		// Remove []
		if (options.mask) {
			if (options.mask.indexOf(';') !== -1) {
				var t = options.mask.split(';');
				options.mask = t[0];
			}
			options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
			options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
			options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
		}

		var type = null;
		if (options.type == 'percent' || options.options.style == 'percent') {
			type = 'percentage';
		} else if (options.mask) {
			type = getType.call(options, options.mask);
		} else if (value instanceof Date) {
			type = 'datetime';
		}

		// Fill with blanks
		var fillWithBlanks = false;

		if (type =='datetime' || options.type == 'calendar') {
			var t = jSuites.calendar.getDateString(value, options.mask);
			if (t) {
				value = t;
			}
			if (options.mask && fullMask) {
				fillWithBlanks = true;
			}
		} else {
			// Percentage
			if (type == 'percentage') {
				value *= 100;
			}
			// Number of decimal places
			if (typeof(value) === 'number') {
				var t = null;
				if (options.mask && fullMask && ((''+value).indexOf('e') === -1)) {
					var d = getDecimal.call(options, options.mask);
					if (options.mask.indexOf(d) !== -1) {
						d = options.mask.split(d);
						d = (''+d[1].match(/[0-9]+/g))
						d = d.length;
						t = value.toFixed(d);
					} else {
						t = value.toFixed(0);
					}
				} else if (options.locale && fullMask) {
					// Append zeros
					var d = (''+value).split('.');
					if (options.options) {
						if (typeof(d[1]) === 'undefined') {
							d[1] = '';
						}
						var len = d[1].length;
						if (options.options.minimumFractionDigits > len) {
							for (var i = 0; i < options.options.minimumFractionDigits - len; i++) {
								d[1] += '0';
							}
						}
					}
					if (! d[1].length) {
						t = d[0]
					} else {
						t = d.join('.');
					}
					var len = d[1].length;
					if (options.options && options.options.maximumFractionDigits < len) {
						t = parseFloat(t).toFixed(options.options.maximumFractionDigits);
					}
				} else {
					t = toPlainString(value);
				}

				if (t !== null) {
					value = t;
					// Get decimal
					getDecimal.call(options, options.mask);
					// Replace to the correct decimal
					if (options.options.decimal) {
						value = value.replace('.', options.options.decimal);
					}
				}
			} else {
				if (options.mask && fullMask) {
					fillWithBlanks = true;
				}
			}
		}

		if (fillWithBlanks) {
			var s = options.mask.length - value.length;
			if (s > 0) {
				for (var i = 0; i < s; i++) {
					value += ' ';
				}
			}
		}

		value = obj(value, options);

		// Numeric mask, number of zeros
		if (fullMask && type === 'numeric') {
			var maskZeros = options.mask.match(new RegExp(/^[0]+$/gm));
			if (maskZeros && maskZeros.length === 1) {
				var maskLength = maskZeros[0].length;
				if (maskLength > 3) {
					value = '' + value;
					while (value.length < maskLength) {
						value = '0' + value;
					}
				}
			}
		}

		return value;
	}

	obj.set = function(e, m) {
		if (m) {
			e.setAttribute('data-mask', m);
			// Reset the value
			var event = new Event('input', {
				bubbles: true,
				cancelable: true,
			});
			e.dispatchEvent(event);
		}
	}

	if (typeof document !== 'undefined') {
		document.addEventListener('input', function(e) {
			if (e.target.getAttribute('data-mask') || e.target.mask) {
				obj(e);
			}
		});
	}

	return obj;
})();

jSuites.notification = (function(options) {
	var obj = {};
	obj.options = {};

	// Default configuration
	var defaults = {
		icon: null,
		name: 'Notification',
		date: null,
		error: null,
		title: null,
		message: null,
		timeout: 4000,
		autoHide: true,
		closeable: true,
	};

	// Loop through our object
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	var notification = document.createElement('div');
	notification.className = 'jnotification';

	if (obj.options.error) {
		notification.classList.add('jnotification-error');
	}

	var notificationContainer = document.createElement('div');
	notificationContainer.className = 'jnotification-container';
	notification.appendChild(notificationContainer);

	var notificationHeader = document.createElement('div');
	notificationHeader.className = 'jnotification-header';
	notificationContainer.appendChild(notificationHeader);

	var notificationImage = document.createElement('div');
	notificationImage.className = 'jnotification-image';
	notificationHeader.appendChild(notificationImage);

	if (obj.options.icon) {
		var notificationIcon = document.createElement('img');
		notificationIcon.src = obj.options.icon;
		notificationImage.appendChild(notificationIcon);
	}

	var notificationName = document.createElement('div');
	notificationName.className = 'jnotification-name';
	notificationName.innerHTML = obj.options.name;
	notificationHeader.appendChild(notificationName);

	if (obj.options.closeable == true) {
		var notificationClose = document.createElement('div');
		notificationClose.className = 'jnotification-close';
		notificationClose.onclick = function() {
			obj.hide();
		}
		notificationHeader.appendChild(notificationClose);
	}

	var notificationDate = document.createElement('div');
	notificationDate.className = 'jnotification-date';
	notificationHeader.appendChild(notificationDate);

	var notificationContent = document.createElement('div');
	notificationContent.className = 'jnotification-content';
	notificationContainer.appendChild(notificationContent);

	if (obj.options.title) {
		var notificationTitle = document.createElement('div');
		notificationTitle.className = 'jnotification-title';
		notificationTitle.innerHTML = obj.options.title;
		notificationContent.appendChild(notificationTitle);
	}

	var notificationMessage = document.createElement('div');
	notificationMessage.className = 'jnotification-message';
	notificationMessage.innerHTML = obj.options.message;
	notificationContent.appendChild(notificationMessage);

	obj.show = function() {
		document.body.appendChild(notification);
		if (jSuites.getWindowWidth() > 800) {
			jSuites.animation.fadeIn(notification);
		} else {
			jSuites.animation.slideTop(notification, 1);
		}
	}

	obj.hide = function() {
		if (jSuites.getWindowWidth() > 800) {
			jSuites.animation.fadeOut(notification, function() {
				if (notification.parentNode) {
					notification.parentNode.removeChild(notification);
					if (notificationTimeout) {
						clearTimeout(notificationTimeout);
					}
				}
			});
		} else {
			jSuites.animation.slideTop(notification, 0, function() {
				if (notification.parentNode) {
					notification.parentNode.removeChild(notification);
					if (notificationTimeout) {
						clearTimeout(notificationTimeout);
					}
				}
			});
		}
	};

	obj.show();

	if (obj.options.autoHide == true) {
		var notificationTimeout = setTimeout(function() {
			obj.hide();
		}, obj.options.timeout);
	}

	if (jSuites.getWindowWidth() < 800) {
		notification.addEventListener("swipeup", function(e) {
			obj.hide();
			e.preventDefault();
			e.stopPropagation();
		});
	}

	return obj;
});

jSuites.notification.isVisible = function() {
	var j = document.querySelector('.jnotification');
	return j && j.parentNode ? true : false;
}

// More palettes https://coolors.co/ or https://gka.github.io/palettes/#/10|s|003790,005647,ffffe0|ffffe0,ff005e,93003a|1|1
jSuites.palette = (function() {
	/**
	 * Available palettes
	 */
	var palette = {
		material: [
			[ "#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1" ],
			[ "#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc" ],
			[ "#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5" ],
			[ "#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae" ],
			[ "#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c" ],
			[ "#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b" ],
			[ "#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a" ],
			[ "#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64" ],
			[ "#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f" ],
			[ "#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238" ],
		],
		fire: [
			["0b1a6d","840f38","b60718","de030b","ff0c0c","fd491c","fc7521","faa331","fbb535","ffc73a"],
			["071147","5f0b28","930513","be0309","ef0000","fa3403","fb670b","f9991b","faad1e","ffc123"],
			["03071e","370617","6a040f","9d0208","d00000","dc2f02","e85d04","f48c06","faa307","ffba08"],
			["020619","320615","61040d","8c0207","bc0000","c82a02","d05203","db7f06","e19405","efab00"],
			["020515","2d0513","58040c","7f0206","aa0000","b62602","b94903","c57205","ca8504","d89b00"],
		],
		baby: [
			["eddcd2","fff1e6","fde2e4","fad2e1","c5dedd","dbe7e4","f0efeb","d6e2e9","bcd4e6","99c1de"],
			["e1c4b3","ffd5b5","fab6ba","f5a8c4","aacecd","bfd5cf","dbd9d0","baceda","9dc0db","7eb1d5"],
			["daa990","ffb787","f88e95","f282a9","8fc4c3","a3c8be","cec9b3","9dbcce","82acd2","649dcb"],
			["d69070","ff9c5e","f66770","f05f8f","74bbb9","87bfae","c5b993","83aac3","699bca","4d89c2"],
			["c97d5d","f58443","eb4d57","e54a7b","66a9a7","78ae9c","b5a67e","7599b1","5c88b7","4978aa"],
		],
		chart: [
			['#C1D37F','#4C5454','#FFD275','#66586F','#D05D5B','#C96480','#95BF8F','#6EA240','#0F0F0E','#EB8258','#95A3B3','#995D81'],
		],
	}

	/**
	 * Get a pallete
	 */
	var component = function(o) {
		// Otherwise get palette value
		if (palette[o]) {
			return palette[o];
		} else {
			return palette.material;
		}
	}

	component.get = function(o) {
		// Otherwise get palette value
		if (palette[o]) {
			return palette[o];
		} else {
			return palette;
		}
	}

	component.set = function(o, v) {
		palette[o] = v;
	}

	return component;
})();


jSuites.picker = (function(el, options) {
	// Already created, update options
	if (el.picker) {
		return el.picker.setOptions(options, true);
	}

	// New instance
	var obj = { type: 'picker' };
	obj.options = {};

	var dropdownHeader = null;
	var dropdownContent = null;

	/**
	 * The element passed is a DOM element
	 */
	var isDOM = function(o) {
		return (o instanceof Element || o instanceof HTMLDocument);
	}

	/**
	 * Create the content options
	 */
	var createContent = function() {
		dropdownContent.innerHTML = '';

		// Create items
		var keys = Object.keys(obj.options.data);

		// Go though all options
		for (var i = 0; i < keys.length; i++) {
			// Item
			var dropdownItem = document.createElement('div');
			dropdownItem.classList.add('jpicker-item');
			dropdownItem.k = keys[i];
			dropdownItem.v = obj.options.data[keys[i]];
			// Label
			var item = obj.getLabel(keys[i], dropdownItem);
			if (isDOM(item)) {
				dropdownItem.appendChild(item);
			} else {
				dropdownItem.innerHTML = item;
			}
			// Append
			dropdownContent.appendChild(dropdownItem);
		}
	}

	/**
	 * Set or reset the options for the picker
	 */
	obj.setOptions = function(options, reset) {
		// Default configuration
		var defaults = {
			value: 0,
			data: null,
			render: null,
			onchange: null,
			onmouseover: null,
			onselect: null,
			onopen: null,
			onclose: null,
			onload: null,
			width: null,
			header: true,
			right: false,
			bottom: false,
			content: false,
			columns: null,
			grid: null,
			height: null,
		}

		// Legacy purpose only
		if (options && options.options) {
			options.data = options.options;
		}

		// Loop through the initial configuration
		for (var property in defaults) {
			if (options && options.hasOwnProperty(property)) {
				obj.options[property] = options[property];
			} else {
				if (typeof(obj.options[property]) == 'undefined' || reset === true) {
					obj.options[property] = defaults[property];
				}
			}
		}

		// Start using the options
		if (obj.options.header === false) {
			dropdownHeader.style.display = 'none';
		} else {
			dropdownHeader.style.display = '';
		}

		// Width
		if (obj.options.width) {
			dropdownHeader.style.width = parseInt(obj.options.width) + 'px';
		} else {
			dropdownHeader.style.width = '';
		}

		// Height
		if (obj.options.height) {
			dropdownContent.style.maxHeight = obj.options.height + 'px';
			dropdownContent.style.overflow = 'scroll';
		} else {
			dropdownContent.style.overflow = '';
		}

		if (obj.options.columns > 0) {
			if (! obj.options.grid) {
				dropdownContent.classList.add('jpicker-columns');
				dropdownContent.style.width = obj.options.width ? obj.options.width : 36 * obj.options.columns + 'px';
			} else {
				dropdownContent.classList.add('jpicker-grid');
				dropdownContent.style.gridTemplateColumns = 'repeat(' + obj.options.grid + ', 1fr)';
			}
		}

		if (isNaN(obj.options.value)) {
			obj.options.value = '0';
		}

		// Create list from data
		createContent();

		// Set value
		obj.setValue(obj.options.value);

		// Set options all returns the own instance
		return obj;
	}

	obj.getValue = function() {
		return obj.options.value;
	}

	obj.setValue = function(v) {
		// Set label
		obj.setLabel(v);

		// Update value
		obj.options.value = String(v);

		// Lemonade JS
		if (el.value != obj.options.value) {
			el.value = obj.options.value;
			if (typeof(el.oninput) == 'function') {
				el.oninput({
					type: 'input',
					target: el,
					value: el.value
				});
			}
		}

		if (dropdownContent.children[v].getAttribute('type') !== 'generic') {
			obj.close();
		}
	}

	obj.getLabel = function(v, item) {
		var label = obj.options.data[v] || null;
		if (typeof(obj.options.render) == 'function') {
			label = obj.options.render(label, item);
		}
		return label;
	}

	obj.setLabel = function(v) {
		var item;

		if (obj.options.content) {
			item = '<i class="material-icons">' + obj.options.content + '</i>';
		} else {
			item = obj.getLabel(v, null);
		}
		// Label
		if (isDOM(item)) {
			dropdownHeader.innerHTML = '';
			dropdownHeader.appendChild(item);
		} else {
			dropdownHeader.innerHTML = item;
		}
	}

	obj.open = function() {
		if (! el.classList.contains('jpicker-focus')) {
			// Start tracking the element
			jSuites.tracking(obj, true);

			// Open picker
			el.classList.add('jpicker-focus');
			el.focus();

			var top = 0;
			var left = 0;

			dropdownContent.style.marginLeft = '';

			var rectHeader = dropdownHeader.getBoundingClientRect();
			var rectContent = dropdownContent.getBoundingClientRect();

			if (window.innerHeight < rectHeader.bottom + rectContent.height || obj.options.bottom) {
				top = -1 * (rectContent.height + 4);
			} else {
				top = rectHeader.height + 4;
			}

			if (obj.options.right === true) {
				left = -1 * rectContent.width + rectHeader.width;
			}

			if (rectContent.left + left < 0) {
				left = left + rectContent.left + 10;
			}
			if (rectContent.left + rectContent.width > window.innerWidth) {
				left = -1 * (10 + rectContent.left + rectContent.width - window.innerWidth);
			}

			dropdownContent.style.marginTop = parseInt(top) + 'px';
			dropdownContent.style.marginLeft = parseInt(left) + 'px';

			//dropdownContent.style.marginTop
			if (typeof obj.options.onopen == 'function') {
				obj.options.onopen(el, obj);
			}
		}
	}

	obj.close = function() {
		if (el.classList.contains('jpicker-focus')) {
			el.classList.remove('jpicker-focus');

			// Start tracking the element
			jSuites.tracking(obj, false);

			if (typeof obj.options.onclose == 'function') {
				obj.options.onclose(el, obj);
			}
		}
	}

	/**
	 * Create floating picker
	 */
	var init = function() {
		// Class
		el.classList.add('jpicker');
		el.setAttribute('tabindex', '900');
		el.onmousedown = function(e) {
			if (! el.classList.contains('jpicker-focus')) {
				obj.open();
			}
		}

		// Dropdown Header
		dropdownHeader = document.createElement('div');
		dropdownHeader.classList.add('jpicker-header');

		// Dropdown content
		dropdownContent = document.createElement('div');
		dropdownContent.classList.add('jpicker-content');
		dropdownContent.onclick = function(e) {
			var item = jSuites.findElement(e.target, 'jpicker-item');
			if (item) {
				if (item.parentNode === dropdownContent) {
					// Update label
					obj.setValue(item.k);
					// Call method
					if (typeof(obj.options.onchange) == 'function') {
						obj.options.onchange.call(obj, el, obj, item.v, item.v, item.k, e);
					}
				}
			}
		}

		// Append content and header
		el.appendChild(dropdownHeader);
		el.appendChild(dropdownContent);

		// Default value
		el.value = options.value || 0;

		// Set options
		obj.setOptions(options);

		if (typeof(obj.options.onload) == 'function') {
			obj.options.onload(el, obj);
		}

		// Change
		el.change = obj.setValue;

		// Global generic value handler
		el.val = function(val) {
			if (val === undefined) {
				return obj.getValue();
			} else {
				obj.setValue(val);
			}
		}

		// Reference
		el.picker = obj;
	}

	init();

	return obj;
});

jSuites.rating = (function(el, options) {
	// Already created, update options
	if (el.rating) {
		return el.rating.setOptions(options, true);
	}

	// New instance
	var obj = {};
	obj.options = {};

	obj.setOptions = function(options, reset) {
		// Default configuration
		var defaults = {
			number: 5,
			value: 0,
			tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ],
			onchange: null,
		};

		// Loop through the initial configuration
		for (var property in defaults) {
			if (options && options.hasOwnProperty(property)) {
				obj.options[property] = options[property];
			} else {
				if (typeof(obj.options[property]) == 'undefined' || reset === true) {
					obj.options[property] = defaults[property];
				}
			}
		}

		// Make sure the container is empty
		el.innerHTML = '';

		// Add elements
		for (var i = 0; i < obj.options.number; i++) {
			var div = document.createElement('div');
			div.setAttribute('data-index', (i + 1))
			div.setAttribute('title', obj.options.tooltip[i])
			el.appendChild(div);
		}

		// Selected option
		if (obj.options.value) {
			for (var i = 0; i < obj.options.number; i++) {
				if (i < obj.options.value) {
					el.children[i].classList.add('jrating-selected');
				}
			}
		}

		return obj;
	}

	// Set value
	obj.setValue = function(index) {
		for (var i = 0; i < obj.options.number; i++) {
			if (i < index) {
				el.children[i].classList.add('jrating-selected');
			} else {
				el.children[i].classList.remove('jrating-over');
				el.children[i].classList.remove('jrating-selected');
			}
		}

		obj.options.value = index;

		if (typeof(obj.options.onchange) == 'function') {
			obj.options.onchange(el, index);
		}

		// Lemonade JS
		if (el.value != obj.options.value) {
			el.value = obj.options.value;
			if (typeof(el.oninput) == 'function') {
				el.oninput({
					type: 'input',
					target: el,
					value: el.value
				});
			}
		}
	}

	obj.getValue = function() {
		return obj.options.value;
	}

	var init = function() {
		// Start plugin
		obj.setOptions(options);

		// Class
		el.classList.add('jrating');

		// Events
		el.addEventListener("click", function(e) {
			var index = e.target.getAttribute('data-index');
			if (index != undefined) {
				if (index == obj.options.value) {
					obj.setValue(0);
				} else {
					obj.setValue(index);
				}
			}
		});

		el.addEventListener("mouseover", function(e) {
			var index = e.target.getAttribute('data-index');
			for (var i = 0; i < obj.options.number; i++) {
				if (i < index) {
					el.children[i].classList.add('jrating-over');
				} else {
					el.children[i].classList.remove('jrating-over');
				}
			}
		});

		el.addEventListener("mouseout", function(e) {
			for (var i = 0; i < obj.options.number; i++) {
				el.children[i].classList.remove('jrating-over');
			}
		});

		// Change
		el.change = obj.setValue;

		// Global generic value handler
		el.val = function(val) {
			if (val === undefined) {
				return obj.getValue();
			} else {
				obj.setValue(val);
			}
		}

		// Reference
		el.rating = obj;
	}

	init();

	return obj;
});


jSuites.sorting = (function(el, options) {
	var obj = {};
	obj.options = {};

	var defaults = {
		pointer: null,
		direction: null,
		ondragstart: null,
		ondragend: null,
		ondrop: null,
	}

	var dragElement = null;

	// Loop through the initial configuration
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	el.classList.add('jsorting');

	el.addEventListener('dragstart', function(e) {
		var position = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
		dragElement = {
			element: e.target,
			o: position,
			d: position
		}
		e.target.style.opacity = '0.25';

		if (typeof(obj.options.ondragstart) == 'function') {
			obj.options.ondragstart(el, e.target, e);
		}
	});

	el.addEventListener('dragover', function(e) {
		e.preventDefault();

		if (getElement(e.target) && dragElement) {
			if (e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
				if (! obj.options.direction) {
					var condition = e.target.clientHeight / 2 > e.offsetY;
				} else {
					var condition = e.target.clientWidth / 2 > e.offsetX;
				}

				if (condition) {
					e.target.parentNode.insertBefore(dragElement.element, e.target);
				} else {
					e.target.parentNode.insertBefore(dragElement.element, e.target.nextSibling);
				}

				dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
			}
		}
	});

	el.addEventListener('dragleave', function(e) {
		e.preventDefault();
	});

	el.addEventListener('dragend', function(e) {
		e.preventDefault();

		if (dragElement) {
			if (typeof(obj.options.ondragend) == 'function') {
				obj.options.ondragend(el, dragElement.element, e);
			}

			// Cancelled put element to the original position
			if (dragElement.o < dragElement.d) {
				e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o]);
			} else {
				e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o].nextSibling);
			}

			dragElement.element.style.opacity = '';
			dragElement = null;
		}
	});

	el.addEventListener('drop', function(e) {
		e.preventDefault();

		if (dragElement && (dragElement.o != dragElement.d)) {
			if (typeof(obj.options.ondrop) == 'function') {
				obj.options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
			}
		}

		dragElement.element.style.opacity = '';
		dragElement = null;
	});

	var getElement = function(element) {
		var sorting = false;

		function path (element) {
			if (element.className) {
				if (element.classList.contains('jsorting')) {
					sorting = true;
				}
			}

			if (! sorting) {
				path(element.parentNode);
			}
		}

		path(element);

		return sorting;
	}

	for (var i = 0; i < el.children.length; i++) {
		if (! el.children[i].hasAttribute('draggable')) {
			el.children[i].setAttribute('draggable', 'true');
		}
	}

	el.val = function() {
		var id = null;
		var data = [];
		for (var i = 0; i < el.children.length; i++) {
			if (id = el.children[i].getAttribute('data-id')) {
				data.push(id);
			}
		}
		return data;
	}

	return el;
});

jSuites.tabs = (function(el, options) {
	var obj = {};
	obj.options = {};

	// Default configuration
	var defaults = {
		data: [],
		position: null,
		allowCreate: false,
		allowChangePosition: false,
		onclick: null,
		onload: null,
		onchange: null,
		oncreate: null,
		ondelete: null,
		onbeforecreate: null,
		onchangeposition: null,
		animation: false,
		hideHeaders: false,
		padding: null,
		palette: null,
		maxWidth: null,
	}

	// Loop through the initial configuration
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	// Class
	el.classList.add('jtabs');

	var prev = null;
	var next = null;
	var border = null;

	// Helpers
	var setBorder = function(index) {
		if (obj.options.animation) {
			setTimeout(function() {
				var rect = obj.headers.children[index].getBoundingClientRect();

				if (obj.options.palette == 'modern') {
					border.style.width = rect.width - 4 + 'px';
					border.style.left = obj.headers.children[index].offsetLeft + 2 + 'px';
				} else {
					border.style.width = rect.width + 'px';
					border.style.left = obj.headers.children[index].offsetLeft + 'px';
				}

				if (obj.options.position == 'bottom') {
					border.style.top = '0px';
				} else {
					border.style.bottom = '0px';
				}
			}, 150);
		}
	}

	var updateControls = function(x) {
		if (typeof(obj.headers.scrollTo) == 'function') {
			obj.headers.scrollTo({
				left: x,
				behavior: 'smooth',
			});
		} else {
			obj.headers.scrollLeft = x;
		}

		if (x <= 1) {
			prev.classList.add('disabled');
		} else {
			prev.classList.remove('disabled');
		}

		if (x >= obj.headers.scrollWidth - obj.headers.offsetWidth) {
			next.classList.add('disabled');
		} else {
			next.classList.remove('disabled');
		}

		if (obj.headers.scrollWidth <= obj.headers.offsetWidth) {
			prev.style.display = 'none';
			next.style.display = 'none';
		} else {
			prev.style.display = '';
			next.style.display = '';
		}
	}

	obj.setBorder = setBorder;

	// Set value
	obj.open = function(index) {
		var previous = null;
		for (var i = 0; i < obj.headers.children.length; i++) {
			if (obj.headers.children[i].classList.contains('jtabs-selected')) {
				// Current one
				previous = i;
			}
			// Remote selected
			obj.headers.children[i].classList.remove('jtabs-selected');
			if (obj.content.children[i]) {
				obj.content.children[i].classList.remove('jtabs-selected');
			}
		}

		obj.headers.children[index].classList.add('jtabs-selected');
		if (obj.content.children[index]) {
			obj.content.children[index].classList.add('jtabs-selected');
		}

		if (previous != index && typeof(obj.options.onchange) == 'function') {
			if (obj.content.children[index]) {
				obj.options.onchange(el, obj, index, obj.headers.children[index], obj.content.children[index]);
			}
		}

		// Hide
		if (obj.options.hideHeaders == true && (obj.headers.children.length < 3 && obj.options.allowCreate == false)) {
			obj.headers.parentNode.style.display = 'none';
		} else {
			// Set border
			setBorder(index);

			obj.headers.parentNode.style.display = '';

			var x1 = obj.headers.children[index].offsetLeft;
			var x2 = x1 + obj.headers.children[index].offsetWidth;
			var r1 = obj.headers.scrollLeft;
			var r2 = r1 + obj.headers.offsetWidth;

			if (! (r1 <= x1 && r2 >= x2)) {
				// Out of the viewport
				updateControls(x1 - 1);
			}
		}
	}

	obj.selectIndex = function(a) {
		var index = Array.prototype.indexOf.call(obj.headers.children, a);
		if (index >= 0) {
			obj.open(index);
		}

		return index;
	}

	obj.rename = function(i, title) {
		if (! title) {
			title = prompt('New title', obj.headers.children[i].innerText);
		}
		obj.headers.children[i].innerText = title;
		obj.open(i);
	}

	obj.create = function(title, url) {
		if (typeof(obj.options.onbeforecreate) == 'function') {
			var ret = obj.options.onbeforecreate(el);
			if (ret === false) {
				return false;
			} else {
				title = ret;
			}
		}

		var div = obj.appendElement(title);

		if (typeof(obj.options.oncreate) == 'function') {
			obj.options.oncreate(el, div)
		}

		setBorder();

		return div;
	}

	obj.remove = function(index) {
		return obj.deleteElement(index);
	}

	obj.nextNumber = function() {
		var num = 0;
		for (var i = 0; i < obj.headers.children.length; i++) {
			var tmp = obj.headers.children[i].innerText.match(/[0-9].*/);
			if (tmp > num) {
				num = parseInt(tmp);
			}
		}
		if (! num) {
			num = 1;
		} else {
			num++;
		}

		return num;
	}

	obj.deleteElement = function(index) {
		if (! obj.headers.children[index]) {
			return false;
		} else {
			obj.headers.removeChild(obj.headers.children[index]);
			obj.content.removeChild(obj.content.children[index]);
		}

		obj.open(0);

		if (typeof(obj.options.ondelete) == 'function') {
			obj.options.ondelete(el, index)
		}
	}

	obj.appendElement = function(title, cb) {
		if (! title) {
			var title = prompt('Title?', '');
		}

		if (title) {
			// Add content
			var div = document.createElement('div');
			obj.content.appendChild(div);

			// Add headers
			var h = document.createElement('div');
			h.innerHTML = title;
			h.content = div;
			obj.headers.insertBefore(h, obj.headers.lastChild);

			// Sortable
			if (obj.options.allowChangePosition) {
				h.setAttribute('draggable', 'true');
			}
			// Open new tab
			obj.selectIndex(h);

			// Callback
			if (typeof(cb) == 'function') {
				cb(div, h);
			}

			// Return element
			return div;
		}
	}

	obj.getActive = function() {
		for (var i = 0; i < obj.headers.children.length; i++) {
			if (obj.headers.children[i].classList.contains('jtabs-selected')) {
				return i
			}
		}
		return 0;
	}

	obj.updateContent = function(position, newContent) {
		if (typeof newContent !== 'string') {
			var contentItem = newContent;
		} else {
			var contentItem = document.createElement('div');
			contentItem.innerHTML = newContent;
		}

		if (obj.content.children[position].classList.contains('jtabs-selected')) {
			newContent.classList.add('jtabs-selected');
		}

		obj.content.replaceChild(newContent, obj.content.children[position]);

		setBorder();
	}

	obj.updatePosition = function(f, t) {
		// Ondrop update position of content
		if (f > t) {
			obj.content.insertBefore(obj.content.children[f], obj.content.children[t]);
		} else {
			obj.content.insertBefore(obj.content.children[f], obj.content.children[t].nextSibling);
		}

		// Open destination tab
		obj.open(t);

		// Call event
		if (typeof(obj.options.onchangeposition) == 'function') {
			obj.options.onchangeposition(obj.headers, f, t);
		}
	}

	obj.move = function(f, t) {
		if (f > t) {
			obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t]);
		} else {
			obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t].nextSibling);
		}

		obj.updatePosition(f, t);
	}

	obj.setBorder = setBorder;

	obj.init = function() {
		el.innerHTML = '';

		// Make sure the component is blank
		obj.headers = document.createElement('div');
		obj.content = document.createElement('div');
		obj.headers.classList.add('jtabs-headers');
		obj.content.classList.add('jtabs-content');

		if (obj.options.palette) {
			el.classList.add('jtabs-modern');
		} else {
			el.classList.remove('jtabs-modern');
		}

		// Padding
		if (obj.options.padding) {
			obj.content.style.padding = parseInt(obj.options.padding) + 'px';
		}

		// Header
		var header = document.createElement('div');
		header.className = 'jtabs-headers-container';
		header.appendChild(obj.headers);
		if (obj.options.maxWidth) {
			header.style.maxWidth = parseInt(obj.options.maxWidth) + 'px';
		}

		// Controls
		var controls = document.createElement('div');
		controls.className = 'jtabs-controls';
		controls.setAttribute('draggable', 'false');
		header.appendChild(controls);

		// Append DOM elements
		if (obj.options.position == 'bottom') {
			el.appendChild(obj.content);
			el.appendChild(header);
		} else {
			el.appendChild(header);
			el.appendChild(obj.content);
		}

		// New button
		if (obj.options.allowCreate == true) {
			var add = document.createElement('div');
			add.className = 'jtabs-add';
			add.onclick = function() {
				obj.create();
			}
			controls.appendChild(add);
		}

		prev = document.createElement('div');
		prev.className = 'jtabs-prev';
		prev.onclick = function() {
			updateControls(obj.headers.scrollLeft - obj.headers.offsetWidth);
		}
		controls.appendChild(prev);

		next = document.createElement('div');
		next.className = 'jtabs-next';
		next.onclick = function() {
			updateControls(obj.headers.scrollLeft + obj.headers.offsetWidth);
		}
		controls.appendChild(next);

		// Data
		for (var i = 0; i < obj.options.data.length; i++) {
			// Title
			if (obj.options.data[i].titleElement) {
				var headerItem = obj.options.data[i].titleElement;
			} else {
				var headerItem = document.createElement('div');
			}
			// Icon
			if (obj.options.data[i].icon) {
				var iconContainer = document.createElement('div');
				var icon = document.createElement('i');
				icon.classList.add('material-icons');
				icon.innerHTML = obj.options.data[i].icon;
				iconContainer.appendChild(icon);
				headerItem.appendChild(iconContainer);
			}
			// Title
			if (obj.options.data[i].title) {
				var title = document.createTextNode(obj.options.data[i].title);
				headerItem.appendChild(title);
			}
			// Width
			if (obj.options.data[i].width) {
				headerItem.style.width = obj.options.data[i].width;
			}
			// Content
			if (obj.options.data[i].contentElement) {
				var contentItem = obj.options.data[i].contentElement;
			} else {
				var contentItem = document.createElement('div');
				contentItem.innerHTML = obj.options.data[i].content;
			}
			obj.headers.appendChild(headerItem);
			obj.content.appendChild(contentItem);
		}

		// Animation
		border = document.createElement('div');
		border.className = 'jtabs-border';
		obj.headers.appendChild(border);

		if (obj.options.animation) {
			el.classList.add('jtabs-animation');
		}

		// Events
		obj.headers.addEventListener("click", function(e) {
			if (e.target.parentNode.classList.contains('jtabs-headers')) {
				var target = e.target;
			} else {
				if (e.target.tagName == 'I') {
					var target = e.target.parentNode.parentNode;
				} else {
					var target = e.target.parentNode;
				}
			}

			var index = obj.selectIndex(target);

			if (typeof(obj.options.onclick) == 'function') {
				obj.options.onclick(el, obj, index, obj.headers.children[index], obj.content.children[index]);
			}
		});

		obj.headers.addEventListener("contextmenu", function(e) {
			obj.selectIndex(e.target);
		});

		if (obj.headers.children.length) {
			// Open first tab
			obj.open(0);
		}

		// Update controls
		updateControls(0);

		if (obj.options.allowChangePosition == true) {
			jSuites.sorting(obj.headers, {
				direction: 1,
				ondrop: function(a,b,c) {
					obj.updatePosition(b,c);
				},
			});
		}

		if (typeof(obj.options.onload) == 'function') {
			obj.options.onload(el, obj);
		}
	}

	// Loading existing nodes as the data
	if (el.children[0] && el.children[0].children.length) {
		// Create from existing elements
		for (var i = 0; i < el.children[0].children.length; i++) {
			var item = obj.options.data && obj.options.data[i] ? obj.options.data[i] : {};

			if (el.children[1] && el.children[1].children[i]) {
				item.titleElement = el.children[0].children[i];
				item.contentElement = el.children[1].children[i];
			} else {
				item.contentElement = el.children[0].children[i];
			}

			obj.options.data[i] = item;
		}
	}

	// Remote controller flag
	var loadingRemoteData = false;

	// Create from data
	if (obj.options.data) {
		// Append children
		for (var i = 0; i < obj.options.data.length; i++) {
			if (obj.options.data[i].url) {
				jSuites.ajax({
					url: obj.options.data[i].url,
					type: 'GET',
					dataType: 'text/html',
					index: i,
					success: function(result) {
						obj.options.data[this.index].content = result;
					},
					complete: function() {
						obj.init();
					}
				});

				// Flag loading
				loadingRemoteData = true;
			}
		}
	}

	if (! loadingRemoteData) {
		obj.init();
	}

	el.tabs = obj;

	return obj;
});

jSuites.toolbar = (function(el, options) {
	// New instance
	var obj = { type:'toolbar' };
	obj.options = {};

	// Default configuration
	var defaults = {
		app: null,
		container: false,
		badge: false,
		title: false,
		responsive: false,
		maxWidth: null,
		bottom: true,
		items: [],
	}

	// Loop through our object
	for (var property in defaults) {
		if (options && options.hasOwnProperty(property)) {
			obj.options[property] = options[property];
		} else {
			obj.options[property] = defaults[property];
		}
	}

	if (! el && options.app && options.app.el) {
		el = document.createElement('div');
		options.app.el.appendChild(el);
	}

	// Arrow
	var toolbarArrow = document.createElement('div');
	toolbarArrow.classList.add('jtoolbar-item');
	toolbarArrow.classList.add('jtoolbar-arrow');

	var toolbarFloating = document.createElement('div');
	toolbarFloating.classList.add('jtoolbar-floating');
	toolbarArrow.appendChild(toolbarFloating);

	obj.selectItem = function(element) {
		var elements = toolbarContent.children;
		for (var i = 0; i < elements.length; i++) {
			if (element != elements[i]) {
				elements[i].classList.remove('jtoolbar-selected');
			}
		}
		element.classList.add('jtoolbar-selected');
	}

	obj.hide = function() {
		jSuites.animation.slideBottom(el, 0, function() {
			el.style.display = 'none';
		});
	}

	obj.show = function() {
		el.style.display = '';
		jSuites.animation.slideBottom(el, 1);
	}

	obj.get = function() {
		return el;
	}

	obj.setBadge = function(index, value) {
		toolbarContent.children[index].children[1].firstChild.innerHTML = value;
	}

	obj.destroy = function() {
		toolbar.remove();
		el.innerHTML = '';
	}

	obj.update = function(a, b) {
		for (var i = 0; i < toolbarContent.children.length; i++) {
			// Toolbar element
			var toolbarItem = toolbarContent.children[i];
			// State management
			if (typeof(toolbarItem.updateState) == 'function') {
				toolbarItem.updateState(el, obj, toolbarItem, a, b);
			}
		}
		for (var i = 0; i < toolbarFloating.children.length; i++) {
			// Toolbar element
			var toolbarItem = toolbarFloating.children[i];
			// State management
			if (typeof(toolbarItem.updateState) == 'function') {
				toolbarItem.updateState(el, obj, toolbarItem, a, b);
			}
		}
	}

	obj.create = function(items) {
		// Reset anything in the toolbar
		toolbarContent.innerHTML = '';
		// Create elements in the toolbar
		for (var i = 0; i < items.length; i++) {
			var toolbarItem = document.createElement('div');
			toolbarItem.classList.add('jtoolbar-item');

			if (items[i].width) {
				toolbarItem.style.width = parseInt(items[i].width) + 'px';
			}

			if (items[i].k) {
				toolbarItem.k = items[i].k;
			}

			if (items[i].tooltip) {
				toolbarItem.setAttribute('title', items[i].tooltip);
			}

			// Id
			if (items[i].id) {
				toolbarItem.setAttribute('id', items[i].id);
			}

			// Selected
			if (items[i].updateState) {
				toolbarItem.updateState = items[i].updateState;
			}

			if (items[i].active) {
				toolbarItem.classList.add('jtoolbar-active');
			}

			if (items[i].disabled) {
				toolbarItem.classList.add('jtoolbar-disabled');
			}

			if (items[i].type == 'select' || items[i].type == 'dropdown') {
				jSuites.picker(toolbarItem, items[i]);
			} else if (items[i].type == 'divisor') {
				toolbarItem.classList.add('jtoolbar-divisor');
			} else if (items[i].type == 'label') {
				toolbarItem.classList.add('jtoolbar-label');
				toolbarItem.innerHTML = items[i].content;
			} else {
				// Material icons
				var toolbarIcon = document.createElement('i');
				if (typeof(items[i].class) === 'undefined') {
					toolbarIcon.classList.add('material-icons');
				} else {
					var c = items[i].class.split(' ');
					for (var j = 0; j < c.length; j++) {
						toolbarIcon.classList.add(c[j]);
					}
				}
				toolbarIcon.innerHTML = items[i].content ? items[i].content : '';
				toolbarItem.appendChild(toolbarIcon);

				// Badge options
				if (obj.options.badge == true) {
					var toolbarBadge = document.createElement('div');
					toolbarBadge.classList.add('jbadge');
					var toolbarBadgeContent = document.createElement('div');
					toolbarBadgeContent.innerHTML = items[i].badge ? items[i].badge : '';
					toolbarBadge.appendChild(toolbarBadgeContent);
					toolbarItem.appendChild(toolbarBadge);
				}

				// Title
				if (items[i].title) {
					if (obj.options.title == true) {
						var toolbarTitle = document.createElement('span');
						toolbarTitle.innerHTML = items[i].title;
						toolbarItem.appendChild(toolbarTitle);
					} else {
						toolbarItem.setAttribute('title', items[i].title);
					}
				}

				if (obj.options.app && items[i].route) {
					// Route
					toolbarItem.route = items[i].route;
					// Onclick for route
					toolbarItem.onclick = function() {
						obj.options.app.pages(this.route);
					}
					// Create pages
					obj.options.app.pages(items[i].route, {
						toolbarItem: toolbarItem,
						closed: true
					});
				}
			}

			if (items[i].onclick) {
				toolbarItem.onclick = items[i].onclick.bind(items[i], el, obj, toolbarItem);
			}

			toolbarContent.appendChild(toolbarItem);
		}

		// Fits to the page
		setTimeout(function() {
			obj.refresh();
		}, 0);
	}

	obj.open = function() {
		toolbarArrow.classList.add('jtoolbar-arrow-selected');

		var rectElement = el.getBoundingClientRect();
		var rect = toolbarFloating.getBoundingClientRect();
		if (rect.bottom > window.innerHeight || obj.options.bottom) {
			toolbarFloating.style.bottom = '0';
		} else {
			toolbarFloating.style.removeProperty('bottom');
		}

		toolbarFloating.style.right = '0';

		toolbarArrow.children[0].focus();
		// Start tracking
		jSuites.tracking(obj, true);
	}

	obj.close = function() {
		toolbarArrow.classList.remove('jtoolbar-arrow-selected')
		// End tracking
		jSuites.tracking(obj, false);
	}

	obj.refresh = function() {
		if (obj.options.responsive == true) {
			// Width of the c
			var rect = el.parentNode.getBoundingClientRect();
			if (! obj.options.maxWidth) {
				obj.options.maxWidth = rect.width;
			}
			// Available parent space
			var available = parseInt(obj.options.maxWidth);
			// Remove arrow
			if (toolbarArrow.parentNode) {
				toolbarArrow.parentNode.removeChild(toolbarArrow);
			}
			// Move all items to the toolbar
			while (toolbarFloating.firstChild) {
				toolbarContent.appendChild(toolbarFloating.firstChild);
			}
			// Toolbar is larger than the parent, move elements to the floating element
			if (available < toolbarContent.offsetWidth) {
				// Give space to the floating element
				available -= 50;
				// Move to the floating option
				while (toolbarContent.lastChild && available < toolbarContent.offsetWidth) {
					toolbarFloating.insertBefore(toolbarContent.lastChild, toolbarFloating.firstChild);
				}
			}
			// Show arrow
			if (toolbarFloating.children.length > 0) {
				toolbarContent.appendChild(toolbarArrow);
			}
		}
	}

	obj.setReadonly = function(state) {
		state = state ? 'add' : 'remove';
		el.classList[state]('jtoolbar-disabled');
	}

	el.onclick = function(e) {
		var element = jSuites.findElement(e.target, 'jtoolbar-item');
		if (element) {
			obj.selectItem(element);
		}

		if (e.target.classList.contains('jtoolbar-arrow')) {
			obj.open();
		}
	}

	window.addEventListener('resize', function() {
		obj.refresh();
	});

	// Toolbar
	el.classList.add('jtoolbar');
	// Reset content
	el.innerHTML = '';
	// Container
	if (obj.options.container == true) {
		el.classList.add('jtoolbar-container');
	}
	// Content
	var toolbarContent = document.createElement('div');
	el.appendChild(toolbarContent);
	// Special toolbar for mobile applications
	if (obj.options.app) {
		el.classList.add('jtoolbar-mobile');
	}
	// Create toolbar
	obj.create(obj.options.items);
	// Shortcut
	el.toolbar = obj;

	return obj;
});



	return jSuites;

})));