Code Monkey home page Code Monkey logo

tincanjs's Introduction

A JavaScript library for implementing the Experience API (Tin Can API).

Build Status GitHub release npm license

For hosted API documentation, basic usage instructions, supported version listing, etc. visit the main project website at:

http://rusticisoftware.github.io/TinCanJS/

For more information about the Experience API visit:

http://experienceapi.com/

Browser Usage

TinCanJS is available via npm and Bower.

The browser environment is well tested and supports two kinds of Cross Origin requests which is sufficient to cover most versions of Chrome, FireFox, Safari as well as IE 8+. IE 6+ are supported for non-CORS (because they don't support it).

Include one of build/tincan-min.js or build/tincan.js as follows:

<script src="build/tincan-min.js"></script>

Node.js Usage

TinCanJS is available via npm.

The Environment/Node.js wrapper used in this version has a dependency on the 'xhr2' module which is also available via npm. It is used to allow the interfaces to the underlying LRS requests to have the same API. As such currently there is no support for synchronous requests when using this environment.

Install via:

npm install tincanjs

And within code:

var TinCan = require('tincanjs');

Environments

Implementing a new Environment should be straightforward and requires overloading a couple of methods in the library. There are currently two examples, Environment/Browser and Environment/Node.

Attachment Support

Sending and retrieving statements with attachments via the multipart/mixed request/response cycle works end to end with binary attachments in Node.js 4+ and in the typical modern browsers: Chrome 53+, Firefox 48+, Safari 9+, IE 10+ (current versions at time of implementation, older versions may work without changes but have not been tested). Attachments without included content (those using only the fileUrl property) should be supported in all environments supported by the library.

Several polyfills (TypedArrays, ArrayBuffer w/ slice, Blob, TextDecoder/TextEncoder) are needed to support various browser versions, if you are targeting a recent enough set of browsers you can reduce the overall size of the built library by commenting out those polyfills in the Gruntfile.js file and building yourself.

tincanjs's People

Contributors

brianjmiller avatar brianrogers avatar bscscorm avatar davidells avatar ervinmagic avatar fishhooks avatar garemoko avatar grahamgoudeau avatar limikael avatar rachelarmstrong avatar usernolan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tincanjs's Issues

moreInfo

We need to implement the moreInfo property for Activity Definition.

asVersion

I'm working on a project with metro style (html5 javascript css) on Microsoft Visual Studio.
When it comes to this part
result = {
actor: this.actor.asVersion(version),
verb: this.verb.asVersion(version),
object: this.target.asVersion(version)
};
I have this error: "Object doesn't support property or method 'asVersion'" and the actor is undefined.

Thanks for the help!

Cancelled XHR requests dont give a callback

Hi,

I'm just trying to implement the TinCan-Api in an WBT of mine.

But somehow I have some problems with the sendStatement method.

I want to Queue all Statements, that couldnt be send (like demanded in the xApi-Docs)

I'm thinking of the case, that someone starts my course and then goes offline for a short period of time while going through the course (mobile app).

In that case, there is no connection to the server and the requests are cancelled. Then I want to Queue them in LocalStorage and send them with sendStatements() when the next Request is working.

I'm Calling the method with callback because I want to do it async:

tinCan.sendStatement(stmt, function(results){
//check xhr-Results if everything went fine
//when there is an error, queue the Statement
////if everything worked,
////check if there is a queue and send it with sendStatements()
});

But somehow, there is an exception for the case of "no-connection" in the sendStatement method:

(from the code)
// Alert all errors except cancelled XHR requests
if (httpStatus > 0) {
requestCompleteResult = {
err: httpStatus,
xhr: xhr
}
if (self.alertOnRequestFailure) {
alert("[warning] There was a problem communicating with the Learning Record Store. (" + httpStatus + " | " + xhr.responseText+ ")");
}
if (cfg.callback) {
cfg.callback(httpStatus, xhr);
}
}

So if I use the callback function, to do something AFTER the request (and asynchronous, because I don't want to block everything while sending the Statement), I get NO CALLBACK if the browser is offline...

If the LRS isn't configured right (400-error), I get the callback and the failed-xhr-response. Everything OK with that.

But if the browser is offline, the callback isn't called (because of httpStatus = 0), and I can't do anything because my function isn't called.

So, is this a bug, or is it intentional, that there isn't a callback on cancelled/aborted Requests?
I think it would be good, to give back the xhr-response in ANY case.

Can someone help me with this, if I'm doing something wrong, or do I just don't understand your intention in not fireing the callback?

THX,
BG

Implement Document API Merge

See https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#json-procedure-with-requirements

The spec allows you to POST json, and if you do so, the existing contents are merged with whatever is posted (or conflicting fields are overwritten). Since many use cases will utilitize JSON for the document value, this is another way to avoid the heavy lifting of get / set with the ETag. Plus, there are probably some less likely use cases that actually want to utilize the merging semantics themselves.

TinCanJS statements work in Chrome but not IE9

Is there something extra I have to do to make the library work in IE? When I upload the client side files to a GitHub static page, all the requests seem to fail with 400~ errors.

I tried debugging this piece of code by running it in the browser:

    var tinCan = tinCanConfig();

                    var query = tinCan.getStatements({
                      params: {
                        verb: {
                          id: 'http://adlnet.gov/expapi/verbs/watched'
                        },
                        activity: {
                          id: 'http://example.mp4'
                        },
                        related_activities: false,
                        related_agents: false
                      }
                    });          

console.log('query: ', JSON.stringify(query, null, 2) );

It works fine in Chrome, but IE gives a 400 error in the console:

query: {
  "err": 400,
  "xhr": {},
  "config": {
    "url": "statements",
    "method": "GET",
    "params": {
      "verb": "http://adlnet.gov/expapi/verbs/watched",
      "activity": "http://example.mp4",
      "related_activities": false,
      "related_agents": false
    }
  },
  "statementsResult": null
} 

The response headers from IE read as - HTTP/1.1 401 Unauthorized, though I see my auth string in the request body.

IE Mode not used often enough

IE even up until recent versions, 10 (maybe 11), still has the URL length limit of 2048 (or thereabouts) and so we need to trigger IE mode POST requests more aggressively even in modern IE.

default to async?

TinCanJS supports synchronous calls and the basic usage example in the documentation is synchronous.

Synchronous XMLHttpRequests are now deprecated in Chrome and Firefox.

We should update the documentation example to only show asynchronous examples and perhaps consider deprecating synchronous calls in TinCanJS in favour of making all calls asynchronous by default. This could be a breaking change for some implementers, but they will break in some future version of Chrome and Firefox anyway.

Statement context statementRef causing statement not to be sent

Hello,

I'm trying to send an example statement to a LRS using TinCanJS and I'm experiencing an issue with the statementRef of a context of a statement.

I'm trying to send the long example statement which I found in the spec (https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#appendix-a-example-statements)

{
    "id": "6690e6c9-3ef0-4ed3-8b37-7f3964730bee",
    "actor": {
        "name": "Team PB",
        "mbox": "mailto:[email protected]",
        "member": [
            {
                "name": "Andrew Downes",
                "account": {
                    "homePage": "http://www.example.com",
                    "name": "13936749"
                },
                "objectType": "Agent"
            },
            {
                "name": "Toby Nichols",
                "openid": "http://toby.openid.example.org/",
                "objectType": "Agent"
            },
            {
                "name": "Ena Hills",
                "mbox_sha1sum": "ebd31e95054c018b10727ccffd2ef2ec3a016ee9",
                "objectType": "Agent"
            }
        ],
        "objectType": "Group"
    },
    "verb": {
        "id": "http://adlnet.gov/expapi/verbs/attended",
        "display": {
            "en-GB": "attended",
            "en-US": "attended"
        }
    },
    "result": {
        "extensions": {
            "http://example.com/profiles/meetings/resultextensions/minuteslocation": "X:\\meetings\\minutes\\examplemeeting.one"
        },
        "success": true,
        "completion": true,
        "response": "We agreed on some example actions.",
        "duration": "PT1H0M0S"
    },
    "context": {
        "registration": "ec531277-b57b-4c15-8d91-d292c5b2b8f7",
        "contextActivities": {
            "parent": [
                {
                    "id": "http://www.example.com/meetings/series/267",
                    "objectType": "Activity"
                }
            ],
            "category": [
                {
                    "id": "http://www.example.com/meetings/categories/teammeeting",
                    "objectType": "Activity",
                    "definition": {
                        "name": {
                            "en": "team meeting"
                        },
                        "description": {
                            "en": "A category of meeting used for regular team meetings."
                        },
                        "type": "http://example.com/expapi/activities/meetingcategory"
                    }
                }
            ],
            "other": [
                {
                    "id": "http://www.example.com/meetings/occurances/34257",
                    "objectType": "Activity"
                },
                {
                    "id": "http://www.example.com/meetings/occurances/3425567",
                    "objectType": "Activity"
                }
            ]
        },
        "instructor" :
        {
            "name": "Andrew Downes",
            "account": {
                "homePage": "http://www.example.com",
                "name": "13936749"
            },
            "objectType": "Agent"
        },
        "team":
        {
            "name": "Team PB",
            "mbox": "mailto:[email protected]",
            "objectType": "Group"
        }, 
        "platform" : "Example virtual meeting software",
        "language" : "tlh",
        "statement" : {
            "objectType":"StatementRef",
            "id" :"6690e6c9-3ef0-4ed3-8b37-7f3964730bee"
        }

    },
    "timestamp": "2013-05-18T05:32:34.804Z",
    "stored": "2013-05-18T05:32:34.804Z",
    "authority": {
        "account": {
            "homePage": "http://cloud.scorm.com/",
            "name": "anonymous"
        },
        "objectType": "Agent"
    },
    "version": "1.0.0",
    "object": {
        "id": "http://www.example.com/meetings/occurances/34534",
        "definition": {
            "extensions": {
                "http://example.com/profiles/meetings/activitydefinitionextensions/room": {"name": "Kilby", "id" : "http://example.com/rooms/342"}
            },
            "name": {
                "en-GB": "example meeting",
                "en-US": "example meeting"
            },
            "description": {
                "en-GB": "An example meeting that happened on a specific occasion with certain people present.",
                "en-US": "An example meeting that happened on a specific occasion with certain people present."
            },
            "type": "http://adlnet.gov/expapi/activities/meeting",
            "moreInfo": "http://virtualmeeting.example.com/345256"
        },
        "objectType": "Activity"
    }
}

The statement is just not sent if the following is included:

        "statement" : {
            "objectType":"StatementRef",
            "id" :"6690e6c9-3ef0-4ed3-8b37-7f3964730bee"
        }

I am using the sendStatement method to send it, all other tests have been working so far. Looks like the TinCanJS has a problem with this. Any idea what's wrong ?

Thanks in advance,

TinCan interface shouldn't provide 'xhr' to final callback

The TinCan interface provides the xhr for the last LRS response in the final callback, but shouldn't. Optionally it'd be better to pass 'null' and include the results as a second arg but that would break backwards compat relatively badly.

getLangDictionaryValue should take a list

This method should probably take a list of languages in preferred order for either multi-lingual users or for languages that are close enough to be usable such as 'en-US' and 'en-UK'.

Document APIs allow 'save' method to take an object

To prevent the need for end users to mess with etags and SHA1s make the LRS save methods take the existing object, and then update that object with the new etag on success. For now this may require always computing it client side as with PUT and 204 no updated Etag is returned.

Node.js Issues

I've been trying to integrate tincanJS into a node project for sending statements, and running into a couple of problems.

  1. If you don't provide a username and password, you get a "TypeError: Cannot call method 'toString' of null", as TinCanJS sets authorization to null, and then xhr tries to call the toString method when setting headers.
  2. Any attempt to send statements without a callback gets a "Error: Synchronous XHR processing not implemented".
  3. Sending statements asynchronously works well enough, but does not conform to node JS standards for async functions - ie, the pattern callback(err, result) - and what it does return is not documented anywhere I could find.

cfg.agent.asVersion

Hi Guys,

The LRS.saveState and LRS.retreiveState functions make a call to the following -

cfg.agent.asVersion

When testing with the Golf example the value of cfg.agent == undefined. This causes both function calls to fail.

I have commented them out for testing. How can I populate cfg.agent with an object the supports the asVersion function?

Regards,
Tarek

withCredentials() on Cross-Origin-Calls

Hello,

we are working on a learning platform based upon TinCan. Most course suppliers are using the Rustici-Tincan-Library and we like it. The course storage is on a different domain than the LRS-Endpoint. However we would like our users to be authenticated at the LRS in order to check if users are allowed to send this specific Tincan-Statement.

In our case Authentication is done via Cookies. Now we have the problem, that the TinCan-Calls do not contain any cookies at all. As they are Cross-Origin-Calls (CORS-Headers are all set) they need to have 'withCredentials' set inside the XMLHttpRequest object, otherwise cookies won't be send. Interestingly TinCanJS looks for this specific attribute and sets env.hasCORS to true but this gets not evaluated...

Is it planned to call withCredentials() in the future?

Offline support

Is there a support for offline reporting using localStorage?

ContextActivities

ContextActivites.Parent and ContextActivities.Grouping are arrays in the JS objects. However in the .NET classes (https://github.com/RusticiSoftware/TinCanAPILibraryCSharp), they are objects. Hence when I create a Statement using the TinCanJS and try to deserialize it with the .NET class, it throws an error.

When I changed the .NET classes to have Parent and Grouping as Lists of Activities, I ran into another error. The default "type" for and Activitiy sent by TinCanJS is "course" whereas in the .NET class, it tries to parse it as an Uri and fails.

Is there a way to make the JS and the .NET classes consistent with each other?

LRS.saveState does not send application/json

Hi Guys,

I am trying to implement the State API using a ServiceStack service. I seem to be encountering an issue with the following item -

The golf example makes a PUT call to /activities/state by passing 0 for the current page.

The LRS saveState function seems to try and JSON.stringify objects. However, non objects get sent to the sendRequest function as is. Regardless of the data type the sendRequest function sets the content type to application/json event though the content in this case is an int with a value of 0.

This causes the servicestack PUT call to fail because it could not deserialize the body.

Is this a bug?
How can I handle this situation?

Regards,
Tarek

Implement spec 1.0 changes

Land changes that occur for spec 1.0.

Requirements:

  • Bump version list (#33)
  • Context Activities as arrays (#33)
  • deprecated 'voided' (#33)
  • Add Statement 'version' property (#33)
  • Query API (authoritative removal, mode expansion) (#33)
  • Fetch voided statements (#33)
  • Anonymous Groups
  • Document merge/partial updates (State, Activity Profile, Agent Profile)
  • Statement attachments
  • Statement signing
  • Fetch of URL related items
  • About request + auto detection of LRS version
  • Activity Definition "moreInfo" property

400 Fake status for XDR triggers error popup on missing state

The fake status used for XDR is 400 which prevents the ignore404 configuration value for document APIs, in particular State, from preventing the popup error message when it should. XDR doesn't provide a proper way to get the real returned status for .onerror .

additional querystring parameters are passed on when making calls

http://cdn.tincanapi.com/wp-content/assets/ClientPrototypes/GolfExample_TCAPI/index.html?endpoint=http%3A%2F%2Fcloud.scorm.com%2FScormEngineInterface%2FTCAPI%2Fpublic%2F&auth=Basic%20VGVzdFVzZXI6cGFzc3dvcmQ%3D&actor=%7B%22mbox%22%3A%5B%22mailto%3Atest%40beta.projecttincan.com%22%5D%2C%22name%22%3A%5B%22Test%20User%22%5D%7D

I've only been able to replicate this on the golf example (link above) but passing additional querystring parameters to that example causes those parameters to be passed on when PUTing statements or GETing state (other methods and endpoints not tested). This causes errors on the ADL and Wax LRS because the additional querystring parameters are not supported. SCORM Cloud seems to happily ignore the additional parameters.

You might want to use additional parameters for some reason for the activity that you don't want to pass to the LRS.

I'm not sure if this is an issue with the prototype or with TinCanJS. I couldn't replicate the issue with some of my own code that uses TinCanJS.

Make LRS auth configuration be friendly

Having to pre-base64 encode the username + password together for basic auth really stinks. We should be able to take an account + password combo and do the "hard" bits under the hood for the developer.

Utils.parseURL does not handle special case of '==' character padding at end of 'auth'

Hello,

Utils.parseURL is called by TinCan.initFromQueryString

Within the query string parameters, 'auth' property may end with a few '=' for bit padding, and these are necessary for 'auth' to be decoded.

But Utils.parseURL splits pairs on '=' character and does not handle this special case of '=' characters being part of a meaningful value.

I hope this makes sense :)

Cheers.

Melanie.

Fake status on IE request causing problems

In IE requests, when the XHR object is not populated, TinCanJS tries to pick a sensible error code to send through. the trouble is that for GETs which legitimately return 404 codes, TinCanJS assumes these should be 400. For application using TinCanJS will handle 400 and 404 very differently so this is a problem.

We've replaced the 400 with 404 in our copy of TinCanJS. Are there any problems with doing that and is there a better solution?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.