xAPIWrapper 1.11.0 Reference

XAPIstatement

XAPIStatement (actor, verb, object)

A convenient JSON-compatible xAPI statement wrapper
All args are optional, but the statement may not be complete or valid
Can also pass an Agent IFI, Verb ID, and an Activity ID in lieu of these args

Arguments
actor string

The Agent or Group committing the action described by the statement

optional
verb string

The Verb for the action described by the statement

optional
object string

The receiver of the action. An Agent, Group, Activity, SubStatement, or StatementRef

optional
var stmt = new ADL.XAPIStatement(
    'mailto:steve.vergenz.ctr@adlnet.gov',
   'http://adlnet.gov/expapi/verbs/launched',
   'http://vwf.adlnet.gov/xapi/virtual_world_sandbox'
);
>> {
"actor": {
    "objectType": "Agent",
    "mbox": "mailto:steve.vergenz.ctr@adlnet.gov" },
"verb": {
    "id": "http://adlnet.gov/expapi/verbs/launched" },
"object": {
    "objectType": "Activity",
    "id": "http://vwf.adlnet.gov/xapi/virtual_world_sandbox" },
"result": {
     "An optional property that represents a measured outcome related to the Statement in which it is included."}}
var XAPIStatement = function(actor, verb, object, result)
{

  // initialize

  // if first arg is an xapi statement, parse
  if( actor && actor.actor && actor.verb && actor.object ){
    var stmt = actor;
    for(var i in stmt){
      if(i != 'actor' && i != 'verb' && i != 'object')
        this[i] = stmt[i];
    }
    actor = stmt.actor;
    verb = stmt.verb;
    object = stmt.object;
  }
  
  if(actor){
    if( actor instanceof Agent )
      this.actor = actor;
    else if(actor.objectType === 'Agent' || !actor.objectType)
      this.actor = new Agent(actor);
    else if(actor.objectType === 'Group')
      this.actor = new Group(actor);
  }
  else this.actor = null;
  
  if(verb){
    if( verb instanceof Verb )
      this.verb = verb;
    else
      this.verb = new Verb(verb);
  }
  else this.verb = null;

  // decide what kind of object was passed
  if(object)
  {
    if( object.objectType === 'Activity' || !object.objectType ){
      if( object instanceof Activity )
        this.object = object;
      else
        this.object = new Activity(object);
    }
    else if( object.objectType === 'Agent' ){
      if( object instanceof Agent )
        this.object = object;
      else
        this.object = new Agent(object);
    }
    else if( object.objectType === 'Group' ){
      if( object instanceof Group )
        this.object = object;
      else
        this.object = new Group(object);
    }
    else if( object.objectType === 'StatementRef' ){
      if( object instanceof StatementRef )
        this.object = object;
      else
        this.object = new StatementRef(object);
    }
    else if( object.objectType === 'SubStatement' ){
      if( object instanceof SubStatement )
        this.object = object;
      else
        this.object = new SubStatement(object);
    }
    else this.object = null;
  }
  else this.object = null;
  
  // add support for result object
  if(result)
  {
     this.result = result;
  }

  this.generateId = function(){
    this.id = ADL.ruuid();
  };
};

XAPIStatement.prototype.toString = function(){
  return this.actor.toString() + " " + this.verb.toString() + " " + this.object.toString() + " " + this.result.toString();
};

XAPIStatement.prototype.isValid = function(){
  return this.actor && this.actor.isValid() 
    && this.verb && this.verb.isValid() 
    && this.object && this.object.isValid()
    && this.result && this.result.isValid();
};

XAPIStatement.prototype.generateRegistration = function(){
  _getobj(this,'context').registration = ADL.ruuid();
};

XAPIStatement.prototype.addParentActivity = function(activity){
  _getobj(this,'context.contextActivities.parent[]').push(new Activity(activity));
};

XAPIStatement.prototype.addGroupingActivity = function(activity){
  _getobj(this,'context.contextActivities.grouping[]').push(new Activity(activity));
};

XAPIStatement.prototype.addOtherContextActivity = function(activity){
  _getobj(this,'context.contextActivities.other[]').push(new Activity(activity));
};

Agent (identifier, name)

Provides an easy constructor for xAPI agent objects

Arguments
identifier string

One of the Inverse Functional Identifiers specified in the spec. That is, an email, a hashed email, an OpenID, or an account object.
See (https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#inversefunctional)

name string

The natural-language name of the agent

optional
// Sorry, no example available.
var Agent = function(identifier, name)
{
  this.objectType = 'Agent';
  this.name = name;

  // figure out what type of identifier was given
  if( identifier && (identifier.mbox || identifier.mbox_sha1sum || identifier.openid || identifier.account) ){
    for(var i in identifier){
      this[i] = identifier[i];
    }
  }
  else if( /^mailto:/.test(identifier) ){
    this.mbox = identifier;
  }
  else if( /^[0-9a-f]{40}$/i.test(identifier) ){
    this.mbox_sha1sum = identifier;
  }
  else if( /^http[s]?:/.test(identifier) ){
    this.openid = identifier;
  }
  else if( identifier && identifier.homePage && identifier.name ){
    this.account = identifier;
  }
};
Agent.prototype.toString = function(){
  return this.name || this.mbox || this.openid || this.mbox_sha1sum || this.account.name;
};
Agent.prototype.isValid = function()
{
  return this.mbox || this.mbox_sha1sum || this.openid
    || (this.account.homePage && this.account.name)
    || (this.objectType === 'Group' && this.member);
};

Group (identifier, members, name)

A type of agent, can contain multiple agents

Arguments
identifier string

(optional if members specified) See Agent.

optional
members string

An array of Agents describing the membership of the group

optional
name string

The natural-language name of the agent

optional
// Sorry, no example available.
var Group = function(identifier, members, name)
{
  Agent.call(this, identifier, name);
  this.member = members;
  this.objectType = 'Group';
};
Group.prototype = new Agent;

Verb (id, description)

Really only provides a convenient language map

Arguments
id string

The IRI of the action taken

description string

An English-language description, or a Language Map

optional
// Sorry, no example available.
var Verb = function(id, description)
{
  // if passed a verb object then copy and return
  if( id && id.id ){
    for(var i in id){
      this[i] = id[i];
    }
    return;
  }

  // save id and build language map
  this.id = id;
  if(description)
  {
    if( typeof(description) === 'string' || description instanceof String ){
      this.display = {'en-US': description};
    }
    else {
      this.display = description;
    }
  }
};
Verb.prototype.toString = function(){
  if(this.display && (this.display['en-US'] || this.display['en']))
    return this.display['en-US'] || this.display['en'];
  else
    return this.id;
};
Verb.prototype.isValid = function(){
  return this.id;
};

Activity (id, name, description)

Describes an object that an agent interacts with

Arguments
id string

The unique activity IRI

name string

An English-language identifier for the activity, or a Language Map

description string

An English-language description of the activity, or a Language Map

// Sorry, no example available.
var Activity = function(id, name, description)
{
  // if first arg is activity, copy everything over
  if(id && id.id){
    var act = id;
    for(var i in act){
      this[i] = act[i];
    }
    return;
  }
  
  this.objectType = 'Activity';
  this.id = id;
  if( name || description )
  {
    this.definition = {};
    
    if( typeof(name) === 'string' || name instanceof String )
      this.definition.name = {'en-US': name};
    else if(name)
      this.definition.name = name;
    
    if( typeof(description) === 'string' || description instanceof String )
      this.definition.description = {'en-US': description};
    else if(description)
      this.definition.description = description;
  }
};
Activity.prototype.toString = function(){
  if(this.definition && this.definition.name && (this.definition.name['en-US'] || this.definition.name['en']))
    return this.definition.name['en-US'] || this.definition.name['en'];
  else
    return this.id;
};
Activity.prototype.isValid = function(){
  return this.id && (!this.objectType || this.objectType === 'Activity');
};

StatementRef (id)

An object that refers to a separate statement

Arguments
id string

The UUID of another xAPI statement

// Sorry, no example available.
var StatementRef = function(id){
  if(id && id.id){
    for(var i in id){
      this[i] = id[i];
    }
  }
  else {
    this.objectType = 'StatementRef';
    this.id = id;
  }
};
StatementRef.prototype.toString = function(){
  return 'statement('+this.id+')';
};
StatementRef.prototype.isValid = function(){
  return this.id && this.objectType && this.objectType === 'StatementRef';
};

SubStatement (actor, verb, object)

A self-contained statement as the object of another statement
See XAPIStatement for constructor details

Arguments
actor string

The Agent or Group committing the action described by the statement

verb string

The Verb for the action described by the statement

object string

The receiver of the action. An Agent, Group, Activity, or StatementRef

// Sorry, no example available.
var SubStatement = function(actor, verb, object){
  XAPIStatement.call(this,actor,verb,object);
  this.objectType = 'SubStatement';

  delete this.id;
  delete this.stored;
  delete this.version;
  delete this.authority;
};
SubStatement.prototype = new XAPIStatement;
SubStatement.prototype.toString = function(){
  return '"' + SubStatement.prototype.prototype.toString.call(this) + '"';
};

XAPIStatement.Agent = Agent;
XAPIStatement.Group = Group;
XAPIStatement.Verb = Verb;
XAPIStatement.Activity = Activity;
XAPIStatement.StatementRef = StatementRef;
XAPIStatement.SubStatement = SubStatement;
ADL.XAPIStatement = XAPIStatement;

}(window.ADL = window.ADL || {}));

XAPIwrapper

Config ()

Config object used w/ url params to configure the lrs object
change these to match your lrs

Returns
object  

config object

var conf = {
   "endpoint" : "https://lrs.adlnet.gov/xapi/",
   "auth" : "Basic " + toBase64('tom:1234'),
};
ADL.XAPIWrapper.changeConfig(conf);
var Config = function()
{
    var conf = {};
    conf['endpoint'] = "http://localhost:8000/xapi/";
    //try
    //{
        conf['auth'] = "Basic " + toBase64('tom:1234');
    //}
    //catch (e)
    //{
    //    log("Exception in Config trying to encode auth: " + e);
    //}

    // Statement defaults
    // conf["actor"] = {"mbox":"default@example.com"};
    // conf["registration"] =  ruuid();
    // conf["grouping"] = {"id":"ctxact:default/grouping"};
    // conf["activity_platform"] = "default platform";

    // Behavior defaults
    // conf["strictCallbacks"] = false; // Strict error-first callbacks
    return conf
}();

XAPIWrapper (config, verifyxapiversion)

XAPIWrapper Constructor

Arguments
config object

with a minimum of an endoint property

verifyxapiversion boolean

indicating whether to verify the version of the LRS is compatible with this wrapper

// Sorry, no example available.
var XAPIWrapper = function(config, verifyxapiversion)
{



    this.lrs = getLRSObject(config || {});
    if (this.lrs.user && this.lrs.password)
        updateAuth(this.lrs, this.lrs.user, this.lrs.password);
    this.base = getbase(this.lrs.endpoint);

    this.withCredentials = false;
    if (config && typeof(config.withCredentials) != 'undefined') {
        this.withCredentials = config.withCredentials;
    }

    // Ensure that callbacks are always executed, first param is error (null if no error) followed
    // by the result(s)
    this.strictCallbacks = false;
    this.strictCallbacks = config && config.strictCallbacks;

    function getbase(url)
    {
        var l = document.createElement("a");
        l.href = url;
        if (l.protocol && l.host) {
            return l.protocol + "//" + l.host;
        } else if (l.href) {
            // IE 11 fix.
            var parts = l.href.split("//");
            return parts[0] + "//" + parts[1].substr(0, parts[1].indexOf("/"));
        }
        else
            ADL.XAPIWrapper.log("Couldn't create base url from endpoint: " + url);
    }

    function updateAuth(obj, username, password){
        obj.auth = "Basic " + toBase64(username + ":" + password);
    }

    if (verifyxapiversion && testConfig.call(this))
    {
        window.ADL.XHR_request(this.lrs, this.lrs.endpoint+"about", "GET", null, null,
            function(r){
                if(r.status == 200)
                {
                    try
                    {
                        var lrsabout = JSON.parse(r.response);
                        var versionOK = false;
                        for (var idx in lrsabout.version)
                        {
                            if (lrsabout.version.hasOwnProperty(idx))
                                if(lrsabout.version[idx] == ADL.XAPIWrapper.xapiVersion)
                                {
                                    versionOK = true;
                                    break;
                                }
                        }
                        if (!versionOK)
                        {
                            ADL.XAPIWrapper.log("The lrs version [" + lrsabout.version +"]"+
                                " does not match this wrapper's XAPI version [" + ADL.XAPIWrapper.xapiVersion + "]");
                        }
                    }
                    catch(e)
                    {
                        ADL.XAPIWrapper.log("The response was not an about object")
                    }
                }
                else
                {
                    ADL.XAPIWrapper.log("The request to get information about the LRS failed: " + r);
                }
            }, null, false, null, this.withCredentials, false);


    }

    this.searchParams = function(){
        var sp = {"format" : "exact"};
        return sp;
    };

    this.hash = function(tohash){
        if (!tohash) return null;
        try
        {
            return toSHA1(tohash);
        }
        catch(e)
        {
            ADL.XAPIWrapper.log("Error trying to hash -- " + e);
            return null;
        }
    };

    this.changeConfig = function(config){
        try
        {
            ADL.XAPIWrapper.log("updating lrs object with new configuration");
            this.lrs = mergeRecursive(this.lrs, config);
            if (config.user && config.password)
                this.updateAuth(this.lrs, config.user, config.password);
            this.base = getbase(this.lrs.endpoint);
            this.withCredentials = config.withCredentials;
            this.strictCallbacks = config.strictCallbacks;
        }
        catch(e)
        {
            ADL.XAPIWrapper.log("error while changing configuration -- " + e);
        }
    };

    this.updateAuth = updateAuth;
};

// This wrapper is based on the Experience API Spec version:
XAPIWrapper.prototype.xapiVersion = "1.0.1";

prepareStatement (stmt)

Adds info from the lrs object to the statement, if available.
These values could be initialized from the Config object or from the url query string.

Arguments
stmt object

the statement object

// Sorry, no example available.
XAPIWrapper.prototype.prepareStatement = function(stmt)
{
    if(stmt.actor === undefined){
        stmt.actor = JSON.parse(this.lrs.actor);
    }
    else if(typeof stmt.actor === "string") {
        stmt.actor = JSON.parse(stmt.actor);
    }
    if (this.lrs.grouping ||
        this.lrs.registration ||
        this.lrs.activity_platform) {
        if (!stmt.context) {
            stmt.context = {};
        }
    }

    if (this.lrs.grouping) {
        if (!stmt.context.contextActivities) {
            stmt.context.contextActivities = {};
        }
        
        // PR from brian-learningpool to resolve context overwriting
        if (!Array.isArray(stmt.context.contextActivities.grouping)) {
            stmt.context.contextActivities.grouping = [{ id : this.lrs.grouping }];
        } else {
            stmt.context.contextActivities.grouping.splice(0, 0, { id : this.lrs.grouping });
        }
    }
    if (this.lrs.registration) {
        stmt.context.registration = this.lrs.registration;
    }
    if (this.lrs.activity_platform) {
        stmt.context.platform = this.lrs.activity_platform;
    }
};

// tests the configuration of the lrs object
XAPIWrapper.prototype.testConfig = testConfig;

// writes to the console if available
XAPIWrapper.prototype.log = log;

// Default encoding
XAPIWrapper.prototype.defaultEncoding = 'utf-8';

sendStatement (stmt, callback)

Send a single statement to the LRS. Makes a Javascript object
with the statement id as 'id' available to the callback function.

Arguments
stmt object

statement object to send

callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object
and an object with an id property assigned the id
of the statement

optional

Returns
object  

object containing xhr object and id of statement

// Send Statement
var stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
            "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
                      "display" : {"en-US" : "answered"}},
            "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
var resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
ADL.XAPIWrapper.log("[" + resp_obj.id + "]: " + resp_obj.xhr.status + " - " + resp_obj.xhr.statusText);
>> [3e616d1c-5394-42dc-a3aa-29414f8f0dfe]: 204 - NO CONTENT

// Send Statement with Callback
var stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
            "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
                      "display" : {"en-US" : "answered"}},
            "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
ADL.XAPIWrapper.sendStatement(stmt, function(resp, obj){
    ADL.XAPIWrapper.log("[" + obj.id + "]: " + resp.status + " - " + resp.statusText);});
>> [4edfe763-8b84-41f1-a355-78b7601a6fe8]: 204 - NO CONTENT
XAPIWrapper.prototype.sendStatement = function(stmt, callback, attachments)
{
    if (this.testConfig())
    {
        this.prepareStatement(stmt);
        var id;
        if (stmt['id'])
        {
            id = stmt['id'];
        }
        else
        {
            id = ADL.ruuid();
            stmt['id'] = id;
        }

        var payload = JSON.stringify(stmt);
        var extraHeaders = null;
        if(attachments && attachments.length > 0)
        {
            extraHeaders = {}
            payload = this.buildMultipartPost(stmt,attachments,extraHeaders);
        }
        var resp = ADL.XHR_request(this.lrs, this.lrs.endpoint+"statements",
            "POST", payload, this.lrs.auth, callback, {"id":id}, null, extraHeaders,
            this.withCredentials, this.strictCallbacks);
        if (!callback)
            return {"xhr":resp,
                    "id" :id};
    }
};

XAPIWrapper.prototype.stringToArrayBuffer = function(content, encoding)
{
    encoding = encoding || ADL.XAPIWrapper.defaultEncoding;

    return new TextEncoder(encoding).encode(content).buffer;
};

XAPIWrapper.prototype.stringFromArrayBuffer = function(content, encoding) {
    encoding = encoding || ADL.XAPIWrapper.defaultEncoding;

    return new TextDecoder(encoding).decode(content);
};

sendStatements (stmtArray, callback)

Send a list of statements to the LRS.

Arguments
stmtArray array

the list of statement objects to send

callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object

var stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
            "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
                      "display" : {"en-US" : "answered"}},
            "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
var resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
ADL.XAPIWrapper.getStatements({"statementId":resp_obj.id});
>> {"version": "1.0.0",
    "timestamp": "2013-09-09 21:36:40.185841+00:00",
    "object": {"id": "http://adlnet.gov/expapi/activities/question", "objectType": "Activity"},
    "actor": {"mbox": "mailto:tom@example.com", "name": "tom creighton", "objectType": "Agent"},
    "stored": "2013-09-09 21:36:40.186124+00:00",
    "verb": {"id": "http://adlnet.gov/expapi/verbs/answered", "display": {"en-US": "answered"}},
    "authority": {"mbox": "mailto:tom@adlnet.gov", "name": "tom", "objectType": "Agent"},
    "context": {"registration": "51a6f860-1997-11e3-8ffd-0800200c9a66"},
    "id": "ea9c1d01-0606-4ec7-8e5d-20f87b1211ed"}
XAPIWrapper.prototype.sendStatements = function(stmtArray, callback)
{
    if (this.testConfig())
    {
        for(var i in stmtArray)
        {
            if (stmtArray.hasOwnProperty(i))
                this.prepareStatement(stmtArray[i]);
        }
        var resp = ADL.XHR_request(this.lrs,this.lrs.endpoint+"statements",
            "POST", JSON.stringify(stmtArray), this.lrs.auth, callback, null,
            false, null, this.withCredentials, this.strictCallbacks);


        if (!callback)
        {
            return resp;
        }
    }
};

getStatements (searchparams, more, callback)

Get statement(s) based on the searchparams or more url.

Arguments
searchparams object

an ADL.XAPIWrapper.searchParams object of key(search parameter)-value(parameter value) pairs.
Example:
var myparams = ADL.XAPIWrapper.searchParams();
myparams['verb'] = ADL.verbs.completed.id;
var completedStmts = ADL.XAPIWrapper.getStatements(myparams);

more string

the more url found in the StatementResults object, if there are more statements available based on your get statements request. Pass the
more url as this parameter to retrieve those statements.

callback function
  • function to be called after the LRS responds to this request (makes the call asynchronous)
    the function will be passed the XMLHttpRequest object
optional

Returns
object  

xhr response object or null if 404

var ret = ADL.XAPIWrapper.getStatements();
if (ret)
    ADL.XAPIWrapper.log(ret.statements);

>> <Array of statements>
XAPIWrapper.prototype.getStatements = function(searchparams, more, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "statements";
        if (more)
        {
            url = this.base + more;
        }
        else
        {
            var urlparams = new Array();

            for (s in searchparams)
            {
                if (searchparams.hasOwnProperty(s))
                {
                    if (s == "until" || s == "since") {
                        var d = new Date(searchparams[s]);
                        urlparams.push(s + "=" + encodeURIComponent(d.toISOString()));
                    } else {
                        urlparams.push(s + "=" + encodeURIComponent(searchparams[s]));
                    }
                }
            }
            if (urlparams.length > 0)
                url = url + "?" + urlparams.join("&");
        }

        var res = ADL.XHR_request(this.lrs,url, "GET", null, this.lrs.auth,
            callback, null, false, null, this.withCredentials, this.strictCallbacks);

        if(res === undefined || res.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(res.response);
        }
        catch(e)
        {
            return res.response;
        }
    }
};

getActivities (activityid, callback)

Gets the Activity object from the LRS.

Arguments
activityid string

the id of the Activity to get

callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

var res = ADL.XAPIWrapper.getActivities("http://adlnet.gov/expapi/activities/question");
ADL.XAPIWrapper.log(res);
>> <Activity object>
XAPIWrapper.prototype.getActivities = function(activityid, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities?activityId=<activityid>";
        url = url.replace('<activityid>', encodeURIComponent(activityid));

        var result = ADL.XHR_request(this.lrs, url, "GET", null, this.lrs.auth,
            callback, null, true, null, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result.response;
        }
    }
};

sendState (activityid, agent, stateid, registration, stateval, matchHash, noneMatchHash, callback)

Store activity state in the LRS

Arguments
activityid string

the id of the Activity this state is about

agent object

the agent this Activity state is related to

stateid string

the id you want associated with this state

registration string

the registraton id associated with this state

optional
stateval string

the state

matchHash string

the hash of the state to replace or * to replace any

optional
noneMatchHash string

the hash of the current state or * to indicate no previous state

optional
callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
boolean  

false if no activity state is included

var stateval = {"info":"the state info"};
ADL.XAPIWrapper.sendState("http://adlnet.gov/expapi/activities/question",
                   {"mbox":"mailto:tom@example.com"},
                   "questionstate", null, stateval);
XAPIWrapper.prototype.sendState = function(activityid, agent, stateid, registration, stateval, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/state?activityId=<activity ID>&agent=<agent>&stateId=<stateid>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));
        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));
        url = url.replace('<stateid>',encodeURIComponent(stateid));

        if (registration)
        {
            url += "&registration=" + encodeURIComponent(registration);
        }

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var method = "PUT";
        if (stateval)
        {
            if (stateval instanceof Array)
            {
                stateval = JSON.stringify(stateval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
            }
            else if (stateval instanceof Object)
            {
                stateval = JSON.stringify(stateval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
                method = "POST";
            }
            else
            {
                headers = headers || {};
                headers["Content-Type"] ="application/octet-stream";
            }
        }
        else
        {
            this.log("No activity state was included.");
            return false;
        }
        //(lrs, url, method, data, auth, callback, callbackargs, ignore404, extraHeaders)

        ADL.XHR_request(this.lrs, url, method, stateval, this.lrs.auth, callback,
            null, null, headers, this.withCredentials, this.strictCallbacks);
    }
};

getState (activityid, agent, stateid, registration, since, callback)

Get activity state from the LRS

Arguments
activityid string

the id of the Activity this state is about

agent object

the agent this Activity state is related to

stateid string

the id of the state, if not included, the response will be a list of stateids associated with the activity and agent)

optional
registration string

the registraton id associated with this state

optional
since object

date object or date string telling the LRS to return objects newer than the date supplied

optional
callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
                 {"mbox":"mailto:tom@example.com"}, "questionstate");
>> {info: "the state info"}
XAPIWrapper.prototype.getState = function(activityid, agent, stateid, registration, since, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/state?activityId=<activity ID>&agent=<agent>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));
        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));

        if (stateid)
        {
            url += "&stateId=" + encodeURIComponent(stateid);
        }

        if (registration)
        {
            url += "&registration=" + encodeURIComponent(registration);
        }

        if(since)
        {
            since = isDate(since);
            if (since != null) {
                url += '&since=' + encodeURIComponent(since.toISOString());
            }
        }

        var result = ADL.XHR_request(this.lrs, url, "GET", null, this.lrs.auth,
            callback, null, true, null, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result.response;
        }
    }
};

deleteState (activityid, agent, stateid, registration, matchHash, noneMatchHash, callback)

Delete activity state in the LRS

Arguments
activityid string

the id of the Activity this state is about

agent object

the agent this Activity state is related to

stateid string

the id you want associated with this state

registration string

the registraton id associated with this state

optional
matchHash string

the hash of the state to replace or * to replace any

optional
noneMatchHash string

the hash of the current state or * to indicate no previous state

optional
callback string

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

var stateval = {"info":"the state info"};
ADL.XAPIWrapper.sendState("http://adlnet.gov/expapi/activities/question",
                          {"mbox":"mailto:tom@example.com"},
                          "questionstate", null, stateval);
ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
                        {"mbox":"mailto:tom@example.com"}, "questionstate");
>> {info: "the state info"}

ADL.XAPIWrapper.deleteState("http://adlnet.gov/expapi/activities/question",
                        {"mbox":"mailto:tom@example.com"}, "questionstate");
>> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}

ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
                        {"mbox":"mailto:tom@example.com"}, "questionstate");
>> 404
XAPIWrapper.prototype.deleteState = function(activityid, agent, stateid, registration, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/state?activityId=<activity ID>&agent=<agent>&stateId=<stateid>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));
        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));
        url = url.replace('<stateid>',encodeURIComponent(stateid));

        if (registration)
        {
            url += "&registration=" + encodeURIComponent(registration);
        }

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var result = ADL.XHR_request(this.lrs, url, "DELETE", null, this.lrs.auth,
            callback, null, false, headers, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result;
        }
    }
};

sendActivityProfile (activityid, profileid, profileval, matchHash, noneMatchHash, callback)

Store activity profile in the LRS

Arguments
activityid string

the id of the Activity this profile is about

profileid string

the id you want associated with this profile

profileval string

the profile

matchHash string

the hash of the profile to replace or * to replace any

optional
noneMatchHash string

the hash of the current profile or * to indicate no previous profile

optional
callback string

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
bolean  

false if no activity profile is included

var profile = {"info":"the profile"};
ADL.XAPIWrapper.sendActivityProfile("http://adlnet.gov/expapi/activities/question",
                                    "actprofile", profile, null, "*");
XAPIWrapper.prototype.sendActivityProfile = function(activityid, profileid, profileval, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/profile?activityId=<activity ID>&profileId=<profileid>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));
        url = url.replace('<profileid>',encodeURIComponent(profileid));

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var method = "PUT";
        if (profileval)
        {
            if (profileval instanceof Array)
            {
                profileval = JSON.stringify(profileval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
            }
            else if (profileval instanceof Object)
            {
                profileval = JSON.stringify(profileval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
                method = "POST";
            }
            else
            {
                headers = headers || {};
                headers["Content-Type"] ="application/octet-stream";
            }
        }
        else
        {
            this.log("No activity profile was included.");
            return false;
        }

        ADL.XHR_request(this.lrs, url, method, profileval, this.lrs.auth, callback,
            null, false, headers, this.withCredentials, this.strictCallbacks);
    }
};

getActivityProfile (activityid, profileid, since, {function [callback] function to be called after the LRS responds)

Get activity profile from the LRS

Arguments
activityid string

the id of the Activity this profile is about

profileid string

the id of the profile, if not included, the response will be a list of profileids associated with the activity

optional
since object

date object or date string telling the LRS to return objects newer than the date supplied

optional
{function [callback] function to be called after the LRS responds
       to this request (makes the call asynchronous)
       the function will be passed the XMLHttpRequest object

Returns
object  

xhr response object or null if 404

ADL.XAPIWrapper.getActivityProfile("http://adlnet.gov/expapi/activities/question",
                                   "actprofile", null,
                                   function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
>> {info: "the profile"}
XAPIWrapper.prototype.getActivityProfile = function(activityid, profileid, since, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/profile?activityId=<activity ID>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));

        if (profileid)
        {
            url += "&profileId=" + encodeURIComponent(profileid);
        }

        if(since)
        {
            since = isDate(since);
            if (since != null) {
                url += '&since=' + encodeURIComponent(since.toISOString());
            }
        }

        var result = ADL.XHR_request(this.lrs, url, "GET", null, this.lrs.auth,
            callback, null, true, null, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result.response;
        }
    }
};

deleteActivityProfile (activityid, profileid, matchHash, noneMatchHash, callback)

Delete activity profile in the LRS

Arguments
activityid string

the id of the Activity this profile is about

profileid string

the id you want associated with this profile

matchHash string

the hash of the profile to replace or * to replace any

optional
noneMatchHash string

the hash of the current profile or * to indicate no previous profile

optional
callback string

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

ADL.XAPIWrapper.deleteActivityProfile("http://adlnet.gov/expapi/activities/question",
                                      "actprofile");
>> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
XAPIWrapper.prototype.deleteActivityProfile = function(activityid, profileid, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "activities/profile?activityId=<activity ID>&profileId=<profileid>";

        url = url.replace('<activity ID>',encodeURIComponent(activityid));
        url = url.replace('<profileid>',encodeURIComponent(profileid));

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var result = ADL.XHR_request(this.lrs, url, "DELETE", null, this.lrs.auth,
            callback, null, false, headers,this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result;
        }
    }
};

getAgents (agent, {function [callback] function to be called after the LRS responds)

Gets the Person object from the LRS based on an agent object.
The Person object may contain more information about an agent.
See the xAPI Spec for details.

Arguments
agent object

the agent object to get a Person

{function [callback] function to be called after the LRS responds
       to this request (makes the call asynchronous)
       the function will be passed the XMLHttpRequest object

Returns
object  

xhr response object or null if 404

var res = ADL.XAPIWrapper.getAgents({"mbox":"mailto:tom@example.com"});
ADL.XAPIWrapper.log(res);
>> <Person object>
XAPIWrapper.prototype.getAgents = function(agent, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "agents?agent=<agent>";
        url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)));

        var result = ADL.XHR_request(this.lrs, url, "GET", null, this.lrs.auth,
            callback, null, true, null, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result.response;
        }
    }
};

sendAgentProfile (agent, profileid, profileval, matchHash, noneMatchHash, callback)

Store agent profile in the LRS

Arguments
agent object

the agent this profile is related to

profileid string

the id you want associated with this profile

profileval string

the profile

matchHash string

the hash of the profile to replace or * to replace any

optional
noneMatchHash string

the hash of the current profile or * to indicate no previous profile

optional
callback string

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

false if no agent profile is included

var profile = {"info":"the agent profile"};
ADL.XAPIWrapper.sendAgentProfile({"mbox":"mailto:tom@example.com"},
                                  "agentprofile", profile, null, "*");
XAPIWrapper.prototype.sendAgentProfile = function(agent, profileid, profileval, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "agents/profile?agent=<agent>&profileId=<profileid>";

        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));
        url = url.replace('<profileid>',encodeURIComponent(profileid));

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var method = "PUT";
        if (profileval)
        {
            if (profileval instanceof Array)
            {
                profileval = JSON.stringify(profileval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
            }
            else if (profileval instanceof Object)
            {
                profileval = JSON.stringify(profileval);
                headers = headers || {};
                headers["Content-Type"] ="application/json";
                method = "POST";
            }
            else
            {
                headers = headers || {};
                headers["Content-Type"] ="application/octet-stream";
            }
        }
        else
        {
            this.log("No agent profile was included.");
            return false;
        }

        ADL.XHR_request(this.lrs, url, method, profileval, this.lrs.auth, callback,
            null, false, headers, this.withCredentials, this.strictCallbacks);
    }
};

getAgentProfile (agent, profileid, since, callback)

Get agnet profile from the LRS

Arguments
agent object

the agent associated with this profile

profileid string

the id of the profile, if not included, the response will be a list of profileids associated with the agent

optional
since object

date object or date string telling the LRS to return objects newer than the date supplied

optional
callback function

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

ADL.XAPIWrapper.getAgentProfile({"mbox":"mailto:tom@example.com"},
                                 "agentprofile", null,
                                 function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
>> {info: "the agent profile"}
XAPIWrapper.prototype.getAgentProfile = function(agent, profileid, since, callback)
{
    if (this.testConfig()){
        var url = this.lrs.endpoint + "agents/profile?agent=<agent>";

        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));
        url = url.replace('<profileid>',encodeURIComponent(profileid));

        if (profileid)
        {
            url += "&profileId=" + encodeURIComponent(profileid);
        }

        if(since)
        {
            since = isDate(since);
            if (since != null) {
                url += '&since=' + encodeURIComponent(since.toISOString());
            }
        }

        var result = ADL.XHR_request(this.lrs, url, "GET", null, this.lrs.auth,
            callback, null, true, null, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result.response;
        }
    }
};

deleteAgentProfile (agent, profileid, matchHash, noneMatchHash, callback)

Delete agent profile in the LRS

Arguments
agent oject

the id of the Agent this profile is about

profileid string

the id you want associated with this profile

matchHash string

the hash of the profile to replace or * to replace any

optional
noneMatchHash string

the hash of the current profile or * to indicate no previous profile

optional
callback string

function to be called after the LRS responds to this request (makes the call asynchronous)
the function will be passed the XMLHttpRequest object

optional

Returns
object  

xhr response object or null if 404

ADL.XAPIWrapper.deleteAgentProfile({"mbox":"mailto:tom@example.com"},
                                    "agentprofile");
>> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
XAPIWrapper.prototype.deleteAgentProfile = function(agent, profileid, matchHash, noneMatchHash, callback)
{
    if (this.testConfig())
    {
        var url = this.lrs.endpoint + "agents/profile?agent=<agent>&profileId=<profileid>";

        url = url.replace('<agent>',encodeURIComponent(JSON.stringify(agent)));
        url = url.replace('<profileid>',encodeURIComponent(profileid));

        var headers = null;
        if(matchHash && noneMatchHash)
        {
            log("Can't have both If-Match and If-None-Match");
        }
        else if (matchHash)
        {
            headers = {"If-Match":ADL.formatHash(matchHash)};
        }
        else if (noneMatchHash)
        {
            headers = {"If-None-Match":ADL.formatHash(noneMatchHash)};
        }

        var result = ADL.XHR_request(this.lrs, url, "DELETE", null, this.lrs.auth,
            callback, null, false, headers, this.withCredentials, this.strictCallbacks);

        if(result === undefined || result.status == 404)
        {
            return null
        }

        try
        {
            return JSON.parse(result.response);
        }
        catch(e)
        {
            return result;
        }
    }
};

ie_request (method, url, headers, data)

formats a request in a way that IE will allow

Arguments
method string

the http request method (ex: "PUT", "GET")

url string

the url to the request (ex: ADL.XAPIWrapper.lrs.endpoint + "statements")

headers array

headers to include in the request

optional
data string

the body of the request, if there is one

optional

Returns
object  

xhr response object

// Sorry, no example available.
function ie_request(method, url, headers, data)
{
    var newUrl = url;

    //Everything that was on query string goes into form vars
    var formData = new Array();
    var qsIndex = newUrl.indexOf('?');
    if(qsIndex > 0){
        formData.push(newUrl.substr(qsIndex+1));
        newUrl = newUrl.substr(0, qsIndex);
    }

    //Method has to go on querystring, and nothing else
    newUrl = newUrl + '?method=' + method;

    //Headers
    if(headers !== null){
        for (var headerName in headers) {
            if (headers.hasOwnProperty(headerName))
                formData.push(headerName + "=" + encodeURIComponent(headers[headerName]));
        }
    }

    //The original data is repackaged as "content" form var
    if(data !== null){
        formData.push('content=' + encodeURIComponent(data));
    }

    return {
        "method":"POST",
        "url":newUrl,
        "headers":{},
        "data":formData.join("&")
    };
}

XHR_request (lrs, url, method, data, auth, callback, callbackargs, ignore404, extraHeaders, withCredentials, strictCallbacks)

makes a request to a server (if possible, use functions provided in XAPIWrapper)

Arguments
lrs string

the lrs connection info, such as endpoint, auth, etc

url string

the url of this request

method string

the http request method

data string

the payload

auth string

the value for the Authorization header

callback function

function to be called after the LRS responds to this request (makes the call asynchronous)

callbackargs object

additional javascript object to be passed to the callback function

optional
ignore404 boolean

allow page not found errors to pass

extraHeaders object

other header key-values to be added to this request

withCredentials boolean
strictCallbacks boolean

Callback must be executed and first param is error or null if no error

Returns
object  

xhr response object

// Sorry, no example available.
ADL.XHR_request = function(lrs, url, method, data, auth, callback, callbackargs, ignore404, extraHeaders, withCredentials, strictCallbacks)
{
    "use strict";

    var xhr,
        finished = false,
        xDomainRequest = false,
        ieXDomain = false,
        ieModeRequest,
        urlparts = url.toLowerCase().match(/^(.+):\/\/([^:\/]*):?(\d+)?(\/.*)?$/),
        location = window.location,
        urlPort,
        result,
        extended,
        prop,
        until;

    //Consolidate headers
    var headers = {};
    headers["Content-Type"] = "application/json";
    headers["Authorization"] = auth;
    headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion;
    if(extraHeaders !== null){
        for (var headerName in extraHeaders) {
            if (extraHeaders.hasOwnProperty(headerName))
                headers[headerName] = extraHeaders[headerName];
        }
    }

    //See if this really is a cross domain
    xDomainRequest = (location.protocol.toLowerCase() !== urlparts[1] || location.hostname.toLowerCase() !== urlparts[2]);
    if (!xDomainRequest) {
        urlPort = (urlparts[3] === null ? ( urlparts[1] === 'http' ? '80' : '443') : urlparts[3]);
        xDomainRequest = (urlPort === location.port);
    }

    //Add extended LMS-specified values to the URL
    if (lrs !== null && lrs.extended !== undefined) {
        extended = new Array();
        for (prop in lrs.extended) {
            extended.push(prop + "=" + encodeURIComponent(lrs.extended[prop]));
        }
        if (extended.length > 0) {
            url += (url.indexOf("?") > -1 ? "&" : "?") + extended.join("&");
        }
    }

    //If it's not cross domain or we're not using IE, use the usual XmlHttpRequest
    var windowsVersionCheck = window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined);
    if (!xDomainRequest || windowsVersionCheck === undefined || windowsVersionCheck===false) {
        xhr = new XMLHttpRequest();
        xhr.withCredentials = withCredentials; //allow cross domain cookie based auth
        xhr.open(method, url, callback != null);
        for(var headerName in headers){
            xhr.setRequestHeader(headerName, headers[headerName]);
        }
    }
    //Otherwise, use IE's XDomainRequest object
    else {
        ieXDomain = true;
        ieModeRequest = ie_request(method, url, headers, data);
        xhr = new XDomainRequest();
        xhr.open(ieModeRequest.method, ieModeRequest.url);
    }

    //Setup request callback
    function requestComplete() {
        if(!finished){
            // may be in sync or async mode, using XMLHttpRequest or IE XDomainRequest, onreadystatechange or
            // onload or both might fire depending upon browser, just covering all bases with event hooks and
            // using 'finished' flag to avoid triggering events multiple times
            finished = true;
            var notFoundOk = (ignore404 && xhr.status === 404);
            if (xhr.status === undefined || (xhr.status >= 200 && xhr.status < 400) || notFoundOk) {
                if (callback) {
                    if(callbackargs){
                        strictCallbacks ? callback(null, xhr, callbackargs) : callback(xhr, callbackargs);
                    }
                    else {
                      var body;

                        try {
                            body = JSON.parse(xhr.responseText);
                        }
                        catch(e){
                            body = xhr.responseText;
                        }

                      strictCallbacks ? callback(null, xhr, body) : callback(xhr,body);
                    }
                } else {
                    result = xhr;
                    return xhr;
                }
            } else {
                var warning;
                try {
                    warning = "There was a problem communicating with the Learning Record Store. ( "
                        + xhr.status + " | " + xhr.response+ " )" + url
                } catch (ex) {
                    warning = ex.toString();
                }
                ADL.XAPIWrapper.log(warning);
                ADL.xhrRequestOnError(xhr, method, url, callback, callbackargs, strictCallbacks);
                result = xhr;
                return xhr;
            }
        } else {
            return result;
        }
    };

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
           return requestComplete();
        }
    };

    xhr.onload = requestComplete;
    xhr.onerror = requestComplete;
    //xhr.onerror =  ADL.xhrRequestOnError(xhr, method, url);

    xhr.send(ieXDomain ? ieModeRequest.data : data);

    if (!callback) {
        // synchronous
        if (ieXDomain) {
            // synchronous call in IE, with no asynchronous mode available.
            until = 1000 + new Date();
            while (new Date() < until && xhr.readyState !== 4 && !finished) {
                delay();
            }
        }
        return requestComplete();
    }
};

xhrRequestOnError (xhr, method, url, callback, callbackargs, strictCallbacks)

Holder for custom global error callback

Arguments
xhr object

xhr object or null

method string

XMLHttpRequest request method

url string

full endpoint url

callback function

function to be called after the LRS responds to this request (makes the call asynchronous)

callbackargs object

additional javascript object to be passed to the callback function

optional
strictCallbacks boolean

Callback must be executed and first param is error or null if no error

ADL.xhrRequestOnError = function(xhr, method, url, callback, callbackargs) {
  console.log(xhr);
  alert(xhr.status + " " + xhr.statusText + ": " + xhr.response);
};
ADL.xhrRequestOnError = function(xhr, method, url, callback, callbackargs, strictCallbacks){
    if (callback && strictCallbacks) {
        var status = xhr ? xhr.status : undefined;
        var error;
        if (status) {
            error = new Error('Request error: ' + xhr.status);
        } else if (status === 0 || status === null) { // 0 and null = aborted
            error = new Error('Request error: aborted');
        } else {
            error = new Error('Reqeust error: unknown');
        }

        if (callbackargs) {
            callback(error, xhr, callbackargs);
        } else {
          var body;

            try {
                body = JSON.parse(xhr.responseText);
            } catch(e){
                body = xhr.responseText;
            }

          callback(error, xhr, body);
        }
    }
};

ADL.formatHash = function(hash)
{
  return (hash==="*") ? hash : '"'+hash+'"';
}

ADL.XAPIWrapper = new XAPIWrapper(Config, false);

}(window.ADL = window.ADL || {}));