/* This notice must be untouched at all times.

cvs_livelecture.js    v. 2.00		03-09-2006

Copyright (c) 2006 Clear Voice Systems Ltd. All rights reserved.

Provides Index, Caption, Captioner, IQuiz and IQuizPopup classes.

Requires cvs_utils.js

*/


// Class IQuiz
function IQuiz() {
	
  // PUBLIC MEMBER PROPERTIES
  
  this.name =  "iquizmanifest";	// Name of iFrame element that opens the quiz manifest. Default: iquizmanifest.
  this.text;					// Put the quiz manifest text hear and then call open().
  this.lines;					// String array of individual manifest lines
  this.manifest = new Array();	// Parsed manifest array
  this.start;					// Current quiz start time set using next()
  this.n;						// Number of validly parsed lines in manifest file
  this.line;					// Number from 1 to the number of quizes. Increments each time next() is used.
  								// Set this value to 1 to reset popup quiz
  this.last;					// True if last quiz entry has been read. False otherwise.
  this.quiz_id;					// Set to product code string
  this.r_id;					// Set to result record ID of current quiz session
  
  //
  // PRIVATE VARIABLES
  //
  var ls = '#';
  var fs=/[ \t]*\|[ \t]*/;

  //
  // function next([line])
  //
  this.next=function next(start) {
	  
    var thisline;
	if ( !isNaN(parseInt(start)) && start >= 0 && start <= this.n)
	   this.line=start;

	thisline = this.line;
	
	if ( this.n > 0 ) {
	  var t_coderaw = this.manifest[thisline-1][0]; 
	  this.t_code = ""+t_coderaw.match(/\S+/);
	  this.start = time2int(this.t_code);
	}else {
	  this.start = 0;
	}
	
	thisline++;
	
	( thisline > this.n ) ? this.last = true : this.last = false;
	
	if ( thisline <= this.n )
    	this.line = thisline;	

	return(this.line);
  }

  //
  // function open() 
  //
  this.open=function open() {
	this.n = 0;
	this.line = 0;
	  
    if ( !this.text )
	  return(this.n);
	  
    this.lines = this.text.split(ls);
	for ( i = 0; i < this.lines.length; i++ ) {
      fields = this.lines[i].split(fs);
	  if ( !(fields.length == 1) )
	    continue;	// Skip any line that doesn't have exactly one field
	  
	  if ( time2int(fields[0]) == -1 )
	    continue;	// Skip any line that has a time value that is not in the form [[hh:]mm:]ss
	
	  this.manifest[this.n] = fields;
	  this.n++;
	}
	if ( this.n > 0 )
	  this.line=1;
	  
	return(this.n);
  }

  //
  // function seek(seek)
  // parameters: 
  //   seek  - Time in seconds expressed as whole number of decimal value. e.g. 20, 10.3 0.46
  //
  this.seek=function seek(seek) {
    this.line=1;

    if ( isNaN(parseFloat(seek)) || isNaN(parseInt(seek)) )
	  return(false);
       
    do {
	  line = this.line;
	  this.next();
	  currentstart = this.start;

	  if ( !this.last ) {
	    this.next();				// Retrieve next quiz to get it's start time
	  }

	  this.next(line);				// Retrieve the current quiz
 
	  if ( seek < currentstart || (seek >= currentcaptionstart && seek <= currentcaptionend) ) {
	    return(true);
	  }

    } while( !this.last );

    return(false);
  }

}	// IQuiz


// Class IQuizPopup
function IQuizPopup(instance) {
  this.instance='iquizpopup';    // Name of object reference used to instatiate this class. Override this as needed. Default 'captioner'
  this.iquizpopupthread=null;
  this.isactivequiz = false;
  this.winref = null;
  this.winreopen = true; 

  this.initialize = function initalize() {
    if ( instance ) this.instance=instance;
  }

  this.start = function start(line) {
    // If a start quiz is specified, retrieve that quiz
    if (line != null )
      iquiz.next(line);

	iquizstatus = WM_readCookie('iquizstatus'); iquizstatus = iquizstatus.split('|');

    if ( !this.isactivequiz && movie1.getCurrentPosition() >= iquiz.start  ) {
	  this.isactivequiz = true;
	  iquizstatus = iquizstatus[0]+'|'+iquizstatus[1]+'|'+iquizstatus[2];
	  WM_setCookie('iquizstatus', iquizstatus, '', '/', connection.domainname, '');
	  iquizuri = iquizurl + '?quiz_id='+ iquiz.quiz_id + '&r_id=' + iquiz.r_id + '&t_code=' + iquiz.t_code;  
	  // Pause the movie - Press the Pause button
	  PauseHandler('click');

      this.winref = window.open(iquizuri, '', 'top=30,left=100,width=760,height=560,resizable=1,toolbar=0,location=0,status=0,menubar=0,scrollbars=1');

	  if ( !iquiz.last ) {
	    // Retrieve the next IQuiz quiz
  	    iquiz.next();
	  }
	  this.iquizpopupthread = window.setTimeout(this.instance + ".start();", 500);
    }else if ( this.isactivequiz ) {
      // Monitor the play-head while IQuiz question is display
	  if (iquizstatus[3] == 'stop') {
		 this.isactivequiz = false;
		iquizstatus[1] = 'false';
		// Play the movie - press the play button
		PlayHandler('click');
		iquizstatus = iquizstatus[0]+'|'+iquizstatus[1]+'|'+iquizstatus[2];
		WM_setCookie ('iquizstatus', iquizstatus, '', '/', connection.domainname, '');		 
	  }
	  else if (iquizstatus[3] == 'continue') {
		this.isactivequiz = false;
		if ( iquizstatus[2] == iquizstatus[0] ) {	// We've answered the last quiz
		  iquizstatus[1] = 'false';
		}else {
	      this.iquizpopupthread = window.setTimeout(this.instance + ".start();", 500); 
		}
		// Play the movie - press the play button
		PlayHandler('click');
		iquizstatus = iquizstatus[0]+'|'+iquizstatus[1]+'|'+iquizstatus[2];
		WM_setCookie ('iquizstatus', iquizstatus, '', '/', connection.domainname, '');
	  }
	  else {
		if (iquizstatus[1] == 'true')	// Only keep monitoring while active if we haven't answered last quiz
		  this.iquizpopupthread = window.setTimeout(this.instance + ".start();", 500);
	  }
    }else {
      // Monitor the play-head until an IQuiz question is found
      this.iquizpopupthread = window.setTimeout(this.instance + ".start();", 500); 
    }
  }

  this.stop = function stop() {
  if ( this.iquizpopupthread )
    window.clearTimeout(this.iquizpopupthread);
  }

}  //IQuizPopup


function Forum() {
  this.name = "forummanifest";	// ID of iFrame that opens the forum manifest. Default 'forummanifest'.
  this.uri = null;				// URI of the forum
  this.available = false;		// Set to true when validly formed forum URI is found

  //
  // function open()
  //
  this.open=function open() {
	manifesttext = null;
	if ( window.frames[this.name].document.getElementById('forummanifesttext') )
      manifesttext = window.frames[this.name].document.getElementById('forummanifesttext').innerHTML;
    if ( !manifesttext )
	  return(this.available);

    // Test if it is a valid URL
    if ( manifesttext.indexOf("http://")!=-1 || manifesttext.indexOf("https://")!=-1 ) {
      //Valid URL found
	  this.available = true; 
	  this.uri = manifesttext;
	  return(true);        
    }else {
	  return(false);
	}
  }

}	// End of Forum



// Class Index
function Index() {
	
  // PUBLIC MEMBER PROPERTIES
  
  this.name =  "indexfile";		// ID of iFrame element that opens the seekindex file. Default: 'indexfile'.
  this.selectname =  "searchindex";	// ID of DIV element that contains the list of search points. Default 'seekindex'.
  this.indextext;				// Contains the index text after openIndex() is called.
  this.indexlines;				// String array of individual index search lines
  this.index = new Array();		// Parsed index search array
  this.noindextext = "<P>No search list has been added to this Video Lecture Pack</P>";		// Text that is displayed if index file is empty or contains no valid captions
  this.n;						// Number of validly parsed lines in index file
  this.currentindex;			// Current index text set using nextIndex()
  this.start;					// Current index start time set using nextIndex()
  this.end;					    // Current index end time set using seekIndex()
  this.searchnode;				// Node that contains search point set using nextIndex()
  this.line = 1;				// Number from 1 to the number of index points. Increments each time nextIndex() is used.
  								// To retrieve a particular index point data first set this to the desired line number
								// before calling nextIndex();
  this.last;					// True if last nextIndex has been read. False otherwise.
  this.bknormal='#F4F4F4';		// Background color normal state for index points
  this.bkhighlight='#A4A4A4';	// Background color highlight state for index points
  this.bkactive='#7ABCC0';		// Background color active state for index points
  
  // Node (HTML element) that contains the list of searchpoint index points
  this.selectid = document.getElementById(this.selectname);
								
  //
  // PRIVATE VARIABLES
  //

  var ls = '#';
  var fs=/[ \t]*\|[ \t]*/;


  //
  // function nextIndex([line])
  //
  this.nextIndex=function nextIndex(start) {
	  
    var thisline;
	if ( !isNaN(parseInt(start)) && start >= 0 && start <= this.n)
	   this.line=start;

	thisline = this.line;

	if ( this.n > 0 ) {
	  this.currentindex = this.index[thisline-1][0]; 
	  this.start = time2int(this.index[thisline-1][1]);
	  this.searchnode =  document.getElementById(this.index[thisline-1][2]);
	}else {
	  this.currentindex = this.noindextext;
	  this.start = 0;
	  this.searchnodename = null;
	}
	
	thisline++;
	
	( thisline > this.n ) ? this.last = true : this.last = false;

	if ( thisline <= this.n )
    	this.line = thisline;	

	return(this.line);
  }


  //
  // function openindex()
  //
  // returns number of index lines in index file( Value also stored in this.n), or -1 if no index file found
  //
  this.openIndex=function openIndex() {
	this.n = 0;

	// Remove all existing search points from search list
    this.selectid.innerHTML="";
	
	try {	// Catch any errors that may occur accessing indextext element (in the indexfile) 
	  if ( window.frames[this.name].document.getElementById('indextext') )
        this.indextext = window.frames[this.name].document.getElementById('indextext').innerHTML;
	} catch(e) {
		this.indextext=null;
	}

    // No index file found so return 0 list size and set default message
	if ( !this.indextext ) {
      this.selectid.innerHTML = this.noindextext;
	  return(-1) // No index file found;
	}
    
    this.indexlines = this.indextext.split(ls);
	for ( i = 0; i < this.indexlines.length; i++ ) {
      fields = this.indexlines[i].split(fs);
	  if ( !(fields.length == 2) )
	    continue;	//Skip any line that doesn't have exactly two fields
	  
	  if ( time2int(fields[1]) == -1 )
	    continue;	//Skip any line that has a time value that is not in the form [[hh:]mm:]ss
	
	  this.index[this.n] = fields;

      timecodevalue = fields[1];
	  searchpointtext = fields[0];
	  searchnodename = 'sp' + this.n;
	  // Name of the search node
	  this.index[this.n][2] = searchnodename;
	  // Build list of search points
      this.selectid.innerHTML = this.selectid.innerHTML + '<A class="searchpoint" style="background-color:' + this.bknormal + '" name="'+searchnodename+'" id="'+searchnodename+'" href="#">'+timecodevalue + '  ' + searchpointtext + '</A>';

	  this.n++;
    }

	// Register functions for onMouseover, onMouseout events for index search points
	for ( i = 0; i < this.n; i++ ) {
	  searchnodename = 'sp' + i;
      (document.all) ? eval(searchnodename + '.onmouseover=SeekIndex') : eval('document.links["'+searchnodename+'"].onmouseover=SeekIndex');
      (document.all) ? eval(searchnodename + '.onmouseout=SeekIndex') : eval('document.links["'+searchnodename+'"].onmouseout=SeekIndex');

      if ( !connection.isonline || (connection.isonline && connection.isstreamed ) ) {
	    (document.all) ? eval(searchnodename + '.onclick=dummyeventhandler') : eval('document.links["'+searchnodename+'"].onclick=dummyeventhandler');
	  }else {
        (document.all) ? eval(searchnodename + '.onclick=tiptexthandler') : eval('document.links["'+searchnodename+'"].onclick=tiptexthandler');
      }
    }

	// If there are no validly parsed lines in the indexfile, Displayed default text.
	if ( this.n == 0 )
      this.selectid.innerHTML = this.noindextext;
	  
	return(this.n);
  }
  

  //
  // function seekIndex(seek)
  //
  this.seekIndex=function seekIndex(seek) {
    this.line=1;

    if ( isNaN(parseFloat(seek)) || isNaN(parseInt(seek)) )
	  return(false);
    
    do {
	  line = this.line;
	  this.nextIndex();		// Retrieves this.start. Set this.last if last index point was read.

	  if ( seek < this.start ) {
		(line == 1) ? this.nextIndex(1) : this.nextIndex(line-1);	//Read the previous index point, or 1st point
		return(true);
	  }

    } while( !this.last );

    return(false);
  }
  
}



// Class Indexer
function Indexer(instance) {
  this.name='searchindex';			// Name of positionable element <div> that hold index search points
  this.instance='indexer';    // Name of object reference used to instatiate this class. Override this as needed. Default 'indexer'
  this.indexerthread=null;
  this.scrollthread=null;
  this.currentindexstart=null;
  this.currentsearchnode=null;
  this.isactiveindex=false;	// Set to true when the play-head is position between start of index point and beginning of next
  this.getfirstindex = false;

  this.start = function start(line) {
    // If a start index point is specified, retrieve that indexpoint.
    if ( line !=null )
  	    index.nextIndex(line);

	if ( !this.getfirstindex ) {
	  // Run these only while first index point hasn't been reached
	  this.currentindexstart = index.start;
      this.currentsearchnode = index.searchnode;
	}
	  
    // play-head has reached current requested index point, so highlight it.
    if ( !this.isactiveindex && movie1.getCurrentPosition() >= this.currentindexstart ) {
	  this.getfirstindex = true;
	  this.isactiveindex=true;	
	  this.currentsearchnode.style.backgroundColor = index.bkactive;	//Set backgound color 
	  if ( !index.last ) {
	    // Retrieve the next index point
  	    index.nextIndex();
	    this.indexerthread = window.setTimeout(this.instance + ".start();", 200);
	  }
	}
	else if ( this.isactiveindex && movie1.getCurrentPosition() >= index.start) {  
	  this.isactiveindex=false;	
      this.currentsearchnode.style.backgroundColor = index.bknormal;	//Restore backgound color 
	  this.currentindexstart = index.start;
	  this.currentsearchnode=index.searchnode;
	  this.indexerthread = eval(this.instance + ".start();");
    }else {
      // Monitor the play-head until an index point is found
      this.indexerthread = window.setTimeout(this.instance + ".start();", 200); 
    }
  }

  this.stop = function stop() {
	 window.clearTimeout(this.indexerthread);
	 this.restorebkcolorthread = window.setTimeout("indexer.currentsearchnode.style.backgroundColor = index.bknormal;",95);	//Restore backgound color
  }

}



// Class Caption
function Caption() {
	
  // PUBLIC MEMBER PROPERTIES
  
  this.name =  "captionfile";	// Name of iFrame element that opens the caption file. Default: captionfile.
  this.captiontext;				// Contains the caption text after openCaption() is called.
  this.captionlines;			// String array of individual caption lines
  this.caption = new Array();	// Parsed caption array
  this.nocaptiontext = " ";	//Text that is displayed if caption file is empty or contains no valid captions
  this.currentcaption;			// Current caption text set using nextCaption()
  this.start;					// Current caption start time set using nextCaption()
  this.duration	;				// Current caption duration set using nextCaption()
  this.n;						// Number of validly parsed lines in caption file
  this.line;					// Number from 1 to the number of captions. Increments each time nextCaption() is used.
  								// Set this value to 1 to reset captions
  this.last;					// True if last caption has been read. False otherwise.
								
  //
  // PRIVATE VARIABLES
  //
  var ls = '#';
  var fs=/[ \t]*\|[ \t]*/;

  //
  // function nextCaption([line])
  //
  this.nextCaption=function nextCaption(start) {
	  
    var thisline;
	if ( !isNaN(parseInt(start)) && start >= 0 && start <= this.n)
	   this.line=start;

	thisline = this.line;
	
	if ( this.n > 0 ) {
	  this.currentcaption = this.caption[thisline-1][0];
	  this.start = time2int(this.caption[thisline-1][1]);
	  this.duration = time2int(this.caption[thisline-1][2]);
	}else {
	  this.currentcaption = this.nocaptiontext;
	  this.start = 0;
	  this.duration = 10;
	}
	
	thisline++;
	
	( thisline > this.n ) ? this.last = true : this.last = false;
	
	if ( thisline <= this.n )
    	this.line = thisline;	

	return(this.line);
  }

  //
  // function openCaption()
  //
  this.openCaption=function openCaption() {
	this.n = 0;
	this.line = 0;
	
	if ( window.frames[this.name].document.getElementById('captiontext') )
      this.captiontext = window.frames[this.name].document.getElementById('captiontext').innerHTML;

    if ( !this.captiontext )
	  return(this.n);
	  
    this.captionlines = this.captiontext.split(ls);
	for ( i = 0; i < this.captionlines.length; i++ ) {
      fields = this.captionlines[i].split(fs);
	  if ( !(fields.length == 3 || fields.length == 2) )
	    continue;	//Skip any line that doesn't have extactly three or exactly two fields
	  
	  if ( fields.length == 2 )
	    fields[2] = '99:99:99';		// If no duration data in caption line, force it to '99:99:99'
		
	  if ( time2int(fields[1]) == -1 || time2int(fields[2]) == -1 )
	    continue;	//Skip any line that has a time value that is not in the form [[hh:]mm:]ss
	
	  this.caption[this.n] = fields;
	  this.n++;
	}
	if ( this.n > 0 )
	  this.line=1;
	  
	return(this.n);
  }

  //
  // function seekCaption(seek)
  //
  this.seekCaption=function seekCaption(seek) {
    this.line=1;

    if ( isNaN(parseFloat(seek)) || isNaN(parseInt(seek)) )
	  return(false);
    
    
    do {
	  line = this.line;
	  this.nextCaption();
	  currentcaptionstart = this.start;
	  currentcaptionend = this.start + this.duration;
	  if ( !this.last ) {
	    this.nextCaption();				// Retrieve next caption to get it's start time
		if ( currentcaptionend > this.start ) {
		// Set the current caption end to value of next caption start
		currentcaptionend = this.start;
		}
	  }

	  this.nextCaption(line);		// Retrieve the current caption
 
	  if ( seek < currentcaptionstart || (seek >= currentcaptionstart && seek <= currentcaptionend) ) {
	    return(true);
	  }

    } while( !this.last );

    return(false);
  }

}



// Class Captioner
function Captioner(instance) {
  this.name='caption';			// Name of positionable element <div> that hold caption text
  this.instance='captioner';    // Name of object reference used to instatiate this class. Override this as needed. Default 'captioner'
  this.captionthread=null;
  this.scrollthread=null;
  this.scrollYstep = 4;			// Scroller step increment (pixels)
  this.scrollinterval = 50;		// Scroller step interval (ms)
  this.leftX=25;				// Captionbox pixel X left stop 
  this.leftY=622;				// Captionbox pixel Y top stop 
  this.isactivecaption=false;	// Set to true when the play-head is position between start and end time of current caption
  this.captionendtime;			// End time of the current caption

  var iscaptionactive = false;			// True if a caption is currently being displayed

  this.start = function start(line) {
    // If a start caption is specified, retrieve that caption.
    if ( line !=null )
  	    caption.nextCaption(line);
		
    if ( !this.isactivecaption && movie1.getCurrentPosition() >= caption.start && movie1.getCurrentPosition() <= (caption.start + caption.duration) ) {
	  // Monitor the play-head having found a caption to display
	  this.isactivecaption = true;
	  this.captionendtime = caption.start + caption.duration;	// Store end time of current caption
	  this.stopScroller();									// Stop the scroller
      cc = document.getElementById(this.name);
      cc.childNodes[0].nodeValue=caption.currentcaption;	// Load caption field with current caption text
	  // eval(this.name + '=\"' + caption.currentcaption + '\"');	// Load caption field with current caption text
      this.startScroller();									// Do scroll to Top
	  if ( !caption.last ) {
	    // Retrieve the next caption
  	    caption.nextCaption();
	    this.captionthread = window.setTimeout(this.instance + ".start();", 1000);
	  }
    }else if ( this.isactivecaption && (movie1.getCurrentPosition() > this.captionendtime || movie1.getCurrentPosition() > caption.start) ) {
     // Monitor the play-head until end of caption
	  this.isactivecaption = false;      
	  this.stopScroller();									// Stop the scroller
	  this.captionthread = window.setTimeout(this.instance + ".start();", 1000);
    }else {
      // Monitor the play-head until a caption is found
      this.captionthread = window.setTimeout(this.instance + ".start();", 1000); 
    }
  }

  this.stop = function stop() {
  if ( this.captionthread )
    window.clearTimeout(this.captionthread);
  this.stopScroller();
  }

  this.startScroller = function startScroller() {
    var scrollstep = this.scrollYstep;
    if (  dd.elements[this.name].y - this.scrollYstep < this.leftY ) {
      scrollstep = Math.abs(this.leftY - dd.elements[this.name].y);
      dd.elements[this.name].moveBy(0, -scrollstep);
	  return(true);
    }else {
      dd.elements[this.name].moveBy(0, -scrollstep);
      this.scrollthread=window.setTimeout(this.instance + ".startScroller();", this.scrollinterval);
    }
  }

  this.stopScroller = function stopScroller() {
    if ( this.scrollthread )
      window.clearTimeout(this.scrollthread);
    dd.elements[this.name].moveTo(dd.elements[this.name].defx, dd.elements[this.name].defy);
  }

}

