diff --git a/bower.json b/bower.json index 86baa13..8fcce01 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "tincan", - "version": "0.31.1", + "version": "0.32.0", "homepage": "http://rusticisoftware.github.com/TinCanJS/", "authors": [ "Brian J. Miller " diff --git a/build/tincan-min.js b/build/tincan-min.js index 99d66e1..6972aae 100644 --- a/build/tincan-min.js +++ b/build/tincan-min.js @@ -1,4 +1,4 @@ -"0.31.1";var CryptoJS=CryptoJS||function(a,b){var c={},d=c.lib={},e=d.Base=function(){function a(){}return{extend:function(b){a.prototype=this;var c=new a;return b&&c.mixIn(b),c.$super=this,c},create:function(){var a=this.extend();return a.init.apply(a,arguments),a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.$super.extend(this)}}}(),f=d.WordArray=e.extend({init:function(a,c){a=this.words=a||[],this.sigBytes=c!=b?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var b=this.words,c=a.words,d=this.sigBytes,a=a.sigBytes;if(this.clamp(),d%4)for(var e=0;a>e;e++)b[d+e>>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535e;e+=4)b[d+e>>>2]=c[e>>>2];else b.push.apply(b,c);return this.sigBytes+=a,this},clamp:function(){var b=this.words,c=this.sigBytes;b[c>>>2]&=4294967295<<32-8*(c%4),b.length=a.ceil(c/4)},clone:function(){var a=e.clone.call(this);return a.words=this.words.slice(0),a},random:function(b){for(var c=[],d=0;b>d;d+=4)c.push(4294967296*a.random()|0);return f.create(c,b)}}),g=c.enc={},h=g.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,c=[],d=0;a>d;d++){var e=b[d>>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((15&e).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;b>d;d+=2)c[d>>>3]|=parseInt(a.substr(d,2),16)<<24-4*(d%8);return f.create(c,b/2)}},i=g.Latin1={stringify:function(a){for(var b=a.words,a=a.sigBytes,c=[],d=0;a>d;d++)c.push(String.fromCharCode(b[d>>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;b>d;d++)c[d>>>2]|=(255&a.charCodeAt(d))<<24-8*(d%4);return f.create(c,b)}},j=g.Utf8={stringify:function(a){try{return decodeURIComponent(escape(i.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data")}},parse:function(a){return i.parse(unescape(encodeURIComponent(a)))}},k=d.BufferedBlockAlgorithm=e.extend({reset:function(){this._data=f.create(),this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a)),this._data.concat(a),this._nDataBytes+=a.sigBytes},_process:function(b){var c=this._data,d=c.words,e=c.sigBytes,g=this.blockSize,h=e/(4*g),h=b?a.ceil(h):a.max((0|h)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;b>i;i+=g)this._doProcessBlock(d,i);i=d.splice(0,b),c.sigBytes-=e}return f.create(i,e)},clone:function(){var a=e.clone.call(this);return a._data=this._data.clone(),a},_minBufferSize:0});d.Hasher=k.extend({init:function(){this.reset()},reset:function(){k.reset.call(this),this._doReset()},update:function(a){return this._append(a),this._process(),this},finalize:function(a){return a&&this._append(a),this._doFinalize(),this._hash},clone:function(){var a=k.clone.call(this);return a._hash=this._hash.clone(),a},blockSize:16,_createHelper:function(a){return function(b,c){return a.create(c).finalize(b)}},_createHmacHelper:function(a){return function(b,c){return l.HMAC.create(a,c).finalize(b)}}});var l=c.algo={};return c}(Math);!function(){var a=CryptoJS,b=a.lib,c=b.WordArray,b=b.Hasher,d=[],e=a.algo.SHA1=b.extend({_doReset:function(){this._hash=c.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(a,b){for(var c=this._hash.words,e=c[0],f=c[1],g=c[2],h=c[3],i=c[4],j=0;80>j;j++){if(16>j)d[j]=0|a[b+j];else{var k=d[j-3]^d[j-8]^d[j-14]^d[j-16];d[j]=k<<1|k>>>31}k=(e<<5|e>>>27)+i+d[j],k=20>j?k+((f&g|~f&h)+1518500249):40>j?k+((f^g^h)+1859775393):60>j?k+((f&g|f&h|g&h)-1894007588):k+((f^g^h)-899497514),i=h,h=g,g=f<<30|f>>>2,f=e,e=k}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+h|0,c[4]=c[4]+i|0},_doFinalize:function(){var a=this._data,b=a.words,c=8*this._nDataBytes,d=8*a.sigBytes;b[d>>>5]|=128<<24-d%32,b[(d+64>>>9<<4)+15]=c,a.sigBytes=4*b.length,this._process()}});a.SHA1=b._createHelper(e),a.HmacSHA1=b._createHmacHelper(e)}(),function(){{var a=CryptoJS,b=a.lib,c=b.WordArray,d=a.enc;d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();for(var e=[],f=0;c>f;f+=3)for(var g=b[f>>>2]>>>24-f%4*8&255,h=b[f+1>>>2]>>>24-(f+1)%4*8&255,i=b[f+2>>>2]>>>24-(f+2)%4*8&255,j=g<<16|h<<8|i,k=0;4>k&&c>f+.75*k;k++)e.push(d.charAt(j>>>6*(3-k)&63));var l=d.charAt(64);if(l)for(;e.length%4;)e.push(l);return e.join("")},parse:function(a){a=a.replace(/\s/g,"");var b=a.length,d=this._map,e=d.charAt(64);if(e){var f=a.indexOf(e);-1!=f&&(b=f)}for(var g=[],h=0,i=0;b>i;i++)if(i%4){var j=d.indexOf(a.charAt(i-1))<>>6-i%4*2;g[h>>>2]|=(j|k)<<24-h%4*8,h++}return c.create(g,h)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}}();var TinCan;!function(){"use strict";var a={statementId:!0,voidedStatementId:!0,verb:!0,object:!0,registration:!0,context:!0,actor:!0,since:!0,until:!0,limit:!0,authoritative:!0,sparse:!0,instructor:!0,ascending:!0,continueToken:!0,agent:!0,activityId:!0,stateId:!0,profileId:!0,activity_platform:!0,grouping:!0,"Accept-Language":!0};TinCan=function(a){this.log("constructor"),this.recordStores=[],this.actor=null,this.activity=null,this.registration=null,this.context=null,this.init(a)},TinCan.prototype={LOG_SRC:"TinCan",log:function(a,b){TinCan.DEBUG&&"undefined"!=typeof console&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;if(a=a||{},a.hasOwnProperty("url")&&""!==a.url&&this._initFromQueryString(a.url),a.hasOwnProperty("recordStores")&&void 0!==a.recordStores)for(b=0;b0)for("function"==typeof b&&(e=function(a,c){var d;f.log("sendStatement - callbackWrapper: "+h),h>1?(h-=1,j.push({err:a,xhr:c})):1===h?(j.push({err:a,xhr:c}),d=[j,g],b.apply(this,d)):f.log("sendStatement - unexpected record store count: "+h)}),d=0;h>d;d+=1)c=this.recordStores[d],i.push(c.saveStatement(g,{callback:e}));else this.log("[warning] sendStatement: No LRSs added yet (statement not sent)"),"function"==typeof b&&b.apply(this,[null,g]);return{statement:g,results:i}},getStatement:function(a,b){this.log("getStatement");var c;return this.recordStores.length>0?(c=this.recordStores[0],c.retrieveStatement(a,{callback:b})):void this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d,e,f,g,h,i=this,j=this.recordStores.length,k=[],l=[];if(a instanceof TinCan.Statement&&(a=a.id),"undefined"!=typeof c.actor?e=c.actor:null!==this.actor&&(e=this.actor),f=new TinCan.Statement({actor:e,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}}),j>0)for("function"==typeof b&&(h=function(a,c){var d;i.log("voidStatement - callbackWrapper: "+j),j>1?(j-=1,l.push({err:a,xhr:c})):1===j?(l.push({err:a,xhr:c}),d=[l,f],b.apply(this,d)):i.log("voidStatement - unexpected record store count: "+j)}),g=0;j>g;g+=1)d=this.recordStores[g],k.push(d.saveStatement(f,{callback:h}));else this.log("[warning] voidStatement: No LRSs added yet (statement not sent)"),"function"==typeof b&&b.apply(this,[null,f]);return{statement:f,results:k}},getVoidedStatement:function(a,b){this.log("getVoidedStatement");var c;return this.recordStores.length>0?(c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b})):void this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c,d,e,f=this,g=[],h=this.recordStores.length,i=[],j=[];if(0===a.length)"function"==typeof b&&b.apply(this,[null,g]);else{for(d=0;d0)for("function"==typeof b&&(e=function(a,c){var d;f.log("sendStatements - callbackWrapper: "+h),h>1?(h-=1,j.push({err:a,xhr:c})):1===h?(j.push({err:a,xhr:c}),d=[j,g],b.apply(this,d)):f.log("sendStatements - unexpected record store count: "+h)}),d=0;h>d;d+=1)c=this.recordStores[d],i.push(c.saveStatements(g,{callback:e}));else this.log("[warning] sendStatements: No LRSs added yet (statements not sent)"),"function"==typeof b&&b.apply(this,[null,g])}return{statements:g,results:i}},getStatements:function(a){this.log("getStatements");var b,c,d={};return this.recordStores.length>0?(b=this.recordStores[0],a=a||{},c=a.params||{},a.sendActor&&null!==this.actor&&("0.9"===b.version||"0.95"===b.version?c.actor=this.actor:c.agent=this.actor),a.sendActivity&&null!==this.activity&&("0.9"===b.version||"0.95"===b.version?c.target=this.activity:c.activity=this.activity),"undefined"==typeof c.registration&&null!==this.registration&&(c.registration=this.registration),d={params:c},"undefined"!=typeof a.callback&&(d.callback=a.callback),b.queryStatements(d)):void this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor,activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.registration?c.registration=b.registration:null!==this.registration&&(c.registration=this.registration),"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveState(a,c)):void this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={agent:"undefined"!=typeof c.agent?c.agent:this.actor,activity:"undefined"!=typeof c.activity?c.activity:this.activity},"undefined"!=typeof c.registration?d.registration=c.registration:null!==this.registration&&(d.registration=this.registration),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType),"undefined"!=typeof c.callback&&(d.callback=c.callback),e.saveState(a,b,d)):void this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor,activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.registration?c.registration=b.registration:null!==this.registration&&(c.registration=this.registration),"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropState(a,c)):void this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveActivityProfile(a,c)):void this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={activity:"undefined"!=typeof c.activity?c.activity:this.activity},"undefined"!=typeof c.callback&&(d.callback=c.callback),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType),e.saveActivityProfile(a,b,d)):void this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropActivityProfile(a,c)):void this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveAgentProfile(a,c)):void this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={agent:"undefined"!=typeof c.agent?c.agent:this.actor},"undefined"!=typeof c.callback&&(d.callback=c.callback),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType),e.saveAgentProfile(a,b,d)):void this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropAgentProfile(a,c)):void this.log("[warning] deleteAgentProfile: No LRSs added yet (agent profile not deleted)")}},TinCan.DEBUG=!1,TinCan.enableDebug=function(){TinCan.DEBUG=!0},TinCan.disableDebug=function(){TinCan.DEBUG=!1},TinCan.versions=function(){return["1.0.1","1.0.0","0.95","0.9"]},"object"==typeof module&&(module.exports=TinCan)}(),function(){"use strict";TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"==a?b:3&b|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;for(("undefined"==typeof a||null===a)&&(a=0),("undefined"==typeof b||null===b)&&(b=2),c=Math.pow(10,b-1),d=a.toString();c>a&&c>1;)d="0"+d,c/=10;return d}return a.getUTCFullYear()+"-"+b(a.getUTCMonth()+1)+"-"+b(a.getUTCDate())+"T"+b(a.getUTCHours())+":"+b(a.getUTCMinutes())+":"+b(a.getUTCSeconds())+"."+b(a.getUTCMilliseconds(),3)+"Z"},getSHA1String:function(a){return CryptoJS.SHA1(a).toString(CryptoJS.enc.Hex)},getBase64String:function(a){return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Latin1.parse(a))},getLangDictionaryValue:function(a,b){var c,d=this[a];if("undefined"!=typeof b&&"undefined"!=typeof d[b])return d[b];if("undefined"!=typeof d.und)return d.und;if("undefined"!=typeof d["en-US"])return d["en-US"];for(c in d)if(d.hasOwnProperty(c))return d[c];return""},parseURL:function(a){var b,c,d,e=String(a).split("?"),f={};if(2===e.length)for(b=e[1].split("&"),d=0;d0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),""!==a.name&&(a.name+=" "),"undefined"!=typeof a.lastName&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0))):("undefined"!=typeof a.familyName||"undefined"!=typeof a.givenName)&&(a.name="","undefined"!=typeof a.givenName&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),""!==a.name&&(a.name+=" "),"undefined"!=typeof a.familyName&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0))),"object"==typeof a.name&&null!==a.name&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),"object"==typeof a.mbox&&null!==a.mbox&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),"object"==typeof a.mbox_sha1sum&&null!==a.mbox_sha1sum&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),"object"==typeof a.openid&&null!==a.openid&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),"object"==typeof a.account&&null!==a.account&&"undefined"==typeof a.account.homePage&&"undefined"==typeof a.account.name&&(0===a.account.length?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(this.account=a.account instanceof TinCan.AgentAccount?a.account:new TinCan.AgentAccount(a.account)),b=0;b0)for(b.member=[],c=0;c0))for(c=0;c0)if("0.9"===a||"0.95"===a)this[e[b]].length>1&&this.log("[warning] version does not support multiple values in: "+e[b]),d[e[b]]=this[e[b]][0].asVersion(a);else for(d[e[b]]=[],c=0;c0){if("0.9"===a||"0.95"===a)throw this.log("[error] version does not support the 'category' property: "+a),new Error(a+" does not support the 'category' property");for(d.category=[],b=0;b=200&&400>f||e?b.callback?void b.callback(null,a):d={err:null,xhr:a}:(d={err:f,xhr:a},0===f?log("[warning] There was a problem communicating with the Learning Record Store. Aborted, offline, or invalid CORS endpoint ("+f+")",LOG_SRC):log("[warning] There was a problem communicating with the Learning Record Store. ("+f+" | "+a.responseText+")",LOG_SRC),b.callback&&b.callback(f,a),d))},__IEModeConversion=function(a,b,c,d){var e;for(e in b)b.hasOwnProperty(e)&&c.push(e+"="+encodeURIComponent(b[e]));return"undefined"!=typeof d.data&&c.push("content="+encodeURIComponent(d.data)),b["Content-Type"]="application/x-www-form-urlencoded",a+="?method="+d.method,d.method="POST",d.params={},c.length>0&&(d.data=c.join("&")),a},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d,e,f,g,h=this,i=[],j={finished:!1,fakeStatus:null},k="undefined"!=typeof c.callback,l=a,m=2048;log("sendRequest using XMLHttpRequest - async: "+k,LOG_SRC);for(e in c.params)c.params.hasOwnProperty(e)&&i.push(e+"="+encodeURIComponent(c.params[e]));if(i.length>0&&(l+="?"+i.join("&")),l.length>=m){if("undefined"!=typeof b["Content-Type"]&&"application/json"!==b["Content-Type"])return g=new Error("Unsupported content type for IE Mode request"),"undefined"!=typeof c.callback&&c.callback(g,null),{err:g,xhr:null};if("undefined"==typeof c.method)return g=new Error("method must not be undefined for an IE Mode Request conversion"),"undefined"!=typeof c.callback&&c.callback(g,null),{err:g,xhr:null};a=__IEModeConversion(a,b,i,c)}else a=l;d="undefined"!=typeof XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"),d.open(c.method,a,k);for(e in b)b.hasOwnProperty(e)&&d.setRequestHeader(e,b[e]);"undefined"!=typeof c.data&&(c.data+=""),f=c.data,k&&(d.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+d.readyState,LOG_SRC),4===d.readyState&&requestComplete.call(h,d,c,j)});try{d.send(f)}catch(n){log("sendRequest caught send exception: "+n,LOG_SRC)}return k?d:requestComplete.call(this,d,c,j)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d,e,f,g,h,i=this,j=[],k={finished:!1,fakeStatus:null};if("undefined"!=typeof b["Content-Type"]&&"application/json"!==b["Content-Type"])return h=new Error("Unsupported content type for IE Mode request"),c.callback?(c.callback(h,null),null):{err:h,xhr:null};a+="?method="+c.method;for(f in c.params)c.params.hasOwnProperty(f)&&j.push(f+"="+encodeURIComponent(c.params[f]));for(f in b)b.hasOwnProperty(f)&&j.push(f+"="+encodeURIComponent(b[f]));null!==c.data&&j.push("content="+encodeURIComponent(c.data)),e=j.join("&"),d=new XDomainRequest,d.open("POST",a),c.callback?(d.onload=function(){k.fakeStatus=200,requestComplete.call(i,d,c,k) -},d.onerror=function(){k.fakeStatus=400,requestComplete.call(i,d,c,k)},d.ontimeout=function(){k.fakeStatus=0,requestComplete.call(i,d,c,k)}):(d.onload=function(){k.fakeStatus=200},d.onerror=function(){k.fakeStatus=400},d.ontimeout=function(){k.fakeStatus=0}),d.onprogress=function(){},d.timeout=0;try{d.send(e)}catch(l){log("sendRequest caught send exception: "+l,LOG_SRC)}if(!c.callback){for(g=1e4+Date.now(),log("sendRequest - until: "+g+", finished: "+k.finished,LOG_SRC);Date.now()e;e++)b[d+e>>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535e;e+=4)b[d+e>>>2]=c[e>>>2];else b.push.apply(b,c);return this.sigBytes+=a,this},clamp:function(){var b=this.words,c=this.sigBytes;b[c>>>2]&=4294967295<<32-8*(c%4),b.length=a.ceil(c/4)},clone:function(){var a=e.clone.call(this);return a.words=this.words.slice(0),a},random:function(b){for(var c=[],d=0;b>d;d+=4)c.push(4294967296*a.random()|0);return f.create(c,b)}}),g=c.enc={},h=g.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,c=[],d=0;a>d;d++){var e=b[d>>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((15&e).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;b>d;d+=2)c[d>>>3]|=parseInt(a.substr(d,2),16)<<24-4*(d%8);return f.create(c,b/2)}},i=g.Latin1={stringify:function(a){for(var b=a.words,a=a.sigBytes,c=[],d=0;a>d;d++)c.push(String.fromCharCode(b[d>>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;b>d;d++)c[d>>>2]|=(255&a.charCodeAt(d))<<24-8*(d%4);return f.create(c,b)}},j=g.Utf8={stringify:function(a){try{return decodeURIComponent(escape(i.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data")}},parse:function(a){return i.parse(unescape(encodeURIComponent(a)))}},k=d.BufferedBlockAlgorithm=e.extend({reset:function(){this._data=f.create(),this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a)),this._data.concat(a),this._nDataBytes+=a.sigBytes},_process:function(b){var c=this._data,d=c.words,e=c.sigBytes,g=this.blockSize,h=e/(4*g),h=b?a.ceil(h):a.max((0|h)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;b>i;i+=g)this._doProcessBlock(d,i);i=d.splice(0,b),c.sigBytes-=e}return f.create(i,e)},clone:function(){var a=e.clone.call(this);return a._data=this._data.clone(),a},_minBufferSize:0});d.Hasher=k.extend({init:function(){this.reset()},reset:function(){k.reset.call(this),this._doReset()},update:function(a){return this._append(a),this._process(),this},finalize:function(a){return a&&this._append(a),this._doFinalize(),this._hash},clone:function(){var a=k.clone.call(this);return a._hash=this._hash.clone(),a},blockSize:16,_createHelper:function(a){return function(b,c){return a.create(c).finalize(b)}},_createHmacHelper:function(a){return function(b,c){return l.HMAC.create(a,c).finalize(b)}}});var l=c.algo={};return c}(Math);!function(){var a=CryptoJS,b=a.lib,c=b.WordArray,b=b.Hasher,d=[],e=a.algo.SHA1=b.extend({_doReset:function(){this._hash=c.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(a,b){for(var c=this._hash.words,e=c[0],f=c[1],g=c[2],h=c[3],i=c[4],j=0;80>j;j++){if(16>j)d[j]=0|a[b+j];else{var k=d[j-3]^d[j-8]^d[j-14]^d[j-16];d[j]=k<<1|k>>>31}k=(e<<5|e>>>27)+i+d[j],k=20>j?k+((f&g|~f&h)+1518500249):40>j?k+((f^g^h)+1859775393):60>j?k+((f&g|f&h|g&h)-1894007588):k+((f^g^h)-899497514),i=h,h=g,g=f<<30|f>>>2,f=e,e=k}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+h|0,c[4]=c[4]+i|0},_doFinalize:function(){var a=this._data,b=a.words,c=8*this._nDataBytes,d=8*a.sigBytes;b[d>>>5]|=128<<24-d%32,b[(d+64>>>9<<4)+15]=c,a.sigBytes=4*b.length,this._process()}});a.SHA1=b._createHelper(e),a.HmacSHA1=b._createHmacHelper(e)}(),function(){{var a=CryptoJS,b=a.lib,c=b.WordArray,d=a.enc;d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();for(var e=[],f=0;c>f;f+=3)for(var g=b[f>>>2]>>>24-f%4*8&255,h=b[f+1>>>2]>>>24-(f+1)%4*8&255,i=b[f+2>>>2]>>>24-(f+2)%4*8&255,j=g<<16|h<<8|i,k=0;4>k&&c>f+.75*k;k++)e.push(d.charAt(j>>>6*(3-k)&63));var l=d.charAt(64);if(l)for(;e.length%4;)e.push(l);return e.join("")},parse:function(a){a=a.replace(/\s/g,"");var b=a.length,d=this._map,e=d.charAt(64);if(e){var f=a.indexOf(e);-1!=f&&(b=f)}for(var g=[],h=0,i=0;b>i;i++)if(i%4){var j=d.indexOf(a.charAt(i-1))<>>6-i%4*2;g[h>>>2]|=(j|k)<<24-h%4*8,h++}return c.create(g,h)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}}();var TinCan;!function(){"use strict";var a={statementId:!0,voidedStatementId:!0,verb:!0,object:!0,registration:!0,context:!0,actor:!0,since:!0,until:!0,limit:!0,authoritative:!0,sparse:!0,instructor:!0,ascending:!0,continueToken:!0,agent:!0,activityId:!0,stateId:!0,profileId:!0,activity_platform:!0,grouping:!0,"Accept-Language":!0};TinCan=function(a){this.log("constructor"),this.recordStores=[],this.actor=null,this.activity=null,this.registration=null,this.context=null,this.init(a)},TinCan.prototype={LOG_SRC:"TinCan",log:function(a,b){TinCan.DEBUG&&"undefined"!=typeof console&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;if(a=a||{},a.hasOwnProperty("url")&&""!==a.url&&this._initFromQueryString(a.url),a.hasOwnProperty("recordStores")&&void 0!==a.recordStores)for(b=0;b0)for("function"==typeof b&&(e=function(a,c){var d;f.log("sendStatement - callbackWrapper: "+h),h>1?(h-=1,j.push({err:a,xhr:c})):1===h?(j.push({err:a,xhr:c}),d=[j,g],b.apply(this,d)):f.log("sendStatement - unexpected record store count: "+h)}),d=0;h>d;d+=1)c=this.recordStores[d],i.push(c.saveStatement(g,{callback:e}));else this.log("[warning] sendStatement: No LRSs added yet (statement not sent)"),"function"==typeof b&&b.apply(this,[null,g]);return{statement:g,results:i}},getStatement:function(a,b){this.log("getStatement");var c;return this.recordStores.length>0?(c=this.recordStores[0],c.retrieveStatement(a,{callback:b})):void this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d,e,f,g,h,i=this,j=this.recordStores.length,k=[],l=[];if(a instanceof TinCan.Statement&&(a=a.id),"undefined"!=typeof c.actor?e=c.actor:null!==this.actor&&(e=this.actor),f=new TinCan.Statement({actor:e,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}}),j>0)for("function"==typeof b&&(h=function(a,c){var d;i.log("voidStatement - callbackWrapper: "+j),j>1?(j-=1,l.push({err:a,xhr:c})):1===j?(l.push({err:a,xhr:c}),d=[l,f],b.apply(this,d)):i.log("voidStatement - unexpected record store count: "+j)}),g=0;j>g;g+=1)d=this.recordStores[g],k.push(d.saveStatement(f,{callback:h}));else this.log("[warning] voidStatement: No LRSs added yet (statement not sent)"),"function"==typeof b&&b.apply(this,[null,f]);return{statement:f,results:k}},getVoidedStatement:function(a,b){this.log("getVoidedStatement");var c;return this.recordStores.length>0?(c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b})):void this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c,d,e,f=this,g=[],h=this.recordStores.length,i=[],j=[];if(0===a.length)"function"==typeof b&&b.apply(this,[null,g]);else{for(d=0;d0)for("function"==typeof b&&(e=function(a,c){var d;f.log("sendStatements - callbackWrapper: "+h),h>1?(h-=1,j.push({err:a,xhr:c})):1===h?(j.push({err:a,xhr:c}),d=[j,g],b.apply(this,d)):f.log("sendStatements - unexpected record store count: "+h)}),d=0;h>d;d+=1)c=this.recordStores[d],i.push(c.saveStatements(g,{callback:e}));else this.log("[warning] sendStatements: No LRSs added yet (statements not sent)"),"function"==typeof b&&b.apply(this,[null,g])}return{statements:g,results:i}},getStatements:function(a){this.log("getStatements");var b,c,d={};return this.recordStores.length>0?(b=this.recordStores[0],a=a||{},c=a.params||{},a.sendActor&&null!==this.actor&&("0.9"===b.version||"0.95"===b.version?c.actor=this.actor:c.agent=this.actor),a.sendActivity&&null!==this.activity&&("0.9"===b.version||"0.95"===b.version?c.target=this.activity:c.activity=this.activity),"undefined"==typeof c.registration&&null!==this.registration&&(c.registration=this.registration),d={params:c},"undefined"!=typeof a.callback&&(d.callback=a.callback),b.queryStatements(d)):void this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor,activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.registration?c.registration=b.registration:null!==this.registration&&(c.registration=this.registration),"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveState(a,c)):void this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={agent:"undefined"!=typeof c.agent?c.agent:this.actor,activity:"undefined"!=typeof c.activity?c.activity:this.activity},"undefined"!=typeof c.registration?d.registration=c.registration:null!==this.registration&&(d.registration=this.registration),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType,"undefined"!=typeof c.overwriteJSON&&!c.overwriteJSON&&TinCan.Utils.isApplicationJSON(c.contentType)&&(d.method="POST")),"undefined"!=typeof c.callback&&(d.callback=c.callback),e.saveState(a,b,d)):void this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor,activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.registration?c.registration=b.registration:null!==this.registration&&(c.registration=this.registration),"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropState(a,c)):void this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveActivityProfile(a,c)):void this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={activity:"undefined"!=typeof c.activity?c.activity:this.activity},"undefined"!=typeof c.callback&&(d.callback=c.callback),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType,"undefined"!=typeof c.overwriteJSON&&!c.overwriteJSON&&TinCan.Utils.isApplicationJSON(c.contentType)&&(d.method="POST")),e.saveActivityProfile(a,b,d)):void this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={activity:"undefined"!=typeof b.activity?b.activity:this.activity},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropActivityProfile(a,c)):void this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.retrieveAgentProfile(a,c)):void this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;return this.recordStores.length>0?(e=this.recordStores[0],c=c||{},d={agent:"undefined"!=typeof c.agent?c.agent:this.actor},"undefined"!=typeof c.callback&&(d.callback=c.callback),"undefined"!=typeof c.lastSHA1&&(d.lastSHA1=c.lastSHA1),"undefined"!=typeof c.contentType&&(d.contentType=c.contentType,"undefined"!=typeof c.overwriteJSON&&!c.overwriteJSON&&TinCan.Utils.isApplicationJSON(c.contentType)&&(d.method="POST")),e.saveAgentProfile(a,b,d)):void this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;return this.recordStores.length>0?(d=this.recordStores[0],b=b||{},c={agent:"undefined"!=typeof b.agent?b.agent:this.actor},"undefined"!=typeof b.callback&&(c.callback=b.callback),d.dropAgentProfile(a,c)):void this.log("[warning] deleteAgentProfile: No LRSs added yet (agent profile not deleted)")}},TinCan.DEBUG=!1,TinCan.enableDebug=function(){TinCan.DEBUG=!0},TinCan.disableDebug=function(){TinCan.DEBUG=!1},TinCan.versions=function(){return["1.0.1","1.0.0","0.95","0.9"]},"object"==typeof module&&(module.exports=TinCan)}(),function(){"use strict";TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"==a?b:3&b|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;for(("undefined"==typeof a||null===a)&&(a=0),("undefined"==typeof b||null===b)&&(b=2),c=Math.pow(10,b-1),d=a.toString();c>a&&c>1;)d="0"+d,c/=10;return d}return a.getUTCFullYear()+"-"+b(a.getUTCMonth()+1)+"-"+b(a.getUTCDate())+"T"+b(a.getUTCHours())+":"+b(a.getUTCMinutes())+":"+b(a.getUTCSeconds())+"."+b(a.getUTCMilliseconds(),3)+"Z"},convertISO8601DurationToMilliseconds:function(a){var b,c,d,e,f=a.indexOf("-")>=0,g=a.indexOf("T"),h=a.indexOf("H"),i=a.indexOf("M"),j=a.indexOf("S");if(-1===g||-1!==i&&g>i||-1!==a.indexOf("D")||-1!==a.indexOf("Y"))throw new Error("ISO 8601 timestamps including years, months and/or days are not currently supported");return-1===h?(h=g,b=0):b=parseInt(a.slice(g+1,h),10),-1===i?(i=g,c=0):c=parseInt(a.slice(h+1,i),10),d=parseFloat(a.slice(i+1,j)),e=parseInt(1e3*(60*(60*b+c)+d),10),isNaN(e)&&(e=0),f&&(e=-1*e),e},convertMillisecondsToISO8601Duration:function(a){var b,c,d,e=parseInt(a,10),f="",g="";return 0>e&&(f="-",e=-1*e),b=parseInt(e/36e5,10),c=parseInt(e%36e5/6e4,10),d=e%36e5%6e4/1e3,g=f+"PT",b>0&&(g+=b+"H"),c>0&&(g+=c+"M"),g+=d+"S"},getSHA1String:function(a){return CryptoJS.SHA1(a).toString(CryptoJS.enc.Hex)},getBase64String:function(a){return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Latin1.parse(a))},getLangDictionaryValue:function(a,b){var c,d=this[a];if("undefined"!=typeof b&&"undefined"!=typeof d[b])return d[b];if("undefined"!=typeof d.und)return d.und;if("undefined"!=typeof d["en-US"])return d["en-US"];for(c in d)if(d.hasOwnProperty(c))return d[c];return""},parseURL:function(a){var b,c,d,e=String(a).split("?"),f={};if(2===e.length)for(b=e[1].split("&"),d=0;d0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),""!==a.name&&(a.name+=" "),"undefined"!=typeof a.lastName&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0))):("undefined"!=typeof a.familyName||"undefined"!=typeof a.givenName)&&(a.name="","undefined"!=typeof a.givenName&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),""!==a.name&&(a.name+=" "),"undefined"!=typeof a.familyName&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0))),"object"==typeof a.name&&null!==a.name&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),"object"==typeof a.mbox&&null!==a.mbox&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),"object"==typeof a.mbox_sha1sum&&null!==a.mbox_sha1sum&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),"object"==typeof a.openid&&null!==a.openid&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),"object"==typeof a.account&&null!==a.account&&"undefined"==typeof a.account.homePage&&"undefined"==typeof a.account.name&&(0===a.account.length?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(this.account=a.account instanceof TinCan.AgentAccount?a.account:new TinCan.AgentAccount(a.account)),b=0;b0)for(b.member=[],c=0;c0))for(c=0;c0)if("0.9"===a||"0.95"===a)this[e[b]].length>1&&this.log("[warning] version does not support multiple values in: "+e[b]),d[e[b]]=this[e[b]][0].asVersion(a);else for(d[e[b]]=[],c=0;c0){if("0.9"===a||"0.95"===a)throw this.log("[error] version does not support the 'category' property: "+a),new Error(a+" does not support the 'category' property");for(d.category=[],b=0;b=200&&400>f||e?b.callback?void b.callback(null,a):d={err:null,xhr:a}:(d={err:f,xhr:a},0===f?log("[warning] There was a problem communicating with the Learning Record Store. Aborted, offline, or invalid CORS endpoint ("+f+")",LOG_SRC):log("[warning] There was a problem communicating with the Learning Record Store. ("+f+" | "+a.responseText+")",LOG_SRC),b.callback&&b.callback(f,a),d))},__IEModeConversion=function(a,b,c,d){var e;for(e in b)b.hasOwnProperty(e)&&c.push(e+"="+encodeURIComponent(b[e]));return"undefined"!=typeof d.data&&c.push("content="+encodeURIComponent(d.data)),b["Content-Type"]="application/x-www-form-urlencoded",a+="?method="+d.method,d.method="POST",d.params={},c.length>0&&(d.data=c.join("&")),a +},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d,e,f,g,h=this,i=[],j={finished:!1,fakeStatus:null},k="undefined"!=typeof c.callback,l=a,m=2048;log("sendRequest using XMLHttpRequest - async: "+k,LOG_SRC);for(e in c.params)c.params.hasOwnProperty(e)&&i.push(e+"="+encodeURIComponent(c.params[e]));if(i.length>0&&(l+="?"+i.join("&")),l.length>=m){if("undefined"!=typeof b["Content-Type"]&&"application/json"!==b["Content-Type"])return g=new Error("Unsupported content type for IE Mode request"),"undefined"!=typeof c.callback&&c.callback(g,null),{err:g,xhr:null};if("undefined"==typeof c.method)return g=new Error("method must not be undefined for an IE Mode Request conversion"),"undefined"!=typeof c.callback&&c.callback(g,null),{err:g,xhr:null};a=__IEModeConversion(a,b,i,c)}else a=l;d="undefined"!=typeof XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"),d.open(c.method,a,k);for(e in b)b.hasOwnProperty(e)&&d.setRequestHeader(e,b[e]);"undefined"!=typeof c.data&&(c.data+=""),f=c.data,k&&(d.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+d.readyState,LOG_SRC),4===d.readyState&&requestComplete.call(h,d,c,j)});try{d.send(f)}catch(n){log("sendRequest caught send exception: "+n,LOG_SRC)}return k?d:requestComplete.call(this,d,c,j)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d,e,f,g,h,i=this,j=[],k={finished:!1,fakeStatus:null};if("undefined"!=typeof b["Content-Type"]&&"application/json"!==b["Content-Type"])return h=new Error("Unsupported content type for IE Mode request"),c.callback?(c.callback(h,null),null):{err:h,xhr:null};a+="?method="+c.method;for(f in c.params)c.params.hasOwnProperty(f)&&j.push(f+"="+encodeURIComponent(c.params[f]));for(f in b)b.hasOwnProperty(f)&&j.push(f+"="+encodeURIComponent(b[f]));"undefined"!=typeof c.data&&j.push("content="+encodeURIComponent(c.data)),e=j.join("&"),d=new XDomainRequest,d.open("POST",a),c.callback?(d.onload=function(){k.fakeStatus=200,requestComplete.call(i,d,c,k)},d.onerror=function(){k.fakeStatus=400,requestComplete.call(i,d,c,k)},d.ontimeout=function(){k.fakeStatus=0,requestComplete.call(i,d,c,k)}):(d.onload=function(){k.fakeStatus=200},d.onerror=function(){k.fakeStatus=400},d.ontimeout=function(){k.fakeStatus=0}),d.onprogress=function(){},d.timeout=0;try{d.send(e)}catch(l){log("sendRequest caught send exception: "+l,LOG_SRC)}if(!c.callback){for(g=1e4+Date.now(),log("sendRequest - until: "+g+", finished: "+k.finished,LOG_SRC);Date.now()= 0), + indexOfT = ISO8601Duration.indexOf("T"), + indexOfH = ISO8601Duration.indexOf("H"), + indexOfM = ISO8601Duration.indexOf("M"), + indexOfS = ISO8601Duration.indexOf("S"), + hours, + minutes, + seconds, + durationInMilliseconds; + + if ((indexOfT === -1) || ((indexOfM !== -1) && (indexOfM < indexOfT)) || (ISO8601Duration.indexOf("D") !== -1) || (ISO8601Duration.indexOf("Y") !== -1)) { + throw new Error("ISO 8601 timestamps including years, months and/or days are not currently supported"); + } + + if (indexOfH === -1) { + indexOfH = indexOfT; + hours = 0; + } + else { + hours = parseInt(ISO8601Duration.slice(indexOfT + 1, indexOfH), 10); + } + + if (indexOfM === -1) { + indexOfM = indexOfT; + minutes = 0; + } + else { + minutes = parseInt(ISO8601Duration.slice(indexOfH + 1, indexOfM), 10); + } + + seconds = parseFloat(ISO8601Duration.slice(indexOfM + 1, indexOfS)); + + durationInMilliseconds = parseInt((((((hours * 60) + minutes) * 60) + seconds) * 1000), 10); + if (isNaN(durationInMilliseconds)){ + durationInMilliseconds = 0; + } + if (isValueNegative) { + durationInMilliseconds = durationInMilliseconds * -1; + } + + return durationInMilliseconds; + }, + + /** + @method convertMillisecondsToISO8601Duration + @static + @param {Int} inputMilliseconds Duration in milliseconds + @return {String} Duration in ISO8601 format + */ + convertMillisecondsToISO8601Duration: function (inputMilliseconds) { + var hours, + minutes, + seconds, + i_inputMilliseconds = parseInt(inputMilliseconds, 10), + inputIsNegative = "", + rtnStr = ""; + + if (i_inputMilliseconds < 0) { + inputIsNegative = "-"; + i_inputMilliseconds = i_inputMilliseconds * -1; + } + + hours = parseInt(((i_inputMilliseconds) / 3600000), 10); + minutes = parseInt((((i_inputMilliseconds) % 3600000) / 60000), 10); + seconds = (((i_inputMilliseconds) % 3600000) % 60000) / 1000; + + rtnStr = inputIsNegative + "PT"; + if (hours > 0) { + rtnStr += hours + "H"; + } + + if (minutes > 0) { + rtnStr += minutes + "M"; + } + + rtnStr += seconds + "S"; + + return rtnStr; + }, + /** @method getSHA1String @static @@ -2459,7 +2560,7 @@ TinCan client library else { requestParams.agent = JSON.stringify(cfg.agent.asVersion(this.version)); } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2575,6 +2676,7 @@ TinCan client library @param {String} [cfg.registration] Registration @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing state @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveState: function (key, val, cfg) { @@ -2590,6 +2692,10 @@ TinCan client library val = JSON.stringify(val); } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + requestParams = { stateId: key, activityId: cfg.activity.id @@ -2600,7 +2706,7 @@ TinCan client library else { requestParams.agent = JSON.stringify(cfg.agent.asVersion(this.version)); } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2611,7 +2717,7 @@ TinCan client library requestCfg = { url: "activities/state", - method: "PUT", + method: cfg.method, params: requestParams, data: val, headers: { @@ -2657,7 +2763,7 @@ TinCan client library if (key !== null) { requestParams.stateId = key; } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2803,6 +2909,7 @@ TinCan client library @param {Object} cfg.activity TinCan.Activity @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveActivityProfile: function (key, val, cfg) { @@ -2813,13 +2920,17 @@ TinCan client library cfg.contentType = "application/octet-stream"; } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + if (typeof val === "object" && TinCan.Utils.isApplicationJSON(cfg.contentType)) { val = JSON.stringify(val); } requestCfg = { url: "activities/profile", - method: "PUT", + method: cfg.method, params: { profileId: key, activityId: cfg.activity.id @@ -3005,6 +3116,7 @@ TinCan client library @param {Object} cfg.agent TinCan.Agent @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveAgentProfile: function (key, val, cfg) { @@ -3015,12 +3127,16 @@ TinCan client library cfg.contentType = "application/octet-stream"; } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + if (typeof val === "object" && TinCan.Utils.isApplicationJSON(cfg.contentType)) { val = JSON.stringify(val); } requestCfg = { - method: "PUT", + method: cfg.method, params: { profileId: key }, @@ -5101,7 +5217,6 @@ TinCan client library "revision", "platform", "language", - "statement", "extensions" ], agentGroupProps = [ @@ -5147,6 +5262,24 @@ TinCan client library this.contextActivities = new TinCan.ContextActivities(cfg.contextActivities); } } + + if (cfg.hasOwnProperty("statement") && cfg.statement !== null) { + if (cfg.statement instanceof TinCan.StatementRef) { + this.statement = cfg.statement; + } + else if (cfg.statement instanceof TinCan.SubStatement) { + this.statement = cfg.statement; + } + else if (cfg.statement.objectType === "StatementRef") { + this.statement = new TinCan.StatementRef(cfg.statement); + } + else if (cfg.statement.objectType === "SubStatement") { + this.statement = new TinCan.SubStatement(cfg.statement); + } + else { + this.log("Unable to parse statement.context.statement property."); + } + } }, /** @@ -5173,6 +5306,11 @@ TinCan client library version = version || TinCan.versions()[0]; + if (this.statement instanceof TinCan.SubStatement && version !== "0.9" && version !== "0.95") { + this.log("[error] version does not support SubStatements in the 'statement' property: " + version); + throw new Error(version + " does not support SubStatements in the 'statement' property"); + } + for (i = 0; i < optionalDirectProps.length; i += 1) { if (this[optionalDirectProps[i]] !== null) { result[optionalDirectProps[i]] = this[optionalDirectProps[i]]; @@ -5562,6 +5700,10 @@ TinCan client library result.object = this.target.asVersion(version); } + if (version === "0.9") { + result.objectType = "Statement"; + } + return result; } }; diff --git a/build/tincan.js b/build/tincan.js index 1ce0196..6967e2b 100644 --- a/build/tincan.js +++ b/build/tincan.js @@ -1,4 +1,4 @@ -"0.31.1"; +"0.32.0"; /* CryptoJS v3.0.2 code.google.com/p/crypto-js @@ -980,6 +980,7 @@ var TinCan; defaults to 'registration' property if empty @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing state @param {String} [cfg.contentType] Content-Type to specify in headers + @param {Boolean} [cfg.overwriteJSON] If the Content-Type is JSON, should a PUT be used? @param {Function} [cfg.callback] Function to run with state */ setState: function (key, val, cfg) { @@ -1016,6 +1017,9 @@ var TinCan; } if (typeof cfg.contentType !== "undefined") { queryCfg.contentType = cfg.contentType; + if ((typeof cfg.overwriteJSON !== "undefined") && (! cfg.overwriteJSON) && (TinCan.Utils.isApplicationJSON(cfg.contentType))) { + queryCfg.method = "POST"; + } } if (typeof cfg.callback !== "undefined") { queryCfg.callback = cfg.callback; @@ -1127,6 +1131,7 @@ var TinCan; defaults to 'activity' property if empty @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers + @param {Boolean} [cfg.overwriteJSON] If the Content-Type is JSON, should a PUT be used? @param {Function} [cfg.callback] Function to run with activity profile */ setActivityProfile: function (key, val, cfg) { @@ -1159,6 +1164,9 @@ var TinCan; } if (typeof cfg.contentType !== "undefined") { queryCfg.contentType = cfg.contentType; + if ((typeof cfg.overwriteJSON !== "undefined") && (! cfg.overwriteJSON) && (TinCan.Utils.isApplicationJSON(cfg.contentType))) { + queryCfg.method = "POST"; + } } return lrs.saveActivityProfile(key, val, queryCfg); @@ -1256,6 +1264,7 @@ var TinCan; defaults to 'actor' property if empty @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers + @param {Boolean} [cfg.overwriteJSON] If the Content-Type is JSON, should a PUT be used? @param {Function} [cfg.callback] Function to run with agent profile */ setAgentProfile: function (key, val, cfg) { @@ -1288,6 +1297,9 @@ var TinCan; } if (typeof cfg.contentType !== "undefined") { queryCfg.contentType = cfg.contentType; + if ((typeof cfg.overwriteJSON !== "undefined") && (! cfg.overwriteJSON) && (TinCan.Utils.isApplicationJSON(cfg.contentType))) { + queryCfg.method = "POST"; + } } return lrs.saveAgentProfile(key, val, queryCfg); @@ -1475,6 +1487,95 @@ TinCan client library pad(d.getUTCMilliseconds(), 3) + "Z"; }, + /** + @method convertISO8601DurationToMilliseconds + @static + @param {String} ISO8601Duration Duration in ISO8601 format + @return {Int} Duration in milliseconds + + Note: does not handle input strings with years, months and days + */ + convertISO8601DurationToMilliseconds: function (ISO8601Duration) { + var isValueNegative = (ISO8601Duration.indexOf("-") >= 0), + indexOfT = ISO8601Duration.indexOf("T"), + indexOfH = ISO8601Duration.indexOf("H"), + indexOfM = ISO8601Duration.indexOf("M"), + indexOfS = ISO8601Duration.indexOf("S"), + hours, + minutes, + seconds, + durationInMilliseconds; + + if ((indexOfT === -1) || ((indexOfM !== -1) && (indexOfM < indexOfT)) || (ISO8601Duration.indexOf("D") !== -1) || (ISO8601Duration.indexOf("Y") !== -1)) { + throw new Error("ISO 8601 timestamps including years, months and/or days are not currently supported"); + } + + if (indexOfH === -1) { + indexOfH = indexOfT; + hours = 0; + } + else { + hours = parseInt(ISO8601Duration.slice(indexOfT + 1, indexOfH), 10); + } + + if (indexOfM === -1) { + indexOfM = indexOfT; + minutes = 0; + } + else { + minutes = parseInt(ISO8601Duration.slice(indexOfH + 1, indexOfM), 10); + } + + seconds = parseFloat(ISO8601Duration.slice(indexOfM + 1, indexOfS)); + + durationInMilliseconds = parseInt((((((hours * 60) + minutes) * 60) + seconds) * 1000), 10); + if (isNaN(durationInMilliseconds)){ + durationInMilliseconds = 0; + } + if (isValueNegative) { + durationInMilliseconds = durationInMilliseconds * -1; + } + + return durationInMilliseconds; + }, + + /** + @method convertMillisecondsToISO8601Duration + @static + @param {Int} inputMilliseconds Duration in milliseconds + @return {String} Duration in ISO8601 format + */ + convertMillisecondsToISO8601Duration: function (inputMilliseconds) { + var hours, + minutes, + seconds, + i_inputMilliseconds = parseInt(inputMilliseconds, 10), + inputIsNegative = "", + rtnStr = ""; + + if (i_inputMilliseconds < 0) { + inputIsNegative = "-"; + i_inputMilliseconds = i_inputMilliseconds * -1; + } + + hours = parseInt(((i_inputMilliseconds) / 3600000), 10); + minutes = parseInt((((i_inputMilliseconds) % 3600000) / 60000), 10); + seconds = (((i_inputMilliseconds) % 3600000) % 60000) / 1000; + + rtnStr = inputIsNegative + "PT"; + if (hours > 0) { + rtnStr += hours + "H"; + } + + if (minutes > 0) { + rtnStr += minutes + "M"; + } + + rtnStr += seconds + "S"; + + return rtnStr; + }, + /** @method getSHA1String @static @@ -2459,7 +2560,7 @@ TinCan client library else { requestParams.agent = JSON.stringify(cfg.agent.asVersion(this.version)); } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2575,6 +2676,7 @@ TinCan client library @param {String} [cfg.registration] Registration @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing state @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveState: function (key, val, cfg) { @@ -2590,6 +2692,10 @@ TinCan client library val = JSON.stringify(val); } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + requestParams = { stateId: key, activityId: cfg.activity.id @@ -2600,7 +2706,7 @@ TinCan client library else { requestParams.agent = JSON.stringify(cfg.agent.asVersion(this.version)); } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2611,7 +2717,7 @@ TinCan client library requestCfg = { url: "activities/state", - method: "PUT", + method: cfg.method, params: requestParams, data: val, headers: { @@ -2657,7 +2763,7 @@ TinCan client library if (key !== null) { requestParams.stateId = key; } - if (typeof cfg.registration !== "undefined") { + if ((typeof cfg.registration !== "undefined") && (cfg.registration !== null)) { if (this.version === "0.9") { requestParams.registrationId = cfg.registration; } @@ -2803,6 +2909,7 @@ TinCan client library @param {Object} cfg.activity TinCan.Activity @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveActivityProfile: function (key, val, cfg) { @@ -2813,13 +2920,17 @@ TinCan client library cfg.contentType = "application/octet-stream"; } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + if (typeof val === "object" && TinCan.Utils.isApplicationJSON(cfg.contentType)) { val = JSON.stringify(val); } requestCfg = { url: "activities/profile", - method: "PUT", + method: cfg.method, params: { profileId: key, activityId: cfg.activity.id @@ -3005,6 +3116,7 @@ TinCan client library @param {Object} cfg.agent TinCan.Agent @param {String} [cfg.lastSHA1] SHA1 of the previously seen existing profile @param {String} [cfg.contentType] Content-Type to specify in headers (defaults to 'application/octet-stream') + @param {String} [cfg.method] Method to use. Default: PUT @param {Function} [cfg.callback] Callback to execute on completion */ saveAgentProfile: function (key, val, cfg) { @@ -3015,12 +3127,16 @@ TinCan client library cfg.contentType = "application/octet-stream"; } + if (typeof cfg.method === "undefined" || cfg.method !== "POST") { + cfg.method = "PUT"; + } + if (typeof val === "object" && TinCan.Utils.isApplicationJSON(cfg.contentType)) { val = JSON.stringify(val); } requestCfg = { - method: "PUT", + method: cfg.method, params: { profileId: key }, @@ -5101,7 +5217,6 @@ TinCan client library "revision", "platform", "language", - "statement", "extensions" ], agentGroupProps = [ @@ -5147,6 +5262,24 @@ TinCan client library this.contextActivities = new TinCan.ContextActivities(cfg.contextActivities); } } + + if (cfg.hasOwnProperty("statement") && cfg.statement !== null) { + if (cfg.statement instanceof TinCan.StatementRef) { + this.statement = cfg.statement; + } + else if (cfg.statement instanceof TinCan.SubStatement) { + this.statement = cfg.statement; + } + else if (cfg.statement.objectType === "StatementRef") { + this.statement = new TinCan.StatementRef(cfg.statement); + } + else if (cfg.statement.objectType === "SubStatement") { + this.statement = new TinCan.SubStatement(cfg.statement); + } + else { + this.log("Unable to parse statement.context.statement property."); + } + } }, /** @@ -5173,6 +5306,11 @@ TinCan client library version = version || TinCan.versions()[0]; + if (this.statement instanceof TinCan.SubStatement && version !== "0.9" && version !== "0.95") { + this.log("[error] version does not support SubStatements in the 'statement' property: " + version); + throw new Error(version + " does not support SubStatements in the 'statement' property"); + } + for (i = 0; i < optionalDirectProps.length; i += 1) { if (this[optionalDirectProps[i]] !== null) { result[optionalDirectProps[i]] = this[optionalDirectProps[i]]; @@ -5562,6 +5700,10 @@ TinCan client library result.object = this.target.asVersion(version); } + if (version === "0.9") { + result.objectType = "Statement"; + } + return result; } }; @@ -6909,7 +7051,7 @@ TinCan client library } // the original data is repackaged as "content" form var - if (cfg.data !== null) { + if (typeof cfg.data !== "undefined") { pairs.push("content=" + encodeURIComponent(cfg.data)); } diff --git a/package.json b/package.json index 68377bc..cbcbbb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tincanjs", "description": "Tin Can API Library", - "version": "0.31.1", + "version": "0.32.0", "private": false, "main": "build/tincan-node.js", "directories": {