



/*
	
	
TIMELINE TO DO


chains:
-standard time_delta vs steps paramaters handling in all chains, see tvgs_element_drop_rise for setup


- delay issue: meauring current state of elements, then postponing with delay, then executing will be working with the wrong element state if the target element has been changing during the wait for delay
+ make a delayed action immediately reschedule an instigate to fire at the delayed time? so when it measures current states they are correct


- opacity discontinuity: when using diff opacity settings on an object for open/close, when the direction switches the resulting opacity is off
+ make opacity read current opacity and then calculate from there to target


- have chain generators scan for identical steps, and add only the first to the timeline
+ track vital params of previouw step and when they are identical, skip the step (obviously does not affect obligatory steps)


- instead of execution action while scanning, make a list (like for deletion) for execution, this prevents actions of the same chain to all get executed in the same frame (only the last needs to be run)
- make instigates part of the relay, or setup a separate relay for them
- add a framerate tracker that displays current framerate for a shorter period, and calculate long term framerate over longer periods (per frame is a bit overkill)
- add total (real) running time to the console
- add average frame duration to the console
- add frame counter for frames that took longer to execute then the framerate setting

- make delayed frames only count as delayed when they frame has managed to execute fully (so delay + action duration is within the frame, and the next can proceed as normal) else when the delay is part of an overshot frame do not count it as normal delay
+ for this the counters need to move to the bottom of the timeline repeater

- make an average time counter for overshot frames
+ add separate fields for frames that were overshot due to delay (for else the action duration would have fit), or due to actions themselves taking to long, or both taking too long.

- chain control
+ the timeline can check if another chain of the same type is already running on the same element, and then delete the remaining actions of the running chain
+ instead of the timeline, each chain creator can do this, so the timeline does not have to do extra checking
+ it should be done first thing in the chain creator, if it fails, the previous chains will also have failed so no problem there
+ in case of the drop down/rise up, when the action is repeated halfway the chain creator will pick up the current height, so the action is nicely reversed (switching between up/down is possible but not recommended ofcourse)
+ using timestamp as chain id should be no problem, in the rare case the timestamp is identical to another chain, there will always be only one chain with the current element
+ see what to do with the timeline instruction "smother" maybe this can be used to kill entirely different chains

+ option: link chain control to timeline anyway, the first action encountered of the new chain has the smother property set with info, and is made obligatory
+ then the last action of the chain must have a bit set to delete the finished chain info
+ this way, the smother is the chain info (element + name) and a chain array is not needed
+ however, then chains need a unique id to keep the first frame from smothering it's own actions, maybe use timestamp after all
+ the delete cahin function would then be called from within the timeline, preventing any further confusion
+ drawback might be: actions that should be deleted in the frame wil still get carried out if they come before the first action of the new chain
+ since actions are gathered before execution, this could be filtered based on unique chain id, not only for smothers, but also for multiple actions of the same chain within the same frame.
+ then smother could always be used to kill whatever chain from within a frame, an action could also be just a timed smother command, or a timed instigate, etc.


+ event negation, when a chain is in progress ignore any new actions on that element of the same type until completed (smother command checks if old chain is protected, then cancels generation of new chain)


"BUGS"

- when a chain is reversed and reads the current element state, now this happens:
+ the action is executed, the new chain reads this state, (the old chain is wiped), the next frame is the first reverse frame
+ this seems to go wrong on a few browsers, which make it look like the old chain overshots one frame, before the first new frame is shown
+ fix: do not execute the last old chain action when the new chain kicks in (in the frame which has a smother for the old chain, do not execute any old chain actions), effectively, because the new chain is pushed ahead one frame, this means no action is exdecuted on that element for one frame, which should fix the jumpy browsers, and show a tiny dely in other browsers)


- mouseenter and leave are not working properly on an image with a resize


- when an action screws up the original elements properties, they should be checked from stored values
+ for instance clicking an expanding menu off halfway leaves its percieved original size at half the original
+ so actions/chains that are capable of messing things up should store important values in tvgs_timeline.reference or something, and check them back at certain points to prevent odd behavior

- option to let all chain functions sync to the main framerate

- make a global start time for the time function to use instead of the 40 years

- make readout show difference in really dropped frames, and eclipsed frames from chains or smothers
+ this could be handy to see if you can optimize a page



- image loading problems
+ when an image is not yet complete in the dom, calls to getting width and height will fail
+ this can be solved by starting the timeline and config only when  all-images-loaded have been added, but scripts start earlier and when an image is missign will break the entire page
+ a construct could be made to pass the size of a config itme as an "evaluate when used", so when an action is triggered, for instance the start size is measuerd at that moment, instead of predefined in the config



TO CONSIDER

- when an instigate seeds the timeline with new actions halfway through a frame, a part of those actions will immediately be considered late in the next frame, while this is correct and the next frame will do the right follow-up action, the frame before that should have ended with another action, because the new actions are inserted at the end of the timeline, and will not be scanned till the next frame (when the timeline is sorted again)
+ a solution would involve targeted insertion of new actions, or extra sorting

- make the timeline offset its start-time to zero when it starts (instead of the forty years), see whats faster: correcting it on every timestamp call, or just working with huge numbers (which uses more memory in any case)

- when the timer skips to the next frame based on frame duration, it is possible that because of the clock being slightly off one way or the other, that some frames that were perfectly on time get skipped because the timer sets in a few ms late
+ this would require a marker to state that the previous frame has finished in time, and a projected absolute time for the next frame to start, and in this case allow for a slight tolerance for late actions
+ this tolerance can be measured by counting skipped actions following a timely frame, and getting info on how often/how much the timer is off
+ when the timeout is late by an amount that falls before the end of its own projected frame, any actions that would otherwise be skipped get accepted
+ this is a one frame lookback/lookahead, but not tied to an absolute timer, because when a frame really takes way long, usurping the time of future frames, the actions should certainly be skipped anyway
+ this is only for giving those few unlucky frames a chance :)



LEFTOVERS

// this interval might still be used to check periodically if the timeline is still running, by analyzing the global array, once timeline items no longer get procesed, it needs to restart the timeline
// check this in explorer! the timeline quits after some long hours, keep console visible to see if one of the counters is the problem when it reaches max val??
//timeline_interval_id = setInterval(timeline_step, timeline_frame_interval);





easy clone object code:

var clone_of_object = eval ( old_object.toSource() );  // tosource not universally supported!!!



*/









//tvgs_element_set_opacity_by_id("testpic3", 50);



/*
if (22>timestamp) alert ("urgh");


timestamp = timestamp / 1;

testarr = [];

testarr[testarr.length] = [timestamp,"veel"];

testarr[testarr.length] = [68976,"twaalf"];

testarr[testarr.length] = [34,"vijf"];
testarr[testarr.length] = [23456,"acht"];


testarr[testarr.length] = [2345,"zes"];


testarr.sort(function(a, b) {return a[0] - b[0]});


for(var key in testarr)
{

 alert (key +' = '+ testarr[key][0] + ' / ' + testarr[key][1]);


}
*/







/*
tvgs_element_waver_by_id('testpic1', 5, 25, 1000, 1250);

tvgs_element_waver_by_id('testpic2', 4, 20, 5000, 800);

tvgs_element_waver_by_id('testpic3', 3, 15, 4500, 750);

tvgs_element_waver_by_id('testpic4', 5, 25, 3000, 1250);

tvgs_element_waver_by_id('testpic5', 5, 25, 4000, 600);

tvgs_element_waver_by_id('testpic6', 8, 5, 1200, 400);

tvgs_element_waver_by_id('testpic7', 5, 8, 0, 300);

tvgs_element_waver_by_id('testpic8', 2, 40, 5500, 200);

*/





/*
	
	for (var g in window.tvgs_timeline.actions)
	{
		var readout = '';
		for (var i in window.tvgs_timeline.actions[g])
		{
			readout += ' - ' + window.tvgs_timeline.actions[g][i];
		}
		document.getElementById('output').innerHTML += readout + '<br>';
	}
	
	*/



// toggles a checkbox on and off
// switches the foreground image, the first image is the off state, if the checkbox is constructed of a bg/fg image, use a blank gif as the first foreground frame
function tvgs_checkbox_toggle_by_id(id, size, input_id)
		{
		
		
			element = document.getElementById(id);
			input_element = document.getElementById(input_id);
			
			//alert(input_element.value);
		
			var foreground_src = [];
			foreground_src[0] = 'tvgi.php?i&op=0&ft=gif&w=' + size + '&h=' + size;
			foreground_src[1] = 'tvgi.php?i&si=img/checkboxes/20px_checkmark_scribble_1.png&w=' + size;
			foreground_src[2] = 'tvgi.php?i&si=img/checkboxes/20px_checkmark_scribble_2.png&w=' + size;
			foreground_src[3] = 'tvgi.php?i&si=img/checkboxes/20px_checkmark_scribble_3.png&w=' + size;
			foreground_src[4] = 'tvgi.php?i&si=img/checkboxes/20px_checkmark_scribble_4.png&w=' + size;
			foreground_src[5] = 'tvgi.php?i&si=img/checkboxes/20px_checkmark_scribble_5.png&w=' + size;
			
			
			
			
			if (element.name == 'checked')
			{
			
				element.src = foreground_src[0];
				
				
				element.name = 'unchecked';
				
				input_element.value = 0;
				
				//alert('interval cleared');
				return;
			}
			else
			{
			
			
			
				
				var steps = foreground_src.length;
				var step = 1;
				
				
				function anim_step()
				{
					
					if (step == steps)
					{
					
						clearInterval(interval_id);
						
						element.name = 'checked';
						
						input_element.value = 1;
						
						//alert(input_element.value);
						
						//alert('interval cleared');
						return;
					}
					
					
					
					element.src = foreground_src[step];
					
					
					step = step + 1;
					//alert(step);
				
				}
				
				
				
				
				//instruction = 'this.style.backgroundColor='';
				
				var interval_id = setInterval(anim_step,80);
				
				element.name = interval_id;
				
				//alert(interval_id);
			
			}
		
		
		}
		
		
		
		
// test readout

function readout()
{

//alert(document.getElementById('project_started').value);

//alert(document.getElementById('project_started').value + document.getElementById('project_running').value + document.getElementById('project_finished').value + document.getElementById('project_billed').value);


var inputvalues = document.getElementById('project_started').value + document.getElementById('project_running').value + document.getElementById('project_finished').value + document.getElementById('project_billed').value;


document.getElementById('readout').innerHTML = inputvalues;


}



// takes a block of source containing (optionally nested) uniform info blocks (the list_id), collects all the blocks with info (the block_class), and their info fields (the content_class)
// these info fields are scanned for the search_term, the search term is highlighted in the info fields, blocks with hits are shown, other blocks are hidden
// note: block_class elements must be DIV nodes
// note: content_class elements must be SPAN nodes
// note: when any formatting is present in the content fields that coincidentally uses the exact same tags as the ones used for highlighting, they will be stripped on the first pass
// to-do: determine info_field_limit automatically
// to-do: requirement for div/span nodes could be removed

// totally reworked, no more nesting support, class based data read that rebuilds table structure
if (TVGS) TVGS.tvgs_exclude_and_highlight_by_class = function (table_id, row_class, field_class, search_term)
{
	
	
	// check search term
	if (search_term.length < 1) return;
	
	
	
	
	// check element
	if (!document.getElementById(table_id))
	{
		alert('list_id not found! terminating...');
		return;
	}
	
	
	var start = new Date().getTime();
	

	// get element
	var table_element = document.getElementById(table_id);
		
	
	
	
		
	// attach and fill list data storage to element when not yet set
	if (TVGS.nil(table_element.table_rows))
	{	
		
		table_element.table_rows = [];
	
		// get row_class elements
		var table_rows = tvg_get_elements_by_class(row_class, null, table_element, 'SPAN');
		
		// read every block
		for (var i in table_rows)
		{
			
			var row_number = table_element.table_rows.length;
			
			var row_fields = tvg_get_elements_by_class(field_class, null, table_rows[i], 'SPAN');

			// add row data
			table_element.table_rows[row_number] = [];
			
			for (var j in row_fields)
			{
			
				table_element.table_rows[row_number][j] = row_fields[j].innerHTML;
			
			}			

		}
		
		// config read, prepare for new output
		table_element.innerHTML = '';
	
	}
	
	
	
	
	
	//alert (table_element.table_data);
	

	//alert ('gathered table data in ' + (new Date().getTime() - start) + ' msec');
	
	var total_rows = 0;
	var total_fields = 0;
	var total_characters = 0;
	
	
	
	
	//alert ('going1');

	// set highlight code
	var match_highlight_prefix = '<span style="background-color: red;">';
	var match_highlight_postfix = '</span>';
	
	var match_highlight_prefix_placeholder = '%%%#%%%';
	var match_highlight_postfix_placeholder = '###%###';
	
	
	//var match_highlight_string = '#match#';
	//var match_highlight_mask = match_highlight_prefix + match_highlight_string + match_highlight_postfix;
	//alert ('going2');
	// set regexes
	var match_regex = eval('/' + search_term.regex_string_fix() + '/gi');
	var replace_regex = '';
	//var replace_regex_highlight_prefix = eval('/' + match_highlight_prefix.replace(/\//g, '\\/') + '/gi');
	//var replace_regex_highlight_postfix = eval('/' + match_highlight_postfix.replace(/\//g, '\\/') + '/gi');
//alert ('going3');
	// set content string
	var content_string = '';
	
	// build new output block
	var output_block = '';
	

	
	
	//alert ('going4');
	
	// search each block for the search term
	for (var i in table_element.table_rows)
	{
		
		 total_rows++;
	
	
	
		// set block match
		var block_match = false;
		

		
		// proces content elements
		for (var j in table_element.table_rows[i])
		{


			total_fields++;
			
	
	
			// get content string
			content_string = table_element.table_rows[i][j];
			
			
			total_characters = total_characters + content_string.length;
			
			// backreference replace
			var match_regex_backreference = eval('/(' + search_term.regex_string_fix() + ')/gi');
			
			content_string = content_string.replace(match_regex_backreference, match_highlight_prefix + '$1' + match_highlight_postfix);
			
			
			
			
			/*
				
				
				
			//alert(content_string);
			// clear content string of previous highlights
			// note: explorer has capitalized any tags and css definitions when they are read back out with innerhtml, and also removed the ending semicolon in the style attribute
			// example: <span style="background-color: red;"> becomes <SPAN style="BACKGROUND-COLOR: red">
			
		 	replace_regex = eval('/' + match_highlight_prefix.regex_string_fix().regex_string_fix_style_attribute() + '/gi');
			content_string = content_string.replace(replace_regex, '');
			
			replace_regex = eval('/' + match_highlight_postfix.regex_string_fix() + '/gi');
			content_string = content_string.replace(replace_regex, '');
			
			// note: this is used because explorer did not get the regex right (the ending semicolon in the style attribute is removed once it is in explorers dom, so the original tag is no langer recognized)
			// note: this effectively strips tags, which for the original purpose of this script is ok
			//content_string = content_string.replace(/<.*?>/g, '');
			
			//content_string = content_string.split(match_highlight_prefix).join('');
			//content_string = content_string.split(match_highlight_postfix).join('');
			
			//alert(content_string);
			
			// no highlights
			//window.tvgs_storage[work_storage_id][2][i][2][j][1].innerHTML = content_string;
		
			//alert ('going4');
			
			
			
			
			
			
			
			
		
			
			if (content_string.match(match_regex) != null)
			{
				
				//alert(content_string.match(match_regex));
				
				
				// highlight all occurences of the search term in the content string
				// note: this replaces with the actual found string, to preserve capital letters
				
				
			//	content_string = content_string.replace(replace_regex, match_highlight_mask.replace(match_highlight_string, content_string.match(match_regex)));
				
				// get all matches
				var content_string_matches = content_string.match(match_regex);
				
				
				//alert(content_string_matches);
				
				var unique_matches = [];
				var match_exists = false;
				
				// clean out duplicates
				// note: duplicates are same-case duplicates, like: 'aa' & 'aa', while 'aa' & 'Aa' are considered different
				for (var k in content_string_matches)
				{
					
					if (is_numeric(k))
					{
					
						// check if this match exists
						match_exists = false;
						
						for (var l in unique_matches)
						{
							
							if (content_string_matches[k] == unique_matches[l])
							{
							
								match_exists = true;
								
							}
											
						}
						
						// add it
						if (match_exists == false)
						{
						
							unique_matches[unique_matches.length] = content_string_matches[k];
						
						}
					
					}

				}
				
				// update matches
				content_string_matches = unique_matches;
				
				//alert(content_string_matches);
				
				// set the content string with placeholders for the highlights code
				for (var k in content_string_matches)
				{
				
					replace_regex = eval('/' + content_string_matches[k].regex_string_fix() + '/g');
					
					replace_string = match_highlight_prefix_placeholder + content_string_matches[k] + match_highlight_postfix_placeholder;

					content_string = content_string.replace(replace_regex, replace_string);
				
				}
				
				// replace the placeholders with actual highlight code
				replace_regex = eval('/' + match_highlight_prefix_placeholder + '/g');
				content_string = content_string.replace(replace_regex, match_highlight_prefix);
				
				replace_regex = eval('/' + match_highlight_postfix_placeholder + '/g');
				content_string = content_string.replace(replace_regex, match_highlight_postfix);
				
				
			
				// this block has a match
				block_match = true;
					
			}
			
			*/
			
			
			// add to output block
			output_block += content_string;
			
			// clear content string, just in case
			content_string = '';
	

		}
		
		
		/*
		
		"een"
		Searched 304110 content characters (567 rows, 1701 fields) in 4689 msec.
		Searched 304110 content characters (567 rows, 1701 fields) in 4713 msec.
		Searched 304110 content characters (567 rows, 1701 fields) in 4675 msec.
		Searched 304110 content characters (567 rows, 1701 fields) in 4718 msec.
		
		
		using backreference
		"een"




		
		*/
		
		/*
		
		// mark block for no match
		if (block_match == false)
		{
			
			// set status to 3 (do not show at all)
			window.tvgs_storage[work_storage_id][2][i][0] = 3;
	
		}
		else
		{
			
			// set status to 1 (show with full info)
			window.tvgs_storage[work_storage_id][2][i][0] = 1;
	
		}
	*/
	
	
		
		
		//alert(block_match);
		
	
	
	
	}
	
	//alert (window.tvgs_storage[work_storage_id][2]);
	
	
	table_element.innerHTML = output_block;
	
	
	
	
	alert ('Searched and highlighted ' + total_characters + ' content characters (' + total_rows + ' rows, ' + total_fields + ' fields) in ' + (new Date().getTime() - start) + ' msec.');
	

	
	/*
	
	// traverse the list, check for parents to show
	// note: this checks each block for parents up to the list_element
	var check_element = null;
	
	block_class_match_regex = eval('/^' + block_class + '$|^' + block_class + ' | ' + block_class + '$| ' + block_class + ' /i');
	
	for (var i in window.tvgs_storage[work_storage_id][2])
	{
	
		// if a block with visible status (1) finds a parent block with invisible status (3), set it to visible with minimal info status (2)

		// check only visible blocks
		if (window.tvgs_storage[work_storage_id][2][i][0] == 1)
		{
		
			// start at first parentnode, this also prevents the block matching itself as parent block
			check_element = window.tvgs_storage[work_storage_id][2][i][1].parentNode;
			
			
			// stop when exiting list
			while (check_element != list_element)
			{
				
				// only check nodes that have the right block_class
				if (check_element.className.match(block_class_match_regex))
				{
					
					//alert('checking block_class block');
					
					// check if parent is in the list of blocks
					// note: this compares the same list, but a match here means the found item is higher in the dom tree
					for (var j in window.tvgs_storage[work_storage_id][2])
					{
						
						// check only invisible blocks
						if (window.tvgs_storage[work_storage_id][2][j][0] == 3)
						{
							
							//alert('checking invisible block_class block');
							
							// if the check_element is in the list, and it is marked as invisible (3), set it to visible with minimal info status (2)
							if (window.tvgs_storage[work_storage_id][2][j][1] == check_element)
							{
							
								window.tvgs_storage[work_storage_id][2][j][0] = 2;
								
								// unique match found
								break;
								
							}
					
						}
					
					}			
				
				}
				
				// step up to to parent
				check_element = check_element.parentNode;
			
			}
		
		}
	
	}
	
	//alert (window.tvgs_storage[work_storage_id][2]);
	
	
	
	// update visibility of blocks
	var current_block = null;
	var current_block_status = null;
	
	for (var i in window.tvgs_storage[work_storage_id][2])
	{
	
		current_block_status = window.tvgs_storage[work_storage_id][2][i][0];
		current_block = window.tvgs_storage[work_storage_id][2][i][1];

		if (current_block_status == 1)
		{
			
			// show block entirely (1)
			current_block.style.display = '';
	
		}
		else if (current_block_status == 2)
		{
			
			// show block partially (2)
			current_block.style.display = '';
	
		}
		else if (current_block_status == 3)
		{
			
			// hide block (3)
			current_block.style.display = 'none';
	
		}
			
	}
	
	*/
	
	
}




// build element info-tree to document HTML root
function tvgs_build_element_to_root_element_tree_by_node_type(current_element, node_name)
{
	
	var element_to_root_element_tree = [];
	var element_parent_count = 0;
	var check_element = current_element;
	
	
	while (check_element.nodeName != 'HTML')
	{
		
		// expand tree
		element_to_root_element_tree[element_parent_count] = check_element; // element reference
		
		// step up to to parent
		check_element = check_element.parentNode;
		
		// increase parent count
		element_parent_count = element_parent_count + 1;
	
	}
	
	
	// HTML node parent number
	// used by info tracker, move this global to somewhere else
	window.distance_to_html_node = element_parent_count;
	
	
	return element_to_root_element_tree;
	
}

	
	
	
function explorertest()
{


	var testline = document.getElementById('readout').innerHTML;
	
	//'this is the test line with a <span style="background-color: red;">red</span> word';
	
	var orig_testline = testline;

	// set highlight code
	var match_highlight_prefix = '<span style="background-color: red;">';
	var match_highlight_postfix = '</span>';



	replace_regex = eval('/' + match_highlight_prefix.regex_string_fix().regex_string_fix_style_attribute() + '/gi');
	testline = testline.replace(replace_regex, '');
	
	
	replace_regex = eval('/' + match_highlight_postfix.regex_string_fix() + '/gi');
	testline = testline.replace(replace_regex, '');
		
		


	document.getElementById('readout').innerHTML = orig_testline + '<br><br>' + orig_testline.replace(/</g, '&lt;') + '<br><br>' + testline + '<br><br>' + testline.replace(/</g, '&lt;');


}














 

