@CindyFenske
I don't use SCORM, so I cannot test this. I'll just speak about things that cause me concern. They may be unfounded since I cannot test.
The first thing that struck my attention is your getWindow() function.
You're trying to find the top window. SCORM or other tools are probably loaded in an iframe and the parent window is the window embedding the iframe. It exists, but it is never the same as the window itself. That is, both win.parent exists (is truthy) and win.parent !== win because you cannot break out of the sandbox.
That means that it would have to find win.GetPlayer to get out of the loop, but that isn't enough. I don't know what the GetPlayer() function does, but if it returns a false value such as 0 or null, then your loop keeps going and it will never exit because you'll never get win.parent === win.
If the GetPlayer() was in the initial window, it would never trigger because you reset win before checking.
The next thing that strikes me as weird is that you call GetPlayer() when it may not exist. The whole idea of GetAPI() is to return the GetPlayer object, which you assign to API. That may not be on the current window, but when you invoke it in the third line of code from the bottom, you don't use API, you just use GetPlayer(). There is no guarantee that GetPlayer() exists there and may not because you always force the check to bypass the initial window and look at a parent.
It seems that the bottom three lines of code should be inside the check to make sure that API exists. Actually, I would let player = getAPI(); and not mess with the API variable at all. Then you could check to see if player exists before invoking player.GetStudentId() to get the userId.
As is, if GetPlayer is in the current window, then API will always be null and so the userId will be "unknown".