COMPONENT('exec', function(self, config) {	self.readonly();	self.blind();	self.make = function() {		self.event('click', config.selector || '.exec', function(e) {			var el = $(this);			var attr = el.attrd('exec');			var path = el.attrd('path');			var href = el.attrd('href');			if (el.attrd('prevent') === 'true') {				e.preventDefault();				e.stopPropagation();			}			attr && EXEC(attr, el, e);			href && NAV.redirect(href);			if (path) {				var val = el.attrd('value');				if (val == null) {					var a = new Function('return ' + el.attrd('value-a'))();					var b = new Function('return ' + el.attrd('value-b'))();					var v = GET(path);					if (v === a)						SET(path, b, true);					else						SET(path, a, true);				} else					SET(path, new Function('return ' + val)(), true);			}		});	};});COMPONENT('loading', function(self) {	var pointer;	self.readonly();	self.singleton();	self.nocompile();	self.make = function() {		self.aclass('ui-loading');		self.append('<div></div>');	};	self.show = function() {		clearTimeout(pointer);		self.rclass('hidden');		return self;	};	self.hide = function(timeout) {		clearTimeout(pointer);		pointer = setTimeout(function() {			self.aclass('hidden');		}, timeout || 1);		return self;	};});COMPONENT('grid', 'filter:true;external:false;fillcount:50;filterlabel:Filtering values ...;boolean:true|on|yes;pluralizepages:# pages,# page,# pages,# pages;pluralizeitems:# items,# item,# items,# items;pagination:false;rowheight:32', function(self, config) {	var tbody, thead, tbodyhead, container, pagination;	var options = { columns: {}, items: [], indexer: 0, filter: {} };	var isFilter = false;	var ppages, pitems, cache, eheight, wheight, scroll, filtercache, filled = false;	self.template = Tangular.compile('<td data-index="{{ index }}"{{ if $.cls }} class="{{ $.cls }}"{{ fi }}><div class="wrap{{ if align }} {{ align }}{{ fi }}"{{ if background }} style="background-color:{{ background }}"{{ fi }}>{{ value | raw }}</div></td>');	self.options = options;	self.readonly();	self.nocompile();	self.make = function() {		var meta = self.find('script').html();		self.aclass('ui-grid-container' + (config.autosize ? '' : ' hidden'));		self.html('<div class="ui-grid"><table class="ui-grid-header"><thead></thead></table><div class="ui-grid-scroller"><table class="ui-grid-data"><thead></thead><tbody></tbody></table></div></div>' + (config.pagination ? '<div class="ui-grid-footer hidden"><div class="ui-grid-meta"></div><div class="ui-grid-pagination"><button class="ui-grid-button" name="first"><i class="fa fa-angle-double-left"></i></button><button class="ui-grid-button" name="prev"><i class="fa fa-angle-left"></i></button><div class="page"><input type="text" maxlength="5" class="ui-grid-input" /></div><button class="ui-grid-button" name="next"><i class="fa fa-angle-right"></i></button><button class="ui-grid-button" name="last"><i class="fa fa-angle-double-right"></i></button></div><div class="ui-grid-pages"></div></div></div>' : ''));		var body = self.find('.ui-grid-data');		tbody = $(body.find('tbody')[0]);		tbodyhead = $(body.find('thead')[0]);		thead = $(self.find('.ui-grid-header').find('thead')[0]);		container = $(self.find('.ui-grid-scroller')[0]);		if (config.pagination) {			var el = self.find('.ui-grid-footer');			pagination = {};			pagination.main = el;			pagination.page = el.find('input');			pagination.first = el.find('button[name="first"]');			pagination.last = el.find('button[name="last"]');			pagination.prev = el.find('button[name="prev"]');			pagination.next = el.find('button[name="next"]');			pagination.meta = el.find('.ui-grid-meta');			pagination.pages = el.find('.ui-grid-pages');		}		meta && self.meta(meta);		self.event('click', '.ui-grid-columnsort', function() {			var obj = {};			obj.columns = options.columns;			obj.column = options.columns[+$(this).attrd('index')];			self.sort(obj);		});		self.event('change', '.ui-grid-filter', function() {			var el = $(this).parent();			if (this.value)				options.filter[this.name] = this.value;			else				delete options.filter[this.name];			el.tclass('ui-grid-selected', !!this.value);			scroll = true;			self.filter();		});		self.event('change', 'input', function() {			var el = this;			if (el.type === 'checkbox') {				el && !el.value && self.checked(el.checked);				config.checked && EXEC(config.checked, el, self);			}		});		self.event('click', '.ui-grid-button', function() {			switch (this.name) {				case 'first':					scroll = true;					cache.page = 1;					self.operation('pagination');					break;				case 'last':					scroll = true;					cache.page = cache.pages;					self.operation('pagination');					break;				case 'prev':					scroll = true;					cache.page -= 1;					self.operation('pagination');					break;				case 'next':					scroll = true;					cache.page += 1;					self.operation('pagination');					break;			}		});		self.event('change', '.ui-grid-input', function() {			var page = (+this.value) >> 0;			if (isNaN(page) || page < 0 || page > cache.pages || page === cache.page)				return;			scroll = true;			cache.page = page;			self.operation('pagination');		});		tbody.on('click', 'button', function() {			var btn = $(this);			var tr = btn.closest('tr');			config.button && EXEC(config.button, btn, options.items[+tr.attrd('index')], self);		});		var ALLOWED = { INPUT: 1, SELECT: 1 };		tbody.on('click', '.ui-grid-row', function(e) {			!ALLOWED[e.target.nodeName] && config.click && EXEC(config.click, options.items[+$(this).attrd('index')], self);		});		self.on('resize', self.resize);		config.init && EXEC(config.init);		wheight = WH;	};	self.checked = function(value) {		if (typeof(value) === 'boolean')			self.find('input[type="checkbox"]').prop('checked', value);		else			return tbody.find('input:checked');	};	self.meta = function(html) {		switch (typeof(html)) {			case 'string':				options.columns = new Function('return ' + html.trim())();				break;			case 'function':				options.columns = html(self);				break;			case 'object':				options.columns = html;				break;		}		options.columns = options.columns.remove(function(column) {			return !!(column.remove && FN(column.remove)());		});		options.customsearch = false;		for (var i = 0; i < options.columns.length; i++) {			var column = options.columns[i];			if (typeof(column.header) === 'string')				column.header = column.header.indexOf('{{') === -1 ? new Function('return \'' + column.header + '\'') : Tangular.compile(column.header);			if (typeof(column.template) === 'string')				column.template = column.template.indexOf('{{') === -1 ? new Function('a', 'b', 'return \'' + column.template + '\'') : Tangular.compile(column.template);			if (column.search) {				options.customsearch = true;				column.search = column.search === true ? column.template : Tangular.compile(column.search);			}		}		self.rebuild(true);	};	self.configure = function(key, value) {		switch (key) {			case 'pluralizepages':				ppages = value.split(',').trim();				break;			case 'pluralizeitems':				pitems = value.split(',').trim();				break;		}	};	self.cls = function(d) {		var a = [];		for (var i = 1; i < arguments.length; i++) {			var cls = arguments[i];			cls && a.push(cls);		}		return a.length ? ((d ? ' ' : '') + a.join(' ')) : '';	};	self.rebuild = function(init) {		var data = ['<tr class="ui-grid-empty">'];		var header = ['<tr>'];		var filter = ['<tr>'];		var size = 0;		var columns = options.columns;		var scrollbar = SCROLLBARWIDTH();		for (var i = 0, length = columns.length; i < length; i++) {			var col = columns[i];			if (typeof(col.size) !== 'string')				size += col.size || 1;			col.sorting = null;			if (typeof(col.render) === 'string')				col.render = FN(col.render);			if (typeof(col.header) === 'string')				col.header = FN(col.header);			col.cls = self.cls(0, col.classtd, col.class);		}		for (var i = 0, length = columns.length; i < length; i++) {			var col = columns[i];			var width = typeof(col.size) === 'string' ? col.size : ((((col.size || 1) / size) * 100).floor(2) + '%');			data.push('<td style="width:{0}" data-index="{1}" class="{2}"></td>'.format(width, i, self.cls(0, col.classtd, col.class)));			header.push('<th class="ui-grid-columnname{3}{5}" style="width:{0};text-align:center" data-index="{1}" title="{6}" data-name="{4}"><div class="wrap"><i class="fa hidden ui-grid-fa"></i>{2}</div></th>'.format(width, i, col.header ? col.header(col) : (col.text || col.name), self.cls(1, col.classth, col.class), col.name, col.sort === false ? '' : ' ui-grid-columnsort', col.title || col.text || col.name));			if (col.filter === false)				filter.push('<th class="ui-grid-columnfilterempty ui-grid-columnfilter{1}" style="width:{0}">&nbsp;</th>'.format(width, self.cls(1, col.classfilter, col.class)));			else				filter.push('<th class="ui-grid-columnfilter{4}" style="width:{0}"><input type="text" placeholder="{3}" name="{2}" autocomplete="off" class="ui-grid-filter" /></th>'.format(width, i, col.name, col.filter || config.filterlabel, self.cls(1, col.classfilter, col.class)));		}		if (scrollbar) {			header.push('<th class="ui-grid-columnname ui-grid-scrollbar" style="width:{0}px"></th>'.format(scrollbar));			filter.push('<th class="ui-grid-columnfilterempty ui-grid-scrollbar ui-grid-columnfilter{1}" style="width:{0}px">&nbsp;</th>'.format(scrollbar, self.cls(1, col.classtd, col.class)));		}		tbodyhead.html(data.join('') + '</tr>');		thead.html(header.join('') + '</tr>' + (config.filter ? (filter.join('') + '</tr>') : ''));		!init && self.refresh();		isFilter = false;		options.filter = {};	};	self.fill = function() {		if (config.autosize === false || filled)			return;		filled = true;		tbody.find('.emptyfill').remove();		var builder = ['<tr class="emptyfill">'];		var cols = options.columns;		for (var i = 0, length = cols.length; i < length; i++) {			var col = cols[i];			if (!col.hidden) {				var cls = self.cls(0, col.classtd, col.class);				builder.push('<td{0}>'.format(cls ? (' class="' + cls + '"') : '') + (i ? '' : '<div class="wrap">&nbsp;</div>') + '</td>');			}		}		builder.push('</tr>');		builder = builder.join('');		var buffer = [];		for (var i = 0; i < config.fillcount; i++)			buffer.push(builder);		tbody.append(buffer.join(''));	};	self.resize = function(delay) {		if (config.autosize === false) {			self.hclass('hidden') && self.rclass('hidden');			return;		}		setTimeout2(self.id + '.resize', function() {			var parent = self.parent().height();			if (parent < wheight / 3)				return;			var value = options.items;			var height = parent - (config.padding || 0) - (config.pagination ? 105 : 74);			if (height === eheight)				return;			container.height(height);			eheight = height;			var cls = 'ui-grid-noscroll';			var count = (height / config.rowheight) >> 0;			if (count > value.length) {				self.fill(config.fillcount);				self.aclass(cls);			} else				self.rclass(cls);			pagination && pagination.main.rclass('hidden');			eheight && self.rclass('hidden');		}, typeof(delay) === 'number' ? delay : 50);	};	self.limit = function() {		return Math.ceil(container.height() / config.rowheight);	};	self.filter = function() {		isFilter = Object.keys(options.filter).length > 0;		!config.external && self.refresh();		self.operation('filter');	};	self.operation = function(type) {		if (type === 'filter')			cache.page = 1;		config.exec && EXEC(config.exec, type, isFilter ? options.filter : null, options.lastsort ? options.lastsort : null, cache.page, self);	};	self.sort = function(data) {		options.lastsortelement && options.lastsortelement.rclass('fa-caret-down fa-caret-up').aclass('hidden');		if (data.column.sorting === 'desc') {			options.lastsortelement.find('.ui-grid-fa').rclass('fa-caret-down fa-caret-up').aclass('hidden');			options.lastsortelement = null;			options.lastsort = null;			data.column.sorting = null;			if (config.external)				self.operation('sort');			else				self.refresh();		} else if (data.column) {			data.column.sorting = data.column.sorting === 'asc' ? 'desc' : 'asc';			options.lastsortelement = thead.find('th[data-name="{0}"]'.format(data.column.name)).find('.ui-grid-fa').rclass('hidden').tclass('fa-caret-down', data.column.sorting === 'asc').tclass('fa-caret-up', data.column.sorting === 'desc');			options.lastsort = data.column;			var name = data.column.name;			var sort = data.column.sorting;			!config.external && options.lastsort && options.items.quicksort(name, sort !== 'asc');			self.operation('sort');			self.redraw();		}	};	self.can = function(row) {		var keys = Object.keys(options.filter);		for (var i = 0; i < keys.length; i++) {			var column = keys[i];			var filter = options.filter[column];			var val2 = filtercache[column];			var val = row['$' + column] || row[column];			var type = typeof(val);			if (val instanceof Array) {				val = val.join(' ');				type = 'string';			}			if (type === 'number') {				if (val2 == null)					val2 = filtercache[column] = self.parseNumber(filter);				if (val2.length === 1 && val !== val2[0])					return false;				if (val < val2[0] || val > val2[1])					return false;			} else if (type === 'string') {				if (val2 == null) {					val2 = filtercache[column] = filter.split(/\/\|\\|,/).trim();					for (var j = 0; j < val2.length; j++)						val2[j] = val2[j].toSearch();				}				var is = false;				var s = val.toSearch();				for (var j = 0; j < val2.length; j++) {					if (s.indexOf(val2[j]) !== -1) {						is = true;						break;					}				}				if (!is)					return false;			} else if (type === 'boolean') {				if (val2 == null)					val2 = filtercache[column] = config.boolean.indexOf(filter.replace(/\s/g, '')) !== -1;				if (val2 !== val)					return false;			} else if (val instanceof Date) {				val.setHours(0);				val.setMinutes(0);				if (val2 == null) {					val2 = filter.trim().replace(/\s-\s/, '/').split(/\/|\||\\|,/).trim();					var arr = filtercache[column] = [];					for (var j = 0; j < val2.length; j++) {						var dt = val2[j].trim();						var a = self.parseDate(dt);						if (a instanceof Array) {							if (val2.length === 2) {								arr.push(j ? a[1] : a[0]);							} else {								arr.push(a[0]);								if (j === val2.length - 1) {									arr.push(a[1]);									break;								}							}						} else							arr.push(a);					}					if (val2.length === 2 && arr.length === 2) {						arr[1].setHours(23);						arr[1].setMinutes(59);						arr[1].setSeconds(59);					}					val2 = arr;				}				if (val2.length === 1 && val.format('yyyyMMdd') !== val2[0].format('yyyyMMdd'))					return false;				if (val < val2[0] || val > val2[1])					return false;			} else				return false;		}		return true;	};	self.parseDate = function(val) {		var index = val.indexOf('.');		if (index === -1) {			if ((/[a-z]+/).test(val)) {				var dt = NOW.add(val);				return dt > NOW ? [NOW, dt] : [dt, NOW];			}			if (val.length === 4)				return [new Date(+val, 0, 1), new Date(+val + 1, 0	, 1)];		} else if (val.indexOf('.', index + 1) === -1) {			var a = val.split('.');			return new Date(NOW.getFullYear(), +a[1] - 1, +a[0]);		}		index = val.indexOf('-');		if (index !== -1 && val.indexOf('-', index + 1) === -1) {			var a = val.split('-');			return new Date(NOW.getFullYear(), +a[0] - 1, +a[1]);		}		return val.parseDate();	};	self.parseNumber = function(val) {		var arr = [];		var num = val.replace(/\s-\s/, '/').replace(/\s/g, '').replace(/,/g, '.').split(/\/|\|\s-\s|\\/).trim();		for (var i = 0, length = num.length; i < length; i++) {			var n = num[i];			arr.push(+n);		}		return arr;	};	self.reset = function() {		options.filter = {};		isFilter = false;		thead.find('input').val('');		thead.find('.ui-grid-selected').rclass('ui-grid-selected');		options.lastsortelement && options.lastsortelement.rclass('fa-caret-down fa-caret-up');		options.lastsortelement = null;		if (options.lastsort)			options.lastsort.sorting = null;		options.lastsort = null;	};	self.redraw = function() {		var items = options.items;		var columns = options.columns;		var builder = [];		var m = {};		for (var i = 0, length = items.length; i < length; i++) {			builder.push('<tr class="ui-grid-row" data-index="' + i + '">');			for (var j = 0, jl = columns.length; j < jl; j++) {				var column = columns[j];				var val = items[i][column.name];				// BUG: CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')				// m.value = column.template ? column.template(items[i], column) : column.render ? column.render(val, column, items[i]) : val == null ? '' : (column.format ? val.format(column.format) : val);				// FIXED: 				m.value = column.template ? column.template(items[i], column) : column.render ? column.render(val, column, items[i]) : val == null ? '' : Thelpers.encode((column.format ? val.format(column.format) : val));				m.index = j;				m.align = column.align;				m.background = column.background;				builder.push(self.template(m, column));			}			builder.push('</tr>');		}		tbody.find('.ui-grid-row').remove();		tbody.prepend(builder.join(''));		container.rclass('noscroll');		scroll && container.prop('scrollTop', 0);		scroll = false;		eheight = 0;		self.resize(0);	};	self.setter = function(value) {		// value.items		// value.limit		// value.page		// value.pages		// value.count		if (!value) {			tbody.find('.ui-grid-row').remove();			self.resize();			return;		}		cache = value;		if (config.pagination) {			pagination.prev.prop('disabled', value.page === 1);			pagination.first.prop('disabled', value.page === 1);			pagination.next.prop('disabled', value.page >= value.pages);			pagination.last.prop('disabled', value.page === value.pages);			pagination.page.val(value.page);			pagination.meta.html(value.count.pluralize.apply(value.count, pitems));			pagination.pages.html(value.pages.pluralize.apply(value.pages, ppages));		}		if (options.customsearch) {			for (var i = 0, length = value.items.length; i < length; i++) {				var item = value.items[i];				for (var j = 0; j < options.columns.length; j++) {					var col = options.columns[j];					if (col.search)						item['$' + col.name] = col.search(item);				}			}		}		if (config.external) {			options.items = value.items;		} else {			options.items = [];			filtercache = {};			for (var i = 0, length = value.items.length; i < length; i++) {				var item = value.items[i];				if (isFilter && !self.can(item))					continue;				options.items.push(item);			}			options.lastsort && options.items.quicksort(options.lastsort.name, options.lastsort.sorting === 'asc');		}		self.redraw();		config.checked && EXEC(config.checked, null, self);	};});COMPONENT('contextmenu', function(self) {	var is = false;	var timeout, container, arrow;	self.template = Tangular.compile('<div data-index="{{ index }}"{{ if selected }} class="selected"{{ fi }}><i class="fa {{ icon }}"></i><span>{{ name | raw }}</span></div>');	self.singleton();	self.readonly();	self.callback = null;	self.items = EMPTYARRAY;	self.make = function() {		self.aclass('ui-contextmenu hidden');		self.append('<span class="ui-contextmenu-arrow"></span><div class="ui-contextmenu-items"></div>');		container = self.find('.ui-contextmenu-items');		arrow = self.find('.ui-contextmenu-arrow');		self.event('touchstart mousedown', 'div[data-index]', function(e) {			self.callback && self.callback(self.items[+$(this).attrd('index')], $(self.target));			self.hide();			e.preventDefault();			e.stopPropagation();		});		$(window).on('scroll', function() {			is && self.hide(1);		});		self.on('scroll', function() {			is && self.hide(1);		});		$(document).on('touchstart mousedown', function(e) {			if (is && (self.target !== e.target && !self.target.contains(e.target)))				self.hide(1);		});	};	self.show = function(orientation, target, items, callback, offsetX, offsetY) {		if (is) {			clearTimeout(timeout);			var obj = target instanceof jQuery ? target[0] : target;			if (self.target === obj) {				self.hide(0);				return;			}		}		target = $(target);		var type = typeof(items);		var item;		if (type === 'string')			items = self.get(items);		else if (type === 'function') {			callback = items;			items = (target.attrd('options') || '').split(';');			for (var i = 0, length = items.length; i < length; i++) {				item = items[i];				if (!item)					continue;				var val = item.split('|');				items[i] = { name: val[0], icon: val[1], value: val[2] || val[0] };			}		}		if (!items) {			self.hide(0);			return;		}		self.callback = callback;		var builder = [];		for (var i = 0, length = items.length; i < length; i++) {			item = items[i];			item.index = i;			if (item.icon) {				if (item.icon.substring(0, 3) !== 'fa-')					item.icon = 'fa-' + item.icon;			} else				item.icon = 'fa-caret-right';			builder.push(self.template(item));		}		self.items = items;		self.target = target[0];		var offset = target.offset();		container.html(builder);		switch (orientation) {			case 'left':				arrow.css({ left: '10px' });				break;			case 'right':				arrow.css({ left: '165px' });				break;			case 'center':				arrow.css({ left: '90px' });				break;		}		var options = { left: orientation === 'center' ? Math.ceil((offset.left - self.element.width() / 2) + (target.innerWidth() / 2)) : orientation === 'left' ? (offset.left - 8) + (offsetX || 0) : (offset.left - self.element.width()) + target.innerWidth() + (offsetX || 0) + 8, top: offset.top + target.innerHeight() + 10 + (offsetY || 0) };		self.css(options);		if (is)			return;		self.rclass('hidden');		setTimeout(function() {			self.aclass('ui-contextmenu-visible');			self.emit('contextmenu', true, self, self.target);		}, 100);		is = true;	};	self.hide = function(sleep) {		if (!is)			return;		clearTimeout(timeout);		timeout = setTimeout(function() {			self.aclass('hidden').rclass('ui-contextmenu-visible');			self.emit('contextmenu', false, self, self.target);			self.callback = null;			self.target = null;			is = false;		}, sleep ? sleep : 100);	};});COMPONENT('textbox', function(self, config) {	var input, content = null;	self.nocompile();	self.validate = function(value) {		if (!config.required || config.disabled)			return true;		if (self.type === 'date')			return value instanceof Date && !isNaN(value.getTime());		if (value == null)			value = '';		else			value = value.toString();		EMIT('reflow', self.name);		if (config.minlength && value.length < config.minlength)			return false;		switch (self.type) {			case 'email':				return value.isEmail();			case 'phone':				return value.isPhone();			case 'url':				return value.isURL();			case 'currency':			case 'number':				return value > 0;		}		return config.validation ? !!self.evaluate(value, config.validation, true) : value.length > 0;	};	self.make = function() {		content = self.html();		self.type = config.type;		self.format = config.format;		self.event('click', '.fa-calendar', function(e) {			if (!config.disabled && !config.readonly && config.type === 'date') {				e.preventDefault();				SETTER('calendar', 'toggle', self.element, self.get(), function(date) {					self.change(true);					self.set(date);				});			}		});		self.event('click', '.fa-caret-up,.fa-caret-down', function() {			if (!config.disabled && !config.readonly && config.increment) {				var el = $(this);				var inc = el.hclass('fa-caret-up') ? 1 : -1;				self.change(true);				self.inc(inc);			}		});		self.event('click', '.ui-textbox-control-icon', function() {			if (config.disabled || config.readonly)				return;			if (self.type === 'search') {				self.$stateremoved = false;				$(this).rclass('fa-times').aclass('fa-search');				self.set('');			} else if (config.icon2click)				EXEC(config.icon2click, self);		});		self.event('focus', 'input', function() {			if (!config.disabled && !config.readonly && config.autocomplete)				EXEC(config.autocomplete, self);		});		self.redraw();	};	self.redraw = function() {		var attrs = [];		var builder = [];		var tmp = 'text';		switch (config.type) {			case 'password':				tmp = config.type;				break;			case 'number':			case 'phone':				isMOBILE && (tmp = 'tel');				break;		}		self.tclass('ui-disabled', config.disabled === true);		self.tclass('ui-textbox-required', config.required === true);		self.type = config.type;		attrs.attr('type', tmp);		config.placeholder && attrs.attr('placeholder', config.placeholder);		config.maxlength && attrs.attr('maxlength', config.maxlength);		config.keypress != null && attrs.attr('data-jc-keypress', config.keypress);		config.delay && attrs.attr('data-jc-keypress-delay', config.delay);		config.disabled && attrs.attr('disabled');		config.readonly && attrs.attr('readonly');		config.error && attrs.attr('error');		attrs.attr('data-jc-bind', '');		config.autofill && attrs.attr('name', self.path.replace(/\./g, '_'));		config.align && attrs.attr('class', 'ui-' + config.align);		!isMOBILE && config.autofocus && attrs.attr('autofocus');		builder.push('<div class="ui-textbox-input"><input {0} /></div>'.format(attrs.join(' ')));		var icon = config.icon;		var icon2 = config.icon2;		if (!icon2 && self.type === 'date')			icon2 = 'calendar';		else if (self.type === 'search') {			icon2 = 'search';			self.setter2 = function(value) {				if (self.$stateremoved && !value)					return;				self.$stateremoved = !value;				self.find('.ui-textbox-control-icon').tclass('fa-times', !!value).tclass('fa-search', !value);			};		}		icon2 && builder.push('<div class="ui-textbox-control"><span class="fa fa-{0} ui-textbox-control-icon"></span></div>'.format(icon2));		config.increment && !icon2 && builder.push('<div class="ui-textbox-control"><span class="fa fa-caret-up"></span><span class="fa fa-caret-down"></span></div>');		if (config.label)			content = config.label;		if (content.length) {			var html = builder.join('');			builder = [];			builder.push('<div class="ui-textbox-label">');			icon && builder.push('<i class="fa fa-{0}"></i> '.format(icon));			builder.push('<span>' + content + (content.substring(content.length - 1) === '?' ? '' : ':') + '</span>');			builder.push('</div><div class="ui-textbox">{0}</div>'.format(html));			config.error && builder.push('<div class="ui-textbox-helper"><i class="fa fa-warning" aria-hidden="true"></i> {0}</div>'.format(config.error));			self.html(builder.join(''));			self.aclass('ui-textbox-container');			input = self.find('input');		} else {			config.error && builder.push('<div class="ui-textbox-helper"><i class="fa fa-warning" aria-hidden="true"></i> {0}</div>'.format(config.error));			self.aclass('ui-textbox ui-textbox-container');			self.html(builder.join(''));			input = self.find('input');		}	};	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'readonly':				self.find('input').prop('readonly', value);				break;			case 'disabled':				self.tclass('ui-disabled', value);				self.find('input').prop('disabled', value);				self.reset();				break;			case 'format':				self.format = value;				self.refresh();				break;			case 'required':				self.noValid(!value);				!value && self.state(1, 1);				self.tclass('ui-textbox-required', value === true);				break;			case 'placeholder':				input.prop('placeholder', value || '');				break;			case 'maxlength':				input.prop('maxlength', value || 1000);				break;			case 'autofill':				input.prop('name', value ? self.path.replace(/\./g, '_') : '');				break;			case 'label':				if (content && value)					self.find('.ui-textbox-label span').html(value);				else					redraw = true;				content = value;				break;			case 'type':				self.type = value;				if (value === 'password')					value = 'password';				else					self.type = 'text';				self.find('input').prop('type', self.type);				break;			case 'align':				input.rclass(input.attr('class')).aclass('ui-' + value || 'left');				break;			case 'autofocus':				input.focus();				break;			case 'icon':				var tmp = self.find('.ui-textbox-label .fa');				if (tmp.length)					tmp.rclass2('fa-').aclass('fa-' + value);				else					redraw = true;				break;			case 'icon2':			case 'increment':				redraw = true;				break;		}		redraw && setTimeout2('redraw.' + self.id, function() {			self.redraw();			self.refresh();		}, 100);	};	self.formatter(function(path, value) {		return config.type === 'date' ? (value ? value.format(config.format || 'yyyy-MM-dd') : value) : value;	});	self.parser(function(path, value) {		return value ? config.spaces === false ? value.replace(/\s/g, '') : value : value;	});	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.tclass('ui-textbox-invalid', invalid);		config.error && self.find('.ui-textbox-helper').tclass('ui-textbox-helper-show', invalid);	};});COMPONENT('form', function(self, config) {	var W = window;	var csspos = {};	if (!W.$$form) {		W.$$form_level = W.$$form_level || 1;		W.$$form = true;		$(document).on('click', '.ui-form-button-close', function() {			SET($(this).attrd('path'), '');		});		$(W).on('resize', function() {			SETTER('form', 'resize');		});		$(document).on('click', '.ui-form-container', function(e) {			var el = $(e.target);			if (!(el.hclass('ui-form-container-padding') || el.hclass('ui-form-container')))				return;			var form = $(this).find('.ui-form');			var cls = 'ui-form-animate-click';			form.aclass(cls);			setTimeout(function() {				form.rclass(cls);			}, 300);		});	}	self.readonly();	self.submit = function() {		if (config.submit)			EXEC(config.submit, self);		else			self.hide();	};	self.cancel = function() {		config.cancel && EXEC(config.cancel, self);		self.hide();	};	self.hide = function() {		self.set('');	};	self.icon = function(value) {		var el = this.rclass2('fa');		value.icon && el.aclass('fa fa-' + value.icon);	};	self.resize = function() {		if (!config.center || self.hclass('hidden'))			return;		var ui = self.find('.ui-form');		var fh = ui.innerHeight();		var wh = $(W).height();		var r = (wh / 2) - (fh / 2);		csspos.marginTop = (r > 30 ? (r - 15) : 20) + 'px';		ui.css(csspos);	};	self.make = function() {		$(document.body).append('<div id="{0}" class="hidden ui-form-container"><div class="ui-form-container-padding"><div class="ui-form" style="max-width:{1}px"><div data-bind="@config__html span:value.title__change .ui-form-icon:@icon" class="ui-form-title"><button class="ui-form-button-close{3}" data-path="{2}"><i class="fa fa-times"></i></button><i class="ui-form-icon"></i><span></span></div></div></div>'.format(self.ID, config.width || 800, self.path, config.closebutton == false ? ' hidden' : ''));		var el = $('#' + self.ID);		el.find('.ui-form')[0].appendChild(self.dom);		self.rclass('hidden');		self.replace(el);		self.event('scroll', function() {			EMIT('scroll', self.name);			EMIT('reflow', self.name);		});		self.find('button').on('click', function() {			switch (this.name) {				case 'submit':					self.submit(self.hide);					break;				case 'cancel':					!this.disabled && self[this.name](self.hide);					break;			}		});		config.enter && self.event('keydown', 'input', function(e) {			e.which === 13 && !self.find('button[name="submit"]')[0].disabled && setTimeout(function() {				self.submit(self);			}, 800);		});	};	self.configure = function(key, value, init, prev) {		if (init)			return;		switch (key) {			case 'width':				value !== prev && self.find('.ui-form').css('max-width', value + 'px');				break;			case 'closebutton':				self.find('.ui-form-button-close').tclass(value !== true);				break;		}	};	self.setter = function(value) {		setTimeout2('ui-form-noscroll', function() {			$('html').tclass('ui-form-noscroll', !!$('.ui-form-container').not('.hidden').length);		}, 50);		var isHidden = value !== config.if;		if (self.hclass('hidden') === isHidden)			return;		setTimeout2('formreflow', function() {			EMIT('reflow', self.name);		}, 10);		if (isHidden) {			self.aclass('hidden');			self.release(true);			self.find('.ui-form').rclass('ui-form-animate');			W.$$form_level--;			return;		}		if (W.$$form_level < 1)			W.$$form_level = 1;		W.$$form_level++;		self.css('z-index', W.$$form_level * 10);		self.element.scrollTop(0);		self.rclass('hidden');		self.resize();		self.release(false);		config.reload && EXEC(config.reload, self);		config.default && DEFAULT(config.default, true);		if (!isMOBILE && config.autofocus) {			var el = self.find(config.autofocus === true ? 'input[type="text"],select,textarea' : config.autofocus);			el.length && el[0].focus();		}		setTimeout(function() {			self.element.scrollTop(0);			self.find('.ui-form').aclass('ui-form-animate');		}, 300);		// Fixes a problem with freezing of scrolling in Chrome		setTimeout2(self.ID, function() {			self.css('z-index', (W.$$form_level * 10) + 1);		}, 500);	};});COMPONENT('dropdown', function(self, config) {	var select, condition, content = null;	var render = '';	self.nocompile();	self.validate = function(value) {		if (!config.required || config.disabled)			return true;		var type = typeof(value);		if (type === 'undefined' || type === 'object')			value = '';		else			value = value.toString();		EMIT('reflow', self.name);		switch (self.type) {			case 'currency':			case 'number':				return value > 0;		}		return value.length > 0;	};	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'type':				self.type = value;				break;			case 'items':				if (value instanceof Array) {					self.bind('', value);					return;				}				var items = [];				value.split(',').forEach(function(item) {					item = item.trim().split('|');					var obj = { id: item[1] == null ? item[0] : item[1], name: item[0] };					items.push(obj);				});				self.bind('', items);				break;			case 'if':				condition = value ? FN(value) : null;				break;			case 'required':				self.tclass('ui-dropdown-required', value === true);				self.state(1, 1);				break;			case 'datasource':				self.datasource(value, self.bind);				break;			case 'label':				content = value;				redraw = true;				break;			case 'icon':				redraw = true;				break;			case 'disabled':				self.tclass('ui-disabled', value);				self.find('select').prop('disabled', value);				self.reset();				break;		}		redraw && setTimeout2(self.id + '.redraw', 100);	};	self.bind = function(path, arr) {		if (!arr)			arr = EMPTYARRAY;		var builder = [];		var value = self.get();		var template = '<option value="{0}"{1}>{2}</option>';		var propText = config.text || 'name';		var propValue = config.value || 'id';		config.empty !== undefined && builder.push('<option value="">{0}</option>'.format(config.empty));		var type = typeof(arr[0]);		var notObj = type === 'string' || type === 'number';		for (var i = 0, length = arr.length; i < length; i++) {			var item = arr[i];			if (condition && !condition(item))				continue;			if (notObj)				builder.push(template.format(item, value === item ? ' selected="selected"' : '', item));			else				builder.push(template.format(item[propValue], value === item[propValue] ? ' selected="selected"' : '', item[propText]));		}		render = builder.join('');		select.html(render);	};	self.redraw = function() {		var html = '<div class="ui-dropdown"><select data-jc-bind="">{0}</select></div>'.format(render);		var builder = [];		var label = content || config.label;		if (label) {			builder.push('<div class="ui-dropdown-label">{0}{1}:</div>'.format(config.icon ? '<span class="fa fa-{0}"></span> '.format(config.icon) : '', label));			builder.push('<div class="ui-dropdown-values">{0}</div>'.format(html));			self.html(builder.join(''));		} else			self.html(html).aclass('ui-dropdown-values');		select = self.find('select');		render && self.refresh();		config.disabled && self.reconfigure('disabled:true');		self.tclass('ui-dropdown-required', config.required === true);	};	self.make = function() {		self.type = config.type;		content = self.html();		self.aclass('ui-dropdown-container');		self.redraw();		config.if && (condition = FN(config.if));		config.items && self.reconfigure({ items: config.items });		config.datasource && self.reconfigure('datasource:' + config.datasource);	};	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.tclass('ui-dropdown-invalid', invalid);	};});COMPONENT('validation', 'delay:100;flags:visible', function(self, config) {	var path, elements = null;	var def = 'button[name="submit"]';	var flags = null;	self.readonly();	self.make = function() {		elements = self.find(config.selector || def);		path = self.path.replace(/\.\*$/, '');		setTimeout(function() {			self.watch(self.path, self.state, true);		}, 50);	};	self.configure = function(key, value, init) {		switch (key) {			case 'selector':				if (!init)					elements = self.find(value || def);				break;			case 'flags':				if (value) {					flags = value.split(',');					for (var i = 0; i < flags.length; i++)						flags[i] = '@' + flags[i];				} else					flags = null;				break;		}	};	self.state = function() {		setTimeout2(self.id, function() {			var disabled = DISABLED(path, flags);			if (!disabled && config.if)				disabled = !EVALUATE(self.path, config.if);			elements.prop('disabled', disabled);		}, config.timeout);	};});COMPONENT('textarea', function(self, config) {	var input, content = null;	self.nocompile();	self.validate = function(value) {		if (config.disabled || !config.required || config.readonly)			return true;		if (value == null)			value = '';		else			value = value.toString();		return value.length > 0;	};	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'readonly':				self.find('textarea').prop('readonly', value);				break;			case 'disabled':				self.tclass('ui-disabled', value);				self.find('textarea').prop('disabled', value);				self.reset();				break;			case 'required':				self.noValid(!value);				!value && self.state(1, 1);				self.tclass('ui-textarea-required', value);				break;			case 'placeholder':				input.prop('placeholder', value || '');				break;			case 'maxlength':				input.prop('maxlength', value || 1000);				break;			case 'label':				redraw = true;				break;			case 'autofocus':				input.focus();				break;			case 'monospace':				self.tclass('ui-textarea-monospace', value);				break;			case 'icon':				redraw = true;				break;			case 'format':				self.format = value;				self.refresh();				break;			case 'height':				self.find('textarea').css('height', (value > 0 ? value + 'px' : value));				break;		}		redraw && setTimeout2('redraw' + self.id, function() {			self.redraw();			self.refresh();		}, 100);	};	self.redraw = function() {		var attrs = [];		var builder = [];		self.tclass('ui-disabled', config.disabled === true);		self.tclass('ui-textarea-monospace', config.monospace === true);		self.tclass('ui-textarea-required', config.required === true);		config.placeholder && attrs.attr('placeholder', config.placeholder);		config.maxlength && attrs.attr('maxlength', config.maxlength);		config.error && attrs.attr('error');		attrs.attr('data-jc-bind', '');		config.height && attrs.attr('style', 'height:{0}px'.format(config.height));		config.autofocus === 'true' && attrs.attr('autofocus');		config.disabled && attrs.attr('disabled');		config.readonly && attrs.attr('readonly');		builder.push('<textarea {0}></textarea>'.format(attrs.join(' ')));		var label = config.label || content;		if (!label.length) {			config.error && builder.push('<div class="ui-textarea-helper"><i class="fa fa-warning" aria-hidden="true"></i> {0}</div>'.format(config.error));			self.aclass('ui-textarea ui-textarea-container');			self.html(builder.join(''));			input = self.find('textarea');			return;		}		var html = builder.join('');		builder = [];		builder.push('<div class="ui-textarea-label">');		config.icon && builder.push('<i class="fa fa-{0}"></i>'.format(config.icon));		builder.push(label);		builder.push(':</div><div class="ui-textarea">{0}</div>'.format(html));		config.error && builder.push('<div class="ui-textarea-helper"><i class="fa fa-warning" aria-hidden="true"></i> {0}</div>'.format(config.error));		self.html(builder.join(''));		self.rclass('ui-textarea');		self.aclass('ui-textarea-container');		input = self.find('textarea');	};	self.make = function() {		content = self.html();		self.type = config.type;		self.format = config.format;		self.redraw();	};	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.tclass('ui-textarea-invalid', invalid);		config.error && self.find('.ui-textarea-helper').tclass('ui-textarea-helper-show', invalid);	};});COMPONENT('checkbox', function(self, config) {	self.nocompile();	self.validate = function(value) {		return (config.disabled || !config.required) ? true : (value === true || value === 'true' || value === 'on');	};	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'label':				self.find('span').html(value);				break;			case 'required':				self.find('span').tclass('ui-checkbox-label-required', value);				break;			case 'disabled':				self.tclass('ui-disabled', value);				break;			case 'checkicon':				self.find('i').rclass().aclass('fa fa-' + value);				break;		}	};	self.make = function() {		self.aclass('ui-checkbox');		self.html('<div><i class="fa fa-{2}"></i></div><span{1}>{0}</span>'.format(config.label || self.html(), config.required ? ' class="ui-checkbox-label-required"' : '', config.checkicon || 'check'));		self.event('click', function() {			if (config.disabled)				return;			self.dirty(false);			self.getter(!self.get());		});	};	self.setter = function(value) {		self.toggle('ui-checkbox-checked', !!value);	};});COMPONENT('codemirror', 'linenumbers:false;required:false;trim:false;tabs:false', function(self, config) {	var editor = null;	self.getter = null;	self.bindvisible();	self.nocompile();	self.reload = function() {		editor.refresh();	};	self.validate = function(value) {		return (config.disabled || !config.required ? true : value && value.length > 0) === true;	};	self.insert = function(value) {		editor.replaceSelection(value);		self.change(true);	};	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'disabled':				self.tclass('ui-disabled', value);				editor.readOnly = value;				editor.refresh();				break;			case 'required':				self.find('.ui-codemirror-label').tclass('ui-codemirror-label-required', value);				self.state(1, 1);				break;			case 'icon':				self.find('i').rclass().aclass('fa fa-' + value);				break;		}	};	self.make = function() {		var content = config.label || self.html();		self.html((content ? '<div class="ui-codemirror-label' + (config.required ? ' ui-codemirror-label-required' : '') + '">' + (config.icon ? '<i class="fa fa-' + config.icon + '"></i> ' : '') + content + ':</div>' : '') + '<div class="ui-codemirror"></div>');		var container = self.find('.ui-codemirror');		var options = {};		options.lineNumbers = config.linenumbers;		options.mode = config.type || 'htmlmixed';		options.indentUnit = 4;		if (config.tabs)			options.indentWithTabs = true;		if (config.type === 'markdown') {			options.styleActiveLine = true;			options.lineWrapping = true;			options.matchBrackets = true;		}		editor = CodeMirror(container[0], options);		self.editor = editor;		if (config.height !== 'auto') {			var is = typeof(config.height) === 'number';			editor.setSize('100%', is ? (config.height + 'px') : (config.height || '200px'));			!is && self.css('height', config.height);		}		if (config.disabled) {			self.aclass('ui-disabled');			editor.readOnly = true;			editor.refresh();		}		var can = {};		can['+input'] = can['+delete'] = can.undo = can.redo = can.paste = can.cut = can.clear = true;		editor.on('change', function(a, b) {			if (config.disabled || !can[b.origin])				return;			setTimeout2(self.id, function() {				var val = editor.getValue();				if (config.trim) {					var lines = val.split('\n');					for (var i = 0, length = lines.length; i < length; i++)						lines[i] = lines[i].replace(/\s+$/, '');					val = lines.join('\n').trim();				}				self.getter2 && self.getter2(val);				self.change(true);				self.rewrite(val);				config.required && self.validate2();			}, 200);		});	};	self.setter = function(value) {		editor.setValue(value || '');		editor.refresh();		setTimeout(function() {			editor.refresh();			editor.scrollTo(0, 0);			editor.setCursor(0);		}, 200);		setTimeout(function() {			editor.refresh();		}, 1000);		setTimeout(function() {			editor.refresh();		}, 2000);	};	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.find('.ui-codemirror').tclass('ui-codemirror-invalid', invalid);	};}, ['//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.css', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.js', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/javascript/javascript.min.js', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/htmlmixed/htmlmixed.min.js', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/xml/xml.min.js', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/css/css.min.js', '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/markdown/markdown.min.js', function(next) {	CodeMirror.defineMode('totaljsresources', function() {		var REG_KEY = /^[a-z0-9_\-.#]+/i;		return {			startState: function() {				return { type: 0, keyword: 0 };			},			token: function(stream, state) {				var m;				if (stream.sol()) {					var line = stream.string;					if (line.substring(0, 2) === '//') {						stream.skipToEnd();						return 'comment';					}					state.type = 0;				}				m = stream.match(REG_KEY, true);				if (m)					return 'tag';				if (!stream.string) {					stream.next();					return '';				}				var count = 0;				while (true) {					count++;					if (count > 5000)						break;					var c = stream.peek();					if (c === ':') {						stream.skipToEnd();						return 'def';					}					if (c === '(') {						if (stream.skipTo(')')) {							stream.eat(')');							return 'variable-L';						}					}				}				stream.next();				return '';			}		};	});	next();}]);COMPONENT('nosqlcounter', 'count:0;height:80', function(self, config) {	var months = MONTHS;	var container, labels;	self.bindvisible();	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-nosqlcounter');		self.append('<div class="ui-nosqlcounter-table"{0}><div class="ui-nosqlcounter-cell"></div></div><div class="ui-nosqlcounter-labels"></div>'.format(config.height ? ' style="height:{0}px"'.format(config.height) : ''));		container = self.find('.ui-nosqlcounter-cell');		labels = self.find('.ui-nosqlcounter-labels');	};	self.configure = function(key, value) {		switch (key) {			case 'months':				if (value instanceof Array)					months = value;				else					months = value.split(',').trim();				break;		}	};	self.redraw = function(maxbars) {		var value = self.get();		if (!(value instanceof Array))			value = [];		var dt = new Date();		dt.setDate(1);		var current = dt.format('yyyyMM');		var stats = null;		if (config.lastvalues) {			var max = value.length - maxbars;			if (max < 0)				max = 0;			stats = value.slice(max, value.length);		} else {			stats = [];			for (var i = 0; i < maxbars; i++) {				var id = dt.format('yyyyMM');				var item = value.findItem('id', id);				stats.push(item ? item : { id: id, month: dt.getMonth() + 1, year: dt.getFullYear(), value: 0 });				dt = dt.add('-1 month');			}			stats.reverse();		}		var max = stats.scalar('max', 'value');		var bar = 100 / maxbars;		var builder = [];		var dates = [];		var cls = '';		var min = ((20 / config.height) * 100) >> 0;		var sum = '';		for (var i = 0, length = stats.length; i < length; i++) {			var item = stats[i];			var val = item.value;			if (val > 999)				val = (val / 1000).format(1, 2) + 'K';			sum += val + ',';			var h = max === 0 ? 0 : ((item.value / max) * (100 - min));			h += min;			cls = item.value ? '' : 'empty';			if (item.id === current)				cls += (cls ? ' ' : '') + 'current';			if (i === maxbars - 1)				cls += (cls ? ' ' : '') + 'last';			var w = bar.format(2, '');			builder.push('<div style="width:{0}%" title="{3}" class="{4}"><div style="height:{1}%"><span>{2}</span></div></div>'.format(w, h.format(0, ''), val, months[item.month - 1] + ' ' + item.year, cls));			dates.push('<div style="width:{0}%">{1}</div>'.format(w, months[item.month - 1].substring(0, 3)));		}		if (self.old !== sum) {			self.old = sum;			labels.html(dates.join(''));			container.html(builder.join(''));		}	};	self.setter = function(value) {		if (config.count === 0) {			self.width(function(width) {				self.redraw(width / 30 >> 0);			});		} else			self.redraw(WIDTH() === 'xs' ? config.count / 2 : config.count, value);	};});COMPONENT('keyvalue', 'maxlength:100', function(self, config) {	var container, content = null;	var cempty = 'empty';	var skip = false;	var empty = {};	self.template = Tangular.compile('<div class="ui-keyvalue-item"><div class="ui-keyvalue-item-remove"><i class="fa fa-times"></i></div><div class="ui-keyvalue-item-key"><input type="text" name="key" maxlength="{{ max }}"{{ if disabled }} disabled="disabled"{{ fi }} placeholder="{{ placeholder_key }}" value="{{ key }}" /></div><div class="ui-keyvalue-item-value"><input type="text" maxlength="{{ max }}" placeholder="{{ placeholder_value }}" value="{{ value }}" /></div></div>');	self.nocompile();	self.binder = function(type, value) {		return value;	};	self.configure = function(key, value, init, prev) {		if (init)			return;		var redraw = false;		switch (key) {			case 'disabled':				self.tclass('ui-disabled', value);				self.find('input').prop('disabled', value);				empty.disabled = value;				break;			case 'maxlength':				self.find('input').prop('maxlength', value);				break;			case 'placeholderkey':				self.find('input[name="key"]').prop('placeholder', value);				break;			case 'placeholdervalue':				self.find('input[name="value"]').prop('placeholder', value);				break;			case 'icon':				if (value && prev)					self.find('i').rclass('fa').aclass('fa fa-' + value);				else					redraw = true;				break;			case 'label':				redraw = true;				break;		}		if (redraw) {			self.redraw();			self.refresh();		}	};	self.redraw = function() {		var icon = config.icon;		var label = config.label || content;		if (icon)			icon = '<i class="fa fa-{0}"></i>'.format(icon);		empty.value = '';		self.html((label ? '<div class="ui-keyvalue-label">{1}{0}:</div>'.format(label, icon) : '') + '<div class="ui-keyvalue-items"></div>' + self.template(empty).replace('-item"', '-item ui-keyvalue-base"'));		container = self.find('.ui-keyvalue-items');	};	self.make = function() {		empty.max = config.maxlength;		empty.placeholder_key = config.placeholderkey;		empty.placeholder_value = config.placeholdervalue;		empty.value = '';		empty.disabled = config.disabled;		content = self.html();		self.aclass('ui-keyvalue');		self.disabled && self.aclass('ui-disabled');		self.redraw();		self.event('click', '.fa-times', function() {			if (config.disabled)				return;			var el = $(this);			var parent = el.closest('.ui-keyvalue-item');			var inputs = parent.find('input');			var obj = self.get();			!obj && (obj = {});			var key = inputs[0].value;			parent.remove();			delete obj[key];			self.set(obj, 2);			self.change(true);		});		self.event('change keypress', 'input', function(e) {			if (config.disabled || (e.type !== 'change' && e.which !== 13))				return;			var el = $(this);			var inputs = el.closest('.ui-keyvalue-item').find('input');			var key = self.binder('key', inputs[0].value);			var value = self.binder('value', inputs.get(1).value);			if (!key || !value)				return;			var base = el.closest('.ui-keyvalue-base').length > 0;			if (base && e.type === 'change')				return;			if (base) {				var tmp = self.get();				!tmp && (tmp = {});				tmp[key] = value;				self.set(tmp);				self.change(true);				inputs.val('');				inputs.eq(0).focus();				return;			}			var keyvalue = {};			var k;			container.find('input').each(function() {				if (this.name === 'key') {					k = this.value.trim();				} else if (k) {					keyvalue[k] = this.value.trim();					k = '';				}			});			skip = true;			self.set(keyvalue, 2);			self.change(true);		});	};	self.setter = function(value) {		if (skip) {			skip = false;			return;		}		if (!value) {			container.empty();			self.aclass(cempty);			return;		}		var builder = [];		Object.keys(value).forEach(function(key) {			empty.key = key;			empty.value = value[key];			builder.push(self.template(empty));		});		self.tclass(cempty, builder.length === 0);		container.empty().append(builder.join(''));	};});COMPONENT('expander', function(self, config) {	var prev = false;	self.readonly();	self.blind();	self.toggle = function(v) {		if (v == null)			v = !self.hclass('ui-expander-expanded');		if (v === prev)			return;		prev = v;		self.tclass('ui-expander-expanded', v);		var fa = self.find('.ui-expander-button').find('.fa');		fa.tclass('fa-angle-double-down', !v);		fa.tclass('fa-angle-double-up', v);	};	self.make = function() {		self.aclass('ui-expander' + (config.expand ? ' ui-expander-expanded' : ''));		self.element.wrapInner('<div class="ui-expander-container"></div>');		self.append('<div class="ui-expander-fade"></div><div class="ui-expander-button"><span class="fa fa-angle-double-down"></span></div>');		self.event('click', '.ui-expander-button', function() {			self.toggle();		});	};});COMPONENT('disable', function(self, config) {	var validate = null;	self.readonly();	self.configure = function(key, value) {		if (key === 'validate')			validate = value.split(',').trim();	};	self.setter = function(value) {		var is = true;		if (config.if)			is = EVALUATE(self.path, config.if);		else			is = !value;		self.find(config.selector || '[data-jc]').each(function() {			var com = $(this).component();			com && com.reconfigure('disabled:' + is);		});		validate && validate.forEach(FN('n => RESET({0}n)'.format(self.pathscope ? '\'' + self.pathscope + '.\'+' : '')));	};	self.state = function() {		self.update();	};});COMPONENT('textboxlist', 'maxlength:100', function(self, config) {	var container, content;	var empty = {};	var skip = false;	var cempty = 'empty';	self.readonly();	self.nocompile();	self.template = Tangular.compile('<div class="ui-textboxlist-item"><div><i class="fa fa-times"></i></div><div><input type="text" maxlength="{{ max }}" placeholder="{{ placeholder }}"{{ if disabled}} disabled="disabled"{{ fi }} value="{{ value }}" /></div></div>');	self.configure = function(key, value, init, prev) {		if (init)			return;		var redraw = false;		switch (key) {			case 'disabled':				self.tclass('ui-required', value);				self.find('input').prop('disabled', true);				empty.disabled = value;				break;			case 'maxlength':				empty.max = value;				self.find('input').prop(key, value);				break;			case 'placeholder':				empty.placeholder = value;				self.find('input').prop(key, value);				break;			case 'label':				redraw = true;				break;			case 'icon':				if (value && prev)					self.find('i').rclass().aclass(value);				else					redraw = true;				break;		}		if (redraw) {			skip = false;			self.redraw();			self.refresh();		}	};	self.redraw = function() {		var icon = '';		var html = config.label || content;		if (config.icon)			icon = '<i class="fa fa-{0}"></i>'.format(config.icon);		empty.value = '';		self.html((html ? '<div class="ui-textboxlist-label">{1}{0}:</div>'.format(html, icon) : '') + '<div class="ui-textboxlist-items"></div>' + self.template(empty).replace('-item"', '-item ui-textboxlist-base"'));		container = self.find('.ui-textboxlist-items');	};	self.make = function() {		empty.max = config.max;		empty.placeholder = config.placeholder;		empty.value = '';		empty.disabled = config.disabled;		if (config.disabled)			self.aclass('ui-disabled');		content = self.html();		self.aclass('ui-textboxlist');		self.redraw();		self.event('click', '.fa-times', function() {			if (config.disabled)				return;			var el = $(this);			var parent = el.closest('.ui-textboxlist-item');			var value = parent.find('input').val();			var arr = self.get();			parent.remove();			var index = arr.indexOf(value);			if (index === -1)				return;			arr.splice(index, 1);			self.tclass(cempty, arr.length === 0);			skip = true;			self.set(arr, 2);			self.change(true);		});		self.event('change keypress', 'input', function(e) {			if (config.disabled || (e.type !== 'change' && e.which !== 13))				return;			var el = $(this);			var value = this.value.trim();			if (!value)				return;			var arr = [];			var base = el.closest('.ui-textboxlist-base').length > 0;			if (base && e.type === 'change')				return;			var raw = self.get();			if (base) {				if (!raw || raw.indexOf(value) === -1)					self.push(self.path, value, 2);				this.value = '';				self.change(true);				return;			}			container.find('input').each(function() {				arr.push(this.value.trim());			});			skip = true;			self.set(arr, 2);			self.change(true);		});	};	self.setter = function(value) {		if (skip) {			skip = false;			return;		}		if (!value || !value.length) {			self.aclass(cempty);			container.empty();			return;		}		self.rclass(cempty);		var builder = [];		value.forEach(function(item) {			empty.value = item;			builder.push(self.template(empty));		});		container.empty().append(builder.join(''));	};});COMPONENT('dropdowncheckbox', 'checkicon:check;visible:0;alltext:All selected;limit:0;selectedtext:{0} selected', function(self, config) {	var data = [], render = '';	var container, values, content, datasource = null;	var prepared = false;	var W = window;	!W.$dropdowncheckboxtemplate && (W.$dropdowncheckboxtemplate = Tangular.compile('<div class="ui-dropdowncheckbox-item" data-index="{{ index }}"><div><i class="fa fa-{{ $.checkicon }}"></i></div><span>{{ text }}</span></div>'));	var template = W.$dropdowncheckboxtemplate;	self.nocompile();	self.validate = function(value) {		return config.disabled || !config.required ? true : value && value.length > 0;	};	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'type':				self.type = value;				break;			case 'required':				self.tclass('ui-dropdowncheckbox-required', config.required);				break;			case 'label':				content = value;				redraw = true;				break;			case 'disabled':				self.tclass('ui-disabled', value);				self.reset();				break;			case 'checkicon':				self.find('i').rclass().aclass('fa fa-' + value);				break;			case 'icon':				redraw = true;				break;			case 'datasource':				self.datasource(value, self.bind);				datasource && self.refresh();				datasource = value;				break;			case 'items':				if (value instanceof Array) {					self.bind('', value);					return;				}				var items = [];				value.split(',').forEach(function(item) {					item = item.trim().split('|');					var val = (item[1] == null ? item[0] : item[1]).trim();					if (config.type === 'number')						val = +val;					items.push({ name: item[0].trim(), id: val });				});				self.bind('', items);				self.refresh();				break;		}		redraw && setTimeout2(self.id + '.redraw', self.redraw, 100);	};	self.redraw = function() {		var html = '<div class="ui-dropdowncheckbox"><span class="fa fa-caret-down"></span><div class="ui-dropdowncheckbox-selected"></div></div><div class="ui-dropdowncheckbox-values hidden">{0}</div>'.format(render);		if (content.length)			self.html('<div class="ui-dropdowncheckbox-label">{0}{1}:</div>'.format(config.icon ? ('<i class="fa fa-' + config.icon + '"></i>') : '', content) + html);		else			self.html(html);		container = self.find('.ui-dropdowncheckbox-values');		values = self.find('.ui-dropdowncheckbox-selected');		prepared && self.refresh();		self.tclass('ui-disabled', config.disabled === true);		self.tclass('ui-dropdowncheckbox-required', config.required === true);	};	self.make = function() {		self.type = config.type;		content = self.html();		self.aclass('ui-dropdowncheckbox-container');		self.redraw();		if (config.items)			self.reconfigure({ items: config.items });		else if (config.datasource)			self.reconfigure({ datasource: config.datasource });		else			self.bind('', null);		self.event('click', '.ui-dropdowncheckbox', function(e) {			if (config.disabled)				return;			container.tclass('hidden');			if (W.$dropdowncheckboxelement) {				W.$dropdowncheckboxelement.aclass('hidden');				W.$dropdowncheckboxelement = null;			}			!container.hclass('hidden') && (W.$dropdowncheckboxelement = container);			e.stopPropagation();		});		self.event('click', '.ui-dropdowncheckbox-item', function(e) {			e.stopPropagation();			if (config.disabled)				return;			var el = $(this);			var is = !el.hclass('ui-dropdowncheckbox-checked');			var index = +el.attrd('index');			var value = data[index];			if (value === undefined)				return;			value = value.value;			var arr = self.get();			if (!(arr instanceof Array))				arr = [];			var index = arr.indexOf(value);			if (is) {				if (config.limit && arr.length === config.limit)					return;				index === -1 && arr.push(value);			} else {				index !== -1 && arr.splice(index, 1);			}			self.set(arr);			self.change(true);		});	};	self.bind = function(path, value) {		var clsempty = 'ui-dropdowncheckbox-values-empty';		if (value !== undefined)			prepared = true;		if (!value || !value.length) {			var h = config.empty || '&nbsp;';			if (h === self.old)				return;			container.aclass(clsempty).html(h);			self.old = h;			return;		}		var kv = config.value || 'id';		var kt = config.text || 'name';		render = '';		data = [];		for (var i = 0, length = value.length; i < length; i++) {			var isString = typeof(value[i]) === 'string';			var item = { value: isString ? value[i] : value[i][kv], text: isString ? value[i] : value[i][kt], index: i };			render += template(item, config);			data.push(item);		}		var h = HASH(render);		if (h === self.old)			return;		self.old = h;		if (render)			container.rclass(clsempty).html(render);		else			container.aclass(clsempty).html(config.empty);		self.refresh();	};	self.setter = function(value) {		if (!prepared)			return;		var label = '';		var count = value == null || !value.length ? undefined : value.length;		if (value && count) {			var remove = [];			for (var i = 0; i < count; i++) {				var selected = value[i];				var index = 0;				var is = false;				while (true) {					var item = data[index++];					if (item === undefined)						break;					if (item.value != selected)						continue;					label += (label ? ', ' : '') + item.text;					is = true;				}				!is && remove.push(selected);			}			if (config.cleaner !== false && value) {				var refresh = false;				while (true) {					var item = remove.shift();					if (item === undefined)						break;					value.splice(value.indexOf(item), 1);					refresh = true;				}				refresh && self.set(value);			}		}		container.find('.ui-dropdowncheckbox-item').each(function() {			var el = $(this);			var index = +el.attrd('index');			var checked = false;			if (!value || !value.length)				checked = false;			else if (data[index])				checked = data[index];			checked && (checked = value.indexOf(checked.value) !== -1);			el.tclass('ui-dropdowncheckbox-checked', checked);		});		if (!label && value && config.cleaner !== false) {			// invalid data			// it updates model without notification			self.rewrite([]);		}		if (!label && config.placeholder) {			values.rattr('title', '');			values.html('<span>{0}</span>'.format(config.placeholder));		} else {			if (count == data.length && config.alltext !== 'null' && config.alltext)				label = config.alltext;			else if (config.visible && count > config.visible)				label = config.selectedtext.format(count, data.length);			values.attr('title', label);			values.html(label);		}	};	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.tclass('ui-dropdowncheckbox-invalid', invalid);	};	if (W.$dropdowncheckboxevent)		return;	W.$dropdowncheckboxevent = true;	$(document).on('click', function() {		if (W.$dropdowncheckboxelement) {			W.$dropdowncheckboxelement.aclass('hidden');			W.$dropdowncheckboxelement = null;		}	});});COMPONENT('snackbar', 'timeout:3000;button:Dismiss', function(self, config) {	var show = true;	var callback;	self.readonly();	self.blind();	self.nocompile();	self.make = function() {		self.aclass('ui-snackbar hidden');		self.append('<div><a href="javasc' + 'ript:void(0)" class="ui-snackbar-dismiss"></a><div class="ui-snackbar-body"></div></div>');		self.event('click', '.ui-snackbar-dismiss', function() {			self.hide();			callback && callback();		});	};	self.hide = function() {		self.rclass('ui-snackbar-visible');		setTimeout(function() {			self.aclass('hidden');		}, 1000);		show = true;	};	self.success = function(message, button, close) {		self.show('<i class="fa fa-check-circle ui-snackbar-icon"></i>' + message, button, close);	};	self.warning = function(message, button, close) {		self.show('<i class="fa fa-times-circle ui-snackbar-icon"></i>' + message, button, close);	};	self.show = function(message, button, close) {		if (typeof(button) === 'function') {			close = button;			button = null;		}		callback = close;		self.find('.ui-snackbar-body').html(message);		self.find('.ui-snackbar-dismiss').html(button || config.button);		if (show) {			self.rclass('hidden');			setTimeout(function() {				self.aclass('ui-snackbar-visible');			}, 50);		}		setTimeout2(self.ID, self.hide, config.timeout + 50);		show = false;	};});COMPONENT('repeater', 'hidden:true;check:true', function(self, config) {	var filter = null;	var recompile = false;	var reg = /\$(index|path)/g;	self.readonly();	self.configure = function(key, value) {		if (key === 'filter')			filter = value ? GET(value) : null;	};	self.make = function() {		var element = self.find('script');		if (!element.length) {			element = self.element;			self.element = self.element.parent();		}		var html = element.html();		element.remove();		self.template = Tangular.compile(html);		recompile = (/data-jc="|data-bind="/).test(html);	};	self.setter = function(value) {		if (!value || !value.length) {			config.hidden && self.aclass('hidden');			self.empty();			self.cache = '';			return;		}		var builder = [];		for (var i = 0, length = value.length; i < length; i++) {			var item = value[i];			item.index = i;			if (!filter || filter(item)) {				builder.push(self.template(item).replace(reg, function(text) {					return text.substring(0, 2) === '$i' ? i.toString() : self.path + '[' + i + ']';				}));			}		}		var tmp = builder.join('');		if (config.check) {			if (tmp === self.cache)				return;			self.cache = tmp;		}		self.html(tmp);		config.hidden && self.rclass('hidden');		recompile && self.compile();	};});COMPONENT('confirm', function(self) {	var is, visible = false;	self.readonly();	self.singleton();	self.nocompile();	self.make = function() {		self.aclass('ui-confirm hidden');		self.event('click', 'button', function() {			self.hide($(this).attrd('index').parseInt());		});		self.event('click', function(e) {			var t = e.target.tagName;			if (t !== 'DIV')				return;			var el = self.find('.ui-confirm-body');			el.aclass('ui-confirm-click');			setTimeout(function() {				el.rclass('ui-confirm-click');			}, 300);		});		$(window).on('keydown', function(e) {			if (!visible)				return;			var index = e.which === 13 ? 0 : e.which === 27 ? 1 : null;			if (index != null) {				self.find('button[data-index="{0}"]'.format(index)).trigger('click');				e.preventDefault();				e.stopPropagation();			}		});	};	self.show = self.confirm = function(message, buttons, fn) {		self.callback = fn;		var builder = [];		for (var i = 0; i < buttons.length; i++) {			var item = buttons[i];			var icon = item.match(/"[a-z0-9-]+"/);			if (icon) {				item = item.replace(icon, '').trim();				icon = '<i class="fa fa-{0}"></i>'.format(icon.toString().replace(/"/g, ''));			} else				icon = '';			builder.push('<button data-index="{1}">{2}{0}</button>'.format(item, i, icon));		}		self.content('ui-confirm-warning', '<div class="ui-confirm-message">{0}</div>{1}'.format(message.replace(/\n/g, '<br />'), builder.join('')));	};	self.hide = function(index) {		self.callback && self.callback(index);		self.rclass('ui-confirm-visible');		visible = false;		setTimeout2(self.id, function() {			$('html').rclass('ui-confirm-noscroll');			self.aclass('hidden');		}, 1000);	};	self.content = function(cls, text) {		$('html').aclass('ui-confirm-noscroll');		!is && self.html('<div><div class="ui-confirm-body"></div></div>');		self.find('.ui-confirm-body').empty().append(text);		self.rclass('hidden');		visible = true;		setTimeout2(self.id, function() {			self.aclass('ui-confirm-visible');		}, 5);	};});COMPONENT('crop', 'dragdrop:true;format:{0}', function(self, config) {	var canvas, context;	var img = new Image();	var can = false;	var is = false;	var zoom = 100;	var current = { x: 0, y: 0 };	var offset = { x: 0, y: 0 };	var cache = { x: 0, y: 0, zoom: 0 };	var width = 0;	var samesize = '';	self.bindvisible();	self.novalidate();	self.nocompile && self.nocompile();	self.getter = null;	img.crossOrigin = 'anonymous';	img.onload = function () {		can = true;		zoom = 100;		var width = config.width;		var height = config.height;		samesize = img.width === width && img.height === height && img.src.substring(0, 5) !== 'data:' ? $(img).attr('src') : '';		var nw = (img.width / 2);		var nh = (img.height / 2);		if (img.width > width) {			var p = (width / (img.width / 100));			zoom -= zoom - p;			nh = ((img.height * (p / 100)) / 2);			nw = ((img.width * (p / 100)) / 2);		}		// centering		cache.x = current.x = (width / 2) - nw;		cache.y = current.y = (height / 2) - nh;		cache.zoom = zoom;		self.redraw();	};	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'width':			case 'height':				cache.x = current.x = cache.y = current.y = 0;				setTimeout2(self._id + 'resize', self.redraw, 50);				break;		}	};	self.output = function(type) {		var canvas2 = document.createElement('canvas');		var ctx2 = canvas2.getContext('2d');		canvas2.width = config.width;		canvas2.height = config.height;		ctx2.clearRect(0, 0, canvas2.width, canvas2.height);		if (config.background) {			ctx2.fillStyle = config.background;			ctx2.fillRect(0, 0, canvas2.width, canvas2.height);		}		var w = img.width;		var h = img.height;		w = ((w / 100) * zoom);		h = ((h / 100) * zoom);		ctx2.drawImage(img, current.x || 0, current.y || 0, w, h);		return type ? canvas2.toDataURL(type) : !config.background && self.isTransparent(canvas2) ? canvas2.toDataURL('image/png') : canvas2.toDataURL('image/jpeg');	};	self.make = function() {		self.aclass('ui-crop');		self.append('<ul><li data-type="upload"><span class="fa fa-folder"></span></li><li data-type="plus"><span class="fa fa-plus"></span></li><li data-type="refresh"><span class="fa fa-refresh"></span></li><li data-type="minus"><span class="fa fa-minus"></span></li></ul><div>0x0</div><canvas width="200" height="100"></canvas>');		canvas = self.find('canvas')[0];		context = canvas.getContext('2d');		self.event('click', 'li', function(e) {			e.preventDefault();			e.stopPropagation();			var type = $(this).attr('data-type');			switch (type) {				case 'upload':					cmseditor.instance.filebrowser(img, (/^image\/*/));					self.change(true);					break;				case 'plus':					zoom += 3;					if (zoom > 300)						zoom = 300;					current.x -= 3;					current.y -= 3;					samesize = '';					self.redraw();					break;				case 'minus':					zoom -= 3;					if (zoom < 3)						zoom = 3;					current.x += 3;					current.y += 3;					samesize = '';					self.redraw();					break;				case 'refresh':					zoom = cache.zoom;					self.redraw();					break;			}		});		self.find('input').on('change', function() {			var file = this.files[0];			self.load(file);			this.value = '';		});		$(canvas).on('mousedown', function (e) {			if (self.disabled || !can)				return;			is = true;			var rect = canvas.getBoundingClientRect();			var x = e.clientX - rect.left;			var y = e.clientY - rect.top;			offset.x = x - current.x;			offset.y = y - current.y;			samesize = '';		});		config.dragdrop && $(canvas).on('dragenter dragover dragexit drop dragleave', function (e) {			if (self.disabled)				return;			e.stopPropagation();			e.preventDefault();			switch (e.type) {				case 'drop':					self.rclass('ui-crop-dragdrop');					break;				case 'dragenter':				case 'dragover':					self.aclass('ui-crop-dragdrop');					return;				case 'dragexit':				case 'dragleave':				default:					self.rclass('ui-crop-dragdrop');					return;			}			var files = e.originalEvent.dataTransfer.files;			files[0] && self.load(files[0]);		});		self.load = function(file) {			self.getOrientation(file, function(orient) {				var reader = new FileReader();				reader.onload = function () {					if (orient < 2) {						img.src = reader.result;						setTimeout(function() {							self.change(true);						}, 500);					} else {						SETTER('loading', 'show');						self.resetOrientation(reader.result, orient, function(url) {							SETTER('loading', 'hide', 500);							img.src = url;							self.change(true);						});					}				};				reader.readAsDataURL(file);			});		};		self.event('mousemove mouseup', function (e) {			if (e.type === 'mouseup') {				is && self.change();				is = false;				return;			}			if (self.disabled || !can || !is)				return;			var rect = canvas.getBoundingClientRect();			var x = e.clientX - rect.left;			var y = e.clientY - rect.top;			current.x = x - offset.x;			current.y = y - offset.y;			samesize = '';			self.redraw();		});	};	self.redraw = function() {		var ratio = width < config.width ? width / config.width : 1;		canvas.width = width < config.width ? width : config.width;		canvas.height = width < config.width ? (config.height / config.width) * width : config.height;		var w = img.width;		var h = img.height;		w = ((w / 100) * zoom);		h = ((h / 100) * zoom);		context.clearRect(0, 0, canvas.width, canvas.height);		if (config.background) {			context.fillStyle = config.background;			context.fillRect(0, 0, canvas.width, canvas.height);		}		self.find('div').html(config.width + 'x' + config.height);		context.drawImage(img, (current.x || 0) * ratio, (current.y || 0) * ratio, w * ratio, h * ratio);	};	self.setter = function(value) {		self.filename = '';		self.width(function(w) {			width = w;			if (value)				img.src = config.format.format(value);			else				self.redraw();		});	};	self.getUrl = function() {		return samesize;	};	self.isTransparent = function(canvas) {		var id = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);		for (var i = 0, length = id.data.length; i < length; i += 4) {			if (id.data[i + 3] !== 255)				return true;		}		return false;	};	// http://stackoverflow.com/a/32490603	self.getOrientation = function(file, callback) {		var reader = new FileReader();		reader.onload = function(e) {			var view = new DataView(e.target.result);			if (view.getUint16(0, false) != 0xFFD8)				return callback(-2);			var length = view.byteLength;			var offset = 2;			while (offset < length) {				var marker = view.getUint16(offset, false);				offset += 2;				if (marker == 0xFFE1) {					if (view.getUint32(offset += 2, false) != 0x45786966)						return callback(-1);					var little = view.getUint16(offset += 6, false) == 0x4949;					offset += view.getUint32(offset + 4, little);					var tags = view.getUint16(offset, little);					offset += 2;					for (var i = 0; i < tags; i++)						if (view.getUint16(offset + (i * 12), little) == 0x0112)							return callback(view.getUint16(offset + (i * 12) + 8, little));				} else if ((marker & 0xFF00) != 0xFF00)					break;				else					offset += view.getUint16(offset, false);			}			return callback(-1);		};		reader.readAsArrayBuffer(file.slice(0, 64 * 1024));	};	self.resetOrientation = function(src, srcOrientation, callback) {		var img = new Image();		img.onload = function() {			var width = img.width;			var height = img.height;			var canvas = document.createElement('canvas');			var ctx = canvas.getContext('2d');			// set proper canvas dimensions before transform & export			if (4 < srcOrientation && srcOrientation < 9) {				canvas.width = height;				canvas.height = width;			} else {				canvas.width = width;				canvas.height = height;			}			switch (srcOrientation) {				case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;				case 3: ctx.transform(-1, 0, 0, -1, width, height); break;				case 4: ctx.transform(1, 0, 0, -1, 0, height); break;				case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;				case 6: ctx.transform(0, 1, -1, 0, height, 0); break;				case 7: ctx.transform(0, -1, -1, 0, height, width); break;				case 8: ctx.transform(0, -1, 1, 0, 0, width); break;			}			ctx.drawImage(img, 0, 0);			callback(canvas.toDataURL());		};		img.src = src;	};});COMPONENT('fontawesomebox', 'height:300', function(self, config) {	var container, input, icon, prev;	var template = '<li data-search="{0}"><i class="{1}"></i></li>';	var skip = false;	var refresh = false;	self.init = function() {		W.fontawesomeicons = 'address-book,address-card,adjust,align-center,align-justify,align-left,align-right,allergies,ambulance,american-sign-language-interpreting,anchor,angle-double-down,angle-double-left,angle-double-right,angle-double-up,angle-down,angle-left,angle-right,angle-up,archive,arrow-alt-circle-down,arrow-alt-circle-left,arrow-alt-circle-right,arrow-alt-circle-up,arrow-circle-down,arrow-circle-left,arrow-circle-right,arrow-circle-up,arrow-down,arrow-left,arrow-right,arrow-up,arrows-alt,arrows-alt-h,arrows-alt-v,assistive-listening-systems,asterisk,at,audio-description,backward,balance-scale,ban,band-aid,barcode,bars,baseball-ball,basketball-ball,bath,battery-empty,battery-full,battery-half,battery-quarter,battery-three-quarters,bed,beer,bell,bell-slash,bicycle,binoculars,birthday-cake,blender,blind,bold,bolt,bomb,book,book-open,bookmark,bowling-ball,box,box-open,boxes,braille,briefcase,briefcase-medical,broadcast-tower,broom,bug,building,bullhorn,bullseye,burn,bus,calculator,calendar,calendar-alt,calendar-check,calendar-minus,calendar-plus,calendar-times,camera,camera-retro,capsules,car,caret-down,caret-left,caret-right,caret-square-down,caret-square-left,caret-square-right,caret-square-up,caret-up,cart-arrow-down,cart-plus,certificate,chalkboard,chalkboard-teacher,chart-area,chart-bar,chart-line,chart-pie,check,check-circle,check-square,chess,chess-bishop,chess-board,chess-king,chess-knight,chess-pawn,chess-queen,chess-rook,chevron-circle-down,chevron-circle-left,chevron-circle-right,chevron-circle-up,chevron-down,chevron-left,chevron-right,chevron-up,child,church,circle,circle-notch,clipboard,clipboard-check,clipboard-list,clock,clone,closed-captioning,cloud,cloud-download-alt,cloud-upload-alt,code,code-branch,coffee,cog,cogs,coins,columns,comment,comment-alt,comment-dots,comment-slash,comments,compact-disc,compass,compress,copy,copyright,couch,credit-card,crop,crosshairs,crow,crown,cube,cubes,cut,database,deaf,desktop,diagnoses,dice,dice-five,dice-four,dice-one,dice-six,dice-three,dice-two,divide,dna,dollar-sign,dolly,dolly-flatbed,donate,door-closed,door-open,dot-circle,dove,download,dumbbell,edit,eject,ellipsis-h,ellipsis-v,envelope,envelope-open,envelope-square,equals,eraser,euro-sign,exchange-alt,exclamation,exclamation-circle,exclamation-triangle,expand,expand-arrows-alt,external-link-alt,external-link-square-alt,eye,eye-dropper,eye-slash,fast-backward,fast-forward,fax,feather,female,fighter-jet,file,file-alt,file-archive,file-audio,file-code,file-excel,file-image,file-medical,file-medical-alt,file-pdf,file-powerpoint,file-video,file-word,film,filter,fire,fire-extinguisher,first-aid,flag,flag-checkered,flask,folder,folder-open,font,football-ball,forward,frog,frown,futbol,gamepad,gas-pump,gavel,gem,genderless,gift,glass-martini,glasses,globe,golf-ball,graduation-cap,greater-than,greater-than-equal,h-square,hand-holding,hand-holding-heart,hand-holding-usd,hand-lizard,hand-paper,hand-peace,hand-point-down,hand-point-left,hand-point-right,hand-point-up,hand-pointer,hand-rock,hand-scissors,hand-spock,hands,hands-helping,handshake,hashtag,hdd,heading,headphones,heart,heartbeat,helicopter,history,hockey-puck,home,hospital,hospital-alt,hospital-symbol,hourglass,hourglass-end,hourglass-half,hourglass-start,i-cursor,id-badge,id-card,id-card-alt,image,images,inbox,indent,industry,infinity,info,info-circle,italic,key,keyboard,kiwi-bird,language,laptop,leaf,lemon,less-than,less-than-equal,level-down-alt,level-up-alt,life-ring,lightbulb,link,lira-sign,list,list-alt,list-ol,list-ul,location-arrow,lock,lock-open,long-arrow-alt-down,long-arrow-alt-left,long-arrow-alt-right,long-arrow-alt-up,low-vision,magic,magnet,male,map,map-marker,map-marker-alt,map-pin,map-signs,mars,mars-double,mars-stroke,mars-stroke-h,mars-stroke-v,medkit,meh,memory,mercury,microchip,microphone,microphone-alt,microphone-alt-slash,microphone-slash,minus,minus-circle,minus-square,mobile,mobile-alt,money-bill,money-bill-alt,money-bill-wave,money-bill-wave-alt,money-check,money-check-alt,moon,motorcycle,mouse-pointer,music,neuter,newspaper,not-equal,notes-medical,object-group,object-ungroup,outdent,paint-brush,palette,pallet,paper-plane,paperclip,parachute-box,paragraph,parking,paste,pause,pause-circle,paw,pen-square,pencil-alt,people-carry,percent,percentage,phone,phone-slash,phone-square,phone-volume,piggy-bank,pills,plane,play,play-circle,plug,plus,plus-circle,plus-square,podcast,poo,portrait,pound-sign,power-off,prescription-bottle,prescription-bottle-alt,print,procedures,project-diagram,puzzle-piece,qrcode,question,question-circle,quidditch,quote-left,quote-right,random,receipt,recycle,redo,redo-alt,registered,reply,reply-all,retweet,ribbon,road,robot,rocket,rss,rss-square,ruble-sign,ruler,ruler-combined,ruler-horizontal,ruler-vertical,rupee-sign,save,school,screwdriver,search,search-minus,search-plus,seedling,server,share,share-alt,share-alt-square,share-square,shekel-sign,shield-alt,ship,shipping-fast,shoe-prints,shopping-bag,shopping-basket,shopping-cart,shower,sign,sign-in-alt,sign-language,sign-out-alt,signal,sitemap,skull,sliders-h,smile,smoking,smoking-ban,snowflake,sort,sort-alpha-down,sort-alpha-up,sort-amount-down,sort-amount-up,sort-down,sort-numeric-down,sort-numeric-up,sort-up,space-shuttle,spinner,square,square-full,star,star-half,step-backward,step-forward,stethoscope,sticky-note,stop,stop-circle,stopwatch,store,store-alt,stream,street-view,strikethrough,stroopwafel,subscript,subway,suitcase,sun,superscript,sync,sync-alt,syringe,table,table-tennis,tablet,tablet-alt,tablets,tachometer-alt,tag,tags,tape,tasks,taxi,terminal,text-height,text-width,th,th-large,th-list,thermometer,thermometer-empty,thermometer-full,thermometer-half,thermometer-quarter,thermometer-three-quarters,thumbs-down,thumbs-up,thumbtack,ticket-alt,times,times-circle,tint,toggle-off,toggle-on,toolbox,trademark,train,transgender,transgender-alt,trash,trash-alt,tree,trophy,truck,truck-loading,truck-moving,tshirt,tty,tv,umbrella,underline,undo,undo-alt,universal-access,university,unlink,unlock,unlock-alt,upload,user,user-alt,user-alt-slash,user-astronaut,user-check,user-circle,user-clock,user-cog,user-edit,user-friends,user-graduate,user-lock,user-md,user-minus,user-ninja,user-plus,user-secret,user-shield,user-slash,user-tag,user-tie,user-times,users,users-cog,utensil-spoon,utensils,venus,venus-double,venus-mars,vial,vials,video,video-slash,volleyball-ball,volume-down,volume-off,volume-up,walking,wallet,warehouse,weight,wheelchair,wifi,window-close,window-maximize,window-minimize,window-restore,wine-glass,won-sign,wrench,x-ray,yen-sign,fab 500px,fab accessible-icon,fab accusoft,fab acquisitions-incorporated,fab adn,fab adversal,fab affiliatetheme,fab algolia,fab alipay,fab amazon,fab amazon-pay,fab amilia,fab android,fab angellist,fab angrycreative,fab angular,fab app-store,fab app-store-ios,fab apper,fab apple,fab apple-pay,fab asymmetrik,fab audible,fab autoprefixer,fab avianex,fab aviato,fab aws,fab bandcamp,fab behance,fab behance-square,fab bimobject,fab bitbucket,fab bitcoin,fab bity,fab black-tie,fab blackberry,fab blogger,fab blogger-b,fab bluetooth,fab bluetooth-b,fab btc,fab buromobelexperte,fab buysellads,fab cc-amazon-pay,fab cc-amex,fab cc-apple-pay,fab cc-diners-club,fab cc-discover,fab cc-jcb,fab cc-mastercard,fab cc-paypal,fab cc-stripe,fab cc-visa,fab centercode,fab chrome,fab cloudscale,fab cloudsmith,fab cloudversify,fab codepen,fab codiepie,fab connectdevelop,fab contao,fab cpanel,fab creative-commons,fab creative-commons-by,fab creative-commons-nc,fab creative-commons-nc-eu,fab creative-commons-nc-jp,fab creative-commons-nd,fab creative-commons-pd,fab creative-commons-pd-alt,fab creative-commons-remix,fab creative-commons-sa,fab creative-commons-sampling,fab creative-commons-sampling-plus,fab creative-commons-share,fab css3,fab css3-alt,fab cuttlefish,fab d-and-d,fab dashcube,fab delicious,fab deploydog,fab deskpro,fab deviantart,fab digg,fab digital-ocean,fab discord,fab discourse,fab dochub,fab docker,fab draft2digital,fab dribbble,fab dribbble-square,fab dropbox,fab drupal,fab dyalog,fab earlybirds,fab ebay,fab edge,fab elementor,fab ello,fab ember,fab empire,fab envira,fab erlang,fab ethereum,fab etsy,fab expeditedssl,fab facebook,fab facebook-f,fab facebook-messenger,fab facebook-square,fab firefox,fab first-order,fab first-order-alt,fab firstdraft,fab flickr,fab flipboard,fab fly,fab font-awesome,fab font-awesome-alt,fab font-awesome-flag,fab fonticons,fab fonticons-fi,fab fort-awesome,fab fort-awesome-alt,fab forumbee,fab foursquare,fab free-code-camp,fab freebsd,fab fulcrum,fab galactic-republic,fab galactic-senate,fab get-pocket,fab gg,fab gg-circle,fab git,fab git-square,fab github,fab github-alt,fab github-square,fab gitkraken,fab gitlab,fab gitter,fab glide,fab glide-g,fab gofore,fab goodreads,fab goodreads-g,fab google,fab google-drive,fab google-play,fab google-plus,fab google-plus-g,fab google-plus-square,fab google-wallet,fab gratipay,fab grav,fab gripfire,fab grunt,fab gulp,fab hacker-news,fab hacker-news-square,fab hackerrank,fab hips,fab hire-a-helper,fab hooli,fab hornbill,fab hotjar,fab houzz,fab html5,fab hubspot,fab imdb,fab instagram,fab internet-explorer,fab ioxhost,fab itunes,fab itunes-note,fab java,fab jedi-order,fab jenkins,fab joget,fab joomla,fab js,fab js-square,fab jsfiddle,fab kaggle,fab keybase,fab keycdn,fab kickstarter,fab kickstarter-k,fab korvue,fab laravel,fab lastfm,fab lastfm-square,fab leanpub,fab less,fab line,fab linkedin,fab linkedin-in,fab linode,fab linux,fab lyft,fab magento,fab mailchimp,fab mandalorian,fab markdown,fab mastodon,fab maxcdn,fab medapps,fab medium,fab medium-m,fab medrt,fab meetup,fab megaport,fab microsoft,fab mix,fab mixcloud,fab mizuni,fab modx,fab monero,fab napster,fab neos,fab nimblr,fab nintendo-switch,fab node,fab node-js,fab npm,fab ns8,fab nutritionix,fab odnoklassniki,fab odnoklassniki-square,fab old-republic,fab opencart,fab openid,fab opera,fab optin-monster,fab osi,fab page4,fab pagelines,fab palfed,fab patreon,fab paypal,fab periscope,fab phabricator,fab phoenix-framework,fab phoenix-squadron,fab php,fab pied-piper,fab pied-piper-alt,fab pied-piper-hat,fab pied-piper-pp,fab pinterest,fab pinterest-p,fab pinterest-square,fab playstation,fab product-hunt,fab pushed,fab python,fab qq,fab quinscape,fab quora,fab r-project,fab ravelry,fab react,fab readme,fab rebel,fab red-river,fab reddit,fab reddit-alien,fab reddit-square,fab renren,fab replyd,fab researchgate,fab resolving,fab rev,fab rocketchat,fab rockrms,fab safari,fab sass,fab schlix,fab scribd,fab searchengin,fab sellcast,fab sellsy,fab servicestack,fab shirtsinbulk,fab shopware,fab simplybuilt,fab sistrix,fab sith,fab skyatlas,fab skype,fab slack,fab slack-hash,fab slideshare,fab snapchat,fab snapchat-ghost,fab snapchat-square,fab soundcloud,fab speakap,fab spotify,fab squarespace,fab stack-exchange,fab stack-overflow,fab staylinked,fab steam,fab steam-square,fab steam-symbol,fab sticker-mule,fab strava,fab stripe,fab stripe-s,fab studiovinari,fab stumbleupon,fab stumbleupon-circle,fab superpowers,fab supple,fab teamspeak,fab telegram,fab telegram-plane,fab tencent-weibo,fab the-red-yeti,fab themeco,fab themeisle,fab trade-federation,fab trello,fab tripadvisor,fab tumblr,fab tumblr-square,fab twitch,fab twitter,fab twitter-square,fab typo3,fab uber,fab uikit,fab uniregistry,fab untappd,fab usb,fab ussunnah,fab vaadin,fab viacoin,fab viadeo,fab viadeo-square,fab viber,fab vimeo,fab vimeo-square,fab vimeo-v,fab vine,fab vk,fab vnv,fab vuejs,fab weebly,fab weibo,fab weixin,fab whatsapp,fab whatsapp-square,fab whmcs,fab wikipedia-w,fab windows,fab wix,fab wolf-pack-battalion,fab wordpress,fab wordpress-simple,fab wpbeginner,fab wpexplorer,fab wpforms,fab xbox,fab xing,fab xing-square,fab y-combinator,fab yahoo,fab yandex,fab yandex-international,fab yelp,fab yoast,fab youtube,fab youtube-square,fab zhihu'.split(',');	};	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-fontawesomebox');		self.css('height', config.height + 'px');		self.append('<div class="ui-fontawesomebox-search"><span><i class="fa fa-search clearsearch"></i></span><div><input type="text" maxlength="50" placeholder="{0}" /></div></div><div class="ui-fontawesomebox-search-empty"></div><div class="ui-fontawesomebox-icons"><ul style="height:{1}px"></ul></div>'.format(config.search, config.height - 40));		container = $(self.find('.ui-fontawesomebox-icons').find('ul')[0]);		input = self.find('input');		icon = self.find('.ui-fontawesomebox-search').find('i');		self.event('click', '.clearsearch', function() {			input.val('').trigger('keydown');		});		self.event('click', 'li', function() {			var el = $(this);			var val = '';			if (!el.hclass('selected'))				val = el.find('i').attr('class');			skip = true;			config.exec && EXEC(config.exec, val, self);			self.set(val);			self.change(true);		});		self.event('keydown', 'input', function() {			var self = this;			setTimeout2(self.id, function() {				var hide = [];				var show = [];				var value = self.value.toSearch();				container.find('li').each(function() {					if (value && this.getAttribute('data-search').toSearch().indexOf(value) === -1)						hide.push(this);					else						show.push(this);				});				$(hide).aclass('hidden');				$(show).rclass('hidden');				icon.tclass('fa-times', !!value).tclass('fa-search', !value);			}, 300);		});	};	self.configure = function (key, value, init) {		if (init)			return;		switch (key) {			case 'height':				self.css('height', value + 'px');				container.css('height', value - (38) + 'px');				break;		}	};	self.released = function(is) {		if (is) {			container.empty();		} else {			self.render();			refresh && self.refresh();		}	};	self.render = function() {		var builder = [];		var icons = W.fontawesomeicons;		for (var i = 0, length = icons.length; i < length; i++) {			var icon = icons[i];			builder.push(template.format(icon, icon.indexOf(' ') === -1 ? ('fa fa-' + icon) : icon.replace(' ', ' fa-')));		}		container.empty();		input.val('').trigger('keydown');		container.html(builder.join(''));	};	self.setter = function(value) {		prev && prev.rclass('selected');		if (value) {			if (value.indexOf('fa-') === -1)				value = 'fa-' + value;			var index = value.indexOf(' ');			if (index === -1)				value = '.' + value;			else				value = '.' + value.substring(index + 1);			var fa = container.find(value);			prev = fa.parent().aclass('selected');			setTimeout(function() {				!skip && prev.length && prev.rescroll(-40);			}, 100);		}		skip = false;		refresh = true;	};});COMPONENT('multioptions', function(self) {	var Tarea = Tangular.compile('<textarea class="ui-moi-save ui-moi-value-inputarea" data-name="{{ name }}"{{ if def }} placeholder="{{ def }}"{{ fi }}{{ if max }} maxlength="{{ max }}"{{ fi }} data-type="text">{{ value }}</textarea>');	var Tinput = Tangular.compile('<input class="ui-moi-value-inputtext ui-moi-save" data-name="{{ name }}" type="text" value="{{ value }}"{{ if def }} placeholder="{{ def }}"{{ fi }}{{ if max }} maxlength="{{ max }}"{{ fi }} data-type="text" />');	var Tfile = Tangular.compile('<div class="ui-moi-value-inputfile-buttons"><span class="multioptions-operation" data-name="file"><i class="fa fa-folder"></i></span></div><div class="ui-moi-value-inputfile"><input class="ui-moi-save" data-name="{{ name }}" type="text" value="{{ value }}"{{ if def }} placeholder="{{ def }}"{{ fi }}{{ if max }} maxlength="{{ max }}"{{ fi }} data-type="text" /></div>');	var Tselect = Tangular.compile('<div class="ui-moi-value-select"><i class="fa fa-chevron-down"></i><select data-name="{{ name }}" class="ui-moi-save ui-multioptions-select">{{ foreach m in values }}<option value="{{ $index }}"{{ if value === m.value }} selected="selected"{{ fi }}>{{ m.text }}</option>{{ end }}</select></div>');	var Tnumber = Tangular.compile('<div class="ui-moi-value-inputnumber-buttons"><span class="multioptions-operation" data-type="number" data-step="{{ step }}" data-name="plus" data-max="{{ max }}" data-min="{{ min }}"><i class="fa fa-plus"></i></span><span class="multioptions-operation" data-type="number" data-name="minus" data-step="{{ step }}" data-max="{{ max }}" data-min="{{ min }}"><i class="fa fa-minus"></i></span></div><div class="ui-moi-value-inputnumber"><input data-name="{{ name }}" class="ui-moi-save ui-moi-value-numbertext" type="text" value="{{ value }}"{{ if def }} placeholder="{{ def }}"{{ fi }} data-max="{{ max }}" data-min="{{ max }}" data-type="number" /></div>');	var Tboolean = Tangular.compile('<div data-name="{{ name }}" data-type="boolean" class="ui-moi-save multioptions-operation ui-moi-value-boolean{{ if value }} checked{{ fi }}"><i class="fa fa-check"></i></div>');	var Tdate = Tangular.compile('<div class="ui-moi-value-inputdate-buttons"><span class="multioptions-operation" data-type="date" data-name="date"><i class="fa fa-calendar"></i></span></div><div class="ui-moi-value-inputdate"><input class="ui-moi-save ui-moi-date" data-name="{{ name }}" type="text" value="{{ value | format(\'yyyy-MM-dd\') }}" placeholder="dd.mm.yyyy" maxlength="10" data-type="date" /></div>');	var Tcolor = null;	var skip = false;	var mapping = null;	var dep = {};	var pending = 0;	self.getter = null;	self.novalidate();	self.nocompile();	self.init = function() {		window.Tmultioptionscolor = Tangular.compile('<div class="ui-moi-value-colors ui-moi-save" data-name="{{ name }}" data-value="{{ value }}">{0}</div>'.format(['#ED5565', '#DA4453', '#FC6E51', '#E9573F', '#FFCE54', '#F6BB42', '#A0D468', '#8CC152', '#48CFAD', '#37BC9B', '#4FC1E9', '#3BAFDA', '#5D9CEC', '#4A89DC', '#AC92EC', '#967ADC', '#EC87C0', '#D770AD', '#F5F7FA', '#E6E9ED', '#CCD1D9', '#AAB2BD', '#656D78', '#434A54', '#000000'].map(function(n) { return '<span data-value="{0}" data-type="color" class="multioptions-operation" style="background-color:{0}"><i class="fa fa-check-circle"></i></span>'.format(n); }).join('')));	};	self.form = function() {};	self.make = function() {		Tcolor = window.Tmultioptionscolor;		self.aclass('ui-multioptions');		var el = self.find('script');		if (el.length) {			self.remap(el.html());			el.remove();		}		self.event('click', '.multioptions-operation', function(e) {			var el = $(this);			var name = el.attrd('name');			var type = el.attrd('type');			e.stopPropagation();			if (name === 'file') {				el = el.parent().parent().find('input');				cmseditor.instance.filebrowser(function(url) {					el.val(url);					self.$save();				});				return;			}			if (type === 'date') {				el = el.parent().parent().find('input');				SETTER('calendar', 'show', el, el.val().parseDate(), function(date) {					el.val(date.format('yyyy-MM-dd'));					self.$save();				});				return;			}			if (type === 'color') {				el.parent().find('.selected').rclass('selected');				el.aclass('selected');				self.$save();				return;			}			if (type === 'boolean') {				el.tclass('checked');				self.$save();				return;			}			if (type === 'number') {				var input = el.parent().parent().find('input');				var step = (el.attrd('step') || '0').parseInt();				var min = el.attrd('min');				var max = el.attrd('max');				if (!step)					step = 1;				if (min)					min = min.parseInt();				if (max)					max = max.parseInt();				var value;				if (name === 'plus') {					value = input.val().parseInt() + step;					if (max !== 0 && max && value > max)						value = max;					input.val(value);				} else {					value = input.val().parseInt() - step;					if (min !== 0 && min && value < min)						value = min;					input.val(value);				}				self.$save();				return;			}			self.form(type, el.parent().parent().find('input'), name);			return;		});		self.event('change', 'select', self.$save);		self.event('input', 'input,textarea', self.$save);		self.event('click', '.ui-moi-date', function(e) {			e.stopPropagation();		});		self.event('focus', '.ui-moi-date', function() {			var el = $(this);			SETTER('calendar', 'toggle', el, el.val().parseDate(), function(date) {				el.val(date.format('yyyy-MM-dd'));				self.$save();			});		});	};	self.remap = function(js) {		var fn = new Function('option', js);		mapping = {};		dep = {};		pending = 0;		fn(self.mapping);		self.redraw();	};	self.redraw = function() {		if (pending > 0) {			setTimeout(self.redraw, 500);			return;		}		self.refresh();		self.change(false);		self.$save();	};	self.remap2 = function(callback) {		mapping = {};		dep = {};		pending = 0;		callback(self.mapping);		self.redraw();	};	self.mapping = function(key, label, def, type, max, min, step, validator) {		var T = typeof(type);		if (T === 'number') {			validator = step;			step = min;			min = max;			max = type;			type = 'number';		} else if (!type)			type = def instanceof Date ? 'date' : typeof(def);		var values, multiline;		if (type instanceof Array) {			values = [];			type.forEach(function(val) {				values.push({ text: val.text === undefined ? val : val.text, value: val.value === undefined ? val : val.value });			});			type = 'array';		}		var external = false;		if (T === 'string') {			var tmp = type.substring(0, 6);			external = type.substring(0, 1) === '/' || tmp === 'http:/' || tmp === 'https:';			if (type.toLowerCase() === 'multiline') {				multiline = true;				type = 'string';			}		}		var t = (type || '').toLowerCase();		switch (t) {			case 'posts':			case 'languages':			case 'signals':			case 'notices':			case 'navigations':				values = [{ value: '', text: '' }];				var nav = common.dependencies[t];				for (var i = 0; i < nav.length; i++) {					var n = nav[i];					values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'parts':				values = [{ value: '', text: '' }];				var items = GET('%parts');				for (var i = 0; i < items.length; i++) {					var n = items[i];					values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'inlineparts':				values = [{ value: '', text: '' }];				var items = GET('%parts');				for (var i = 0; i < items.length; i++) {					var n = items[i];					if (n.category === 'Inline' || n.category === 'Custom')						values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'newsletterparts':				values = [{ value: '', text: '' }];				var items = GET('%parts');				for (var i = 0; i < items.length; i++) {					var n = items[i];					if (n.category === 'Newsletter' || n.category === 'Custom')						values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'contentparts':			case 'columnsparts':				values = [{ value: '', text: '' }];				var items = GET('%parts');				for (var i = 0; i < items.length; i++) {					var n = items[i];					if (n.category === 'Content' || n.category === 'Columns' || n.category === 'Custom')						values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'layoutsparts':				values = [{ value: '', text: '' }];				var items = GET('%parts');				for (var i = 0; i < items.length; i++) {					var n = items[i];					if (n.category === 'Layouts' || n.category === 'Custom')						values.push({ value: n.id, text: n.name });				}				type = 'array';				break;			case 'partial':				values = [{ value: '', text: '' }];				var pages = GET('pages.grid.items');				if (pages && pages.length) {					for (var i = 0, length = pages.length; i < length; i++) {						var p = pages[i];						p.ispartial && values.push({ value: p.id, text: p.name });					}				}				type = 'array';				break;		}		if (validator && typeof(validator) !== 'function')			validator = null;		var bindmapping = function(values) {			dep[key] = values;			mapping[key] = { name: key, label: label, type: external ? 'array' : type.toLowerCase(), def: def, max: max, min: min, step: step, value: def, values: values, validator: validator, multiline: multiline };		};		if (external) {			pending++;			AJAX('GET ' + type, function(values) {				pending--;				bindmapping(values);			});		} else			bindmapping(values);	};	self.dependencies = function() {		return dep;	};	self.$save = function() {		setTimeout2('multioptions.' + self._id, self.save, 150);	};	self.save = function() {		var obj = self.get();		var values = self.find('.ui-moi-save');		Object.keys(mapping).forEach(function(key) {			var opt = mapping[key];			var el = values.filter('[data-name="{0}"]'.format(opt.name));			if (el.hclass('ui-moi-value-colors')) {				obj[key] = el.find('.selected').attrd('value');				return;			}			if (el.hclass('ui-moi-value-boolean')) {				obj[key] = el.hclass('checked');				return;			}			if (el.hclass('ui-moi-date')) {				obj[key] = el.val().parseDate();				return;			}			if (el.hclass('ui-moi-value-inputtext') || el.hclass('ui-moi-value-inputarea')) {				obj[key] = el.val();				return;			}			if (opt.type === 'file') {				obj[key] = el.val();				return;			}			if (el.hclass('ui-moi-value-numbertext')) {				obj[key] = el.val().parseFloat();				if (opt.max !== null && obj[key] > opt.max) {					obj[key] = opt.max;					el.val(opt.max);				}				if (opt.min !== null && obj[key] < opt.min) {					obj[key] = opt.min;					el.val(opt.min);				}				return;			}			if (el.hclass('ui-multioptions-select')) {				var index = el.val().parseInt();				var val = opt.values[index];				obj[key] = val ? val.value : null;				if (obj[key] && obj[key].value)					obj[key] = obj[key].value;				return;			}		});		skip = true;		self.set(obj);		self.change(true);	};	self.setter = function(options) {		if (!options || skip || !mapping) {			skip = false;			return;		}		var builder = [];		Object.keys(mapping).forEach(function(key) {			var option = mapping[key];			// option.name			// option.label			// option.type (lowercase)			// option.def			// option.value			// option.max			// option.min			// option.step			option.value = options[key] == null ? option.def : options[key];			var value = '';			switch (option.type.toLowerCase()) {				case 'string':					value = option.multiline ? Tarea(option) : Tinput(option);					break;				case 'file':					value = Tfile(option);					break;				case 'number':					value = Tnumber(option);					break;				case 'boolean':					value = Tboolean(option);					break;				case 'color':					value = Tcolor(option);					break;				case 'array':					value = Tselect(option);					break;				case 'date':					value = Tdate(option);					break;			}			builder.push('<div class="ui-multioptions-item{2}"><div class="ui-moi-name">{0}</div><div class="ui-moi-value">{1}</div></div>'.format(option.label, value, option.multiline ? ' ui-multioptions-multiline' : ''));		});		self.empty().html(builder);		self.find('.ui-moi-value-colors').each(function() {			var el = $(this);			var value = el.attrd('value');			el.find('[data-value="{0}"]'.format(value)).aclass('selected');		});	};});COMPONENT('fileupload', function(self, config) {	var id = 'fileupload' + self._id;	var input = null;	self.readonly();	self.nocompile();	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'disabled':				self.tclass('ui-disabled', value);				break;			case 'accept':				var el = $('#' + id);				if (value)					el.prop('accept', value);				else					el.removeProp('accept');				break;			case 'multiple':				var el = $('#' + id);				if (value)					el.prop('multiple', true);				else					el.removeProp('multiple');				break;			case 'label':				self.html(value);				break;		}	};	self.make = function() {		config.disabled && self.aclass('ui-disabled');		$(document.body).append('<input type="file" id="{0}" class="hidden"{1}{2} />'.format(id, config.accept ? ' accept="{0}"'.format(config.accept) : '', config.multiple ? ' multiple="multiple"' : ''));		input = $('#' + id);		self.event('click', function() {			!config.disabled && input.click();		});		input.on('change', function(evt) {			!config.disabled && self.upload(evt.target.files);		});	};	self.upload = function(files) {		var data = new FormData();		var el = this;		for (var i = 0, length = files.length; i < length; i++)			data.append('file' + i, files[i]);		SETTER('loading', 'show');		UPLOAD(config.url, data, function(response, err) {			el.value = '';			SETTER('loading', 'hide', 500);			if (err) {				SETTER('snackbar', 'warning', err.toString());				return;			}			self.change();			if (config.property) {				for (var i = 0, length = response.length; i < length; i++)					response[i] = response[i][config.property];			}			if (config.array)				self.push(response);			else				self.set(response);		});	};	self.destroy = function() {		input.off().remove();	};});COMPONENT('suggestion', function(self, config) {	var container, arrow, timeout, icon, input = null;	var is = false, selectedindex = 0, resultscount = 0;	self.items = null;	self.template = Tangular.compile('<li data-index="{{ $.index }}"{{ if selected }} class="selected"{{ fi }}>{{ name | raw }}</li>');	self.callback = null;	self.readonly();	self.singleton();	self.nocompile();	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'placeholder':				self.find('input').prop('placeholder', value);				break;		}	};	self.make = function() {		self.aclass('ui-suggestion hidden');		self.append('<span class="ui-suggestion-arrow"></span><div class="ui-suggestion-search"><span class="ui-suggestion-button"><i class="fa fa-search"></i></span><div><input type="text" placeholder="{0}" class="ui-suggestion-search-input" /></div></div><div class="ui-suggestion-container"><ul></ul></div>'.format(config.placeholder));		container = self.find('ul');		arrow = self.find('.ui-suggestion-arrow');		input = self.find('input');		icon = self.find('.ui-suggestion-button').find('.fa');		self.event('mouseenter mouseleave', 'li', function() {			container.find('li.selected').rclass('selected');			$(this).aclass('selected');			var arr = container.find('li:visible');			for (var i = 0; i < arr.length; i++) {				if ($(arr[i]).hclass('selected')) {					selectedindex = i;					break;				}			}		});		self.event('click', '.ui-suggestion-button', function(e) {			input.val('');			self.search();			e.stopPropagation();			e.preventDefault();		});		self.event('touchstart mousedown', 'li', function(e) {			self.callback && self.callback(self.items[+this.getAttribute('data-index')], $(self.target));			self.hide();			e.preventDefault();			e.stopPropagation();		});		$(document).on('click', function(e) {			is && !$(e.target).hclass('ui-suggestion-search-input') && self.hide(0);		});		$(window).on('resize', function() {			is && self.hide(0);		});		self.event('keydown', 'input', function(e) {			var o = false;			switch (e.which) {				case 27:					o = true;					self.hide();					break;				case 13:					o = true;					var sel = self.find('li.selected');					if (sel.length && self.callback)						self.callback(self.items[+sel.attrd('index')]);					self.hide();					break;				case 38: // up					o = true;					selectedindex--;					if (selectedindex < 0)						selectedindex = 0;					else						self.move();					break;				case 40: // down					o = true;					selectedindex++ ;					if (selectedindex >= resultscount)						selectedindex = resultscount;					else						self.move();					break;			}			if (o) {				e.preventDefault();				e.stopPropagation();			}		});		self.event('input', 'input', function() {			setTimeout2(self.ID, self.search, 100, null, this.value);		});		self.on('reflow', function() {			is && self.hide(1);		});		$(window).on('scroll', function() {			is && self.hide(1);		});	};	self.move = function() {		var counter = 0;		var scroller = container.parent();		var h = scroller.height();		container.find('li').each(function() {			var el = $(this);			if (el.hclass('hidden')) {				el.rclass('selected');				return;			}			var is = selectedindex === counter;			el.tclass('selected', is);			if (is) {				var t = (h * counter) - h;				if ((t + h * 4) > h)					scroller.scrollTop(t - h);				else					scroller.scrollTop(0);			}			counter++;		});	};	self.search = function(value) {		icon.tclass('fa-times', !!value).tclass('fa-search', !value);		if (!value) {			container.find('li').rclass('hidden');			resultscount = self.items.length;			selectedindex = 0;			self.move();			return;		}		resultscount = 0;		selectedindex = 0;		value = value.toSearch();		container.find('li').each(function() {			var el = $(this);			var val = this.innerHTML.toSearch();			var is = val.indexOf(value) === -1;			el.tclass('hidden', is);			if (!is)				resultscount++;		});		self.move();	};	self.show = function(orientation, target, items, callback) {		if (is) {			clearTimeout(timeout);			var obj = target instanceof jQuery ? target[0] : target;			if (self.target === obj) {				self.hide(0);				return;			}		}		target = $(target);		var type = typeof(items);		var item;		if (type === 'string')			items = self.get(items);		else if (type === 'function') {			callback = items;			items = (target.attrd('options') || '').split(';');			for (var i = 0, length = items.length; i < length; i++) {				item = items[i];				if (!item)					continue;				var val = item.split('|');				items[i] = { name: val[0], value: val[2] == null ? val[0] : val[2] };			}		}		if (!items) {			self.hide(0);			return;		}		self.items = items;		self.callback = callback;		input.val('');		var builder = [];		var indexer = {};		for (var i = 0, length = items.length; i < length; i++) {			item = items[i];			indexer.index = i;			!item.value && (item.value = item.name);			builder.push(self.template(item, indexer));		}		self.target = target[0];		var offset = target.offset();		container.html(builder);		switch (orientation) {			case 'left':				arrow.css({ left: '15px' });				break;			case 'right':				arrow.css({ left: '210px' });				break;			case 'center':				arrow.css({ left: '107px' });				break;		}		var options = { left: orientation === 'center' ? Math.ceil((offset.left - self.element.width() / 2) + (target.innerWidth() / 2)) : orientation === 'left' ? offset.left - 8 : (offset.left - self.element.width()) + target.innerWidth(), top: offset.top + target.innerHeight() + 10 };		self.css(options);		if (is)			return;		selectedindex = 0;		resultscount = items.length;		self.move();		self.search();		self.rclass('hidden');		setTimeout(function() {			self.aclass('ui-suggestion-visible');			self.emit('suggestion', true, self, self.target);		}, 100);		!isMOBILE && setTimeout(function() {			input.focus();		}, 500);		setTimeout(function() {			is = true;		}, 50);	};	self.hide = function(sleep) {		if (!is)			return;		clearTimeout(timeout);		timeout = setTimeout(function() {			self.rclass('ui-suggestion-visible').aclass('hidden');			self.emit('suggestion', false, self, self.target);			self.callback = null;			self.target = null;			is = false;		}, sleep ? sleep : 100);	};});COMPONENT('calendar', 'today:Set today;firstday:0;close:Close;yearselect:true;monthselect:true;yearfrom:-70 years;yearto:5 years', function(self, config) {	var skip = false;	var skipDay = false;	var visible = false;	self.days = EMPTYARRAY;	self.months = EMPTYARRAY;	self.months_short = EMPTYARRAY;	self.years_from;	self.years_to;	self.nocompile();	self.configure = function(key, value) {		switch (key) {			case 'days':				if (value instanceof Array)					self.days = value;				else					self.days = value.split(',').trim();				for (var i = 0; i < DAYS.length; i++) {					DAYS[i] = self.days[i];					self.days[i] = DAYS[i].substring(0, 2).toUpperCase();				}				break;			case 'months':				if (value instanceof Array)					self.months = value;				else					self.months = value.split(',').trim();				self.months_short = [];				for (var i = 0, length = self.months.length; i < length; i++) {					var m = self.months[i];					MONTHS[i] = m;					if (m.length > 4)						m = m.substring(0, 3) + '.';					self.months_short.push(m);				}				break;			case 'yearfrom':				if (value.indexOf('current') !== -1)					self.years_from = parseInt(new Date().format('yyyy'));				else					self.years_from = parseInt(new Date().add(value).format('yyyy'));				break;			case 'yearto':				if (value.indexOf('current') !== -1)					self.years_to = parseInt(new Date().format('yyyy'));				else					self.years_to = parseInt(new Date().add(value).format('yyyy'));				break;		}	};	self.readonly();	self.click = function() {};	function getMonthDays(dt) {		var m = dt.getMonth();		var y = dt.getFullYear();		if (m === -1) {			m = 11;			y--;		}		return (32 - new Date(y, m, 32).getDate());	}	self.calculate = function(year, month, selected) {		var d = new Date(year, month, 1, 12, 0);		var output = { header: [], days: [], month: month, year: year };		var firstDay = config.firstday;		var firstCount = 0;		var frm = d.getDay() - firstDay;		var today = new Date();		var ty = today.getFullYear();		var tm = today.getMonth();		var td = today.getDate();		var sy = selected ? selected.getFullYear() : -1;		var sm = selected ? selected.getMonth() : -1;		var sd = selected ? selected.getDate() : -1;		var days = getMonthDays(d);		if (frm < 0)			frm = 7 + frm;		while (firstCount++ < 7) {			output.header.push({ index: firstDay, name: self.days[firstDay] });			firstDay++;			if (firstDay > 6)				firstDay = 0;		}		var index = 0;		var indexEmpty = 0;		var count = 0;		var prev = getMonthDays(new Date(year, month - 1, 1, 12, 0)) - frm;		var cur;		for (var i = 0; i < days + frm; i++) {			var obj = { isToday: false, isSelected: false, isEmpty: false, isFuture: false, number: 0, index: ++count };			if (i >= frm) {				obj.number = ++index;				obj.isSelected = sy === year && sm === month && sd === index;				obj.isToday = ty === year && tm === month && td === index;				obj.isFuture = ty < year;				if (!obj.isFuture && year === ty) {					if (tm < month)						obj.isFuture = true;					else if (tm === month)						obj.isFuture = td < index;				}			} else {				indexEmpty++;				obj.number = prev + indexEmpty;				obj.isEmpty = true;				cur = d.add('-' + indexEmpty + ' days');			}			if (!obj.isEmpty)				cur = d.add(i + ' days');			obj.month = i >= frm && obj.number <= days ? d.getMonth() : cur.getMonth();			obj.year = i >= frm && obj.number <= days ? d.getFullYear() : cur.getFullYear();			obj.date = cur;			output.days.push(obj);		}		indexEmpty = 0;		for (var i = count; i < 42; i++) {			var cur = d.add(i + ' days');			var obj = { isToday: false, isSelected: false, isEmpty: true, isFuture: true, number: ++indexEmpty, index: ++count };			obj.month = cur.getMonth();			obj.year = cur.getFullYear();			obj.date = cur;			output.days.push(obj);		}		return output;	};	self.hide = function() {		if (visible) {			self.older = null;			self.aclass('hidden');			self.rclass('ui-calendar-visible');			visible = false;		}		return self;	};	self.toggle = function(el, value, callback, offset) {		if (self.older === el[0]) {			!self.hclass('hidden') && self.hide();		} else {			self.older = el[0];			self.show(el, value, callback, offset);		}		return self;	};	self.show = function(el, value, callback, offset) {		setTimeout(function() {			clearTimeout2('calendarhide');		}, 5);		if (!el)			return self.hide();		var off = el.offset();		var h = el.innerHeight();		var l = off.left + (offset || 0);		var t = off.top + h + 12;		var s = 250;		if (l + s > WW) {			var w = el.innerWidth();			l = (l + w) - s;		}		self.css({ left: l, top: t });		self.rclass('hidden');		self.click = callback;		self.date(value);		visible = true;		self.aclass('ui-calendar-visible', 50);		return self;	};	self.make = function() {		self.aclass('ui-calendar hidden');		var conf = {};		if (!config.days) {			conf.days = [];			for (var i = 0; i < DAYS.length; i++)				conf.days.push(DAYS[i].substring(0, 2).toUpperCase());		}		!config.months && (conf.months = MONTHS);		self.reconfigure(conf);		self.event('click', '.ui-calendar-today-a', function() {			var dt = new Date();			self.hide();			if (self.click) {				if (typeof(self.click) === 'string') {					SET(self.click, dt);					CHANGE(self.click, true);				} else					self.click(dt);			}		});		self.event('click', '.ui-calendar-day', function() {			var arr = this.getAttribute('data-date').split('-');			var dt = new Date(parseInt(arr[0]), parseInt(arr[1]), parseInt(arr[2]), 12, 0);			self.find('.ui-calendar-selected').rclass('ui-calendar-selected');			var el = $(this).aclass('ui-calendar-selected');			skip = !el.hclass('ui-calendar-disabled');			self.hide();			if (self.click) {				if (typeof(self.click) === 'string') {					SET(self.click, dt);					CHANGE(self.click, true);				} else					self.click(dt);			}		});		self.event('click', '.ui-calendar-header', function(e) {			e.stopPropagation();		});		self.event('change', '.ui-calendar-year', function(e) {			clearTimeout2('calendarhide');			e.preventDefault();			e.stopPropagation();			var arr = this.getAttribute('data-date').split('-');			var dt = new Date(parseInt(arr[0]), parseInt(arr[1]), 1, 12, 0);			dt.setFullYear(this.value);			skipDay = true;			self.date(dt);		});		self.event('change', '.ui-calendar-month', function(e){			clearTimeout2('calendarhide');			e.preventDefault();			e.stopPropagation();			var arr = this.getAttribute('data-date').split('-');			var dt = new Date(parseInt(arr[0]), parseInt(arr[1]), 1, 12, 0);			dt.setMonth(this.value);			skipDay = true;			self.date(dt);		});		self.event('click', 'button', function(e) {			e.preventDefault();			e.stopPropagation();			var arr = this.getAttribute('data-date').split('-');			var dt = new Date(parseInt(arr[0]), parseInt(arr[1]), 1, 12, 0);			switch (this.name) {				case 'prev':					dt.setMonth(dt.getMonth() - 1);					break;				case 'next':					dt.setMonth(dt.getMonth() + 1);					break;			}			var current_year = dt.getFullYear();			if (current_year < self.years_from || current_year > self.years_to)				return;			skipDay = true;			self.date(dt);		});		$(window).on('scroll click', function() {			visible && setTimeout2('calendarhide', function() {				EXEC('$calendar.hide');			}, 20);		});		window.$calendar = self;		self.on('reflow', function() {			visible && EXEC('$calendar.hide');		});	};	self.date = function(value) {		var clssel = 'ui-calendar-selected';		if (typeof(value) === 'string')			value = value.parseDate();		if (!value || isNaN(value.getTime())) {			self.find('.' + clssel).rclass(clssel);			value = NOW;		}		var empty = !value;		if (skipDay) {			skipDay = false;			empty = true;		}		if (skip) {			skip = false;			return;		}		if (!value)			value = NOW = new Date();		var output = self.calculate(value.getFullYear(), value.getMonth(), value);		var builder = [];		for (var i = 0; i < 42; i++) {			var item = output.days[i];			if (i % 7 === 0) {				builder.length && builder.push('</tr>');				builder.push('<tr>');			}			var cls = [];			item.isEmpty && cls.push('ui-calendar-disabled');			cls.push('ui-calendar-day');			!empty && item.isSelected && cls.push(clssel);			item.isToday && cls.push('ui-calendar-day-today');			builder.push('<td class="{0}" data-date="{1}-{2}-{3}"><div>{3}</div></td>'.format(cls.join(' '), item.year, item.month, item.number));		}		builder.push('</tr>');		var header = [];		for (var i = 0; i < 7; i++)			header.push('<th>{0}</th>'.format(output.header[i].name));		var years = value.getFullYear();		if (config.yearselect) {			years = '';			var current_year = value.getFullYear();			for (var i = self.years_from; i <= self.years_to; i++)				years += '<option value="{0}" {1}>{0}</option>'.format(i, i === current_year ? 'selected' : '');			years = '<select data-date="{0}-{1}" class="ui-calendar-year">{2}</select>'.format(output.year, output.month, years);		}		var months = self.months[value.getMonth()];		if (config.monthselect) {			months = '';			var current_month = value.getMonth();			for (var i = 0, l = self.months.length; i < l; i++)				months += '<option value="{0}" {2}>{1}</option>'.format(i, self.months[i], i === current_month ? 'selected' : '');			months = '<select data-date="{0}-{1}" class="ui-calendar-month">{2}</select>'.format(output.year, output.month, months);		}		self.html('<div class="ui-calendar-header"><button class="ui-calendar-header-prev" name="prev" data-date="{0}-{1}"><span class="fa fa-arrow-left"></span></button><div class="ui-calendar-header-info">{2} {3}</div><button class="ui-calendar-header-next" name="next" data-date="{0}-{1}"><span class="fa fa-arrow-right"></span></button></div><div class="ui-calendar-table"><table cellpadding="0" cellspacing="0" border="0"><thead>{4}</thead><tbody>{5}</tbody></table></div>'.format(output.year, output.month, months, years, header.join(''), builder.join('')) + (config.today ? '<div class="ui-calendar-today"><a href="javascript:void(0)">{0}</a><a href="javascript:void(0)" class="ui-calendar-today-a"><i class="fa fa-calendar"></i>{1}</a></div>'.format(config.close, config.today) : ''));	};});COMPONENT('mainprogress', function(self) {	var old = null;	self.singleton();	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-mainprogress hidden');	};	self.setter = function(value) {		!value && (value = 0);		if (old === value)			return;		if (value > 100)			value = 100;		else if (value < 0)			value = 0;		old = value >> 0;		self.element.stop().animate({ width: old + '%' }, 80).show();		self.tclass('hidden', old === 0 || old === 100);	};});COMPONENT('progress', 'animate:true', function(self, config) {	var container, old = null;	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-progress');		self.append('<div style="width:10%">0%</div>');		container = self.find('div');	};	self.setter = function(value) {		!value && (value = 0);		if (old === value)			return;		if (value > 100)			value = 100;		else if (value < 0)			value = 0;		old = value >> 0;		if (config.animate)			container.stop().animate({ width: old + '%' }, 80).show();		else			container.css({ width: old + '%' });		container.html(old + '%');	};});COMPONENT('pictures', function() {	var self = this;	self.skip = false;	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-pictures');	};	self.setter = function(value) {		if (typeof(value) === 'string')			value = value.split(',');		if (self.skip) {			self.skip = false;			return;		}		self.find('.fa,img').unbind('click');		if (!(value instanceof Array) || !value.length) {			self.empty();			return;		}		var builder = [];		for (var i = 0, length = value.length; i < length; i++) {			var id = value[i];			id && builder.push('<div data-id="{0}" class="col-xs-3 col-lg-2 m"><span class="fa fa-times"></span><img src="/images/small/{0}.jpg" class="img-responsive" alt="" /></div>'.format(id));		}		self.html(builder);		this.element.find('.fa').bind('click', function() {			var id = [];			$(this).parent().remove();			self.find('div').each(function() {				id.push($(this).attr('data-id'));			});			self.skip = true;			self.set(id);		});		this.element.find('img').bind('click', function() {			var selected = self.find('.selected');			var el = $(this);			el.toggleClass('selected');			if (!selected.length)				return;			var parent1 = el.parent();			var parent2 = selected.parent();			var id1 = parent1.attrd('id');			var id2 = parent2.attrd('id');			var arr = self.get();			var index1 = arr.indexOf(id1);			var index2 = arr.indexOf(id2);			arr[index1] = id2;			arr[index2] = id1;			parent1.attrd('id', id2);			parent2.attrd('id', id1);			var img1 = parent1.find('img');			var img2 = parent2.find('img');			var src1 = img1.attr('src');			img1.attr('src', img2.attr('src'));			img2.attr('src', src1);			setTimeout(function() {				self.skip = true;				img1.rclass('selected');				img2.rclass('selected');				self.change(true);				self.set(arr);			}, 200);		});	};});COMPONENT('textboxtags', function(self, config) {	var isString = false;	var container, content = null;	var refresh = false;	var W = window;	if (!W.$textboxtagstemplate)		W.$textboxtagstemplate = Tangular.compile('<div class="ui-textboxtags-tag" data-name="{{ name }}">{{ name }}<i class="fa fa-times-circle ui-textboxtags-remove"></i></div>');	self.nocompile();	var template = W.$textboxtagstemplate;	self.validate = function(value) {		return config.disabled || !config.required ? true : value && value.length > 0;	};	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'disabled':				self.tclass('ui-disabled', value);				self.state(1, 1);				self.find('input').prop('disabled', value);				break;			case 'required':				self.find('.ui-textboxtags-label').tclass('ui-textboxtags-label-required', value);				self.state(1, 1);				break;			case 'icon':				var fa = self.find('.ui-textboxtags-label > i');				if (fa.length && value)					fa.rclass().aclass('fa fa-' + value);				else					redraw = true;				break;			case 'placeholder':				self.find('input').prop('placeholder', value);				break;			case 'height':				self.find('.ui-textboxtags-values').css('height', value);				break;			case 'label':				redraw = true;				break;			case 'type':				self.type = value;				isString = self.type === 'string';				break;		}		redraw && setTimeout2('redraw' + self.id, function() {			refresh = true;			container.off();			self.redraw();			self.refresh();		}, 100);	};	self.redraw = function() {		var label = config.label || content;		var html = '<div class="ui-textboxtags-values"' + (config.height ? ' style="min-height:' + config.height + 'px"' : '') + '><input type="text" placeholder="' + (config.placeholder || '') + '" /></div>';		isString = self.type === 'string';		if (content.length) {			self.html('<div class="ui-textboxtags-label{0}">{1}{2}:</div><div class="ui-textboxtags">{3}</div>'.format((config.required ? ' ui-textboxtags-label-required' : ''), (config.icon ? '<i class="fa fa-' + config.icon + '"></i> ' : ''), label, html));		} else {			self.aclass('ui-textboxtags');			self.html(html);		}		container = self.find('.ui-textboxtags-values');		config.disabled && self.reconfigure('disabled:true');	};	self.make = function() {		self.aclass('ui-textboxtags-container');		content = self.html();		self.type = config.type || '';		self.redraw();		self.event('click', '.ui-textboxtags-remove', function(e) {			if (config.disabled)				return;			e.preventDefault();			e.stopPropagation();			var el = $(this);			var arr = self.get();			if (isString)				arr = self.split(arr);			if (!arr || !(arr instanceof Array) || !arr.length)				return;			var index = arr.indexOf(el.parent().attr('data-name'));			if (index === -1)				return;			arr.splice(index, 1);			self.set(isString ? arr.join(', ') : arr);			self.change(true);		});		self.event('click', function() {			!config.disabled && self.find('input').focus();		});		self.event('keydown', 'input', function(e) {			if (config.disabled)				return;			if (e.which === 8) {				if (this.value)					return;				var arr = self.get();				if (isString)					arr = self.split(arr);				if (!arr || !(arr instanceof Array) || !arr.length)					return;				arr.pop();				self.set(isString ? arr.join(', ') : arr);				self.change(true);				return;			}			if (e.which !== 13)				return;			e.preventDefault();			if (!this.value)				return;			var arr = self.get();			var value = this.value;			if (config.uppercase)				value = value.toUpperCase();			else if (config.lowercase)				value = value.toLowerCase();			if (isString)				arr = self.split(arr);			if (!(arr instanceof Array))				arr = [];			if (arr.indexOf(value) === -1)				arr.push(value);			else				return;			this.value = '';			self.set(isString ? arr.join(', ') : arr);			self.change(true);		});	};	self.split = function(value) {		if (!value)			return new Array(0);		var arr = value.split(',');		for (var i = 0, length = arr.length; i < length; i++)			arr[i] = arr[i].trim();		return arr;	};	self.setter = function(value) {		if (!refresh && NOTMODIFIED(self.id, value))			return;		refresh = false;		container.find('.ui-textboxtags-tag').remove();		if (!value || !value.length)			return;		var arr = isString ? self.split(value) : value;		var builder = '';		for (var i = 0, length = arr.length; i < length; i++)			builder += template({ name: arr[i] });		container.prepend(builder);	};	self.state = function(type) {		if (!type)			return;		var invalid = config.required ? self.isInvalid() : false;		if (invalid === self.$oldstate)			return;		self.$oldstate = invalid;		self.find('.ui-textboxtags').tclass('ui-textboxtags-invalid', invalid);	};});COMPONENT('websocket', 'reconnect:3000', function(self, config) {	var ws, url;	var queue = [];	var sending = false;	self.online = false;	self.readonly();	self.nocompile();	self.make = function() {		url = (config.url || '').env(true);		if (!url.match(/^(ws|wss):\/\//))			url = (location.protocol.length === 6 ? 'wss' : 'ws') + '://' + location.host + (url.substring(0, 1) !== '/' ? '/' : '') + url;		setTimeout(self.connect, 500);		self.destroy = self.close;	};	self.send = function(obj) {		queue.push(encodeURIComponent(JSON.stringify(obj)));		self.process();		return self;	};	self.process = function(callback) {		if (!ws || sending || !queue.length || ws.readyState !== 1) {			callback && callback();			return;		}		sending = true;		var async = queue.splice(0, 3);		async.wait(function(item, next) {			ws.send(item);			setTimeout(next, 5);		}, function() {			callback && callback();			sending = false;			queue.length && self.process();		});	};	self.close = function(isClosed) {		if (!ws)			return self;		self.online = false;		ws.onopen = ws.onclose = ws.onmessage = null;		!isClosed && ws.close();		ws = null;		EMIT('online', false);		return self;	};	function onClose() {		self.close(true);		setTimeout(self.connect, config.reconnect);	}	function onMessage(e) {		var data;		try {			data = PARSE(decodeURIComponent(e.data));			self.attrd('jc-path') && self.set(data);		} catch (e) {			WARN('WebSocket "{0}": {1}'.format(url, e.toString()));		}		data && EMIT('message', data);	}	function onOpen() {		self.online = true;		self.process(function() {			EMIT('online', true);		});	}	self.connect = function() {		ws && self.close();		setTimeout2(self.id, function() {			ws = new WebSocket(url.env(true));			ws.onopen = onOpen;			ws.onclose = onClose;			ws.onmessage = onMessage;		}, 100);		return self;	};});COMPONENT('donutchart', 'format:{{ value | format(0) }};size:0;tooltip:true;presentation:true;animate:true', function(self, config) {	var svg, g, selected, tooltip;	var strokew = 0;	var animate = true;	var indexer = 0;	var indexerskip = false;	var force = false;	var W = $(window);	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-donutchart');		self.append('<div class="ui-donutchart-tooltip"></div><svg></svg>');		svg = self.find('svg');		g = svg.asvg('g').attr('class', 'pieces');		tooltip = self.find('.ui-donutchart-tooltip');		W.on('resize', self.resize);		self.event('mouseenter touchstart', '.piece', function() {			self.select(+this.getAttribute('data-index'));			!indexerskip && config.presentation && setTimeout2(self.id + '.skip', self.next, 30000);			indexerskip = true;		});	};	self.select = function(index) {		var item = self.get()[index];		if (item === selected)			return;		self.find('.selected').rclass('selected').css('stroke-width', strokew);		selected = item;		var el = self.find('.piece' + (index + 1));		if (config.tooltip) {			var w = self.width();			tooltip.css('font-size', w / 15);			tooltip.html('<b>' + item.name + '</b><br />' + Tangular.render(config.format, item));		}		config.select && EXEC(config.select, item);		el.css('stroke-width', strokew.inc('+15%')).aclass('selected');		indexer = index;	};	self.destroy = function() {		W.off('resize', self.resize);	};	self.resize = function() {		setTimeout2('resize.' + self.id, function() {			animate = false;			force = true;			self.refresh();		}, 100);	};	self.next = function() {		if (self.removed)			return;		if (indexerskip) {			indexerskip = false;			return;		}		indexer++;		var tmp = self.get();		if (!tmp)			return;		if (!tmp[indexer])			indexer = 0;		self.select(indexer);		setTimeout2(self.id + '.next', self.next, 4000);	};	function arcradius(centerX, centerY, radius, degrees) {		var radians = (degrees - 90) * Math.PI / 180.0;		return { x: centerX + (radius * Math.cos(radians)), y: centerY + (radius * Math.sin(radians)) };	}	function arc(x, y, radius, startAngle, endAngle){		var start = arcradius(x, y, radius, endAngle);		var end = arcradius(x, y, radius, startAngle);		var largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;		var d = ['M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y].join(' ');		return d;	}	self.redraw = function(width, value) {		var sum = null;		for (var i = 0, length = value.length; i < length; i++) {			var item = value[i];			if (item.value == null)				item.value = 0;			var val = item.value + 1;			sum = sum ? sum + val : val;		}		var count = 0;		var beg = 0;		var end = 0;		var items = [];		for (var i = 0, length = value.length; i < length; i++) {			var item = value[i];			var p = (((item.value + 1) / sum) * 100).floor(2);			count += p;			if (i === length - 1 && count < 100)				p = p + (100 - count);			end = beg + ((360 / 100) * p);			items.push({ name: item.name, percentage: p, beg: beg, end: end });			beg = end;		}		if (!force && NOTMODIFIED(self.id, items))			return;		var size = width;		var half = size / 2;		var midpoint = size / 2.4;		strokew = (size / 6 >> 0).inc('-15%');		svg.attr('width', size);		svg.attr('height', size);		g.empty();		var pieces = [];		for (var i = 0, length = items.length; i < length; i++) {			var item = items[i];			if (item.percentage === 0)				continue;			if (item.end === 360)				item.end = 359.99;			pieces.push(g.asvg('path').attr('data-index', i).attr('data-beg', item.beg).attr('data-end', item.end).attr('stroke-width', strokew).attr('class', 'piece piece' + (i + 1)).attr('d', arc(half, half, midpoint, item.beg, animate ? item.beg : item.end)));		}		animate && pieces.wait(function(item, next) {			var beg = +item.attrd('beg');			var end = +item.attrd('end');			var diff = end - beg;			if (config.animate) {				item.animate({ end: diff }, { duration: 180, step: function(fx) {					item.attr('d', arc(half, half, midpoint, beg, beg + fx));				}, complete: function() {					next();				}});			} else {				item.attr('d', arc(half, half, midpoint, beg, end));				next();			}		});		selected = null;		animate = true;		force = false;		config.redraw && EXEC(config.redraw);		self.select(0);		if (config.presentation) {			indexerskip = false;			setTimeout(self.next, 4000);		}	};	self.setter = function(value) {		if (!value) {			g.empty();			return;		}		if (config.size) {			self.redraw(config.size, value);		} else {			self.width(function(width) {				self.redraw(width, value);			});		}	};});COMPONENT('tabmenu', 'class:selected', function(self, config) {	var old, oldtab;	self.readonly();	self.nocompile();	self.make = function() {		self.event('click', 'li', function() {			var el = $(this);			!el.hclass(config.class) && self.set(el.attrd('value'), 2);		});	};	self.setter = function(value) {		if (old === value)			return;		oldtab && oldtab.rclass(config.class);		oldtab = self.find('li[data-value="' + value + '"]').aclass(config.class);		old = value;	};});COMPONENT('error', function(self, config) {	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-error hidden');	};	self.setter = function(value) {		if (!(value instanceof Array) || !value.length) {			self.tclass('hidden', true);			return;		}		var builder = [];		for (var i = 0, length = value.length; i < length; i++)			builder.push('<div><span class="fa {1}"></span>{0}</div>'.format(value[i].error, 'fa-' + (config.icon || 'times-circle')));		self.html(builder.join(''));		self.tclass('hidden', false);	};});COMPONENT('barchart', 'pl:20;pt:10;pb:25;prselected:0;axisX:true;axisY:true;paddingbars:5;limit:0;paddinggroup:10;radius:2;offsetX:10;offsetY:10;templateY:{{ value | format(0) }};templateX:{{ value }};height:0', function(self, config) {	var svg, g, axis, selected;	var templateX, templateY;	var W = $(window);	self.readonly();	self.nocompile();	self.make = function() {		self.aclass('ui-barchart');		self.empty().append('<svg></svg>');		svg = self.find('svg');		axis = svg.asvg('g').attr('class', 'axisy');		g = svg.asvg('g').attr('class', 'bars');		selected = svg.asvg('text').attr('class', 'selected').attr('text-anchor', 'end');		W.on('resize', self.resize);		self.event('click mouseenter', 'rect', function(e) {			var rect = $(this);			var index = rect.attrd('index');			if (index === self.$selectedindex && e.type === 'mouseenter')				return;			self.$selectedindex = index;			var arr = index.split(',');			var item = self.get()[+arr[0]];			var value = item.values[+arr[1]];			selected.text(templateY({ value: value.y }));			if (e.type === 'mouseenter') {				setTimeout2(self.id, function() {					selected.text('');				}, 2000);			} else				clearTimeout2(self.id);		});	};	self.destroy = function() {		W.off('resize', self.resize);	};	self.resize = function() {		setTimeout2('resize.' + self.id, function() {			self.refresh();		}, 500);	};	self.configure = function(key, value, init) {		switch (key) {			case 'templateX':				templateX = Tangular.compile(value);				break;			case 'templateY':				templateY = Tangular.compile(value);				break;			default:				!init && self.resize();				break;		}	};	self.released = function(is) {		!is && setTimeout(self.refresh, 1000);	};	self.setter = function(value) {		if (!self.element[0].offsetParent) {			setTimeout(function() {				self.refresh();			}, 1000);			return;		}		if (!value) {			g.empty();			return;		}		var maxX = 0;		var maxY = 0;		var labels = [];		var paddingbars = config.paddingbars;		var paddinggroup = config.paddinggroup;		var len = value.length;		var size = value[0].values.length;		var width = config.width ? config.width : self.element.width();		var height = config.height ? config.height : (width / 100) * 60;		var barwidth = ((width - paddingbars - paddinggroup - config.pl) / (size * len));		var lines = {};		barwidth -= paddingbars + (paddinggroup / len);		for (var i = 0; i < len; i++) {			var item = value[i];			labels.push(item.name);			for (var j = 0, length = item.values.length; j < length; j++) {				var val = item.values[j];				maxX = Math.max(maxX, val.x);				maxY = Math.max(maxY, val.y);			}		}		if (config.limit)			maxY = config.limit;		svg.attr('width', width);		svg.attr('height', height);		selected.attr('transform', 'translate({0},30)'.format(width - config.prselected));		g.empty();		axis.empty();		lines.height = height - config.pt - config.pb;		var T = { value: null };		for (var i = 5; i > 0; i--) {			var val = i * 20;			var y = (((lines.height / 100) * val) + config.pt);			config.axisY && axis.asvg('line').attr('x1', 0).attr('x2', width).attr('y1', y).attr('y2', y).attr('class', 'axis');			T.value = (maxY / 100) * (100 - val);			axis.asvg('text').aclass('ylabel').attr('transform', 'translate({0},{1})'.format(config.offsetX, y - config.offsetY)).text(templateY(T));		}		var offsetX = config.pl + paddingbars + paddinggroup;		var posX = 0;		var offsetL = (len - 1) === 0 ? 0.5 : len - 1;		var offsetY =  config.pb;		for (var i = 0, length = size; i < length; i++) {			for (var j = 0; j < len; j++) {				var item = value[j];				var val = item.values[i];				var rect = g.asvg('rect');				var y = ((val.y / maxY) * 100) >> 0;				var x = posX + (barwidth * j);				var h = lines.height.inc('{0}%'.format(y));				x += offsetX + (paddingbars * j);				T.value = val.y;				rect.attr('x', x).attr('y', ((lines.height - h) + (offsetY / 2)) - 3).attr('width', barwidth).attr('height', h).attr('class', 'bar bar' + (j + 1)).attr('data-index', j + ',' + i);				config.radius && rect.attr('rx', config.radius).attr('ry', config.radius);			}			T.value = val.x;			var text = templateX(T);			var ax = posX + offsetX + (barwidth * len) + (paddingbars * len) + 2;			config.axisX && axis.asvg('line').attr('x1', ax).attr('x2', ax).attr('y1', 0).attr('y2', height - 25).attr('class', 'axis');			g.asvg('text').aclass('xlabel').text(text).attr('text-anchor', 'middle').attr('transform', 'translate({0},{1})'.format(posX + offsetX + (barwidth * offsetL), height - 6));			posX += (len * barwidth) + paddinggroup;			offsetX += len * paddingbars;		}	};});COMPONENT('shortcuts', function(self) {	var items = [];	var length = 0;	self.singleton();	self.readonly();	self.blind();	self.nocompile();	self.make = function() {		$(window).on('keydown', function(e) {			if (length && !e.isPropagationStopped()) {				for (var i = 0; i < length; i++) {					var o = items[i];					if (o.fn(e)) {						if (o.prevent) {							e.preventDefault();							e.stopPropagation();						}						setTimeout(function(o, e) {							o.callback(e);						}, 100, o, e);					}				}			}		});	};	self.exec = function(shortcut) {		var item = items.findItem('shortcut', shortcut.toLowerCase().replace(/\s/g, ''));		item && item.callback(EMPTYOBJECT);	};	self.register = function(shortcut, callback, prevent) {		shortcut.split(',').trim().forEach(function(shortcut) {			var builder = [];			var alias = [];			shortcut.split('+').trim().forEach(function(item) {				var lower = item.toLowerCase();				alias.push(lower);				switch (lower) {					case 'ctrl':					case 'alt':					case 'shift':						builder.push('e.{0}Key'.format(lower));						return;					case 'win':					case 'meta':					case 'cmd':						builder.push('e.metaKey');						return;					case 'space':						builder.push('e.keyCode===32');						return;					case 'tab':						builder.push('e.keyCode===9');						return;					case 'esc':						builder.push('e.keyCode===27');						return;					case 'enter':						builder.push('e.keyCode===13');						return;					case 'backspace':					case 'del':					case 'delete':						builder.push('(e.keyCode===8||e.keyCode===127)');						return;					case 'up':						builder.push('e.keyCode===38');						return;					case 'down':						builder.push('e.keyCode===40');						return;					case 'right':						builder.push('e.keyCode===39');						return;					case 'left':						builder.push('e.keyCode===37');						return;					case 'f1':					case 'f2':					case 'f3':					case 'f4':					case 'f5':					case 'f6':					case 'f7':					case 'f8':					case 'f9':					case 'f10':					case 'f11':					case 'f12':						var a = item.toUpperCase();						builder.push('e.key===\'{0}\''.format(a));						return;					case 'capslock':						builder.push('e.which===20');						return;				}				var num = item.parseInt();				if (num)					builder.push('e.which===' + num);				else					builder.push('e.key===\'{0}\''.format(item));			});			items.push({ shortcut: alias.join('+'), fn: new Function('e', 'return ' + builder.join('&&')), callback: callback, prevent: prevent });			length = items.length;		});		return self;	};});COMPONENT('preview', 'width:200;height:100;background:#FFFFFF;quality:90;schema:{file\\:base64,name\\:filename}', function(self, config) {	var empty, img, canvas, name, content = null;	self.readonly();	self.nocompile();	self.configure = function(key, value, init) {		if (init)			return;		var redraw = false;		switch (key) {			case 'width':			case 'height':			case 'background':				setTimeout2(self.id + 'reinit', self.reinit, 50);				break;			case 'label':			case 'icon':				redraw = true;				break;		}		redraw && setTimeout2(self.id + 'redraw', function() {			self.redraw();			self.refresh();		}, 50);	};	self.reinit = function() {		canvas = document.createElement('canvas');		canvas.width = config.width;		canvas.height = config.height;		var ctx = canvas.getContext('2d');		ctx.fillStyle = config.background;		ctx.fillRect(0, 0, config.width, config.height);		empty = canvas.toDataURL('image/png');		canvas = null;	};	self.resize = function(image) {		var canvas = document.createElement('canvas');		var ctx = canvas.getContext('2d');		canvas.width = config.width;		canvas.height = config.height;		ctx.fillStyle = config.background;		ctx.fillRect(0, 0, config.width, config.height);		var w = 0;		var h = 0;		var x = 0;		var y = 0;		if (image.width < config.width && image.height < config.height) {			w = image.width;			h = image.height;			x = (config.width / 2) - (image.width / 2);			y = (config.height / 2) - (image.height / 2);		} else if (image.width >= image.height) {			w = config.width;			h = image.height * (config.width / image.width);			y = (config.height / 2) - (h / 2);		} else {			h = config.height;			w = (image.width * (config.height / image.height)) >> 0;			x = (config.width / 2) - (w / 2);		}		ctx.drawImage(image, x, y, w, h);		var base64 = canvas.toDataURL('image/jpeg', config.quality * 0.01);		img.attr('src', base64);		self.upload(base64);	};	self.redraw = function() {		var label = config.label || content;		self.html((label ? '<div class="ui-preview-label">{0}{1}:</div>'.format(config.icon ? '<i class="fa fa-{0}"></i>'.format(config.icon) : '', label) : '') + '<input type="file" accept="image/*" class="hidden" /><img src="{0}" class="img-responsive" alt="" />'.format(empty, config.width, config.height));		img = self.find('img');		img.on('click', function() {			self.find('input').trigger('click');		});	};	self.make = function() {		content = self.html();		self.aclass('ui-preview');		self.reinit();		self.redraw();		self.event('change', 'input', function() {			var reader = new FileReader();			reader.onload = function () {				var image = new Image();				image.onload = function() {					self.resize(image);				};				image.src = reader.result;			};			var file = this.files[0];			name = file.name;			reader.readAsDataURL(file);			this.value = '';		});		self.event('dragenter dragover dragexit drop dragleave', function (e) {			e.stopPropagation();			e.preventDefault();			switch (e.type) {				case 'drop':					break;				case 'dragenter':				case 'dragover':					return;				case 'dragexit':				case 'dragleave':				default:					return;			}			var dt = e.originalEvent.dataTransfer;			if (dt && dt.files.length) {				var reader = new FileReader();				reader.onload = function () {					var image = new Image();					image.onload = function() {						self.resize(image);					};					image.src = reader.result;				};				var file = e.originalEvent.dataTransfer.files[0];				name = file.name;				reader.readAsDataURL(file);			}		});	};	self.upload = function(base64) {		if (base64) {			var data = (new Function('base64', 'filename', 'return ' + config.schema))(base64, name);			SETTER('loading', 'show');			AJAX('POST ' + config.url.env(true), data, function(response, err) {				SETTER('loading', 'hide', 100);				if (err) {					SETTER('snackbar', 'warning', err.toString());				} else {					self.change(true);					self.set(response);				}			});		}	};	self.setter = function(value) {		img.attr('src', value ? value : empty);	};});COMPONENT('features', 'height:37', function(self, config) {	var container, timeout, input, search, scroller = null;	var is = false, results = false, selectedindex = 0, resultscount = 0;	self.oldsearch = '';	self.items = null;	self.template = Tangular.compile('<li data-search="{{ $.search }}" data-index="{{ $.index }}"{{ if selected }} class="selected"{{ fi }}>{{ if icon }}<i class="fa fa-{{ icon }}"></i>{{ fi }}{{ name | raw }}</li>');	self.callback = null;	self.readonly();	self.singleton();	self.nocompile();	self.configure = function(key, value, init) {		if (init)			return;		switch (key) {			case 'placeholder':				self.find('input').prop('placeholder', value);				break;		}	};	self.make = function() {		self.aclass('ui-features-layer hidden');		self.append('<div class="ui-features"><div class="ui-features-search"><span><i class="fa fa-search"></i></span><div><input type="text" placeholder="{0}" class="ui-features-search-input" /></div></div><div class="ui-features-container"><ul></ul></div></div>'.format(config.placeholder));		container = self.find('ul');		input = self.find('input');		search = self.find('.ui-features');		scroller = self.find('.ui-features-container');		self.event('touchstart mousedown', 'li[data-index]', function(e) {			self.callback && self.callback(self.items[+this.getAttribute('data-index')]);			self.hide();			e.preventDefault();			e.stopPropagation();		});		$(document).on('touchstart mousedown', function(e) {			is && !$(e.target).hclass('ui-features-search-input') && self.hide(0);		});		$(window).on('resize', function() {			is && self.hide(0);		});		self.event('keydown', 'input', function(e) {			var o = false;			switch (e.which) {				case 27:					o = true;					self.hide();					break;				case 13:					o = true;					var sel = self.find('li.selected');					if (sel.length && self.callback)						self.callback(self.items[+sel.attr('data-index')]);					self.hide();					break;				case 38: // up					o = true;					selectedindex--;					if (selectedindex < 0)						selectedindex = 0;					else						self.move();					break;				case 40: // down					o = true;					selectedindex++ ;					if (selectedindex >= resultscount)						selectedindex = resultscount;					else						self.move();					break;			}			if (o && results) {				e.preventDefault();				e.stopPropagation();			}		});		self.event('keyup', 'input', function() {			setTimeout2(self.id, self.search, 100, null, this.value);		});	};	self.search = function(value) {		if (!value) {			if (self.oldsearch === value)				return;			self.oldsearch = value;			selectedindex = 0;			results = true;			resultscount = self.items.length;			container.find('li').rclass('hidden selected');			self.move();			return;		}		if (self.oldsearch === value)			return;		self.oldsearch = value;		value = value.toSearch().split(' ');		results = false;		resultscount = 0;		selectedindex = 0;		container.find('li').each(function() {			var el = $(this);			var val = el.attr('data-search');			var h = false;			for (var i = 0; i < value.length; i++) {				if (val.indexOf(value[i]) === -1) {					h = true;					break;				}			}			if (!h) {				results = true;				resultscount++;			}			el.tclass('hidden', h);			el.rclass('selected');		});		self.move();	};	self.move = function() {		var counter = 0;		var h = scroller.css('max-height').parseInt();		container.find('li').each(function() {			var el = $(this);			if (el.hclass('hidden'))				return;			var is = selectedindex === counter;			el.tclass('selected', is);			if (is) {				var t = (config.height * counter) - config.height;				if ((t + config.height * 5) > h)					scroller.scrollTop(t);				else					scroller.scrollTop(0);			}			counter++;		});	};	self.show = function(items, callback) {		if (is) {			clearTimeout(timeout);			self.hide(0);			return;		}		var type = typeof(items);		var item;		if (type === 'string')			items = self.get(items);		if (!items) {			self.hide(0);			return;		}		self.items = items;		self.callback = callback;		results = true;		resultscount = self.items.length;		input.val('');		var builder = [];		var indexer = {};		for (var i = 0, length = items.length; i < length; i++) {			item = items[i];			indexer.index = i;			indexer.search = (item.name + ' ' + (item.keywords || '')).trim().toSearch();			!item.value && (item.value = item.name);			builder.push(self.template(item, indexer));		}		container.html(builder);		var W = $(window);		var top = ((W.height() / 2) - (search.height() / 2)) - scroller.css('max-height').parseInt();		var options = { top: top, left: (W.width() / 2) - (search.width() / 2) };		search.css(options);		self.move();		if (is)			return;		self.rclass('hidden');		setTimeout(function() {			self.aclass('ui-features-visible');		}, 100);		!isMOBILE && setTimeout(function() {			input.focus();		}, 500);		is = true;		$('html,body').aclass('ui-features-noscroll');	};	self.hide = function(sleep) {		if (!is)			return;		clearTimeout(timeout);		timeout = setTimeout(function() {			self.aclass('hidden').rclass('ui-features-visible');			self.callback = null;			self.target = null;			is = false;			$('html,body').rclass('ui-features-noscroll');		}, sleep ? sleep : 100);	};});COMPONENT('listing', 'pages:3;count:20', function(self, config) {	var container, paginate, current, items, pages = 0;	var layout;	self.readonly();	self.nocompile();	self.make = function() {		self.find('script').each(function(index) {			var T =  Tangular.compile(this.innerHTML);			if (index)				layout = T;			else				self.template = T;		});		self.aclass('ui-listing');		self.html('<div class="ui-listing-container"></div><div class="ui-listing-paginate"></div>');		container = self.find('.ui-listing-container');		paginate = self.find('.ui-listing-paginate');		paginate.on('click', 'button', function() {			var index = $(this).attrd('index');			switch (index) {				case '+':					index = current + 1;					if (index > pages)						index = 1;					break;				case '-':					index = current - 1;					if (index < 1)						index = pages;					break;				default:					index = +index;					break;			}			self.page(index);		});	};	self.page = function(index) {		var builder = [];		var arr = items.takeskip(config.count, (index - 1) * config.count);		var g = { count: items.length, page: index, pages: pages };		for (var i = 0; i < arr.length; i++) {			g.index = i;			builder.push(self.template(arr[i], g));		}		current = index;		self.paginate(items.length, index);		container.html(layout ? layout({ page: index, pages: pages, body: builder.join(''), count: items.length }) : builder.join(''));	};	self.paginate = function(count, page) {		var max = config.pages;		var half = Math.ceil(max / 2);		pages = Math.ceil(count / config.count);		var pfrom = page - half;		var pto = page + half;		var plus = 0;		if (pfrom <= 0) {			plus = Math.abs(pfrom);			pfrom = 1;			pto += plus;		}		if (pto >= pages) {			pto = pages;			pfrom = pages - max;		}		if (pfrom <= 0)			pfrom = 1;		if (page < half + 1) {			pto++;			if (pto > pages)				pto--;		}		if (page < 2) {			var template = '<button data-index="{0}"><i class="fa fa-caret-{1}"></i></button>';			var builder = [];			builder.push(template.format('-', 'left'));			for (var i = pfrom; i < pto + 1; i++)				builder.push('<button class="ui-listing-page" data-index="{0}">{0}</button>'.format(i));			builder.push(template.format('+', 'right'));			paginate.html(builder.join(''));		} else {			var max = half * 2 + 1;			var cur = (pto - pfrom) + 1;			if (max > cur && pages > config.pages && pfrom > 1)				pfrom--;			paginate.find('.ui-listing-page[data-index]').each(function(index) {				var page = pfrom + index;				$(this).attrd('index', page).html(page);			});		}		paginate.find('.selected').rclass('selected');		paginate.find('.ui-listing-page[data-index="{0}"]'.format(page)).aclass('selected');		paginate.tclass('hidden', pages < 2);		self.tclass('hidden', count === 0);	};	self.setter = function(value) {		if (value) {			items = value;			self.page(1);		} else {			items = null;			container.empty();			paginate.empty();		}	};});COMPONENT('autocomplete', 'height:200', function(self, config) {	var container, old, onSearch, searchtimeout, searchvalue, blurtimeout, onCallback, datasource, offsetter, scroller;	var is = false;	var margin = {};	var prev;	var skipmouse = false;	self.template = Tangular.compile('<li{{ if index === 0 }} class="selected"{{ fi }} data-index="{{ index }}"><span>{{ name }}</span><span>{{ type }}</span></li>');	self.readonly();	self.singleton();	self.nocompile();	self.make = function() {		self.aclass('ui-autocomplete-container hidden');		self.html('<div class="ui-autocomplete"><ul></ul></div>');		scroller = self.find('.ui-autocomplete');		container = self.find('ul');		self.event('click', 'li', function(e) {			e.preventDefault();			e.stopPropagation();			if (onCallback) {				var val = datasource[+$(this).attrd('index')];				if (typeof(onCallback) === 'string')					SET(onCallback, val.value === undefined ? val.name : val.value);				else					onCallback(val, old);			}			self.visible(false);		});		self.event('mouseenter mouseleave', 'li', function(e) {			if (!skipmouse) {				prev && prev.rclass('selected');				prev = $(this).tclass('selected', e.type === 'mouseenter');			}		});		$(document).on('click', function() {			is && self.visible(false);		});		$(window).on('resize', function() {			self.resize();		});	};	self.prerender = function(value) {		self.render(value);	};	self.configure = function(name, value) {		switch (name) {			case 'height':				value && scroller.css('max-height', value);				break;		}	};	function keydown(e) {		var c = e.which;		var input = this;		if (c !== 38 && c !== 40 && c !== 13) {			if (c !== 8 && c < 32)				return;			clearTimeout(searchtimeout);			searchtimeout = setTimeout(function() {				var val = input.value;				if (!val)					return self.render(EMPTYARRAY);				if (searchvalue === val)					return;				searchvalue = val;				self.resize();				onSearch(val, self.prerender);			}, 200);			return;		}		if (!datasource || !datasource.length)			return;		var current = container.find('.selected');		if (c === 13) {			if (prev) {				prev = null;				self.visible(false);				if (current.length) {					if (onCallback) {						var val = datasource[+current.attrd('index')];						if (typeof(onCallback) === 'string')							SET(onCallback, val.value === undefined ? val.name : val.value);						else							onCallback(val, old);					}					e.preventDefault();					e.stopPropagation();				}			}			return;		}		e.preventDefault();		e.stopPropagation();		if (current.length) {			current.rclass('selected');			current = c === 40 ? current.next() : current.prev();		}		skipmouse = true;		!current.length && (current = self.find('li:{0}-child'.format(c === 40 ? 'first' : 'last')));		prev && prev.rclass('selected');		prev = current.aclass('selected');		var index = +current.attrd('index');		var h = current.innerHeight();		var offset = ((index + 1) * h) + (h * 2);		scroller.prop('scrollTop', offset > config.height ? offset - config.height : 0);		setTimeout2(self.ID + 'skipmouse', function() {			skipmouse = false;		}, 100);	}	function blur() {		clearTimeout(blurtimeout);		blurtimeout = setTimeout(function() {			self.visible(false);		}, 300);	}	self.visible = function(visible) {		clearTimeout(blurtimeout);		self.tclass('hidden', !visible);		is = visible;	};	self.resize = function() {		if (!offsetter || !old)			return;		var offset = offsetter.offset();		offset.top += offsetter.height();		offset.width = offsetter.width();		if (margin.left)			offset.left += margin.left;		if (margin.top)			offset.top += margin.top;		if (margin.width)			offset.width += margin.width;		self.css(offset);	};	self.attach = function(input, search, callback, left, top, width) {		self.attachelement(input, input, search, callback, left, top, width);	};	self.attachelement = function(element, input, search, callback, left, top, width) {		if (typeof(callback) === 'number') {			width = left;			left = top;			top = callback;			callback = null;		}		clearTimeout(searchtimeout);		if (input.setter)			input = input.find('input');		else			input = $(input);		if (input[0].tagName !== 'INPUT') {			input = input.find('input');		}		if (element.setter) {			if (!callback)				callback = element.path;			element = element.element;		}		if (old) {			old.removeAttr('autocomplete');			old.off('blur', blur);			old.off('keydown', keydown);		}		input.on('keydown', keydown);		input.on('blur', blur);		input.attr({ 'autocomplete': 'off' });		old = input;		margin.left = left;		margin.top = top;		margin.width = width;		offsetter = $(element);		self.resize();		self.refresh();		searchvalue = '';		onSearch = search;		onCallback = callback;		self.visible(false);	};	self.render = function(arr) {		datasource = arr;		if (!arr || !arr.length) {			self.visible(false);			return;		}		var builder = [];		for (var i = 0, length = arr.length; i < length; i++) {			var obj = arr[i];			obj.index = i;			if (!obj.name)				obj.name = obj.text;			builder.push(self.template(obj));		}		container.empty().append(builder.join(''));		skipmouse = true;		setTimeout(function() {			scroller.prop('scrollTop', 0);			skipmouse = false;		}, 100);		prev = container.find('.selected');		self.visible(true);	};});COMPONENT('avatar', function(self) {	var backgrounds = '#1abc9c,#2ecc71,#3498db,#9b59b6,#34495e,#16a085,#2980b9,#8e44ad,#2c3e50,#f1c40f,#e67e22,#e74c3c,#d35400,#c0392b'.split(',');	var themes = {};	self.readonly();	self.singleton();	window.avatarerror = function(image) {		var img = $(image);		var el = img.parent()[0];		el.$avatar = false;		el.$avatarerror = true;		el = $(el);		el.attr('title', img.attr('title'));		self.create(el);	};	self.rebind = function(el) {		var jq = el ? el.find('.avatar') : $('.avatar');		jq.each(function() {			!this.$avatar && self.create($(this));		});	};	self.create = function(el) {		var theme = el.attrd('a') || el.attrd('avatar') || 'default';		var options = themes[theme];		if (!options)			return false;		var url = el.attrd('a-url') || el.attrd('avatar-url');		var dom = el[0];		var name = dom.$avatarerror ? el.attr('title') : el.text();		dom.$avatar = true;		if (dom.$avatarerror) {			url = '';		} else {			var cls = el.attrd('a-class') || el.attrd('avatar-class') || options.class;			cls && el.tclass(cls);		}		el.aclass('ui-avatar-theme-' + theme);		if (url) {			el.html('<img src="{0}" alt="{1}" title="{1}" border="0" onerror="avatarerror(this)" />'.format(url, name));		} else {			var arr = name.trim().split(' ');			var initials = ((arr[0] || '').substring(0, 1) + (arr[1] || '').substring(0, 1)).toUpperCase();			var css = {};			var can = false;			if (!options.background) {				css.background = backgrounds[name.length % backgrounds.length];				can = true;			}			if (!options.color) {				can = true;				css.color = self.colorize(backgrounds[name.length % backgrounds.length], options.lighten);			}			can && el.css(css);			el.attr('title', name);			el.html(initials);		}	};	self.register = function(id, options) {		options = options.parseConfig('lighten:80;size:50;radius:100;weight:bold;font:Arial');		themes[id] = options;		var builder = [];		var name = '.ui-avatar-theme-' + id;		builder.push('display:block;width:{0}px;height:{0}px;text-align:center;vertical-align:middle;font-style:normal;font-size:{1}px;line-height:{2}px'.format(options.size, Math.floor(options.size / 2.5), (options.size + Math.floor(options.size / 20))));		options.radius && builder.push('border-radius:{0}px'.format(options.radius));		options.weight && builder.push('font-weight:' + options.weight);		options.font && builder.push('font-family:' + options.font);		options.background && builder.push('background:' + options.background);		options.weight && builder.push('font-weight:{0}'.format(options.weight));		options.color && builder.push('color:' + options.color);		var css = name + '{' + builder.join(';') + '}';		builder = [];		builder.push('width:{0}px;height:{0}px;'.format(options.size));		options.radius && builder.push('border-radius:{0}px'.format(options.radius));		css += '\n' + name + ' img{' + builder.join(';') + '}';		CSS(css, 'avatar-' + id);		setTimeout2(self.id + 'rebind', self.rebind, 100, 5);	};	self.refresh = self.rebind;	self.make = function() {		self.register('default', '');		self.on('component', function(component) {			setTimeout2(self._id, function() {				component.element && self.rebind(component.element);			}, 150);		});		setTimeout2(self._id + 'rebind', self.rebind, 100, 5);	};	// Thank to Chris Coyier (https://css-tricks.com/snippets/javascript/lighten-darken-color/)	// LightenDarkenColor	self.colorize = function(col, amt) {		var pound = false;		if (col[0] == '#') {			col = col.slice(1);			pound = true;		}		var num = parseInt(col,16);		var r = (num >> 16) + amt;		if (r > 255)			r = 255;		else if (r < 0) r = 0;		var b = ((num >> 8) & 0x00FF) + amt;		if (b > 255)			b = 255;		else if (b < 0)			b = 0;		var g = (num & 0x0000FF) + amt;		if (g > 255)			g = 255;		else if (g < 0)			g = 0;		return (pound ? '#': '') + (g | (b << 8) | (r << 16)).toString(16);	};});COMPONENT('importer', function(self, config) {	var init = false;	var clid = null;	self.readonly();	self.setter = function(value) {		if (config.if !== value) {			if (config.cleaner && init && !clid)				clid = setTimeout(self.clean, config.cleaner * 60000);			return;		}		if (clid) {			clearTimeout(clid);			clid = null;		}		if (init) {			config.reload && EXEC(config.reload);			return;		}		init = true;		self.import(config.url, function() {			config.reload && EXEC(config.reload);		});	};	self.clean = function() {		config.clean && EXEC(config.clean);		setTimeout(function() {			self.empty();			init = false;			clid = null;		}, 1000);	};});COMPONENT('part', 'hide:true', function(self, config) {	var init = false;	var clid = null;	self.readonly();	self.setter = function(value) {		if (config.if !== value) {			config.hidden && !self.hclass('hidden') && EXEC(config.hidden);			config.hide && self.aclass('hidden');			if (config.cleaner && init && !clid)				clid = setTimeout(self.clean, config.cleaner * 60000);			return;		}		config.hide && self.rclass('hidden');		if (self.element[0].hasChildNodes()) {			if (clid) {				clearTimeout(clid);				clid = null;			}			config.reload && EXEC(config.reload);			config.default && DEFAULT(config.default, true);		} else {			SETTER('loading', 'show');			setTimeout(function() {				self.import(config.url, function() {					if (!init) {						config.init && EXEC(config.init);						init = true;					}					config.reload && EXEC(config.reload);					config.default && DEFAULT(config.default, true);					SETTER('loading', 'hide', 500);				});			}, 200);		}	};	self.clean = function() {		if (self.hclass('hidden')) {			config.clean && EXEC(config.clean);			setTimeout(function() {				self.element.empty();				init = false;				clid = null;				setTimeout(FREE, 1000);			}, 1000);		}	};});COMPONENT('window', function(self, config) {	var W = window;	if (!W.$$window) {		W.$$window_level = W.$$window_level || 1;		W.$$window = true;		$(document).on('click', '.ui-window-button-close', function() {			SET($(this).attrd('path'), '');		});		$(window).on('resize', function() {			SETTER('window', 'resize');		});	}	self.readonly();	self.hide = function() {		self.set('');	};	self.resize = function() {		var el = self.find('.ui-window-body');		el.height(WH - self.find('.ui-window-header').height());	};	self.make = function() {		$(document.body).append('<div id="{0}" class="hidden ui-window-container"><div class="ui-window"><div data-bind="@config__change .ui-window-icon:@icon__html span:value.title" class="ui-window-title"><button class="ui-window-button-close{2}" data-path="{1}"><i class="fa fa-times"></i></button><i class="ui-window-icon"></i><span></span></div><div class="ui-window-header"></div><div class="ui-window-body"></div></div>'.format(self.ID, self.path, config.closebutton == false ? ' hidden' : ''));		var el = $('#' + self.ID);		el.find('.ui-window-body')[0].appendChild(self.dom);		self.rclass('hidden');		self.replace(el);		self.find('button').on('click', function() {			switch (this.name) {				case 'cancel':					self.hide();					break;			}		});	};	self.icon = function(value) {		var el = this.rclass2('fa');		value.icon && el.aclass('fa fa-' + value.icon);	};	self.configure = function(key, value, init) {		if (!init) {			switch (key) {				case 'closebutton':					self.find('.ui-window-button-close').tclass(value !== true);					break;			}		}	};	self.setter = function(value) {		setTimeout2('ui-window-noscroll', function() {			$('html').tclass('ui-window-noscroll', !!$('.ui-window-container').not('.hidden').length);		}, 50);		var isHidden = value !== config.if;		if (self.hclass('hidden') === isHidden)			return;		setTimeout2('windowreflow', function() {			EMIT('reflow', self.name);		}, 10);		if (isHidden) {			self.aclass('hidden');			self.release(true);			self.find('.ui-window').rclass('ui-window-animate');			W.$$window_level--;			return;		}		if (W.$$window_level < 1)			W.$$window_level = 1;		W.$$window_level++;		var body = self.find('.ui-window-body');		self.css('z-index', W.$$window_level * 11);		body.scrollTop(0);		self.rclass('hidden');		self.release(false);		self.resize();		config.reload && EXEC(config.reload, self);		config.default && DEFAULT(config.default, true);		if (!isMOBILE && config.autofocus) {			var el = self.find(config.autofocus === true ? 'input[type="text"],select,textarea' : config.autofocus);			el.length && el[0].focus();		}		setTimeout(function() {			body.scrollTop(0);			self.find('.ui-window').aclass('ui-window-animate');		}, 300);		// Fixes a problem with freezing of scrolling in Chrome		setTimeout2(self.id, function() {			self.css('z-index', (W.$$window_level * 10) + 1);		}, 500);	};});