<!--

//---------------------------------------------------------------------------
// Copyright (c) Pearson Knowledge Technologies.  All Rights Reserved
//
// Proprietary - Use with PKT Written Permission Only
// Module Name:   ssStudent.js
// Primary Author: Scott Dooley
// Last Revised:   $Id: ssStudent.js,v 1.84 2009/01/20 16:01:37 dooley Exp $

function openWindow( url, windowTitle, width, height ) {
    var newWindow =
        window.open( url, windowTitle,
                     "height=" + height + ",width=" + width + 
                     ",menubar=yes,toolbar=no,directories=no,resizable=yes,scrollbars=yes",
                     true );
    newWindow.focus();
}


// Open the glossary window and scroll the the anchor (definition) for the
// given term.
//
function openGlossary( anchorName, docPath, glossaryFile ) {
    openWindow( docPath + "helptext/" + glossaryFile + "#" + anchorName,
                'glossary', 625, 625 );
}


// Open the "more info" window to the given page.
// given term.
//
function openDetail( pageName, docPath ) {
    openWindow( docPath + "helptext/" + pageName,
                'moreInfo', 800, 680 );
}



//---------------------------------------------------------------------------
// BEGIN - Back button detection functions

var EssayFlag = false; // set in onload for scoreboard pages.
var ActivityName = ''; // set in onload for scoreboard pages.
var TimeOut = -1;
var NotSavedWarningTimeout = 900000; //15 minutes
//var NotSavedWarningTimeout = 60000; //1 minute for testing

function toolsUnloadMessage() {
  var msg = "\n\nYou could lose work if you use the Back button.\n" +
    "Please click the tool exit button at the bottom of the page instead.\n\n";
  return msg;
}

function scoreboardUnloadMessage() {

  var activity = ActivityName;  // set in onload from AssessResponse;
  var eFlag = EssayFlag;        // set in onload from AssessResponse;
  var msg = '';

  if ( eFlag ) {
    msg =
      "\n\nYou could lose work if you use the Back button.\n" +
      "To work on something new, click the\"Select New Activity\" button " +
      "at the bottom of this page.\n\n";
  }
  else {
    msg =
      "\n\nYou could lose work if you use the Back button.\n" +
      " * To see the reading, click the link for \"" + activity + "\".\n" +
      " * To work on something new, click the\"Select New Activity\" " +
      "button at the bottom of this page.\n\n";
  }
  return msg;
}

function selectionUnloadToActivityMessage() {
  var msg = "\n\nWARNING.  Do not use the browser back button from this page.\n" +
    "Click the name of the activity you want to work on.\n\n";
  return msg;
}

function selectionUnloadToLoginMessage() {
  var msg = "\n\nWARNING.  Do not use the browser back button from this page.\n" +
    'Click the "Log Out" button at the bottom of the page to exit.\n\n';
  return msg;
}

function setBunloadTools() {
  window.onbeforeunload = toolsUnloadMessage;
}

function setBunloadScoreboard() {
  window.onbeforeunload = scoreboardUnloadMessage;
}

function setBunloadSelectionToActivity() {
  window.onbeforeunload = selectionUnloadToActivityMessage;
}

function setBunloadSelectionToLogin() {
  window.onbeforeunload = selectionUnloadToLoginMessage;
}

function resetBunload() {
  window.onbeforeunload = null;
}

// END - Back button detection functions
//---------------------------------------------------------------------------


// displayBrowserUnsupported
//
//   Something the user wants to do is not supported by the
//   browser they are using.  Alert them to the situation.

function displayBrowserUnsupported( message ) {

  alert( message );
  return false;

}  // displayBrowserUnsupported


// Open the teacher comments response display window.
//
function displayResponseWithComments( windowTitle, displayScript, cellId,
                                      newLabel ) { 

  // Change the Status cell to have the given label and set the
  // background to gray.
  var statusCell = document.getElementById( cellId );
  statusCell.innerHTML = newLabel;
  statusCell.style.background='#999999';

  openWindow( displayScript, windowTitle, 900, 600 );
  return false;
}


// Open the IEA rubric display window.
//
function displayRubric( URL ) { 
    openWindow( URL, 'rubricDisplay', 680, 630 );
}


// Take the user back to the index page.  If requested, confirm that the
// user wants this to happen.
//
function startOver(confirmFlag, blankWarning, demoMode, buttonValue, buttonID) {

  var responseForm = document.summaryForm;
  var retVal = true;
  if (confirmFlag) {
    if (confirm( 'Are you sure that you want to leave this activity and ' +
                 'work on another?' )) {
      preventMultipleSubmissions( responseForm, buttonValue, buttonID );
      retVal = true;
    } else {
      retVal = false;
    }
  }

  // This can be invoked on the "View Last Summary" page, which does not
  // have a summary form, but does have a "Select New Activity" button, 
  // with this function as an action. So we must be careful to only warn
  // about blank summaries when there is a form to do it!
    
  var hasSummary = ( typeof document.summaryForm != "undefined" &&
                     typeof document.summaryForm.summary != "undefined" );
  if ( retVal && hasSummary && !hasContent(document.summaryForm.summary.value)) {
    if ( !demoMode ) {
      alert(blankWarning);
    }
  }

  if (retVal) {
    preventMultipleSubmissions( responseForm, buttonValue, buttonID );
    // special case of link action for restart button.
    if ( typeof responseForm.linkRestartAction != "undefined" ) {
      responseForm.linkRestartAction.value = buttonValue;
    }
  }

  if (retVal) {
    responseForm.submit();
  }
}


// The user has asked to log out.  Ensure this is their heartfelt desire.
//
function confirmExit(blankWarning) {

  if ( typeof document.summaryForm != "undefined" ) {
    clearTarget( document.summaryForm );
  }
    
  var retVal = confirm( 'Are you sure that you want to exit?' );

  if ( retVal &&
       ( typeof document.summaryForm != "undefined" ) &&
       ( typeof document.summaryForm.summary != "undefined" ) &&
       !hasContent( document.summaryForm.summary.value )) {
    alert(blankWarning);
  }

  return retVal;
}

//
// Clears the target window property for <formObj>. If
// <formObj> is undefined, no action is taken.
//

function clearTarget ( formObj ) {
  if ( typeof formObj != "undefined" ) {
    formObj.target = '';
  }
}

// Ensure that the student has entered a user ID and selected a school
//    Take an integer argument (Perl boolean) which determines whether
//    to include a message about logging in as guest.
//
function validateLogin( guestAccess, buttonValue, buttonID ) {
    var form = document.forms.loginForm;

    var userID = form.login.value;
    var password = form.password.value;
    var userIDError = false;
    var passwordError = false;
    var schoolSelectionError = false;

    var returnVal = true;

    if ( !userID ) { userIDError = true; }
    if ( !password ) { passwordError = true; }

    // schoolID may be a hidden field or it may be a selection box.
    if (( typeof form.schoolID != "undefined" ) &&
        (typeof form.schoolID.selectedIndex != "undefined" ) &&
        (form.schoolID.selectedIndex < 1)) {
      schoolSelectionError = true;
    }

    if ( userIDError && passwordError ) {
        var message = "Please type in your user ID and password and select your " +
            "school from the list.";
        if ( guestAccess > 0 ) {
            message += "\n\n(To try out the system, type 'guest'" +
                " as the userID and password and select 'Guest School.')";
        }       
        alert( message );
        returnVal = false;
    }
    else if ( userIDError ) {  
        alert( "Please type in your user ID" );
        returnVal = false;
    }
    else if ( passwordError ) {  
        alert( "Please type in your password" );
        returnVal = false;
    }
    else if ( schoolSelectionError ) {
        returnVal = false;
        alert( "Please select a school." );
    }

    // possible that there is not password for this user so don't call that
    // an error.

    if ( returnVal ) {
      preventMultipleSubmissions( form, buttonValue, buttonID );
    }
    return returnVal;
}


function validateResponseInput( minLength, maxLength, minFeedback, maxFeedback,
                                textType, sectionCount, buttonValue, buttonID )
{
  var returnVal = false;

  var responseForm = document.summaryForm;
  var summaryWidget = responseForm.summary;
  var summary;

  if ( typeof summaryWidget != "undefined" ) {
    summary = summaryWidget.value;
  }
  else {
    // If there is no summary parameter then we are returning from one
    // of the tool pages.  Just return true to get back to the scoreboard.
    preventMultipleSubmissions( responseForm, buttonValue, buttonID );
    returnVal = true;
  }

  if ( summary.length < minLength ) {
    alert( minFeedback );
    returnVal = false;
  }
  else if ( summary.length > maxLength ) {
    alert( maxFeedback );
    returnVal = false;
  }
  else {
    preventMultipleSubmissions( responseForm, buttonValue, buttonID );        
    returnVal = true;
  }

  if (returnVal) {
    responseForm.submit();
  }
}



function enableButton( buttonID ) {
  var button = document.getElementById( buttonID );
  if (  button != null ) {
    button.disabled = false;
  }
}

function disableButton( buttonID ) {
  var button = document.getElementById( buttonID );
  if (  button != null ) {
    button.disabled = true;
  }
}


// Used by validation functions.

function preventMultipleSubmissions( responseForm, buttonValue, buttonID ) {

  if ( typeof buttonValue != "undefined" &&
       typeof buttonID != "undefined" ) {
    var button = document.getElementById( buttonID );
    if (  button != null ) {
      button.disabled = true;  // disabling doesn't work on IE or Safari
    }
    else {
      alert( "Debug: button from ID " + buttonID + " is null." );
    }

    if ( typeof responseForm.linkAction != "undefined" ) {
        responseForm.linkAction.value = buttonValue;
    }
  }

  /**** multiple button disable.  Decided against this. If the user were
        to stop their submission then all buttons would be disabled.

  disableAllButtons( responseForm );
  responseForm.linkAction.value = buttonValue;
  return;

  ***/
}


function validateForSpelling( textType, sectionCount, blankError,
                              buttonValue, buttonID ) {

  var responseForm = document.summaryForm;
  if (validateOtherFunctionInput( textType, sectionCount, blankError,
                                  buttonValue, buttonID )) {
    responseForm.action='checkSpelling.cgi';
    responseForm.submit();
  }
}


function validateForPlagiarism( textType, sectionCount, blankError,
                              buttonValue, buttonID ) {

  var responseForm = document.summaryForm;
  if (validateOtherFunctionInput( textType, sectionCount, blankError,
                                  buttonValue, buttonID )) {
    responseForm.action='checkPlagiarism.cgi';
    responseForm.submit();
  }
}


function validateForRedundance( textType, sectionCount, blankError,
                                buttonValue, buttonID ) {

  var responseForm = document.summaryForm;
  if (validateOtherFunctionInput( textType, sectionCount, blankError,
                                  buttonValue, buttonID )) {
    responseForm.action='checkRedundance.cgi';
    responseForm.submit();
  }
}


function validateForRelevance( textType, sectionCount, blankError,
                               buttonValue, buttonID ) {

  var responseForm = document.summaryForm;
  if (validateOtherFunctionInput( textType, sectionCount, blankError,
                                  buttonValue, buttonID )) {
    responseForm.action='checkRelevance.cgi';
    responseForm.submit();
  }
}


function validateForGrammar( textType, sectionCount, blankError,
                             buttonValue, buttonID ) {

  var responseForm = document.summaryForm;
  if (validateOtherFunctionInput( textType, sectionCount, blankError,
                                  buttonValue, buttonID )) {
    responseForm.action='checkGrammar.cgi';
    responseForm.submit();
  }
}


// Used by tools to determine if the student has content in 
// their response.  If not, display the given error message.
// If so, then call the preventMultipleSubmissions method 
// and return true.
//

function validateOtherFunctionInput( textType, sectionCount, blankError,
                                     buttonValue, buttonID ) {

  var responseForm = document.summaryForm;

  if (typeof blankError == "undefined" ) {
    blankError = "Your answer is blank!";
  }

  var summaryWidget = responseForm.summary;
  if ( typeof summaryWidget == "undefined" ) {
    summaryWidget = responseForm.textToEdit;
  }

  if (( textType == 'narrative segmented' ) ||
      ( textType == 'expository segmented' )) {
    summaryWidget.value = catSummaryChunks( responseForm, sectionCount );
  }
    
  var prose = summaryWidget.value;
  if ( hasContent( prose )) {
    preventMultipleSubmissions( responseForm, buttonValue, buttonID );
    return true;
  }
  else {
    alert(blankError);
    return false;
  }
}


function validateFormatForPrinting ( textType, sectionCount, path, blankError,
                                     buttonValue ) {

  var responseForm = document.summaryForm;

  disableButton( 'formatForPrint' );
  setTimeout( 'enableButton(\'formatForPrint\' );', 1000);

  responseForm.action='assessResponse.cgi';
  responseForm.linkAction.value = buttonValue;

  if (( textType == 'narrative segmented' ) ||
      ( textType == 'expository segmented' )) {
    responseForm.summary.value =
      catSummaryChunks( responseForm, sectionCount );
  }

  var returnVal = false;
  var prose = responseForm.summary.value;
  if ( hasContent( prose )) {
    responseForm.target='feedback';
    openWindow( path, 'feedback', 800, 760 );
    returnVal = true;
  } else {
    alert(blankError);
    returnVal = false;
  }

  if (returnVal) {
    responseForm.submit();
  }
}


function prepareForPortfolioOpen( path ) {
  var responseForm = document.summaryForm;

  disableButton( 'portfolioButton' );
  setTimeout( 'enableButton(\'portfolioButton\' );', 1000);

  responseForm.target='SSPortfolio';
  responseForm.action='studentPortfolio.cgi';
  openWindow( path, 'SSPortfolio', 800, 750 );
  responseForm.submit();
  return true;
}


// validateReturnToScoreboard
//
//    situation: returning to the scoreboard from a tool.
//    checks: 1. if no defined response form, prevent submission
//            2. if no content and a "blank error" defined, show the error
//               and prevent submission.
//    side effects: submit the form if the checks are met.

function validateReturnToScoreboard ( blankError, buttonValue, buttonID ) {

  var prose;
  var returnVal = true;
  var responseForm = document.forms.summaryForm;
  
  if ( typeof responseForm != "undefined" ) {
    var summaryWidget = responseForm.summary;
    if ( typeof summaryWidget == "undefined" ) {
      summaryWidget = responseForm.textToEdit;
    }
    if ( typeof summaryWidget != "undefined" ) {
      prose = summaryWidget.value;
      if ( hasContent( prose )) {
        preventMultipleSubmissions( responseForm, buttonValue, buttonID );
        returnVal = true;
      } 
      else if (blankError) {
        alert(blankError);
        returnVal = false;
      }
      else {
          // else, go ahead and return.  The student somehow managed
          // to get to the tool without any prose, so let them
          // go back to the response input page or scoreboard.
          preventMultipleSubmissions( responseForm, buttonValue, buttonID );
      }
    }
    else {
        // else, go ahead and return.  The student somehow managed
        // to get to the tool without any prose, so let them
        // go back to the response input page or scoreboard.
        preventMultipleSubmissions( responseForm, buttonValue, buttonID );
    }
  }

  if ( returnVal ) {
      responseForm.submit();
  }
  return returnVal;
}  // validateReturnToScoreboard


// save button callback function that is currently used to concatenate
// the summary components for segmented summary types.
//
function responseSave( textType, sectionCount, blankError,
                       buttonValue, buttonID ) {
  clearTimeout(TimeOut);
  var responseForm = document.summaryForm;
  if (( textType == 'narrative segmented' ) ||
      ( textType == 'expository segmented' )) {
    responseForm.summary.value =
      catSummaryChunks( responseForm, sectionCount );
    //confirm( "full summary is " + responseForm.summary.value );
  };
    
  var prose = responseForm.summary.value;
  var returnVal = false;
  if ( hasContent( prose )) {
    preventMultipleSubmissions( responseForm, buttonValue, buttonID );  
    returnVal = true;
  }
  else if (blankError) {
    alert(blankError);
    returnVal = false;
  }
  else {
    // If no blank error provided then allow a blank response.
    returnVal = true;
  }

  if ( returnVal ) {
    responseForm.submit();
  }
}  // responseSave


function startTimer()
{  
      if (TimeOut != -1)
      {
         
      }
      else
      {
           TimeOut = setInterval("displayWorkUnsavedMsg()", NotSavedWarningTimeout);  
      }
      return;
}

// when the student is working on the summary
// and there is text in the summary a popup warning
// will appear every NotSavedWarningTimeout 
//
function displayWorkUnsavedMsg( ) {
  var responseForm = document.summaryForm;
  var prose = responseForm.summary.value;
  var minutes = convertMilliSecondsToMinutes(NotSavedWarningTimeout);
  if ( hasContent( prose) ) {
     alert("You have not saved your work in "+minutes+" minutes. Please save your work.");
  }
  clearTimeout(TimeOut);
  
}


// convert milliseconds to minutes for the display message
//
function convertMilliSecondsToMinutes(NotSavedWarningTimeout ) {
      return (NotSavedWarningTimeout/60000);
  
}

// auxiliary function for validateResponseInput, validateSpellingInput,
// and validateFormatInput.
//
function catSummaryChunks( form, sectionCount ) {
    var index = 0;
    var summaryBox;
    var boxValue = "";
    var summaryString = "";
    while ( index < sectionCount ) {
        summaryBox = form.elements[ 'summary' + index ];
        boxValue = summaryBox.value;
        boxValue = boxValue.replace( /\r\n(\r\n)+/g, "\n" ); //remove paras - Win
        boxValue = boxValue.replace( /\n\n+/g, "\n" );   // remove paras - Unix
        boxValue = boxValue.replace( /\r\r+/g, "\n" );   // remove paras - Mac
        boxValue = boxValue.replace( /\n+$/g, "" ); // remove trailing newlines
        if ( boxValue == '') {
            boxValue = '(empty paragraph)';
        }
        summaryString += boxValue + "\n\n";
        index ++;
    }
    return summaryString;
}


// disableAllButtons
//   disable all the buttons on the given form. 
//   Used for preventing multiple submissions
//

function disableAllButtons( form ) {

  var numElements = form.elements.length;

  var oneField;
  var fieldType;

  var dstring = '';

  for (var i = 0; i < numElements; i++ ) {
    oneField = form.elements[ i ];
    dstring += 'field: ' + oneField.type + ', ';
    fieldType = oneField.type;
    if ( fieldType == 'button' ||
         fieldType == 'submit' ) {
      oneField.disabled = true;
    }
  }
  //alert( "fields: " + dstring );
}


// call back for "start new" or "continue working" button for a summary.
//
function workOnAssignment( buttonValue, buttonID ) {
  var responseForm = document.startForm;

  preventMultipleSubmissions( responseForm, buttonValue, buttonID );
  responseForm.submit();
  //return true;
}


// The user has chosen to try one of the samples.
// Find the main summary street window, paste the chosen sample
// text into the query window and close this sample window.
//
function selectDemo( sampleField ) {
    var sampleValue = sampleField.value;
    var summaryWindow = self.opener;
    var summaryForm;

    if ( (summaryWindow == null) ||
         ( typeof summaryWindow.document == "undefined" )) {
      alert( "Please copy the sample summary and paste it into the text box on the summary input page." );
      return;
    }

    summaryForm = summaryWindow.document.forms.summaryForm;

    if ( typeof summaryForm != "undefined" ) {
      summaryForm.summary.value = sampleField.value;
    }

    var directive = summaryWindow.document.getElementById('demoDirective');
    if ( typeof directive != "undefined" ) {
      var oldNote = directive.innerHTML;
      var newNote = directive.innerHTML; //in case we don't find a familiar string
      if ( oldNote.search(/continue select another/) != -1 ) {
        newNote =
          oldNote.replace( /continue select another/,
                           "continue click \"Get Feedback\" or select another " );
      }
      else if ( oldNote.search(/continue select a/) != -1 ) {
        newNote =
          oldNote.replace( /continue select a/,
                           "continue click \"Get Feedback\" or select another " );
      }
      else if (oldNote.search(/In this demonstration you may select a/) != -1 ) {
        newNote =
          oldNote.replace( /In this demonstration you may select a/,
                           "To continue click \"Get Feedback\" or select another " );
      }

      directive.innerHTML = newNote;
    }

    var mainWindow = summaryWindow.parent;
    if ( mainWindow ) {
        mainWindow.focus();
    }
    top.close();
}

// Display a new window or bring an existing one to the top that
// provides the user with sample queries that can be selected.
//
function displaySampleSummaries( scriptPath, textID, schoolID, sessionID ) {
    textID = escape(textID)
    var newLocation = scriptPath + 'displaySample.cgi?textID=' + textID;
    newLocation += '&schoolID=' + schoolID;
    newLocation += '&sessionID=' + sessionID;

    var summaryWindow =
        window.open( newLocation, "streetSamples",
                     "height=722,width=800,menubar=yes,toolbar=no,directories=no,resizable=yes,scrollbars=yes",
                     //"height=700,width=600,toolbar=yes,resizable=yes",
                     true );
    summaryWindow.focus();
}


// submit the student's form with the given submission function.
//
function submitFromLink( action, blankError ) {

    var form = document.summaryForm;
    form.linkAction.value = action;

    var prose = document.summaryForm.summary.value;
    if ( hasContent( prose )) {
        form.submit();
    } else {
        alert(blankError);
    }

    return false;  // don't submit again.
}


// If blank response then display error.
//
function allowToolNavigation( blankError, newAction, buttonValue ) {

  var form = document.summaryForm;
  var prose = form.summary.value;
  if ( !hasContent( prose )) {
    alert(blankError);
  }
  else { 
    form.action = newAction;
    form.linkAction.value = buttonValue;
    form.submit();
  }

  // never follow the link.  We will have submitted the form here if 
  // all is well
  return false;
}


// hasContent - predicate that returns true only if prose contains actual
// content, not just blanks. In other words, any single word or more will
// cause this function to return true.

function hasContent ( prose ) {
    // Note: (1) text.search(...) returns a position or -1
    //       (2) \w is a character class that matches any 'word character'.
    return prose!=null && prose.search(/\w/)!=-1;
}


function displayHint( hintScript ) {
    openWindow( hintScript, 'hintViewer', 500, 300 );
    return false;
}

//---------------------------------------------------------------------------
// BEGIN - Portfolio functions

// The user has chosen to use one of their previous responses.
// Find the main summary street window, paste the chosen response
// text into the query window and close this window.
//
function selectPreviousResponse(responseID, currentOrLastIndicator, buttonID) {

  var form = document.forms.responseSelectionForm;

  if ( responseID > 0 ) {
    form.selectedResponse.value = responseID;
  }
  else if ( currentOrLastIndicator == 'current' ) {
    form.useCurrentResponse.value = currentOrLastIndicator;
  }

  form.buttonAction.value = 'tryResponseButton';

  disableButton( buttonID );  // prevent a double click.

  var summaryWindow = self.opener;
  summaryWindow.name = 'PKTWTLMain';
  // Reset the unload trigger.
  summaryWindow.resetBunload();
  // Note that setting summaryWindow.onbeforeunload directly fails in IE
  form.target = summaryWindow.name;
  form.submit();
  
  var mainWindow = summaryWindow.parent;
  if ( mainWindow ) {
    mainWindow.focus();
  }

  // Netscape won't submit if the window closes right away. Add a little delay.
  setTimeout( "top.close()", 2500 );
}


// END - Portfolio functions
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// BEGIN - Cloze test functions

function checkClozeAnswers() {
  var form = document.clozeForm;
  var items = form.elements;
  var answer;
  var i;
  for (i=0; i < items.length; i++) {
    if (items[i].type == 'text') {
      answer = items[i].value;
      if ( answer != "" ) {
        if ( answer.match( /\w \w/ )) {
          alert( "Your answers must each be a single word.  Please change " +
                 '"' + answer + '".' );
          return false;
        } else if ( !answer.match( /^[a-zA-Z]+$/ ) ) {
          alert( "Answers cannot contain spaces, digits or punctuation. " +
                 "Please change " + '"' + answer + '".' );               
          return false;  
        }
      } else {
        alert( "Please provide a word for each sentence." );
        return false;
      }
    } else if ((items[i].type == 'select-one' ) ) {
        answer = items[i].value;
        if ( answer == "--select answer--") {
          alert( "Please select an answer in all drop-down menus." );
          return false;
        } else {
          return true;
        }
    }       
    else if (( items[i].type != "hidden" ) &&
             ( items[i].type != "submit" )) { 
      alert( "item is of type " + items[i].type );
   }
  }
  return true;
}

// END - Cloze test functions
//---------------------------------------------------------------------------


//
// Equivalent to window.print, but safely detects whether the brower
// supports window.print, and directs the user to the browser's
// print menu if not.
//
function printWindowSafe() {
  if (window.print) {
    window.print()  
  } else {
    alert( 'Your browser does not support this print button. To print, ' +
           ' select Print from the File menu.' );
  }
}


// END - Student interface functions.
//---------------------------------------------------------------------------

//-->
