From aaaeb7208008fa0c05a8fb79e92233e64ce75f41 Mon Sep 17 00:00:00 2001 From: "Brian J. Miller" Date: Fri, 24 Jan 2014 15:09:09 -0600 Subject: [PATCH 1/4] Handle 'category' property in context.contextActivities --- package.json | 2 +- src/ContextActivities.js | 22 ++++++++++++++++++++-- test/js/unit/ContextActivities.js | 28 +++++++++++++++++++++++++++- yuidoc.json | 2 +- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e3a99c3..5755d0b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tincanjs", "description": "Tin Can API Library", - "version": "0.20.1", + "version": "0.21.1", "private": false, "main": "build/tincan-node.js", "directories": { diff --git a/src/ContextActivities.js b/src/ContextActivities.js index 2efc8a2..f636d9a 100644 --- a/src/ContextActivities.js +++ b/src/ContextActivities.js @@ -30,6 +30,12 @@ TinCan client library var ContextActivities = TinCan.ContextActivities = function (cfg) { this.log("constructor"); + /** + @property category + @type Array + */ + this.category = null; + /** @property parent @type Array @@ -71,6 +77,7 @@ TinCan client library var i, j, objProps = [ + "category", "parent", "grouping", "other" @@ -100,11 +107,11 @@ TinCan client library /** @method add - @param String key Property to add value to one of "parent", "grouping", "other" + @param String key Property to add value to one of "category", "parent", "grouping", "other" @return Number index where the value was added */ add: function (key, val) { - if (key !== "parent" && key !== "grouping" && key !== "other") { + if (key !== "category" && key !== "parent" && key !== "grouping" && key !== "other") { return; } @@ -160,6 +167,17 @@ TinCan client library } } } + if (this.category !== null && this.category.length > 0) { + if (version === "0.9" || version === "0.95") { + this.log("[WARNING] version does not support the 'category' property"); + } + else { + result.category = []; + for (i = 0; i < this.category.length; i += 1) { + result.category.push(this.category[i].asVersion(version)); + } + } + } return result; } diff --git a/test/js/unit/ContextActivities.js b/test/js/unit/ContextActivities.js index 8352db6..9278ae8 100644 --- a/test/js/unit/ContextActivities.js +++ b/test/js/unit/ContextActivities.js @@ -38,6 +38,7 @@ function () { var obj = new TinCan.ContextActivities (), nullProps = [ + "category", "parent", "grouping", "other" @@ -121,7 +122,7 @@ ] } ], - props = [ "parent", "other", "grouping" ], + props = [ "category", "parent", "other", "grouping" ], i, j, instanceConfig, @@ -163,6 +164,31 @@ latest: {} } }, + { + name: "category 1", + instanceConfig: { + category: commonActivity + }, + versions: { + latest: { category: [ commonRaw ] }, + "0.95": {}, + "0.9": {} + } + }, + { + name: "category 2", + instanceConfig: { + category: [ + commonActivity, + commonActivity2 + ] + }, + versions: { + latest: { category: [ commonRaw, commonRaw2 ] }, + "0.95": {}, + "0.9": {} + } + }, { name: "parent 1", instanceConfig: { diff --git a/yuidoc.json b/yuidoc.json index 2134840..aa951f0 100644 --- a/yuidoc.json +++ b/yuidoc.json @@ -1,5 +1,5 @@ { - "version": "0.20.1", + "version": "0.21.1", "name": "TinCanJS", "description": "Library for working with Tin Can API in JavaScript", "url": "http://rusticisoftware.github.com/TinCanJS/", From 46e8adac97b2bde5baddce24a71ec6140fbe068e Mon Sep 17 00:00:00 2001 From: "Brian J. Miller" Date: Fri, 24 Jan 2014 15:09:57 -0600 Subject: [PATCH 2/4] Build 0.21.1 --- build/tincan-min.js | 2 +- build/tincan-node.js | 22 ++++++++++++++++++++-- build/tincan.js | 22 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/build/tincan-min.js b/build/tincan-min.js index adb6540..76c7276 100644 --- a/build/tincan-min.js +++ b/build/tincan-min.js @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -;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&&typeof console!="undefined"&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;a=a||{},a.hasOwnProperty("url")&&a.url!==""&&this._initFromQueryString(a.url);if(a.hasOwnProperty("recordStores")&&a.recordStores!==undefined)for(b=0;b0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatement - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatement - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],c.retrieveStatement(a,{callback:b});this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d=this,e,f,g,h=this.recordStores.length,i,j=[],k,l=[];a instanceof TinCan.Statement&&(a=a.id),typeof c.actor!="undefined"?f=c.actor:this.actor!==null&&(f=this.actor),g=new TinCan.Statement({actor:f,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}});if(h>0){typeof b=="function"&&(k=function(a,c){var e;d.log("voidStatement - callbackWrapper: "+h),h>1?(h-=1,l.push({err:a,xhr:c})):h===1?(l.push({err:a,xhr:c}),e=[l,g],b.apply(this,e)):d.log("voidStatement - unexpected record store count: "+h)});for(i=0;i0)return c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b});this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c=this,d,e=[],f=this.recordStores.length,g,h=[],i,j=[];if(a.length===0)typeof b=="function"&&b.apply(this,[null,e]);else{for(g=0;g0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatements - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatements - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],a=a||{},d=a.params||{},a.sendActor&&this.actor!==null&&(c.version==="0.9"||c.version==="0.95"?d.actor=this.actor:d.agent=this.actor),a.sendActivity&&this.activity!==null&&(c.version==="0.9"||c.version==="0.95"?d.target=this.activity:d.activity=this.activity),typeof d.registration=="undefined"&&this.registration!==null&&(d.registration=this.registration),b={params:d},typeof a.callback!="undefined"&&(b.callback=a.callback),c.queryStatements(b);this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveState(a,c);this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor,activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.registration!="undefined"?d.registration=c.registration:this.registration!==null&&(d.registration=this.registration),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),typeof c.callback!="undefined"&&(d.callback=c.callback),e.saveState(a,b,d);this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropState(a,c);this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveActivityProfile(a,c);this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveActivityProfile(a,b,d);this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropActivityProfile(a,c);this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveAgentProfile(a,c);this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveAgentProfile(a,b,d);this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropAgentProfile(a,c);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"]},typeof module=="object"&&(module.exports=TinCan)})(),function(){"use strict",TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=Math.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;if(typeof a=="undefined"||a===null)a=0;if(typeof b=="undefined"||b===null)b=2;c=Math.pow(10,b-1),d=a.toString();while(a1)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=this[a],d;if(typeof b!="undefined"&&typeof c[b]!="undefined")return c[b];if(typeof c.und!="undefined")return c.und;if(typeof c["en-US"]!="undefined")return c["en-US"];for(d in c)if(c.hasOwnProperty(d))return c[d];return""},parseURL:function(a){var b=String(a).split("?"),c,d,e,f={};if(b.length===2){c=b[1].split("&");for(e=0;e0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.lastName!="undefined"&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0));else if(typeof a.familyName!="undefined"||typeof a.givenName!="undefined")a.name="",typeof a.givenName!="undefined"&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.familyName!="undefined"&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0));typeof a.name=="object"&&a.name!==null&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),typeof a.mbox=="object"&&a.mbox!==null&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),typeof a.mbox_sha1sum=="object"&&a.mbox_sha1sum!==null&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),typeof a.openid=="object"&&a.openid!==null&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),typeof a.account=="object"&&a.account!==null&&typeof a.account.homePage=="undefined"&&typeof a.account.name=="undefined"&&(a.account.length===0?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(a.account instanceof TinCan.AgentAccount?this.account=a.account:this.account=new TinCan.AgentAccount(a.account));for(b=0;b0){b.member=[];for(c=0;c0)for(c=0;c0)if(a==="0.9"||a==="0.95")this[c[d]].length>1&&this.log("[WARNING] version does not support multiple values in: "+c[d]),b[c[d]]=this[c[d]][0].asVersion(a);else{b[c[d]]=[];for(e=0;e=200&&f<400||e))return d={err:f,xhr:a},f===0?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;if(!b.callback)return d={err:null,xhr:a},d;b.callback(null,a)},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d=this,e,f,g=[],h,i={finished:!1,fakeStatus:null},j=typeof c.callback!="undefined";log("sendRequest using XMLHttpRequest - async: "+j,LOG_SRC);for(f in c.params)c.params.hasOwnProperty(f)&&g.push(f+"="+encodeURIComponent(c.params[f]));g.length>0&&(a+="?"+g.join("&")),typeof XMLHttpRequest!="undefined"?e=new XMLHttpRequest:e=new ActiveXObject("Microsoft.XMLHTTP"),e.open(c.method,a,j);for(f in b)b.hasOwnProperty(f)&&e.setRequestHeader(f,b[f]);typeof c.data!="undefined"&&(c.data+=""),h=c.data,j&&(e.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+e.readyState,LOG_SRC),e.readyState===4&&requestComplete.call(d,e,c,i)});try{e.send(h)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}return j?e:requestComplete.call(this,e,c,i)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d=this,e,f=[],g,h,i,j={finished:!1,fakeStatus:null};a+="?method="+c.method;for(h in c.params)c.params.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(c.params[h]));for(h in b)b.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(b[h]));c.data!==null&&f.push("content="+encodeURIComponent(c.data)),g=f.join("&"),e=new XDomainRequest,e.open("POST",a),c.callback?(e.onload=function(){j.fakeStatus=200,requestComplete.call(d,e,c,j)},e.onerror=function(){j.fakeStatus=400,requestComplete.call(d,e,c,j)},e.ontimeout=function(){j.fakeStatus=0,requestComplete.call(d,e,c,j)}):(e.onload=function(){j.fakeStatus=200},e.onerror=function(){j.fakeStatus=400},e.ontimeout=function(){j.fakeStatus=0}),e.onprogress=function(){},e.timeout=0;try{e.send(g)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}if(!c.callback){i=1e4+Date.now(),log("sendRequest - until: "+i+", finished: "+j.finished,LOG_SRC);while(Date.now()>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535>>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;d>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((e&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;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;d>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<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((h|0)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;ik;k++){if(16>k)d[k]=a[b+k]|0;else{var l=d[k-3]^d[k-8]^d[k-14]^d[k-16];d[k]=l<<1|l>>>31}l=(e<<5|e>>>27)+j+d[k],l=20>k?l+((f&g|~f&i)+1518500249):40>k?l+((f^g^i)+1859775393):60>k?l+((f&g|f&i|g&i)-1894007588):l+((f^g^i)-899497514),j=i,i=g,g=f<<30|f>>>2,f=e,e=l}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+i|0,c[4]=c[4]+j|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,e=d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();var e=[];for(var f=0;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;for(var k=0;k<4&&f+k*.75>>6*(3-k)&63))}var l=d.charAt(64);if(l)while(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);f!=-1&&(b=f)}var g=[],h=0;for(var i=0;i>>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&&typeof console!="undefined"&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;a=a||{},a.hasOwnProperty("url")&&a.url!==""&&this._initFromQueryString(a.url);if(a.hasOwnProperty("recordStores")&&a.recordStores!==undefined)for(b=0;b0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatement - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatement - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],c.retrieveStatement(a,{callback:b});this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d=this,e,f,g,h=this.recordStores.length,i,j=[],k,l=[];a instanceof TinCan.Statement&&(a=a.id),typeof c.actor!="undefined"?f=c.actor:this.actor!==null&&(f=this.actor),g=new TinCan.Statement({actor:f,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}});if(h>0){typeof b=="function"&&(k=function(a,c){var e;d.log("voidStatement - callbackWrapper: "+h),h>1?(h-=1,l.push({err:a,xhr:c})):h===1?(l.push({err:a,xhr:c}),e=[l,g],b.apply(this,e)):d.log("voidStatement - unexpected record store count: "+h)});for(i=0;i0)return c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b});this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c=this,d,e=[],f=this.recordStores.length,g,h=[],i,j=[];if(a.length===0)typeof b=="function"&&b.apply(this,[null,e]);else{for(g=0;g0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatements - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatements - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],a=a||{},d=a.params||{},a.sendActor&&this.actor!==null&&(c.version==="0.9"||c.version==="0.95"?d.actor=this.actor:d.agent=this.actor),a.sendActivity&&this.activity!==null&&(c.version==="0.9"||c.version==="0.95"?d.target=this.activity:d.activity=this.activity),typeof d.registration=="undefined"&&this.registration!==null&&(d.registration=this.registration),b={params:d},typeof a.callback!="undefined"&&(b.callback=a.callback),c.queryStatements(b);this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveState(a,c);this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor,activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.registration!="undefined"?d.registration=c.registration:this.registration!==null&&(d.registration=this.registration),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),typeof c.callback!="undefined"&&(d.callback=c.callback),e.saveState(a,b,d);this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropState(a,c);this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveActivityProfile(a,c);this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveActivityProfile(a,b,d);this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropActivityProfile(a,c);this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveAgentProfile(a,c);this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveAgentProfile(a,b,d);this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropAgentProfile(a,c);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"]},typeof module=="object"&&(module.exports=TinCan)})(),function(){"use strict",TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=Math.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;if(typeof a=="undefined"||a===null)a=0;if(typeof b=="undefined"||b===null)b=2;c=Math.pow(10,b-1),d=a.toString();while(a1)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=this[a],d;if(typeof b!="undefined"&&typeof c[b]!="undefined")return c[b];if(typeof c.und!="undefined")return c.und;if(typeof c["en-US"]!="undefined")return c["en-US"];for(d in c)if(c.hasOwnProperty(d))return c[d];return""},parseURL:function(a){var b=String(a).split("?"),c,d,e,f={};if(b.length===2){c=b[1].split("&");for(e=0;e0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.lastName!="undefined"&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0));else if(typeof a.familyName!="undefined"||typeof a.givenName!="undefined")a.name="",typeof a.givenName!="undefined"&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.familyName!="undefined"&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0));typeof a.name=="object"&&a.name!==null&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),typeof a.mbox=="object"&&a.mbox!==null&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),typeof a.mbox_sha1sum=="object"&&a.mbox_sha1sum!==null&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),typeof a.openid=="object"&&a.openid!==null&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),typeof a.account=="object"&&a.account!==null&&typeof a.account.homePage=="undefined"&&typeof a.account.name=="undefined"&&(a.account.length===0?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(a.account instanceof TinCan.AgentAccount?this.account=a.account:this.account=new TinCan.AgentAccount(a.account));for(b=0;b0){b.member=[];for(c=0;c0)for(c=0;c0)if(a==="0.9"||a==="0.95")this[c[d]].length>1&&this.log("[WARNING] version does not support multiple values in: "+c[d]),b[c[d]]=this[c[d]][0].asVersion(a);else{b[c[d]]=[];for(e=0;e0)if(a==="0.9"||a==="0.95")this.log("[WARNING] version does not support the 'category' property");else{b.category=[];for(d=0;d=200&&f<400||e))return d={err:f,xhr:a},f===0?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;if(!b.callback)return d={err:null,xhr:a},d;b.callback(null,a)},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d=this,e,f,g=[],h,i={finished:!1,fakeStatus:null},j=typeof c.callback!="undefined";log("sendRequest using XMLHttpRequest - async: "+j,LOG_SRC);for(f in c.params)c.params.hasOwnProperty(f)&&g.push(f+"="+encodeURIComponent(c.params[f]));g.length>0&&(a+="?"+g.join("&")),typeof XMLHttpRequest!="undefined"?e=new XMLHttpRequest:e=new ActiveXObject("Microsoft.XMLHTTP"),e.open(c.method,a,j);for(f in b)b.hasOwnProperty(f)&&e.setRequestHeader(f,b[f]);typeof c.data!="undefined"&&(c.data+=""),h=c.data,j&&(e.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+e.readyState,LOG_SRC),e.readyState===4&&requestComplete.call(d,e,c,i)});try{e.send(h)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}return j?e:requestComplete.call(this,e,c,i)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d=this,e,f=[],g,h,i,j={finished:!1,fakeStatus:null};a+="?method="+c.method;for(h in c.params)c.params.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(c.params[h]));for(h in b)b.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(b[h]));c.data!==null&&f.push("content="+encodeURIComponent(c.data)),g=f.join("&"),e=new XDomainRequest,e.open("POST",a),c.callback?(e.onload=function(){j.fakeStatus=200,requestComplete.call(d,e,c,j)},e.onerror=function(){j.fakeStatus=400,requestComplete.call(d,e,c,j)},e.ontimeout=function(){j.fakeStatus=0,requestComplete.call(d,e,c,j)}):(e.onload=function(){j.fakeStatus=200},e.onerror=function(){j.fakeStatus=400},e.ontimeout=function(){j.fakeStatus=0}),e.onprogress=function(){},e.timeout=0;try{e.send(g)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}if(!c.callback){i=1e4+Date.now(),log("sendRequest - until: "+i+", finished: "+j.finished,LOG_SRC);while(Date.now()>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535>>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;d>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((e&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;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;d>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<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((h|0)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;ik;k++){if(16>k)d[k]=a[b+k]|0;else{var l=d[k-3]^d[k-8]^d[k-14]^d[k-16];d[k]=l<<1|l>>>31}l=(e<<5|e>>>27)+j+d[k],l=20>k?l+((f&g|~f&i)+1518500249):40>k?l+((f^g^i)+1859775393):60>k?l+((f&g|f&i|g&i)-1894007588):l+((f^g^i)-899497514),j=i,i=g,g=f<<30|f>>>2,f=e,e=l}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+i|0,c[4]=c[4]+j|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,e=d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();var e=[];for(var f=0;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;for(var k=0;k<4&&f+k*.75>>6*(3-k)&63))}var l=d.charAt(64);if(l)while(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);f!=-1&&(b=f)}var g=[],h=0;for(var i=0;i>>6-i%4*2;g[h>>>2]|=(j|k)<<24-h%4*8,h++}return c.create(g,h)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}(); diff --git a/build/tincan-node.js b/build/tincan-node.js index d31409a..2566255 100644 --- a/build/tincan-node.js +++ b/build/tincan-node.js @@ -4565,6 +4565,12 @@ TinCan client library var ContextActivities = TinCan.ContextActivities = function (cfg) { this.log("constructor"); + /** + @property category + @type Array + */ + this.category = null; + /** @property parent @type Array @@ -4606,6 +4612,7 @@ TinCan client library var i, j, objProps = [ + "category", "parent", "grouping", "other" @@ -4635,11 +4642,11 @@ TinCan client library /** @method add - @param String key Property to add value to one of "parent", "grouping", "other" + @param String key Property to add value to one of "category", "parent", "grouping", "other" @return Number index where the value was added */ add: function (key, val) { - if (key !== "parent" && key !== "grouping" && key !== "other") { + if (key !== "category" && key !== "parent" && key !== "grouping" && key !== "other") { return; } @@ -4695,6 +4702,17 @@ TinCan client library } } } + if (this.category !== null && this.category.length > 0) { + if (version === "0.9" || version === "0.95") { + this.log("[WARNING] version does not support the 'category' property"); + } + else { + result.category = []; + for (i = 0; i < this.category.length; i += 1) { + result.category.push(this.category[i].asVersion(version)); + } + } + } return result; } diff --git a/build/tincan.js b/build/tincan.js index ae2d8c5..24d0240 100644 --- a/build/tincan.js +++ b/build/tincan.js @@ -4565,6 +4565,12 @@ TinCan client library var ContextActivities = TinCan.ContextActivities = function (cfg) { this.log("constructor"); + /** + @property category + @type Array + */ + this.category = null; + /** @property parent @type Array @@ -4606,6 +4612,7 @@ TinCan client library var i, j, objProps = [ + "category", "parent", "grouping", "other" @@ -4635,11 +4642,11 @@ TinCan client library /** @method add - @param String key Property to add value to one of "parent", "grouping", "other" + @param String key Property to add value to one of "category", "parent", "grouping", "other" @return Number index where the value was added */ add: function (key, val) { - if (key !== "parent" && key !== "grouping" && key !== "other") { + if (key !== "category" && key !== "parent" && key !== "grouping" && key !== "other") { return; } @@ -4695,6 +4702,17 @@ TinCan client library } } } + if (this.category !== null && this.category.length > 0) { + if (version === "0.9" || version === "0.95") { + this.log("[WARNING] version does not support the 'category' property"); + } + else { + result.category = []; + for (i = 0; i < this.category.length; i += 1) { + result.category.push(this.category[i].asVersion(version)); + } + } + } return result; } From f5b72d6999d6635ab6a895eac2c468ad3821a879 Mon Sep 17 00:00:00 2001 From: "Brian J. Miller" Date: Thu, 13 Feb 2014 09:22:40 -0600 Subject: [PATCH 3/4] Make 'category' usage throw exception in 0.9 and 0.95 * Requires updates to the LRS methods which didn't previously expect statement version conversion to cause exceptions, they need to be handled by the "allowFail" code * Added unit tests around the LRS .saveStatement(s) methods * Couple of other minor fixes --- package.json | 2 +- src/ContextActivities.js | 5 +- src/LRS.js | 71 +++++++++- test/js/unit/ContextActivities.js | 27 +++- test/js/unit/LRS.js | 211 ++++++++++++++++++++++++++++++ yuidoc.json | 2 +- 6 files changed, 301 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 5755d0b..1dc67f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tincanjs", "description": "Tin Can API Library", - "version": "0.21.1", + "version": "0.22.0", "private": false, "main": "build/tincan-node.js", "directories": { diff --git a/src/ContextActivities.js b/src/ContextActivities.js index f636d9a..284098f 100644 --- a/src/ContextActivities.js +++ b/src/ContextActivities.js @@ -152,7 +152,7 @@ TinCan client library if (version === "0.9" || version === "0.95") { if (this[optionalObjProps[i]].length > 1) { // TODO: exception? - this.log("[WARNING] version does not support multiple values in: " + optionalObjProps[i]); + this.log("[warning] version does not support multiple values in: " + optionalObjProps[i]); } result[optionalObjProps[i]] = this[optionalObjProps[i]][0].asVersion(version); @@ -169,7 +169,8 @@ TinCan client library } if (this.category !== null && this.category.length > 0) { if (version === "0.9" || version === "0.95") { - this.log("[WARNING] version does not support the 'category' property"); + this.log("[error] version does not support the 'category' property: " + version); + throw new Error(version + " does not support the 'category' property"); } else { result.category = []; diff --git a/src/LRS.js b/src/LRS.js index 5929719..0bb2a77 100644 --- a/src/LRS.js +++ b/src/LRS.js @@ -251,13 +251,41 @@ TinCan client library */ saveStatement: function (stmt, cfg) { this.log("saveStatement"); - var requestCfg; + var requestCfg, + versionedStatement; cfg = cfg || {}; + try { + versionedStatement = stmt.asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + requestCfg = { url: "statements", - data: JSON.stringify(stmt.asVersion( this.version )), + data: JSON.stringify(versionedStatement), headers: { "Content-Type": "application/json" } @@ -392,6 +420,7 @@ TinCan client library saveStatements: function (stmts, cfg) { this.log("saveStatements"); var requestCfg, + versionedStatement, versionedStatements = [], i ; @@ -400,15 +429,43 @@ TinCan client library if (stmts.length === 0) { if (typeof cfg.callback !== "undefined") { - cfg.callback.apply(this, ["no statements"]); + cfg.callback(new Error("no statements"), null); + return; } - return; + return { + err: new Error("no statements"), + xhr: null + }; } for (i = 0; i < stmts.length; i += 1) { - versionedStatements.push( - stmts[i].asVersion( this.version ) - ); + try { + versionedStatement = stmts[i].asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + versionedStatements.push(versionedStatement); } requestCfg = { diff --git a/test/js/unit/ContextActivities.js b/test/js/unit/ContextActivities.js index 9278ae8..a6f6c93 100644 --- a/test/js/unit/ContextActivities.js +++ b/test/js/unit/ContextActivities.js @@ -171,8 +171,8 @@ }, versions: { latest: { category: [ commonRaw ] }, - "0.95": {}, - "0.9": {} + "0.95": { exception: "0.95 does not support the 'category' property" }, + "0.9": { exception: "0.9 does not support the 'category' property" } } }, { @@ -185,8 +185,8 @@ }, versions: { latest: { category: [ commonRaw, commonRaw2 ] }, - "0.95": {}, - "0.9": {} + "0.95": { exception: "0.95 does not support the 'category' property" }, + "0.9": { exception: "0.9 does not support the 'category' property" } } }, { @@ -295,7 +295,8 @@ i, v, obj, - result + result, + versionCfg ; for (i = 0; i < set.length; i += 1) { @@ -306,8 +307,22 @@ deepEqual(result, row.versions.latest, "object.asVersion() latest: " + row.name); for (v = 0; v < versions.length; v += 1) { + message = "object.asVersion() " + versions[v] + " : " + row.name; + versionCfg = row.versions.hasOwnProperty(versions[v]) ? row.versions[versions[v]] : row.versions.latest; + if (typeof versionCfg.exception !== "undefined") { + throws( + function () { + result = obj.asVersion(versions[v]); + }, + Error, + message + " (exception)" + ); + continue; + } + result = obj.asVersion(versions[v]); - deepEqual(result, (row.versions.hasOwnProperty(versions[v]) ? row.versions[versions[v]] : row.versions.latest), "object.asVersion() " + versions[v] + " : " + row.name); + + deepEqual(result, versionCfg, message + " (deepEqual)"); } } } diff --git a/test/js/unit/LRS.js b/test/js/unit/LRS.js index 03e0ed3..c8eda63 100644 --- a/test/js/unit/LRS.js +++ b/test/js/unit/LRS.js @@ -185,4 +185,215 @@ } } ); + + (function () { + var versions = [ + "0.95", + "0.9" + ], + stCfg = { + actor: { + mbox: "mailto:tincanjs-test-tincan+" + Date.now() + "@tincanapi.com" + }, + verb: { + id: "http://adlnet.gov/expapi/verbs/experienced" + }, + target: { + id: "http://tincanapi.com/TinCanJS/Test/TinCan.LRS_saveStatement/exception-sync" + }, + context: { + contextActivities: { + category: [ + { + id: "http://tincanapi.com/TinCanJS/Test/TinCan.LRS_saveStatement/exception-sync/cat" + } + ] + } + } + }, + doAllowFailFalseSaveStatementExceptionAsyncTest, + doAllowFailTrueSaveStatementExceptionAsyncTest, + doAllowFailFalseSaveStatementExceptionSyncTest, + doAllowFailTrueSaveStatementExceptionSyncTest, + doAllowFailFalseSaveStatementsExceptionAsyncTest, + doAllowFailTrueSaveStatementsExceptionAsyncTest, + doAllowFailFalseSaveStatementsExceptionSyncTest, + doAllowFailTrueSaveStatementsExceptionSyncTest, + i, + lrs_true, + lrs_false; + + /* .saveStatement */ + doAllowFailFalseSaveStatementExceptionAsyncTest = function (lrs, st) { + lrs.allowFail = false; + + asyncTest( + "LRS saveStatement async exception: allowFail false (" + lrs.version + ")", + function () { + var result = lrs.saveStatement( + st, + { + callback: function (err, xhr) { + start(); + ok(typeof err !== "undefined", "callback: has err argument"); + ok(typeof xhr !== "undefined", "callback: has xhr argument"); + + if (typeof err !== "undefined") { + ok(err instanceof Error, "callback err: is Error"); + } + if (typeof err !== "undefined") { + ok(xhr === null, "callback xhr is null"); + } + } + } + ); + ok(typeof result === "undefined", "result is undefined"); + } + ); + }; + + doAllowFailTrueSaveStatementExceptionAsyncTest = function (lrs, st) { + asyncTest( + "LRS saveStatement async exception: allowFail true (" + lrs.version + ")", + function () { + var result = lrs.saveStatement( + st, + { + callback: function (err, xhr) { + start(); + ok(err === null, "callback err argument is null"); + ok(xhr === null, "callback xhr argument is null"); + } + } + ); + ok(typeof result === "undefined", "result is undefined"); + } + ); + }; + + doAllowFailFalseSaveStatementExceptionSyncTest = function (lrs, st) { + lrs.allowFail = false; + + var result = lrs.saveStatement(st); + + ok(result instanceof Object, "allowFail false result: is an object (" + lrs.version + ")"); + ok(typeof result.err !== "undefined", "allowFail false result: has err property (" + lrs.version + ")"); + ok(typeof result.xhr !== "undefined", "allowFail false result: has xhr property (" + lrs.version + ")"); + + if (typeof result.err !== "undefined") { + ok(result.err instanceof Error, "allowFail false result.err: is Error (" + lrs.version + ")"); + } + if (typeof result.err !== "undefined") { + ok(result.xhr === null, "allowFail false result.xhr is null (" + lrs.version + ")"); + } + }; + + doAllowFailTrueSaveStatementExceptionSyncTest = function (lrs, st) { + var result = lrs.saveStatement(st); + deepEqual(result, { err: null, xhr: null }, "allowFail true result: matches deeply (" + lrs.version + ")"); + }; + + /* .saveStatements */ + doAllowFailFalseSaveStatementsExceptionAsyncTest = function (lrs, sts) { + lrs.allowFail = false; + + asyncTest( + "LRS saveStatements async exception: allowFail false (" + lrs.version + ")", + function () { + var result = lrs.saveStatements( + sts, + { + callback: function (err, xhr) { + start(); + ok(typeof err !== "undefined", "callback: has err argument"); + ok(typeof xhr !== "undefined", "callback: has xhr argument"); + + if (typeof err !== "undefined") { + ok(err instanceof Error, "callback err: is Error"); + } + if (typeof err !== "undefined") { + ok(xhr === null, "callback xhr is null"); + } + } + } + ); + ok(typeof result === "undefined", "result is undefined"); + } + ); + }; + + doAllowFailTrueSaveStatementsExceptionAsyncTest = function (lrs, sts) { + asyncTest( + "LRS saveStatements async exception: allowFail true (" + lrs.version + ")", + function () { + var result = lrs.saveStatements( + sts, + { + callback: function (err, xhr) { + start(); + ok(err === null, "callback err argument is null"); + ok(xhr === null, "callback xhr argument is null"); + } + } + ); + ok(typeof result === "undefined", "result is undefined"); + } + ); + }; + + doAllowFailFalseSaveStatementsExceptionSyncTest = function (lrs, sts) { + lrs.allowFail = false; + + var result = lrs.saveStatements(sts); + + ok(result instanceof Object, "allowFail false result: is an object (" + lrs.version + ")"); + ok(typeof result.err !== "undefined", "allowFail false result: has err property (" + lrs.version + ")"); + ok(typeof result.xhr !== "undefined", "allowFail false result: has xhr property (" + lrs.version + ")"); + + if (typeof result.err !== "undefined") { + ok(result.err instanceof Error, "allowFail false result.err: is Error (" + lrs.version + ")"); + } + if (typeof result.err !== "undefined") { + ok(result.xhr === null, "allowFail false result.xhr is null (" + lrs.version + ")"); + } + }; + + doAllowFailTrueSaveStatementsExceptionSyncTest = function (lrs, sts) { + var result = lrs.saveStatements(sts); + deepEqual(result, { err: null, xhr: null }, "allowFail true result: matches deeply (" + lrs.version + ")"); + }; + + test( + "LRS saveStatement/saveStatements sync exception", + function () { + var i, + lrs_true, + lrs_false; + + for (i = 0; i < versions.length; i += 1) { + if (TinCanTestCfg.recordStores[versions[i]]) { + lrs_true = new TinCan.LRS(TinCanTestCfg.recordStores[versions[i]]); + doAllowFailTrueSaveStatementExceptionSyncTest(lrs_true, new TinCan.Statement(stCfg)); + doAllowFailTrueSaveStatementsExceptionSyncTest(lrs_true, [ new TinCan.Statement(stCfg) ]); + + lrs_false = new TinCan.LRS(TinCanTestCfg.recordStores[versions[i]]); + doAllowFailFalseSaveStatementExceptionSyncTest(lrs_false, new TinCan.Statement(stCfg)); + doAllowFailFalseSaveStatementsExceptionSyncTest(lrs_false, [ new TinCan.Statement(stCfg) ]); + } + } + } + ); + + for (i = 0; i < versions.length; i += 1) { + if (TinCanTestCfg.recordStores[versions[i]]) { + lrs_true = new TinCan.LRS(TinCanTestCfg.recordStores[versions[i]]); + doAllowFailFalseSaveStatementExceptionAsyncTest(lrs_true, new TinCan.Statement(stCfg)); + doAllowFailFalseSaveStatementsExceptionAsyncTest(lrs_true, [ new TinCan.Statement(stCfg) ]); + + lrs_false = new TinCan.LRS(TinCanTestCfg.recordStores[versions[i]]); + doAllowFailTrueSaveStatementExceptionAsyncTest(lrs_false, new TinCan.Statement(stCfg)); + doAllowFailTrueSaveStatementsExceptionAsyncTest(lrs_false, [ new TinCan.Statement(stCfg) ]); + } + } + }()); }()); diff --git a/yuidoc.json b/yuidoc.json index aa951f0..d22497c 100644 --- a/yuidoc.json +++ b/yuidoc.json @@ -1,5 +1,5 @@ { - "version": "0.21.1", + "version": "0.22.0", "name": "TinCanJS", "description": "Library for working with Tin Can API in JavaScript", "url": "http://rusticisoftware.github.com/TinCanJS/", From f12d55a719408e4bb490ace4bfdec80b47244c7b Mon Sep 17 00:00:00 2001 From: "Brian J. Miller" Date: Thu, 13 Feb 2014 09:24:45 -0600 Subject: [PATCH 4/4] Build 0.22.0 --- build/tincan-min.js | 2 +- build/tincan-node.js | 76 ++++++++++++++++++++++++++++++++++++++------ build/tincan.js | 76 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 135 insertions(+), 19 deletions(-) diff --git a/build/tincan-min.js b/build/tincan-min.js index 76c7276..601de3b 100644 --- a/build/tincan-min.js +++ b/build/tincan-min.js @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -;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&&typeof console!="undefined"&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;a=a||{},a.hasOwnProperty("url")&&a.url!==""&&this._initFromQueryString(a.url);if(a.hasOwnProperty("recordStores")&&a.recordStores!==undefined)for(b=0;b0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatement - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatement - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],c.retrieveStatement(a,{callback:b});this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d=this,e,f,g,h=this.recordStores.length,i,j=[],k,l=[];a instanceof TinCan.Statement&&(a=a.id),typeof c.actor!="undefined"?f=c.actor:this.actor!==null&&(f=this.actor),g=new TinCan.Statement({actor:f,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}});if(h>0){typeof b=="function"&&(k=function(a,c){var e;d.log("voidStatement - callbackWrapper: "+h),h>1?(h-=1,l.push({err:a,xhr:c})):h===1?(l.push({err:a,xhr:c}),e=[l,g],b.apply(this,e)):d.log("voidStatement - unexpected record store count: "+h)});for(i=0;i0)return c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b});this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c=this,d,e=[],f=this.recordStores.length,g,h=[],i,j=[];if(a.length===0)typeof b=="function"&&b.apply(this,[null,e]);else{for(g=0;g0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatements - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatements - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],a=a||{},d=a.params||{},a.sendActor&&this.actor!==null&&(c.version==="0.9"||c.version==="0.95"?d.actor=this.actor:d.agent=this.actor),a.sendActivity&&this.activity!==null&&(c.version==="0.9"||c.version==="0.95"?d.target=this.activity:d.activity=this.activity),typeof d.registration=="undefined"&&this.registration!==null&&(d.registration=this.registration),b={params:d},typeof a.callback!="undefined"&&(b.callback=a.callback),c.queryStatements(b);this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveState(a,c);this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor,activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.registration!="undefined"?d.registration=c.registration:this.registration!==null&&(d.registration=this.registration),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),typeof c.callback!="undefined"&&(d.callback=c.callback),e.saveState(a,b,d);this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropState(a,c);this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveActivityProfile(a,c);this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveActivityProfile(a,b,d);this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropActivityProfile(a,c);this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveAgentProfile(a,c);this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveAgentProfile(a,b,d);this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropAgentProfile(a,c);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"]},typeof module=="object"&&(module.exports=TinCan)})(),function(){"use strict",TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=Math.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;if(typeof a=="undefined"||a===null)a=0;if(typeof b=="undefined"||b===null)b=2;c=Math.pow(10,b-1),d=a.toString();while(a1)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=this[a],d;if(typeof b!="undefined"&&typeof c[b]!="undefined")return c[b];if(typeof c.und!="undefined")return c.und;if(typeof c["en-US"]!="undefined")return c["en-US"];for(d in c)if(c.hasOwnProperty(d))return c[d];return""},parseURL:function(a){var b=String(a).split("?"),c,d,e,f={};if(b.length===2){c=b[1].split("&");for(e=0;e0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.lastName!="undefined"&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0));else if(typeof a.familyName!="undefined"||typeof a.givenName!="undefined")a.name="",typeof a.givenName!="undefined"&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.familyName!="undefined"&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0));typeof a.name=="object"&&a.name!==null&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),typeof a.mbox=="object"&&a.mbox!==null&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),typeof a.mbox_sha1sum=="object"&&a.mbox_sha1sum!==null&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),typeof a.openid=="object"&&a.openid!==null&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),typeof a.account=="object"&&a.account!==null&&typeof a.account.homePage=="undefined"&&typeof a.account.name=="undefined"&&(a.account.length===0?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(a.account instanceof TinCan.AgentAccount?this.account=a.account:this.account=new TinCan.AgentAccount(a.account));for(b=0;b0){b.member=[];for(c=0;c0)for(c=0;c0)if(a==="0.9"||a==="0.95")this[c[d]].length>1&&this.log("[WARNING] version does not support multiple values in: "+c[d]),b[c[d]]=this[c[d]][0].asVersion(a);else{b[c[d]]=[];for(e=0;e0)if(a==="0.9"||a==="0.95")this.log("[WARNING] version does not support the 'category' property");else{b.category=[];for(d=0;d=200&&f<400||e))return d={err:f,xhr:a},f===0?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;if(!b.callback)return d={err:null,xhr:a},d;b.callback(null,a)},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d=this,e,f,g=[],h,i={finished:!1,fakeStatus:null},j=typeof c.callback!="undefined";log("sendRequest using XMLHttpRequest - async: "+j,LOG_SRC);for(f in c.params)c.params.hasOwnProperty(f)&&g.push(f+"="+encodeURIComponent(c.params[f]));g.length>0&&(a+="?"+g.join("&")),typeof XMLHttpRequest!="undefined"?e=new XMLHttpRequest:e=new ActiveXObject("Microsoft.XMLHTTP"),e.open(c.method,a,j);for(f in b)b.hasOwnProperty(f)&&e.setRequestHeader(f,b[f]);typeof c.data!="undefined"&&(c.data+=""),h=c.data,j&&(e.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+e.readyState,LOG_SRC),e.readyState===4&&requestComplete.call(d,e,c,i)});try{e.send(h)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}return j?e:requestComplete.call(this,e,c,i)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d=this,e,f=[],g,h,i,j={finished:!1,fakeStatus:null};a+="?method="+c.method;for(h in c.params)c.params.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(c.params[h]));for(h in b)b.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(b[h]));c.data!==null&&f.push("content="+encodeURIComponent(c.data)),g=f.join("&"),e=new XDomainRequest,e.open("POST",a),c.callback?(e.onload=function(){j.fakeStatus=200,requestComplete.call(d,e,c,j)},e.onerror=function(){j.fakeStatus=400,requestComplete.call(d,e,c,j)},e.ontimeout=function(){j.fakeStatus=0,requestComplete.call(d,e,c,j)}):(e.onload=function(){j.fakeStatus=200},e.onerror=function(){j.fakeStatus=400},e.ontimeout=function(){j.fakeStatus=0}),e.onprogress=function(){},e.timeout=0;try{e.send(g)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}if(!c.callback){i=1e4+Date.now(),log("sendRequest - until: "+i+", finished: "+j.finished,LOG_SRC);while(Date.now()>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535>>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;d>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((e&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;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;d>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<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((h|0)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;ik;k++){if(16>k)d[k]=a[b+k]|0;else{var l=d[k-3]^d[k-8]^d[k-14]^d[k-16];d[k]=l<<1|l>>>31}l=(e<<5|e>>>27)+j+d[k],l=20>k?l+((f&g|~f&i)+1518500249):40>k?l+((f^g^i)+1859775393):60>k?l+((f&g|f&i|g&i)-1894007588):l+((f^g^i)-899497514),j=i,i=g,g=f<<30|f>>>2,f=e,e=l}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+i|0,c[4]=c[4]+j|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,e=d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();var e=[];for(var f=0;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;for(var k=0;k<4&&f+k*.75>>6*(3-k)&63))}var l=d.charAt(64);if(l)while(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);f!=-1&&(b=f)}var g=[],h=0;for(var i=0;i>>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&&typeof console!="undefined"&&console.log&&(b=b||this.LOG_SRC||"TinCan",console.log("TinCan."+b+": "+a))},init:function(a){this.log("init");var b;a=a||{},a.hasOwnProperty("url")&&a.url!==""&&this._initFromQueryString(a.url);if(a.hasOwnProperty("recordStores")&&a.recordStores!==undefined)for(b=0;b0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatement - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatement - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],c.retrieveStatement(a,{callback:b});this.log("[warning] getStatement: No LRSs added yet (statement not retrieved)")},voidStatement:function(a,b,c){this.log("voidStatement");var d=this,e,f,g,h=this.recordStores.length,i,j=[],k,l=[];a instanceof TinCan.Statement&&(a=a.id),typeof c.actor!="undefined"?f=c.actor:this.actor!==null&&(f=this.actor),g=new TinCan.Statement({actor:f,verb:{id:"http://adlnet.gov/expapi/verbs/voided"},target:{objectType:"StatementRef",id:a}});if(h>0){typeof b=="function"&&(k=function(a,c){var e;d.log("voidStatement - callbackWrapper: "+h),h>1?(h-=1,l.push({err:a,xhr:c})):h===1?(l.push({err:a,xhr:c}),e=[l,g],b.apply(this,e)):d.log("voidStatement - unexpected record store count: "+h)});for(i=0;i0)return c=this.recordStores[0],c.retrieveVoidedStatement(a,{callback:b});this.log("[warning] getVoidedStatement: No LRSs added yet (statement not retrieved)")},sendStatements:function(a,b){this.log("sendStatements");var c=this,d,e=[],f=this.recordStores.length,g,h=[],i,j=[];if(a.length===0)typeof b=="function"&&b.apply(this,[null,e]);else{for(g=0;g0){typeof b=="function"&&(i=function(a,d){var g;c.log("sendStatements - callbackWrapper: "+f),f>1?(f-=1,j.push({err:a,xhr:d})):f===1?(j.push({err:a,xhr:d}),g=[j,e],b.apply(this,g)):c.log("sendStatements - unexpected record store count: "+f)});for(g=0;g0)return c=this.recordStores[0],a=a||{},d=a.params||{},a.sendActor&&this.actor!==null&&(c.version==="0.9"||c.version==="0.95"?d.actor=this.actor:d.agent=this.actor),a.sendActivity&&this.activity!==null&&(c.version==="0.9"||c.version==="0.95"?d.target=this.activity:d.activity=this.activity),typeof d.registration=="undefined"&&this.registration!==null&&(d.registration=this.registration),b={params:d},typeof a.callback!="undefined"&&(b.callback=a.callback),c.queryStatements(b);this.log("[warning] getStatements: No LRSs added yet (statements not read)")},getState:function(a,b){this.log("getState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveState(a,c);this.log("[warning] getState: No LRSs added yet (state not retrieved)")},setState:function(a,b,c){this.log("setState");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor,activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.registration!="undefined"?d.registration=c.registration:this.registration!==null&&(d.registration=this.registration),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),typeof c.callback!="undefined"&&(d.callback=c.callback),e.saveState(a,b,d);this.log("[warning] setState: No LRSs added yet (state not saved)")},deleteState:function(a,b){this.log("deleteState");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor,activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.registration!="undefined"?c.registration=b.registration:this.registration!==null&&(c.registration=this.registration),typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropState(a,c);this.log("[warning] deleteState: No LRSs added yet (state not deleted)")},getActivityProfile:function(a,b){this.log("getActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveActivityProfile(a,c);this.log("[warning] getActivityProfile: No LRSs added yet (activity profile not retrieved)")},setActivityProfile:function(a,b,c){this.log("setActivityProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={activity:typeof c.activity!="undefined"?c.activity:this.activity},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveActivityProfile(a,b,d);this.log("[warning] setActivityProfile: No LRSs added yet (activity profile not saved)")},deleteActivityProfile:function(a,b){this.log("deleteActivityProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={activity:typeof b.activity!="undefined"?b.activity:this.activity},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropActivityProfile(a,c);this.log("[warning] deleteActivityProfile: No LRSs added yet (activity profile not deleted)")},getAgentProfile:function(a,b){this.log("getAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.retrieveAgentProfile(a,c);this.log("[warning] getAgentProfile: No LRSs added yet (agent profile not retrieved)")},setAgentProfile:function(a,b,c){this.log("setAgentProfile");var d,e;if(this.recordStores.length>0)return e=this.recordStores[0],c=c||{},d={agent:typeof c.agent!="undefined"?c.agent:this.actor},typeof c.callback!="undefined"&&(d.callback=c.callback),typeof c.lastSHA1!="undefined"&&(d.lastSHA1=c.lastSHA1),typeof c.contentType!="undefined"&&(d.contentType=c.contentType),e.saveAgentProfile(a,b,d);this.log("[warning] setAgentProfile: No LRSs added yet (agent profile not saved)")},deleteAgentProfile:function(a,b){this.log("deleteAgentProfile");var c,d;if(this.recordStores.length>0)return d=this.recordStores[0],b=b||{},c={agent:typeof b.agent!="undefined"?b.agent:this.actor},typeof b.callback!="undefined"&&(c.callback=b.callback),d.dropAgentProfile(a,c);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"]},typeof module=="object"&&(module.exports=TinCan)})(),function(){"use strict",TinCan.Utils={getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=Math.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)})},getISODateString:function(a){function b(a,b){var c,d;if(typeof a=="undefined"||a===null)a=0;if(typeof b=="undefined"||b===null)b=2;c=Math.pow(10,b-1),d=a.toString();while(a1)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=this[a],d;if(typeof b!="undefined"&&typeof c[b]!="undefined")return c[b];if(typeof c.und!="undefined")return c.und;if(typeof c["en-US"]!="undefined")return c["en-US"];for(d in c)if(c.hasOwnProperty(d))return c[d];return""},parseURL:function(a){var b=String(a).split("?"),c,d,e,f={};if(b.length===2){c=b[1].split("&");for(e=0;e0&&(a.name=a.firstName[0],a.firstName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.lastName!="undefined"&&a.lastName.length>0&&(a.name+=a.lastName[0],a.lastName.length>1&&(this.degraded=!0));else if(typeof a.familyName!="undefined"||typeof a.givenName!="undefined")a.name="",typeof a.givenName!="undefined"&&a.givenName.length>0&&(a.name=a.givenName[0],a.givenName.length>1&&(this.degraded=!0)),a.name!==""&&(a.name+=" "),typeof a.familyName!="undefined"&&a.familyName.length>0&&(a.name+=a.familyName[0],a.familyName.length>1&&(this.degraded=!0));typeof a.name=="object"&&a.name!==null&&(a.name.length>1&&(this.degraded=!0),a.name=a.name[0]),typeof a.mbox=="object"&&a.mbox!==null&&(a.mbox.length>1&&(this.degraded=!0),a.mbox=a.mbox[0]),typeof a.mbox_sha1sum=="object"&&a.mbox_sha1sum!==null&&(a.mbox_sha1sum.length>1&&(this.degraded=!0),a.mbox_sha1sum=a.mbox_sha1sum[0]),typeof a.openid=="object"&&a.openid!==null&&(a.openid.length>1&&(this.degraded=!0),a.openid=a.openid[0]),typeof a.account=="object"&&a.account!==null&&typeof a.account.homePage=="undefined"&&typeof a.account.name=="undefined"&&(a.account.length===0?delete a.account:(a.account.length>1&&(this.degraded=!0),a.account=a.account[0])),a.hasOwnProperty("account")&&(a.account instanceof TinCan.AgentAccount?this.account=a.account:this.account=new TinCan.AgentAccount(a.account));for(b=0;b0){b.member=[];for(c=0;c0)for(c=0;c0)if(a==="0.9"||a==="0.95")this[c[d]].length>1&&this.log("[warning] version does not support multiple values in: "+c[d]),b[c[d]]=this[c[d]][0].asVersion(a);else{b[c[d]]=[];for(e=0;e0){if(a==="0.9"||a==="0.95")throw this.log("[error] version does not support the 'category' property: "+a),new Error(a+" does not support the 'category' property");b.category=[];for(d=0;d=200&&f<400||e))return d={err:f,xhr:a},f===0?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;if(!b.callback)return d={err:null,xhr:a},d;b.callback(null,a)},nativeRequest=function(a,b,c){log("sendRequest using XMLHttpRequest",LOG_SRC);var d=this,e,f,g=[],h,i={finished:!1,fakeStatus:null},j=typeof c.callback!="undefined";log("sendRequest using XMLHttpRequest - async: "+j,LOG_SRC);for(f in c.params)c.params.hasOwnProperty(f)&&g.push(f+"="+encodeURIComponent(c.params[f]));g.length>0&&(a+="?"+g.join("&")),typeof XMLHttpRequest!="undefined"?e=new XMLHttpRequest:e=new ActiveXObject("Microsoft.XMLHTTP"),e.open(c.method,a,j);for(f in b)b.hasOwnProperty(f)&&e.setRequestHeader(f,b[f]);typeof c.data!="undefined"&&(c.data+=""),h=c.data,j&&(e.onreadystatechange=function(){log("xhr.onreadystatechange - xhr.readyState: "+e.readyState,LOG_SRC),e.readyState===4&&requestComplete.call(d,e,c,i)});try{e.send(h)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}return j?e:requestComplete.call(this,e,c,i)},xdrRequest=function(a,b,c){log("sendRequest using XDomainRequest",LOG_SRC);var d=this,e,f=[],g,h,i,j={finished:!1,fakeStatus:null};a+="?method="+c.method;for(h in c.params)c.params.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(c.params[h]));for(h in b)b.hasOwnProperty(h)&&f.push(h+"="+encodeURIComponent(b[h]));c.data!==null&&f.push("content="+encodeURIComponent(c.data)),g=f.join("&"),e=new XDomainRequest,e.open("POST",a),c.callback?(e.onload=function(){j.fakeStatus=200,requestComplete.call(d,e,c,j)},e.onerror=function(){j.fakeStatus=400,requestComplete.call(d,e,c,j)},e.ontimeout=function(){j.fakeStatus=0,requestComplete.call(d,e,c,j)}):(e.onload=function(){j.fakeStatus=200},e.onerror=function(){j.fakeStatus=400},e.ontimeout=function(){j.fakeStatus=0}),e.onprogress=function(){},e.timeout=0;try{e.send(g)}catch(k){log("sendRequest caught send exception: "+k,LOG_SRC)}if(!c.callback){i=1e4+Date.now(),log("sendRequest - until: "+i+", finished: "+j.finished,LOG_SRC);while(Date.now()>>2]|=(c[e>>>2]>>>24-8*(e%4)&255)<<24-8*((d+e)%4);else if(65535>>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;d>>2]>>>24-8*(d%4)&255;c.push((e>>>4).toString(16)),c.push((e&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;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;d>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<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((h|0)-this._minBufferSize,0),b=h*g,e=a.min(4*b,e);if(b){for(var i=0;ik;k++){if(16>k)d[k]=a[b+k]|0;else{var l=d[k-3]^d[k-8]^d[k-14]^d[k-16];d[k]=l<<1|l>>>31}l=(e<<5|e>>>27)+j+d[k],l=20>k?l+((f&g|~f&i)+1518500249):40>k?l+((f^g^i)+1859775393):60>k?l+((f&g|f&i|g&i)-1894007588):l+((f^g^i)-899497514),j=i,i=g,g=f<<30|f>>>2,f=e,e=l}c[0]=c[0]+e|0,c[1]=c[1]+f|0,c[2]=c[2]+g|0,c[3]=c[3]+i|0,c[4]=c[4]+j|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,e=d.Base64={stringify:function(a){var b=a.words,c=a.sigBytes,d=this._map;a.clamp();var e=[];for(var f=0;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;for(var k=0;k<4&&f+k*.75>>6*(3-k)&63))}var l=d.charAt(64);if(l)while(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);f!=-1&&(b=f)}var g=[],h=0;for(var i=0;i>>6-i%4*2;g[h>>>2]|=(j|k)<<24-h%4*8,h++}return c.create(g,h)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}(); diff --git a/build/tincan-node.js b/build/tincan-node.js index 2566255..af6bd5b 100644 --- a/build/tincan-node.js +++ b/build/tincan-node.js @@ -1715,13 +1715,41 @@ TinCan client library */ saveStatement: function (stmt, cfg) { this.log("saveStatement"); - var requestCfg; + var requestCfg, + versionedStatement; cfg = cfg || {}; + try { + versionedStatement = stmt.asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + requestCfg = { url: "statements", - data: JSON.stringify(stmt.asVersion( this.version )), + data: JSON.stringify(versionedStatement), headers: { "Content-Type": "application/json" } @@ -1856,6 +1884,7 @@ TinCan client library saveStatements: function (stmts, cfg) { this.log("saveStatements"); var requestCfg, + versionedStatement, versionedStatements = [], i ; @@ -1864,15 +1893,43 @@ TinCan client library if (stmts.length === 0) { if (typeof cfg.callback !== "undefined") { - cfg.callback.apply(this, ["no statements"]); + cfg.callback(new Error("no statements"), null); + return; } - return; + return { + err: new Error("no statements"), + xhr: null + }; } for (i = 0; i < stmts.length; i += 1) { - versionedStatements.push( - stmts[i].asVersion( this.version ) - ); + try { + versionedStatement = stmts[i].asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + versionedStatements.push(versionedStatement); } requestCfg = { @@ -4687,7 +4744,7 @@ TinCan client library if (version === "0.9" || version === "0.95") { if (this[optionalObjProps[i]].length > 1) { // TODO: exception? - this.log("[WARNING] version does not support multiple values in: " + optionalObjProps[i]); + this.log("[warning] version does not support multiple values in: " + optionalObjProps[i]); } result[optionalObjProps[i]] = this[optionalObjProps[i]][0].asVersion(version); @@ -4704,7 +4761,8 @@ TinCan client library } if (this.category !== null && this.category.length > 0) { if (version === "0.9" || version === "0.95") { - this.log("[WARNING] version does not support the 'category' property"); + this.log("[error] version does not support the 'category' property: " + version); + throw new Error(version + " does not support the 'category' property"); } else { result.category = []; diff --git a/build/tincan.js b/build/tincan.js index 24d0240..d0974e0 100644 --- a/build/tincan.js +++ b/build/tincan.js @@ -1715,13 +1715,41 @@ TinCan client library */ saveStatement: function (stmt, cfg) { this.log("saveStatement"); - var requestCfg; + var requestCfg, + versionedStatement; cfg = cfg || {}; + try { + versionedStatement = stmt.asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + requestCfg = { url: "statements", - data: JSON.stringify(stmt.asVersion( this.version )), + data: JSON.stringify(versionedStatement), headers: { "Content-Type": "application/json" } @@ -1856,6 +1884,7 @@ TinCan client library saveStatements: function (stmts, cfg) { this.log("saveStatements"); var requestCfg, + versionedStatement, versionedStatements = [], i ; @@ -1864,15 +1893,43 @@ TinCan client library if (stmts.length === 0) { if (typeof cfg.callback !== "undefined") { - cfg.callback.apply(this, ["no statements"]); + cfg.callback(new Error("no statements"), null); + return; } - return; + return { + err: new Error("no statements"), + xhr: null + }; } for (i = 0; i < stmts.length; i += 1) { - versionedStatements.push( - stmts[i].asVersion( this.version ) - ); + try { + versionedStatement = stmts[i].asVersion( this.version ); + } + catch (ex) { + if (this.allowFail) { + this.log("[warning] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(null, null); + return; + } + return { + err: null, + xhr: null + }; + } + + this.log("[error] statement could not be serialized in version (" + this.version + "): " + ex); + if (typeof cfg.callback !== "undefined") { + cfg.callback(ex, null); + return; + } + return { + err: ex, + xhr: null + }; + } + versionedStatements.push(versionedStatement); } requestCfg = { @@ -4687,7 +4744,7 @@ TinCan client library if (version === "0.9" || version === "0.95") { if (this[optionalObjProps[i]].length > 1) { // TODO: exception? - this.log("[WARNING] version does not support multiple values in: " + optionalObjProps[i]); + this.log("[warning] version does not support multiple values in: " + optionalObjProps[i]); } result[optionalObjProps[i]] = this[optionalObjProps[i]][0].asVersion(version); @@ -4704,7 +4761,8 @@ TinCan client library } if (this.category !== null && this.category.length > 0) { if (version === "0.9" || version === "0.95") { - this.log("[WARNING] version does not support the 'category' property"); + this.log("[error] version does not support the 'category' property: " + version); + throw new Error(version + " does not support the 'category' property"); } else { result.category = [];