vercengen/components/ComponentTimeline/ComponentTimelineChronology.js

/**
 * Refer to <span color = "yellow">{@link ve.Component}</span> for methods or fields inherited from this Component's parent such as `.options.attributes` or `.element`.
 * 
 * ##### Constructor:
 * - `arg0_value=Date.getCurrentDate()`: {@link number}|{@link Object} - The Date at which to start the Chronology.
 * - `arg1_options`: {@link Object}
 *   - `.timeline_instance`: {@link ve.Timeline}
 *
 * @augments ve.Component
 * @memberof ve.Component
 * @type {ve.TimelineChronology}
 */
ve.TimelineChronology = class extends ve.Component {
	constructor (arg0_value, arg1_options) {
		//Convert from parameters
		let value = (arg0_value !== undefined) ? arg0_value : Date.getCurrentDate();
		let options = {
			...arg1_options
		};
			super(options);
			
		//Declare local instance variables
		this.element = document.createElement("div");
			this.element.setAttribute("component", "ve-timeline-chronology");
			this.element.instance = this;
			if (options.attributes) HTML.setAttributesObject(this.element, options.attributes);
		this.options = options;
		this.value = value;
		
		//Declare table and draw
		this.table = new ve.Table([], {
			non_sortable_columns: [0, 1],
			onrowclick: (v, e) => {
				let timeline_options = this.options.timeline_instance.options;
				
				if (timeline_options.onkeyframeclick)
					timeline_options.onkeyframeclick(v, e);
			},
			onrowrightclick: (v, e) => {
				let timeline_options = this.options.timeline_instance.options;
				e.right_click = true;
				
				if (timeline_options.onkeyframeclick)
					timeline_options.onkeyframeclick(v, e);
			},
			retain: true,
			page_size: 30,
			page_sizes: [10, 20, 30, 40, 50],
			...this.options.table_options
		});
		this.table.bind(this.element);
		
		this.from_binding_fire_silently = true;
		this.v = value;
		delete this.from_binding_fire_silently;
	}
	
	get v () {
		//Return statement
		return Date.getDate(this.value);
	}
	
	set v (arg0_date) {
		//Convert from parameters
		let date = arg0_date;
		
		//Declare local instance variables
		this.timestamp = Date.getTimestamp(date);
		this.value = Date.getDate(date);
		
		//Update draw
		this.draw();
		this.fireFromBinding();
	}
	
	draw () { //[WIP] - Finish event handlers; add handling for .filter_obj.unique_timestamps
		//Declare local instance variables
		let table_array = [];
		let timeline_obj = this.options.timeline_instance;
		let timeline_options = timeline_obj.options;
		let timestamps_obj = {}; //<timestamp>: { count: number, row_value: Array } - Stores both the keyframe count and row_value, updating the description localisation if .filter_obj.unique_timestamps is true
		
		let filter_obj = timeline_options.filter;
			if (filter_obj?.date_window) {
				filter_obj.date_window[0] = Date.getTimestamp(filter_obj.date_window[0]);
				filter_obj.date_window[1] = Date.getTimestamp(filter_obj.date_window[1]);
				filter_obj.date_window.sort((a, b) => a - b); //Sort in ascending order
			}
			
		//Push table_array header
		table_array.push(["Date", "Keyframe"]);
		
		//Iterate over .timeline_options.keyframes; operate over .options.filter
		if (timeline_options.keyframes)
			Object.iterate(timeline_options.keyframes, (local_key, local_value) => {
				//Check against filter_obj
				let display_keyframe = false;
				let local_groups = (local_value.groups) ? local_value.groups : [];
				let local_timestamp = parseFloat(local_key);
				
				//Update timestamps_obj
				if (!timestamps_obj[local_timestamp]) timestamps_obj[local_timestamp] = {
					count: 0, row_value: []
				};
					let local_timestamp_obj = timestamps_obj[local_timestamp];
					local_timestamp_obj.count++;
				
				//Check for filter_obj pass
				if (filter_obj.enabled) {
					if (filter_obj.groups.length > 0)
						for (let i = 0; i < filter_obj.groups.length; i++)
							if (local_groups.includes(filter_obj.groups[i])) {
								display_keyframe = true;
								break;
							}
					if (filter_obj?.date_window)
						if (local_timestamp < filter_obj.date_window[0] || local_timestamp > filter_obj.date_window[1])
							display_keyframe = false;
				} else {
					display_keyframe = true;
				}
				
				//Render keyframe if display_keyframe is true
				if (display_keyframe) {
					let is_unique_timestamps = (filter_obj.enabled && filter_obj?.unique_timestamps);
					
					if (local_timestamp_obj.row_value.length === 0) {
						let keyframe_el = document.createElement("div");
							keyframe_el.id = "keyframe";
							
							//Set keyframe_el attributes
							if (local_value.is_current) {
								keyframe_el.setAttribute("data-is-current", String(local_value.is_current));
								this.current_index = table_array.length;
							}
							keyframe_el.keyframe = {
								key: local_key,
								value: local_value
							};
						local_timestamp_obj.row_value = [String.formatDate(local_timestamp), keyframe_el];
					}
					let local_keyframe_el = local_timestamp_obj.row_value[1];
					
					//.name handler; only format if timestamps are not grouped
					if (local_value.name) {
						let append_name = false;
						let local_name_el = local_keyframe_el.querySelector(".keyframe-name"); 
							if (!local_name_el) {
								local_name_el = document.createElement("div");
								append_name = true;
							}
							local_name_el.setAttribute("class", "keyframe-name");
							
							//Format local_value.name
							local_name_el.innerHTML = (!is_unique_timestamps) ? 
								local_value.name : `${String.formatNumber(local_timestamp_obj.count)} Keyframe(s) changed.`;
							if (append_name) local_keyframe_el.appendChild(local_name_el);
					}
					//.description handler; only show if timestamps are not grouped
					if (local_value.description && !is_unique_timestamps) {
						let local_description_el = document.createElement("div");
							local_description_el.setAttribute("class", "keyframe-description");
							local_description_el.innerHTML = local_value.description;
							local_keyframe_el.appendChild(local_description_el);
					}
					
					//Push to table_array
					if (!local_timestamp_obj.row_value)
						local_timestamp_obj.row_value = [String.formatDate(local_timestamp), local_keyframe_el];
					table_array.push(local_timestamp_obj.row_value);
				}
			}, { sort_mode: "date_ascending" });
		
		//Set table .v
		this.table.do_not_draw = true;
		this.table.v = table_array;
		delete this.table.do_not_draw;
		if (this.current_index !== undefined)
			this.table.jumpToIndex(this.current_index);
	}
};

//Functional binding

/**
 * @returns {ve.TimelineChronology}
 */
veTimelineChronology = function () {
	//Return statement
	return new ve.TimelineChronology(...arguments);
};