(I'm new to SCORM and Web Development, pardon me if I do not explain something too well.)
I'm trying to run some SCORM courses and have been following this tutorial to do so: http://www.vsscorm.net/2009/05/31/getting-started-the-rte-frameset/
However, in this tutorial they use a frameset and frames to establish this connection from the course to the API implementation. I need to run my course in an iframe, and do not know where/how to place my API document so my SCORM course can find it and connect to it, does anybody know how?
In a typical SCORM course, the API connection is maintained in the parent frame while the course content is loaded into a child frame (iframe). The content in the iframe can be loaded and unloaded at will; the content in the iframe will tend to contain the important SCORM calls you'd want to make through the lifespan of a course, such as score and completion status, but they will do so by relaying the info to the parent frame, which owns the communication with the LMS.
Here's quick example using SCORM 1.2 (not tested in an LMS, barebones, would need to be fleshed out)
index.html (parent frame)
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Course Title</title>
<style>
/* Use CSS to make the iframe fill the parent frame,
giving impression no frames are being used */
body { padding: 0; margin: 0; overflow: hidden; }
iframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; }
</style>
</head>
<body>
<iframe src="" id="course-content" frameborder="0"></iframe>
<script>
//Place initialization routine here.
//Connect to SCORM API, run API.LMSInitialize()
var SCORM_API = window.API; //shortcut reference
function setScore(score){
SCORM_API.LMSSetValue("cmi.core.score.raw", score);
}
function setStatus(status){
SCORM_API.LMSSetValue("cmi.core.lesson_status", status);
}
function endCourse(){
SCORM_API.LMSCommit();//Save, just in case
SCORM_API.LMSFinish();//Close API connection
}
SCORM_API.LMSInitialize();
//Load child frame once SCORM_API is ready
document.getElementById("course-content").setAttribute("src", "content.html");
</script>
</body>
</html>
content.html (child frame)
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Course Content</title>
</head>
<body>
<p>This is the content of the course. Add scripts to make it do something.</p>
<script>
//Place course functionality here, such as setting a bookmark or score.
//'parent' is the parent frame.
//This is a very arbitrary example of what you can do when a course loads
parent.setScore("100");
parent.setStatus("completed");
parent.endCourse();
</script>
</body>
</html>
You'd typically want to use a SCORM wrapper to handle some of the heavy lifting, and you'd want to use an abstraction layer to improve code maintainability and centralize your SCORM commands in the parent frame. The endCourse() function is a very simple example of an abstraction layer. Instead of invoking the API directly in the child frame, you invoke a function.
Related
I have created a small imperative vanilla JavaScript script to block distracting news websites I feel an addiction-like behavior to:
// ==UserScript==
// #name blocksite
// #match *://*.news_site_1.com/*
// #match *://*.news_site_2.com/*
// ==/UserScript==
function blocksite () {
document.body.innerHTML =`<div dir="ltr"; style="font-size:100px; font-weight: bold; text-align: center">Blocked !</div>`;
}
setTimeout( blocksite(), 1000)
setTimeout( blocksite(), 5000) // Block possible later DOM mutations;
setTimeout( blocksite(), 10000) // Block possible later DOM mutations;
The script basically works (a popup takes over the DOM), but my problem is that it only blocks sites after all their DOM content was both parsed and rendered, while I am interested to block parsing generally.
While listening to load event is too late, listening to the earlier DOMContentLoaded event can have a better result than either listening to load or listening to setTimeout(), as blocking could occur right after content is parsed, instead of rendered.
Yet, I need a way to totally prevent the parsing of any webpage of the relevant websites (or alternatively, blocking any further parsing after the very first DOM HTML element node was parsed).
What I have tried
Per comments, I tried in Google Chrome:
window.stop(); I don't recall any significant change
window.close(); It worked for me only from devtool console
window.location.replace("about:blank"); It worked for me only after load event finished instead when parsing starts
My question
Does the operation I need even possible with the latest ECMAScript (10) and if so, what command should be used?
Update for Sxribe:
Dear Sxribe, I have created the following file with the following code.
The file does get loaded by Tampermonkey (With a proper #match list) but I saw no change in browser when loading matched websites (these websites aren't blocked and get loaded normally).
Code in file
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
</body>
</html>
File call in Tampermonkey
window.open("C:\Users\MY_USER_NAME\Desktop\blocksite.html");
To abort loading of a website, you can simply use the window.stop() method.
When called, when the current script finish running, the parsing of the website stops completely.
For example:
<p>Before scripts</p>
<script>
document.write('<p>Before stop</p>')
console.log('Before stop')
window.stop()
document.write('<p>After stop</p>')
console.log('After stop')
</script>
<p>Between scripts</p>
<script>
console.log('Second script')
document.write('<p>Second script</p>')
</script>
<p>After scripts</p>
The above HTML displays:
Before scripts
Before stop
while you can see the following in the console:
Before stop
After stop
That shows that the <script> calling .stop() is fully evaluated, but changes made to DOM after .stop() aren't displayed.
Alternatively, to erase the full DOM content, it might be better to redirect the browser, that solution works even after page load:
window.open('about:blank','_self')
Old, wrong answer here: https://hastebin.com/vujuduforu.txt
Alright, so, chrome/firefox do not like having local files being opened with window.close(). In the end, I ended up simply hosting the website on glitch.com (free 100%), then redirecting to it. Here is my code for everything:
Tampermonkey Script:
// ==UserScript==
// #name blocktest
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author You
// #match *://*.bing.com/*
// ==/UserScript==
(function() {
'use strict';
window.open("https://block-sxribe.glitch.me/", "_self");
})();
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>This site has been blocked.</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="flex-container">
<div class="content">
<h1>This site has been blocked.</h1>
<p id="sep"></p>
<p>This site was blocked with Tampermonkey.</p>
</div>
</div>
</body>
</html>
CSS:
#import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
* {
margin: 0;
padding: 0;
font-family: 'Open Sans', sans-serif;
text-transform: uppercase;
}
.flex-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
#sep {
margin-left: 20vw;
width: 60vw;
border-bottom: 1px solid lightgray;
}
tl;dr Host website online, run window.open("website location.com", "_self") to open the website in current window.
If you are in any way in control of the website server, you could proxy the external scripts, or even internal scripts so that when you don't want then to be parsed, you just add return; at the top of each proxied script.
By proxying, I mean something like:
You have a script, http://example.com/script.js
You create a path within the application that loads http://example.com/script.js from the server and returns the result.
If you don't want http://example.com/script.js to be parsed, either return an empty script or return the contents of http://example.com/script.js but after adding return; as the top of the file.
The path/route to proxy could be something like:
const script = encodeUriComponent('http://example.com/script.js')
http://myserver.com/proxy?url=script&allowed=false
Chrome v75 appears to have introduced a bug whereby if you replace an iFrame's src programatically, it will replace the entire page instead of the iFrame.
This didn't happen on v74 and I can't get a test case to work (yet), it just fails in our site. (The site hasn't changed since going from v74 to v75, only Chrome has changed)
It appears to work fine the first time but then when you change it again (in our case viewing report drill downs) it causes the entire page (i.e. the iFrame's Parent) to load the src you were trying to load into the iFrame.
It also doesn't matter if you use pure Javascript or (in our case) JQuery, both cause the same issue.
EDIT: After a few hours detective work, I've found the bug. Setting the tag in the iFrame's content causes Chrome to load the iFrame's content into it's parent rather than the iFrame itself.
I've setup a Plunker account with a demo: https://plnkr.co/edit/UQ0gBY?plnkr=legacy&p=info
Just so I can post the link to Plunker, here is the code for the main file & the iframe content
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function onLoaded() {
// find element
let button = document.getElementById("button");
button.addEventListener("click",function(e){
// Add a random number on the end as a cache buster
document.getElementById('frame-finance-custom').src = 'test2.html?rnd=' + Math.random();
},false);
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
</head>
<body>
<div>IFrame Src Changing Test</div>
<div>
<div id="div-frame-finance-custom" style="float:left;width:33%">
<iframe id="frame-finance-custom" name="frame-finance-custom" class="iframe"
style="border:1px solid black; width: 100%; height: 350px; overflow-y: scroll; vertical-align: top;">
no data
</iframe>
</div>
<div style="float:left;margin-left:1em;">
Detail: Loading an iframe page with a <Base> tag in it with target set to "_parent" will cause any refresh of that frame to replace the parent document<BR>
<BR>Instruction: <UL><LI>Click the 'Update Frame' Button, this will load test2.html into the frame. <LI>Click it again & it will replace the iframe's parent with the content of the iFrame.</UL>
<BR>Confirmation: Remove the <Base> tag from the header of test2.html & reload, it will work as expected.
</div>
</div>
<br clear=both>
<div>
<button id="button">
Update Frame
</button>
</div>
</body>
</html>
IFrame Content (test2.html):
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_parent"/>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>This is the frame content</div>
</body>
</html>
Note, using their new layout it doesn't work, but using their legacy layout it does. Feel free to save the files locally and use chrome directly too.
Ok, so this turned out to be a bug in Chrome rather than anything else, so yes, strictly not a SO question, but seeing as SO ranks so well in Google (other search engines are available), I thought it better to leave it here as a solution rather than simply delete it, just incase anyone else has a similar problem.
The reason is outlined as an edit in my question, the solution is to remove the <base target="_parent"> tag from the iFrame and programatically add the 'target="_parent"' attribute to any links in the iFrame.
We do this via jQuery, I'm sure its just as easy via vanilla Javascript.
$('a').attr('target','_parent');
Add that to the javascript that runs when a page has loaded and it'll replace add target="_parent" to any links on the page.
e.g.
<script>
function onLoaded() {
// find all links and add the target attribute
$('a').attr('target','_parent');
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
As #Kaiido says in his comment, its apparently fixed in Chrome v77, but this isn't the current (as of June 2019) stable release, so we've had to add the workaround into production so that our CRM works with Chrome v75. Thanks to #Kaiido for confirming that.
Is there a good article or how can have an iframe or frame work asynchronously with every page? I have a bottom/fixed div wrapped in jquery to slide up on hover containing an mp3 player. I referenced the player with an iframe. I renderes fine, but how can it keep playing without a reload on page refresh or navigation to another page? I want it to be fixed at the bottom of every page and play continuously without refresh. I tried putting the iframe in each page, but that still didn't work. Any ideas? Thank you.
If it must stay in the browser ( not downloading an application or read stream in a music/video player ), the only way should be to don't really change page, and load content that must change with ajax or javascript ( or have it itself in a (i)frame ).
But it would be a lot easier to do a page with only the lector and put a link on your website to open it in another tab :
Text or what you want
Edit :
So with javascript it would be the same result than ajax, but that means not changing page so for the SEO if it's somewhat important it's not good.
What I meant by javascript was for example if you click on link "home" just adding dynamically a <script type="text/javascript" src="/homepage.js"></script> wich modify content of the page ( while keeping the mp3 player ).
Otherway, maybe with a cookie if it's possible with the player to know by javascript :
at know to wich mp3 file the player is
at wich time in the mp3 playing the player is
to go at a specified mp3 file
to go at a specified time in an mp3
(and if it is possible to pause the player, there should to be the ability to know if the player is playing or not )
It would be possible when changing page to get at the good time ( but there will be the time to load the page and mp3 player without music ).
Or there could be mp3 player which can save a the time at wich we are, and begin at this time on another page ( but still while changing page no sound for several seconds ).
With these methods there would be too the issue of opening several pages.
Edit :
I tried the way with the content of the page in iframe, it works rather well but needs the membre to switch in the mp3 mode.
Here is mp3.html ( to put in root folder, if it's not possible it would need some changes ) :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>MP3 Player</title>
<style type="text/css">
html {
font-size: 100%;
}
body {
margin: 0;
padding: 0em;
}
#frame { width: 100%; height: 100%; border: none; }
#player { position: absolute; right: 20px; bottom: 20px; }
</style>
<script type="text/javascript">
if ("onhashchange" in window) {
var h='';
var command= false;
window.onhashchange = function(){
if(location.hash==h) return;
command= true;
window.frames['frame'].location.replace(location.hash.replace(/^#/,""));
h= window.location.hash;
}
}
</script>
</head>
<body>
<iframe id="frame" onLoad="if(this.src=='')return;if(command)command=!1;else window.location.replace(h='#'+window.frames['frame'].location.href.replace(new RegExp('^'+document.location.origin),''));document.title=window.frames['frame'].document.title;"></iframe>
<script type="text/javascript">
document.getElementById("frame").src=document.location.hash.replace(/^#/,"");
</script>
<div id="player">
<object type="application/x-shockwave-flash" data="http://s301826463.onlinehome.fr/so/dewplayer.swf?mp3=http://s301826463.onlinehome.fr/so/Tokyo.mp3" width="200" height="20" id="dewplayer"><param name="wmode" value="transparent"><param name="movie" value="http://s301826463.onlinehome.fr/so/dewplayer.swf?mp3=http://s301826463.onlinehome.fr/so/Tokyo.mp3"></object>
remove mp3 player
</div>
</body>
</html>
And to put a link that open the current page in an iframe and with an mp3 player, it only needs a link :
add mp3 player
An example using that here.
Either you have an independent Flash/Java/Unity etc player outside the browser window.
Or, You use frames, two frames, one where the main site pages appear, and one where the player resides.
Other way is making the entire navigation in your site (where you want the player to be continuous) using async calls (Ajax).
Google b.t.w uses iframes/frames
When a page loads on my site, the HTML appears before the javascript, which leads to a flicker when the javascript loads. The answer to this stackoverflow post gave a great solution. But I would like to load at least some of the HTML before the Javascript so that the user is not faced with a blank page during a slow connection. For example, I would like to load the header immediately, but wait to load the HTML for the javascript enhanced accordion until after the javascript loads. Any suggestions?
Here's the code that I borrowed from the answer linked above:
CSS:
#hideAll
{
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
background-color: white;
z-index: 99; /* Higher than anything else in the document */
}
HTML:
<div style="display: none" id="hideAll"> </div>
Javascript
window.onload = function()
{ document.getElementById("hideAll").style.display = "none"; }
<script type="text/javascript">
document.getElementById("hideAll").style.display = "block";
</script>
I'd suggest that you define the base/JavaScript-enabled styles of elements you want to display with CSS in the regular style block:
<style type="text/css">
#javaScriptAccordion {
display: none;
}
</style>
And then use the noscript tags (in the head) to amend this in the absence of JavaScript:
<noscript>
<style type="text/css>
#javaScriptAccordion {
display: block;
}
</style>
</noscript>
This ensures that the content is hidden on document load, preventing the flash, but visible to those users that have JavaScript disabled.
The above has been amended to prevent the 'flash of no content' (as described by #Josh3736 in his answer), and now uses opacity to hide the content:
<style type="text/css">
#elementToShowWithJavaScript {
opacity: 0.001;
width: 50%;
margin: 0 auto;
padding: 0.5em;
border-radius: 1em 0;
border: 5px solid #ccc;
}
</style>
<noscript>
<style type="text/css">
#elementToShowWithJavaScript {
opacity: 1;
}
</style>
</noscript>
Live demo.
I'm not, unfortunately, entirely sure that I understand your question. Which leaves me proposing a solution for the question I think you asked (all I can offer, in excuse, is that it's early in the UK. And I'm not awake by choice...sigh); if there is anything further that I'm missing (or I'm answering the wrong question entirely) please leave a comment, and I'll try to be more useful.
The hack in the linked question is—in my opinion—very poor advice. In this case, it is a better idea to include some script directly following your accordion elements.
<div id="accordion">...</div>
<script type="text/javascript">...</script>
However, inline script intermingled with your HTML markup is a Bad Idea and should be avoided as much as possible. For that reason, it is ideal to include inline only a function call to a function declared in your external script file. (When you reference an external script (<script src="...">), the rendering of your page will pause until it has loaded.)
<html>
<head>
<script type="text/javascript" src="script.js"></script> <!-- renderAccordion() defined in this file -->
</head>
<body>
...
<div id="accordion">...</div>
<script type="text/javascript">renderAccordion();</script>
...
</body>
</html>
Of course, the correct way to do this is to just attach to the DOM ready event from script.js and not use any inline script at all. This does, however, open up the possibility of a content flash on extremely slow connections and/or very large documents where downloading all of the HTML itself takes several seconds. It is, however, a much cleaner approach – your script is guaranteed to be loaded before anything is rendered; the only question is how long it takes for DOM ready. Using jQuery, in script.js:
$(document).ready(function() {
// Do whatever with your accordion here -- this is guaranteed to execute
// after the DOM is completely loaded, so the fact that this script is
// referenced from your document's <head> does not matter.
});
Clever use of <style> and <noscript> does a a good job of guaranteeing that there is no flash of all the content in your accordion; however, with that method there will be the opposite effect – there will be a flash of no content.
As the page loads, your accordion will be completely hidden (display:none;), then once your script finally executes and sets display back to block, the accordion will suddenly materialize and push down everything below it. This may or may not be acceptable – there won't be as much movement, but things will still have to jump after they've initially rendered.
At any rate, don't wait until onload to render your accordion. onload doesn't fire until everything—including all images— have fully loaded. There's no reason to wait for images to load; you want to render your accordion as soon as possible.
I have a simple HTML page that rotates through several status pages that I display on several tv's around campus. I regularly update the page and the links. Many times the pages require authentication. It is a pain to remote to ever terminal to supply credentials. Some are HTTP authentication and some are some <form> based authentication baked into the site. Many times I can get around the <form> based authentication with HTML and JavaScript that post the right credentials.
Is there a better way to get around
the <form> based authentication
from the host page? (below)
Is there any way to get around the
Server/HTTP based authentication
from the host page without having to
manually authenticate on ever
display?
By <form> authentication I mean that a <form> action generates a session cookie?
( mikerobi, thanks for the comment)
Here is the code for the host page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>
Important Stuff
</title>
<script src="/scripts/jquery.js" type="text/javascript"></script>
<style type="text/css">
html, body, iframe { margin:0; height:100%; }
iframe { display:block; width:100%; border:none; }
</style>
<script type="text/javascript">
var link = new Array();
link[0] = "http://mycompany.intranet/";
link[1] = "http://mycompany.intranet/weather.htm";
link[2] = "http://mycompany.intranet/systemstatus/";
var linkIndex = 0;
setInterval("doSomething()", 10000);
function doSomething() {
if (linkIndex >= link.length)
{
// reload in case the page has been updated
window.location.reload();
}
$("#frame").attr("src", link[linkIndex]);
linkIndex++;
}
</script>
</head>
<body>
<iframe id="frame" src="http://mycompany.intranet/"></iframe>
</body>
</html>
I don't see your code that sends the credentials for the POST-based login, but if you are using JavaScript to automatically submit a form (using its .submit() method), that is probably the best way. Keep in mind that the target attribute of an HTML form allows you to submit the form in a different window (or in your case, iframe) — just give a name="xyz" attribute to the iframe and use target="xyz" for the form. The form would be located in the host page and could be hidden using the CSS display: none.
You can include the HTTP Basic Auth username and password in the URL, like: http://username:password#www.example.com/path. Note that current web browsers may not allow this in their default configurations as a safeguard against a specific phishing technique, and you may have to change the configuration by editing the Windows Registry or other places where web browser settings are stored.