Source: querydatablock.js

"use strict";

/**
 * QueryDataBlock class holds all data relevant for a single section/query (QueryItem), includin InputItems and OptionItems.
 */
class QueryDataBlock {
	/**
	 * Create QueryDataBlock. 
	 * @param {Object} data - Defines QueryItem's data (data for a specific form section).
	 * @param {string} [data.formIndex] - Current form index used for unique ids.
	 * @param {string} [data.id] - Section id.
	 * @param {string} [data.type] - Query type, default is "text".
	 * @param {string} [data.title] - Query title text.
	 * @param {string} [data.subtitle] - Query subtitle text.
	 * @param {string} [data.label] - Query label text.
	 * @param {string} [data.error] - Query error text.
	 * @param {string} [data.direction] - Query direction, either "vertical" or "horizontal".
	 * @param {string} [data.optionsOrder] - Query options order, either "before" or "after" the input items.
	 * @param {boolean} [data.required] - Sets query filled-in value to be required. Default is false.
	 * @param {string} [data.css] - Query specific css string.
	 * @param {(dom|string)} [data.before] - Defines DOM object or a string as text or html to be placed at the beginning of the query.
	 * @param {(dom|string)} [data.after] - Defines DOM object or a string as text or html to be placed at the end of the query.
	 * @param {(boolean|string|RegExp|boolean[]|string[]|RegExp[])} [data.checkRules] - Defines possible validation queries. If array passed value must match them all.
	 * @param {string} [data.arrayMatch] - If set to "and" it will require input value to have a match, if "or" (default) at least one input value will have to have a match.
	 * @param {Object[]} [data.items] - Defines input items for this Query.
	 * @param {string} [data.items.id] - Defines input items' ids for this Query.
	 * @param {string} [data.items.label] - Defines input items' labels for this Query.
	 * @param {string} [data.items.css] - Defines input items' css strings for this Query.
	 * @param {string} [data.items.error] - Defines input items' error values - if none set defaults to the general QueryDataBlock error value.
	 * @param {(boolean|string|RegExp|boolean[]|string[]|RegExp[])} [data.items.checkRules] - Defines input items for this Query.
	 * @param {string} [data.items.arrayMatch] - If set to "and" it will require input value to have a match, if "or" (default) at least one input value will have to have a match.
	 * @param {Object[]} [data.options] - Defines option items for this Query.
	 * @param {string} [data.options.id] - Defines option items' ids for this Query - will be matched with data.items.checkRules.
	 * @param {string} [data.options.text] - Defines option items' text strings for this Query.
	 * @param {string} [data.options.css] - Defines option items' css strings for this Query.
	 */
	constructor(data) {
		this.data = data || {};
		
		/* Count of existing QueryDataBlocks */
		QueryDataBlock.count = (QueryDataBlock.count || 0) + 1;
		this.uniqueInstanceId = QueryDataBlock.count;

		this.formIndex = data.formIndex || null;
		
		this.idVal = this.createUniqueId(this.data.id) || "";
		this.typeVal = this.data.type || "text";
		this.titleVal = this.data.title || null;
		this.subtitleVal = this.data.subtitle || null;
		this.labelVal = this.data.label || null;
		this.errorVal = this.data.error || "This field has not been correctly filled in.";
		this.directionVal = this.data.direction || null;
		this.optionsOrderVal = this.data.optionsOrder || null;
		this.requiredVal = this.data.required || false;
		this.cssVal = this.data.css || null;
				
		this.before = this.data.before || null;
		this.after = this.data.after || null;
		
		this.items = this.data.items || [];
		this.options = this.data.options || [];
		
		this.queryValidator = new QueryValidator(this.data.checkRules);
		this.queryValidator.setMatch(this.data.arrayMatch || "or");
		this.setProperInputType();
				
		return this;
	}
	
	/**
	 * Get this query item's type.
	 * @return {string} - query type.
	 */
	get type() {
		return this.typeVal;
	}
	
	/**
	 * Get this query item's id.
	 * @return {string} - query id.
	 */
	get id() {
		return this.idVal;
	}
	
	/**
	 * Get this query item's title.
	 * @return {string} - query title.
	 */
	get title() {
		return this.titleVal;
	}
	
	/**
	 * Get this query item's subtitle.
	 * @return {string} - query subtitle.
	 */
	get subtitle() {
		return this.subtitleVal;
	}
	
	/**
	 * Get this query item's label.
	 * @return {string} - query label.
	 */
	get label() {
		return this.labelVal;
	}
	
	/**
	 * Get this query item's error.
	 * @return {string} - query error text.
	 */
	get error() {
		return this.errorVal;
	}
	
	/**
	 * Get this query item's direction. Applicable only for draggable queries.
	 * @return {string} - query direction - as "horizontal" or "vertical".
	 */
	get direction() {
		return this.directionVal;
	}
	
	/**
	 * Find out whether this query fille-in valueis required or if it is optional.
	 * @return {boolen} - Returns true if this query is required, false if not.
	 */
	get required() {
		return this.requiredVal;
	}
	
	/**
	 * Get this query item's css.
	 * @return {string} - query css string.
	 */
	get css() {
		return this.cssVal;
	}
	
	/**
	 * Get this query item's index.
	 * @return {(string|null)} - query index (order number) in form or null if no index found.
	 */
	get index() {
		if(!isNaN(this.indexVal)) {
			return this.indexVal;
		}
		return null;
	}
	
	/**
	 * Get this query item's items.
	 * @return {Object[]} - Array containing object data of each input item.
	 */
	get items() {
		if(typeof this.itemsVal !== typeof undefined) {
			return this.itemsVal;
		}
		return [];
	}
	
	/**
	 * Get this query item's options.
	 * @return {Object[]} - Array containing object data of each option item.
	 */
	get options() {
		return this.optionsVal;
	}
	
	/**
	 * Get this query item's options order.
	 * @return {string} - Either "before" if option items are rendered above input items or "after" is otherwise.
	 */
	get optionsOrder() {
		return this.optionsOrderVal;
	}
	
	/**
	 * Get this query item's base form DOM object.
	 * @return {dom} - An object in which all form elements including section queries are placed.
	 */
	get baseForm() {
		if(this.baseFormVal) {
			return this.baseFormVal;
		}
		return null;
	}
	
	/**
	 * Get this query item's section DOM container.
	 * @return {dom} - Query container.
	 */
	get sectionContainer() {
		if(this.sectionContainerVal) {
			return this.sectionContainerVal;
		}
		return null;
	}
	
	/**
	 * Get proper input type, filtered to a know types only in case unsupported type has been passed.
	 * @return {string} - A proper input type.
	 */
	get properInputType() {
		return this.inputType;
	}
	
	/**
	 * Get before section dom.
	 * @return {dom} - A custom element being inserted at the beginning of this query.
	 */
	get before() {
		return this.beforeDOM;
	}
	
	/**
	 * Get after section dom.
	 * @return {dom} - A custom element being inserted at the end of this query.
	 */
	get after() {
		return this.afterDOM;
	}
	
	/**
	 * Set index.
	 * @param {number} value - Set this item's index value.
	 */
	set index(value) {
		this.indexVal = value;
		if(!this.id) {
			this.idVal = this.indexVal;
		}
	}
	
	/**
	 * Set baseForm DOM element.
	 * @param {dom} baseForm - DOM element holding all form fields.
	 */
	set baseForm(baseForm) {
		this.baseFormVal = baseForm;
	}
	
	/**
	 * Set sectionContainer DOM element.
	 * @param {dom} sectionContainer - DOM element contaning this query.
	 */
	set sectionContainer(sectionContainer) {
		this.sectionContainerVal = sectionContainer;
	}
	
	/**
	 * Takes an array of items (optionally) and generates an Array of Objects, each Object for one item (InputItemWrapper).
	 * @param {Object[]} [items] - Array with input items data.
	 * @see {@link constructor} for more information.
	 */
	set items(items) {
		items = items || [];
		this.itemsVal = [];
		for(var i = 0; i < items.length; i++) {
			this.itemsVal[i] = {};
			this.itemsVal[i].id = items[i].id;
			this.itemsVal[i].htmlId = "input-" + this.id + "-" + items[i].id;
			this.itemsVal[i].label = items[i].label || "";
			this.itemsVal[i].text = items[i].text || "";
			this.itemsVal[i].css = items[i].css || "";
			this.itemsVal[i].error = items[i].error || null;
			this.itemsVal[i].queryValidator = new QueryValidator(items[i].checkRules);
			this.itemsVal[i].queryValidator.setMatch(items[i].arrayMatch || "or");
		}
	}
	
	/**
	 * Takes an array of options (optionally) and generates an Array of Objects, each Object for one option (OptionItem).
	 * @param {Object[]} [options] - Array with option items data.
	 * @see {@link constructor} for more information.
	 */
	set options(options) {
		options = options || [];
		this.optionsVal = [];
		for(var i = 0; i < options.length; i++) {
			this.optionsVal[i] = {};
			this.optionsVal[i].id = options[i].id;
			this.optionsVal[i].htmlId = "option-" + this.id + "-" + options[i].id;
			this.optionsVal[i].validatorMatch = options[i].id;
			this.optionsVal[i].text = options[i].text || "";
			this.optionsVal[i].css = options[i].css || "";
		}
	}
	
	/**
	 * Sets dom which preceeds the query once rendered.
	 * @param {dom} element - An element to be placed at the beggining of this query.
	 */
	set before(element) {
		this.beforeDOM = this.createCustomDomContent(element, "before");
	}
	
	/**
	 * Sets dom which follows the query once rendered.
	 * @param {dom} element - An element to be placed at the end of this query.
	 */
	set after(element) {
		this.afterDOM = this.createCustomDomContent(element, "after");
	}
	
	/**
	 * Creates a unique id for this QuerydataBlock, based on an uniqu InstanceID.
	 * @param {string} [id] - A string to be connected with the unique instance number to create a unique id.
	 */
	createUniqueId(id) {
		id = id || "";
		return CommonFormFunctions.joinStrings(this.uniqueInstanceId, id, "_");
	}
	
	/**
	 * Filters all unsupported input types, picks a default if a wrong input type has been passed.
	 * @return {QueryDataBlock} - Returns this QueryDataBlock instance.
	 */
	setProperInputType() {
		this.inputType = CommonFormFunctions.filterProperInputTypes(this.type);
		return this;
	}
	
	/**
	 * Returns a DOM with "dt-custom-form-content" class name, accepts HTML/text strings as well as DOM structures.
	 * @param {(dom|string)} [element] - A dom object or a string - as text or HTML.
	 * @param {string} [customClass] - Custom class name to be added to the final element.
	 * @return {dom} - An updated or newly created DOM object.
	 */
	createCustomDomContent(element, customClass) {
		if(element) {
			var finalDom = null;
			if(element.nodeType && element.nodeType === 1) {;
				element.classList = [];
				finalDom = element;
			}
			else {
				finalDom = document.createElement("div");
				finalDom.innerHTML = element;
			}
			
			finalDom.classList.add("dt-custom-form-content");
			if(customClass) {
				finalDom.classList.add(customClass);
			}
			return finalDom;
		}
		return null;
	}
}