/// <summary>
/// ajaxcore/scormapi.js
/// scorm interface object for scormframe.aspx
/// ---------------------------------------------------------------
/// Copyright by IM-Systems AG 2009
/// ---------------------------------------------------------------
/// 27.08.2009 MPI created
/// </summary>

var scorm = {};

scorm.utils = {};
scorm.debug = { isActive: true }
scorm.utils.trace = function(msg){
  if(scorm.debug.isActive){
    //Firefox users can use the 'Firebug' extension's console.
    if(window.console && window.console.firebug){
      console.log("scorm> "+msg);
    } else {
      //alert(msg);
    }
  }
}

scorm.api13 = {};
scorm.api13.lastError = 0;
scorm.api13.errormessages = new Array();
scorm.api13.errormessages[0] = "No Error";
scorm.api13.errormessages[101] = "General Exception";
scorm.api13.errormessages[103] = "Already Initialized";
scorm.api13.errormessages[112] = "Termination Before Initialization";
scorm.api13.errormessages[113] = "Termination After Termination";
scorm.api13.errormessages[122] = "Retrieve Data Before Initialization";
scorm.api13.errormessages[123] = "Retrieve Data After Termination";
scorm.api13.errormessages[132] = "Store Data Before Initialization";
scorm.api13.errormessages[133] = "Store Data After Termination";
scorm.api13.errormessages[142] = "Commit Before Initialization";
scorm.api13.errormessages[143] = "Commit After Termination";
scorm.api13.errormessages[402] = "Unimplemented Data Model Element";
scorm.api13.errormessages[405] = "Data Model Element Is Write Only";
scorm.api13.errormessages[406] = "Data Model Element Is Read Only";
//scorm.api13.errormessages[] = "";


// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//      SCORM API 2004 1.3 (Initialize/Terminate: test)
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
/**
  * Begins a communication session with the LMS.
  * @param arg:
  * @param return:
  */
scorm.api13.Initialize = function(arg)
{
  this.lastError = 0;

  //proxies.tracking.db("scormapi.js:scorm.api13.Initialize arg:" + arg);

  if(lmsapi.sco != lmsapi.tmpsco) {
    lmsapi.sco = lmsapi.tmpsco;
  }
    
  if (lmsapi.sco != null && lmsapi.sco.isInitialized == true) {
    this.lastError = 103; // ERROR 103: Already Initialized
    return("false");
  }
  lmsapi.sco.tracking.SESSIONTIME = 0;
  lmsapi.sco.isInitialized = true;
  lmsapi.sco.sessionState = "Running";

  return("true");
} // Initialize


/**
  * Ends a communication session with the LMS.
  * @param arg:
  * @param return:
  */
scorm.api13.Terminate = function(arg)
{
  //proxies.tracking.db("scormapi.js:scorm.api13.Terminate arg:" + arg + " ------------------------------");

  this.lastError = 0;
  if(lmsapi.sco.isInitialized==false){
    this.lastError = 112;
    return("false");
  }
  if(lmsapi.sco.isTerminated==true){
    this.lastError = 113;
    return("false");
  }
  
  this.Commit("intern");
  lmsapi.sco.sessionState = "Terminated";
  lmsapi.sco.isTerminated = true;

  //proxies.tracking.db("scormapi.js:scorm.api13.Terminate: after COMMIT! ------------------------------");
  lmsapi.finalize();
  lmsapi.checkResourceTracking();
  
  return("true");
} // Terminate


// ---------------------------------------------------------------------------------------
//      Commit
// ---------------------------------------------------------------------------------------
/**
  * Indicates to the LMS that all data should be persisted (not required).
  * @param arg:
  * @param return:
  */
scorm.api13.Commit = function(arg)
{
  //proxies.tracking.db("scormapi.js:scorm.api13.Commit = function(arg) arg:" + arg);
  
  this.lastError = 0;
  if(lmsapi.sco.isTerminated == true){
    this.lastError = 143;
    return("false");
  }
  if(lmsapi.sco.isInitialized == false){
    this.lastError = 142;
    return("false");
  }
  
  lmsapi.updateScoTracking();
  lmsapi.sco.sessionState = "Terminated";
  //lmsapi.sco.isTerminated = true;

  lmsapi.updateResourceTracking();
  
  //proxies.tracking.db("scormapi.js:scorm.api13.Commit after: lmsapi.updateResourceTracking();");

  return("true");
} // Commit


// ---------------------------------------------------------------------------------------
//      GetValue
// ---------------------------------------------------------------------------------------
//
/**
  * Retrieves a value from the LMS.
  * @param element:
  * @param return:
  */
scorm.api13.GetValue = function(element)
{
  var result = "";

  //proxies.tracking.db("scormapi.js:scorm.api13.GetValue = function(element) elem:" + element);
  
  this.lastError = 0;
  if(lmsapi.sco.isTerminated==true){
    this.lastError = 123;
    return("false");
  }
  if(lmsapi.sco.isInitialized==false){
    this.lastError = 122;
    return("false");
  }

  if(element.indexOf("cmi.objectives") > -1 && element.indexOf("cmi.objectives._") < 0) {
    var parts = element.split(".");
    var index = parseInt(parts[2]);
    var item = (parts.length == 5) ? parts[3] + "_" + parts[4] : parts[3];
    if(item == "score._children") {
      result = "scaled,max,min,raw";
    } else {
      if(item == "id") {
        item = "OBJECTIVEID"; // objective_id
      } else if(item.indexOf("success_status") > -1) {
        item = "SUCCESS";
      } else if(item.indexOf("completion_status") > -1) {
        item = "COMPLETION";
      } else if(item.indexOf("completion_threshold") > -1) {
        item = "THRESHOLD";
      } else if(item.indexOf("location") > -1) {
        item = "LOCATION";
      } else if(item.indexOf("score_scaled") > -1) {
        item = "SCORESCALED";
      } else if(item.indexOf("score_raw") > -1) {
        item = "SCORERAW";
      } else if(item.indexOf("score_min") > -1) {
        item = "SCOREMIN";
      } else if(item.indexOf("score_max") > -1) {
        item = "SCOREMAX";
      } else if(item.indexOf("session_time") > -1) {
        item = "SESSIONTIME";
      } else {
        proxies.tracking.db("scormapi.js:scorm.api13.GetValue = function(element) elem:" + element + " NOT FOUND!!!");
      }
      var objective = lmsapi.getObjective(index);
      result = objective[item];
      
      if(item.indexOf("SUCCESS") > -1) {
        switch (result) {
          case "0":
            result = "passed";
            break;
          case "1":
            result = "failed";
            break;
          case "2":
            result = "unknown";
          default:
            result = "unknown";
            break;
        }
      } else if(item.indexOf("COMPLETION") > -1) {
        switch (result) {
          case "0":
            result = "completed";
            break;
          case "1":
            result = "incomplete";
            break;
          case "2":
            result = "notattempted";
            break;
          case "3":
            result = "unknown";
            break;
          default:
            result = "unknown";
            break;
        }
      }
    }

  } else { // SCO

    switch(element) {
      case "cmi.core.completion_status": // Indicates whether the learner has completed the SCO, API-Error
      case "cmi.completion_status":      // Indicates whether the learner has completed the SCO
        switch (lmsapi.sco.tracking.COMPLETION) {
          case "0":
            result = "completed";
            break;
          case "1":
            result = "incomplete";
            break;
          case "2":
            result = "notattempted";
            break;
          case "3":
            result = "unknown";
            break;
          default:
            result = "unknown";
            break;
        }
        break;
      case "cmi.success_status": // Indicates whether the learner has mastered the SCO
        switch (lmsapi.sco.tracking.SUCCESS) {
          case 0:
            result = "passed";
            break;
          case 1:
            result = "failed";
            break;
          case 2:
            result = "unknown";
            break;
          default:
            result = "";
            break;
        }
        break;
      case "cmi.learner_id":
        result = lmsapi.user.PROFILEID;
        break;
      case "cmi.learner_name":
        result = lmsapi.user.name;
        break;
      case "cmi.completion_threshold": // (0...1), readonly, Used to determine whether the SCO should be considered complete.
        result = (lmsapi.sco.tracking.COMPLETIONTHRESHOLD != null) ? lmsapi.sco.tracking.COMPLETIONTHRESHOLD : "";
        break;
      case "cmi.location": // The learner’s current location in the SCO
        result = (lmsapi.sco.tracking.LOCATION != null) ? lmsapi.sco.tracking.LOCATION : "";
        break;
      case "cmi.score.scaled": // (-1...1) Number that reflects the performance of the learner
        result = (lmsapi.sco.tracking.SCORESCALED != null) ? lmsapi.sco.tracking.SCORESCALED : "";
        break;
      case "cmi.score.raw": // Number that reflects the performance of the learner relative to the range bounded by the values of min and max
        result = (lmsapi.sco.tracking.SCORERAW != null) ? lmsapi.sco.tracking.SCORERAW : "";
        break;
      case "cmi.score.min": // score_min
        result = (lmsapi.sco.tracking.SCOREMIN != null) ? lmsapi.sco.tracking.SCOREMIN : "";
        break;
      case "cmi.score.max": // score_max
        result = (lmsapi.sco.tracking.SCOREMAX != null) ? lmsapi.sco.tracking.SCOREMAX : "";
        break;
      case "cmi.suspend_data": // Provides space to store and retrieve data between learner sessions
        result = (lmsapi.sco.tracking.SUSPENDDATA != null) ? lmsapi.sco.tracking.SUSPENDDATA : "";
        break;
      case "cmi.total_time": // total_time
        result = (lmsapi.sco.tracking.TOTALTIME != null) ? 
                 scorm.utils.CentisecsToISODuration(parseInt(lmsapi.sco.tracking.TOTALTIME)) : "";
        break;
      case "cmi.session_time":
        this.lastError = 405;
        //scorm.utils.trace('ERROR 405 Data Model Element Is Write Only');
        return("");
        break;
      case "cmi.objectives._count": // readonly, Current number of objectives being stored by the LMS
        result = (typeof (lmsapi.sco.objectives) == "object") ? lmsapi.sco.objectives.length : 0;
        break;
      case "cmi.mode": // readonly, Identifies one of three possible modes in which the SCO may be presented to the learner
        result = lmsapi.sco.mode != null ? lmsapi.sco.mode : "normal";
        break;
      case "cmi.credit": // readonly, Indicates whether the learner will be credited for performance in the SCO
        result = lmsapi.sco.credit != null ? lmsapi.sco.credit : "credit";
        break;
      case "cmi.entry": // readonly, Asserts whether the learner has previously accessed the SCO
        result = (parseInt(lmsapi.sco.tracking.ACTIVITIES) > 1) ? "resume" : "ab_inition";
        break;
      case "cmi.launch_data": // readonly, Data provided to a SCO after launch, initialized from the dataFromLMS manifest element
        result = lmsapi.sco.LAUNCHDATA != null && lmsapi.sco.LAUNCHDATA != "undefined" ? lmsapi.sco.LAUNCHDATA : "";
        break;
      case "cmi.objectives._children": // readonly
        //result = "id,score,success_status,completion_status,progress_measure,description";
        result = "OBJECTIVEID,SCORE,SUCCESS,COMPLETION,PROGRESSMEASURE,DESCRIPTION";
        break;
      case "cmi.score._children":
        //result = "scaled,max,min,raw";
        result = "scaled,max,min,raw";
        break;
      case "cmi.progress_measure":
	      result = (lmsapi.sco.progress_measure != null && lmsapi.sco.progress_measure != null) ? lmsapi.sco.progress_measure : "";
        break;        
      default:
        proxies.tracking.db("scormapi.js:scorm.api13.GetValue = function(element) elem:" + element + " NOT FOUND!!!");
        this.lastError = 402;
        return "false";
        break;
    } // switch
  } // cmi.objectives

  //proxies.tracking.db("scormapi.js:scorm.api13.GetValue = function(element) result=" + result);
  
  return(result);
} // GetValue


// ---------------------------------------------------------------------------------------
//      SetValue
// ---------------------------------------------------------------------------------------
/**
  * Saves a value to the LMS.
  * @param element:
  * @param value:
  * @param return:
  */
scorm.api13.SetValue = function(element, value)
{
  var current;
  var doit = true;
  
  //proxies.tracking.db("scormapi.js:scorm.api13.SetValue e/v:" + element + "," + value);
  
  this.lastError = 0;
  if(lmsapi.sco.isTerminated == true){
    this.lastError = 133;
    return("false");
  }
  if(lmsapi.sco.isInitialized == false){
    this.lastError = 132;
    return("false");
  }

  if(element.indexOf("cmi.objectives") > -1) {
    var parts = element.split(".");
    var index = parseInt(parts[2]);
    var item = (parts.length == 5) ? parts[3] + "_" + parts[4] : parts[3];
    if(item == "id") {
      item = "OBJECTIVEID"; // objective_id
    } else if(item.indexOf("success_status") > -1) {
      item = "SUCCESS";
      switch (value) {
        case "passed":
          value = "0";
          break;
        case "failed":
          value = "1";
          break;
        case "unknown":
          value = "2";
          break;
        default:
          value = "2";
          break;
      }
    } else if(item.indexOf("completion_status") > -1) {
      item = "COMPLETION";
      switch (value) {
        case "completed":
          value = "0";
          break;
        case "incomplete":
          value = "1";
          break;
        case "notattempted":
          value = "2";
          break;
        case "unknown":
          value = "3";
          break;
        default:
          value = "3";
          break;
      }
    } else if(item.indexOf("completion_threshold") > -1) {
      item = "THRESHOLD";
    } else if(item.indexOf("location") > -1) {
      item = "LOCATION";
    } else if(item.indexOf("score_scaled") > -1) {
      item = "SCORESCALED";
    } else if(item.indexOf("score_raw") > -1) {
      item = "SCORERAW";
    } else if(item.indexOf("score_min") > -1) {
      item = "SCOREMIN";
    } else if(item.indexOf("score_max") > -1) {
      item = "SCOREMAX";
    //laut SCORM-Definition auf Objectiveebene nicht definiert
    //} else if(item.indexOf("session_time") > -1) {
    //  item = "SESSIONTIME";
    } else {
      proxies.tracking.db("scormapi.js:scorm.api13.SetValue e/v:" + element + "," + value + " NOT FOUND for setting a Value");
      doit = false;
    }

    if(doit) {    
      var objective = lmsapi.getObjective(index);
      if(objective[item] != value) {
        objective.isUpdated = true;
      }
      objective[item] = value;
    }

  } else { // SCO
  
    switch(element) {
      case "cmi.progress_measure":
        if(lmsapi.sco.tracking.PROGRESSMEASURE != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.PROGRESSMEASURE = value;
        break;
      case "cmi.core.completion_status": // Indicates whether the learner has completed the SCO, API-Error
      case "cmi.completion_status":      // Indicates whether the learner has completed the SCO
        current = lmsapi.sco.tracking.COMPLETION;
        switch (value) {
          case "completed":
            lmsapi.sco.tracking.COMPLETION = "0";
            break;
          case "incomplete":
            lmsapi.sco.tracking.COMPLETION = "1";
            break;
          case "notattempted":
            lmsapi.sco.tracking.COMPLETION = "2";
            break;
          case "unknown":
            lmsapi.sco.tracking.COMPLETION = "3";
            break;
          default:
            lmsapi.sco.tracking.COMPLETION = "3";
            break;
        }
        if(lmsapi.sco.tracking.COMPLETION != current) {
          lmsapi.sco.isUpdated = true;
        }
        break;
      case "cmi.success_status": // Indicates whether the learner has mastered the SCO
        current = lmsapi.sco.tracking.SUCCESS;
        switch (value) {
          case "passed":
            lmsapi.sco.tracking.SUCCESS = "0";
            break;
          case "failed":
            lmsapi.sco.tracking.SUCCESS = "1";
            break;
          case "unknown":
            lmsapi.sco.tracking.SUCCESS = "2";
            break;
          default:
            lmsapi.sco.tracking.SUCCESS = "2";
            break;
        }
        if(lmsapi.sco.tracking.SUCCESS != current) {
          lmsapi.sco.isUpdated = true;
        }
        break;
      case "cmi.exit": // Indicates how or why the learner left the SCO
        current = lmsapi.sco.tracking.EXITSTATUS;
        switch (value) {
          case "timeout":
            lmsapi.sco.tracking.EXITSTATUS = "0";
            break;
          case "suspend":
            lmsapi.sco.tracking.EXITSTATUS = "1";
            break;
          case "logout":
            lmsapi.sco.tracking.EXITSTATUS = "2";
            break;
          case "normal":
            lmsapi.sco.tracking.EXITSTATUS = "3";
            break;
          default:
            lmsapi.sco.tracking.EXITSTATUS = "3";
            break;
        }
        if(lmsapi.sco.tracking.EXITSTATUS != current) {
          lmsapi.sco.isUpdated = true;
        }
        break;
      case "cmi.completion_threshold": // ReadOnly: Used to determine whether the SCO should be considered complete.
        if(lmsapi.sco.tracking.COMPLETIONTHRESHOLD != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.COMPLETIONTHRESHOLD = value;
        break;
      case "cmi.score.scaled": // completion_threshold
        if(lmsapi.sco.tracking.COMPLETIONTHRESHOLD != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.SCORESCALED = value; // score_scaled
        lmsapi.sco.isUpdated = true;
        break;
      case "cmi.score.raw": // score_raw
        if(lmsapi.sco.tracking.SCORERAW != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.SCORERAW = value;
        break;
      case "cmi.score.min": // score_min
        if(lmsapi.sco.tracking.SCOREMIN != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.SCOREMIN = value;
        break;
      case "cmi.score.max": // score_max
        if(lmsapi.sco.tracking.SCOREMAX != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.SCOREMAX = value;
        break;
 
      case "cmi.location": // The learner’s current location in the SCO
        if(lmsapi.sco.tracking.LOCATION != null && 
           lmsapi.sco.tracking.LOCATION.length > 0 && 
           lmsapi.sco.tracking.LOCATION != value) {
          lmsapi.sco.isUpdated = true;
          lmsapi.UpdateObjectiveSessionTime(lmsapi.sco.tracking.LOCATION); // last Objective totaltime-update

          lmsapi.SetObjectiveSessionTime(value); // next Objective start
        } else {
          lmsapi.SetObjectiveSessionTime(value); // first Objective start
        }
        
        if(lmsapi.notrack.theVeryFirst == 0) {
      	  lmsapi.UpdateScoLocation(value);
        } else {
          //das erste automatische Laden ist noch keine Benutzeraktivität (auf Obj/SCO-Ebene)
          lmsapi.notrack.theVeryFirst = 0;
        }
        lmsapi.sco.tracking.LOCATION = value;
        break;

      case "cmi.session_time": // session_time, total_time
        lmsapi.UpdateObjectiveSessionTime(lmsapi.sco.tracking.LOCATION); // last Objective totaltime-update

//        lmsapi.sco.tracking.SESSIONTIME = scorm.utils.ISODurationToCentisecs(value);
//        lmsapi.sco.tracking.TOTALTIME = parseInt(lmsapi.sco._bckp_total_time) + 
//                                        parseInt(lmsapi.sco.tracking.SESSIONTIME);
        lmsapi.UpdateSCOSessionTime();    // NEW: last SCO totaltime-update
        lmsapi.sco.isUpdated = true;
        break;
  
      case "cmi.suspend_data": // suspend_data
        if(lmsapi.sco.tracking.SUSPENDDATA != value) {
          lmsapi.sco.isUpdated = true;
        }
        lmsapi.sco.tracking.SUSPENDDATA = value;
        break;
      case "cmi.total_time":
        this.lastError = 404;
        //scorm.utils.trace('ERROR 404 Data Model Element Is Read Only');
        return "false";
        break;
      default:
        proxies.tracking.db("scormapi.js:scorm.api13.SetValue e/v:" + element + "," + value + " NOT FOUND for setting a Value");
        this.lastError = 402;
        return("false");
        break;
    } // switch
  } // cmi.objectives
  //proxies.tracking.db("scormapi.js:scorm.api13.SetValue lmsapi.sco.isUpdated:" + lmsapi.sco.isUpdated);
  
  return("true");
} // SetValue


//
// ---------------------------------------------------------------------------------------
//      ERROR HANDLING
// ---------------------------------------------------------------------------------------
/**
  * Returns the error code that resulted from the last API call.
  * @param return: last error code
  */
scorm.api13.GetLastError = function()
{
  return this.lastError;
} // GetLastError


/**
  * Returns a short string describing the specified error code.
  * @param return: last error description
  */
scorm.api13.GetErrorString = function(errorCode)
{
  return(this.errormessages[this.lastError]);
} // GetErrorString


/**
  * Returns detailed information about the last error that occurred.
  * @param return: detailed last error description
  */
scorm.api13.GetDiagnostic = function(errorCode)
{
  return ("errordiagnostic: " + this.lastError + " - " + this.errormessages[this.lastError]);
} // GetDiagnostic


// ---------------------------------------------------------------------------------------
//      
//      SCORM API 1.2 (NOT YET!)
//      
// ---------------------------------------------------------------------------------------

scorm.api12 = {};
// Core
scorm.api12.LMSInitialize = function(arg)
{
//debugger;
  return scorm.api13.Initialize(arg);
}
scorm.api12.LMSFinish = function(arg)
{
//debugger;
  return scorm.api13.Terminate(arg);
}
scorm.api12.LMSCommit = function(arg)
{
//debugger;
  return scorm.api13.Commit(arg);
}
// Error Handling
scorm.api12.LMSGetLastError = function(arg)
{
  return scorm.api13.GetLastError(arg);
}
scorm.api12.LMSGetErrorString = function(arg)
{
  return scorm.api13.GetErrorString(arg);
}
scorm.api12.LMSGetDiagnostic = function(arg)
{
  return scorm.api13.GetDiagnostic(arg);
}

// Set
scorm.api12.LMSSetValue = function(arg,value)
{
//debugger;
  arg = scorm.utils.convertElementNameFrom12To13(arg);

  //scorm.utils.trace('LMSSetValue  '+arg+' - '+value);
  if(arg == "cmi.session_time"){
//    lmsapi.sco.tracking.session_time = scorm.utils.CMIDurationToCentisecs(value);
//    lmsapi.sco.tracking.total_time = parseInt(lmsapi.sco._bckp_total_time) + 
//                                     parseInt(lmsapi.sco.tracking.session_time);
    
    
    
    lmsapi.sco.isUpdated = true;
    return "true";
  }
  return scorm.api13.SetValue(arg, value);
} // SetValue


// Get
scorm.api12.LMSGetValue = function(arg)
{
//debugger;
  arg = scorm.utils.convertElementNameFrom12To13(arg);

  //scorm.utils.trace('LMSGetValue  '+arg);
  if(arg == "cmi.total_time"){
    return (lmsapi.sco.tracking.total_time != null) ? 
            scorm.utils.CentisecsToCMIDuration(parseInt(lmsapi.sco.tracking.total_time)) : "";
  }
  if(arg == "cmi.student_data.master_score"){
    return (lmsapi.sco.master_score != null && lmsapi.sco.master_score != undefined) ? lmsapi.sco.master_score : "";
  }
  return scorm.api13.GetValue(arg);
} // LMSGetValue


//
// ---------------------------------------------------------------------------------------
//      Helper functions for duration
// ---------------------------------------------------------------------------------------
// 
scorm.utils.convertElementNameFrom12To13 = function(arg)
{
  arg = arg.replace(/^cmi\.core/g, "cmi");
  arg = arg.replace(/student_id/g, "learner_id");
  arg = arg.replace(/student_name/g, "learner_name");
  arg = arg.replace(/lesson_location/g, "location");
  arg = arg.replace(/lesson_status/g, "completion_status");
  arg = arg.replace(/lesson_mode/g, "mode");
  return arg;
} // convertElementNameFrom12To13


scorm.utils.CentisecsToISODuration = function(n) 
{
  // Note: SCORM and IEEE 1484.11.1 require centisec precision
  // Months calculated by approximation based on average number
  // of days over 4 years (365*4+1), not counting the extra day
  // every 1000 years. If a reference date was available,
  // the calculation could be more precise, but becomes complex,
  // since the exact result depends on where the reference date
  // falls within the period (e.g. beginning, end or ???)
  // 1 year ~ (365*4+1)/4*60*60*24*100 = 3155760000 centiseconds
  // 1 month ~ (365*4+1)/48*60*60*24*100 = 262980000 centiseconds
  // 1 day = 8640000 centiseconds
  // 1 hour = 360000 centiseconds
  // 1 minute = 6000 centiseconds
  n = Math.max(n,0); // there is no such thing as a negative duration
  var str = "P";
  var nCs = n;
  // Next set of operations uses whole seconds
  var nY = Math.floor(nCs / 3155760000);
  nCs -= nY * 3155760000;
  var nM = Math.floor(nCs / 262980000);
  nCs -= nM * 262980000;
  var nD = Math.floor(nCs / 8640000);
  nCs -= nD * 8640000;
  var nH = Math.floor(nCs / 360000);
  nCs -= nH * 360000;
  var nMin = Math.floor(nCs /6000);
  nCs -= nMin * 6000
  // Now we can construct string
  if (nY > 0) str += nY + "Y";
  if (nM > 0) str += nM + "M";
  if (nD > 0) str += nD + "D";
  
  if ((nH > 0) || (nMin > 0) || (nCs > 0))  {
    str += "T";
    if (nH > 0) str += nH + "H";
    if (nMin > 0) str += nMin + "M";
    if (nCs > 0) str += (nCs / 100) + "S";
  }
  if (str == "P") str = "PT0H0M0S";
    // technically PT0S should do as well.
  return str;
} // CentisecsToISODuration


scorm.utils.ISODurationToCentisecs = function(str)
{
  // Only gross syntax check is performed here
  // Months calculated by approximation based on average number
  // of days over 4 years (365*4+1), not counting the extra day
  // every 1000 years. If a reference date was available,
  // the calculation could be more precise, but becomes complex,
  // since the exact result depends on where the reference date
  // falls within the period (e.g. beginning, end or ???)
  // 1 year ~ (365*4+1)/4*60*60*24*100 = 3155760000 centiseconds
  // 1 month ~ (365*4+1)/48*60*60*24*100 = 262980000 centiseconds
  // 1 day = 8640000 centiseconds
  // 1 hour = 360000 centiseconds
  // 1 minute = 6000 centiseconds
  var aV = new Array(0,0,0,0,0,0);
  var bErr = false;
  var bTFound = false;
  if (str.indexOf("P") != 0) bErr = true;
  if (!bErr) {
    var aT = new Array("Y","M","D","H","M","S")
    var p=0;
    var i = 0;
    str = str.substr(1); //get past the P
    for (i = 0 ; i < aT.length; i++) {
      if (str.indexOf("T") == 0) {
        str = str.substr(1);
        i = Math.max(i,3);
        bTFound = true;
      }
      p = str.indexOf(aT[i]);
      //alert("Checking for " + aT[i] + "\nstr = " + str);
      if (p > -1)  {
        // Is this a M before or after T? Month or Minute?
        if ((i == 1) && (str.indexOf("T") > -1) && (str.indexOf("T") < p)) continue;
        if (aT[i] == "S") {
          aV[i] = parseFloat(str.substr(0,p))
        } else {
          aV[i] = parseInt(str.substr(0,p))
        }
        if (isNaN(aV[i])) {
          bErr = true;
          break;
        } else if ((i > 2) && (!bTFound)) {
          bErr = true;
          break;
        }
        str = str.substr(p+1);
      }
    }
    if ((!bErr) && (str.length != 0)) bErr = true;
      //alert(aV.toString())
  }
  if (bErr) {
     //alert("Bad format: " + str)
    return 0
  }
  return aV[0]*3155760000 + aV[1]*262980000
      + aV[2]*8640000 + aV[3]*360000 + aV[4]*6000
      + Math.round(aV[5]*100)
} // ISODurationToCentisecs


scorm.utils.CentisecsToCMIDuration = function(n) 
{
  // Dauer von Centisekunden in Format 0000:00:00.00 konvertieren
  var csh = 100*60*60;
  var csm = 100*60;
  var css = 100;
  var h = "000" + Math.floor(n / csh);
	var m = "0" + Math.floor((n % csh) / csm);
	var s = "0" + Math.floor((n % csm) / css);
	var cs = "0" + (n % css);
  var hms = h.substr(h.length-4) + ":" + m.substr(m.length-2) + ":" + 
            s.substr(s.length-2) + "." + cs.substr(cs.length-2);
  return hms;
} // CentisecsToCMIDuration


scorm.utils.CMIDurationToCentisecs = function(s)
{
  // Dauer von Format 0000:00:00.00 in Centisekunden konvertieren
	var n = 60*60*100*parseInt(s.substr(0,4)) + 
	        60*100*parseInt(s.substr(5,2)) + 
	        100*parseInt(s.substr(8,2)) + 
	        parseInt(s.substr(11,2));
  return n;
} // CMIDurationToCentisecs
