Javascript in publish templates or interaction-free execution

Dec 17, 2020

I have to access several external HTML5/JS services that consume content created by a story, and am looking for a way to bring access to my API without having to wait for a user-driven JavaScript trigger. As it stands, I have to test for the presence of my API object in each-and-every JS trigger, and create it if it isn't yet present. Normally, this would be solved by having a script tag on the HTML page housing the logic and executing the initialization logic once, but I cannot find where the template or "place" to do this would be.

So, a positive answer to any of these questions would be great:

1) Where is the template for the story.html file?

2) Where does one put JS for execution without user interaction? <-- this would solve it just fine

3) Is there some other mechanism that isn't a hack to get JS to run without user interaction? Articulate themselves suggest hacking story.html which, from my perspective, is ridiculous since it gets overwritten each time.

Thanks for any answers or insights!

5 Replies
Joseph Diefenbach

OK I found the templates. The one I'm using is

Articulate/360/Storyline/player/unified/index.html --publish--> ./story.html

This works, but is not ideal since it does it for all projects. I'm still looking for a way to just run some JS without the user having to do an interaction. Please advise!

Math Notermans

The way i trigger Javascripts i need once initialized in a Storyline project is...

- On the first slide i have a trigger 'Execute Javascript' When the Timeline starts.

That first trigger automatically starts and loads all needed external files ( CSS and Javascript ) i am using in my project. When all is loaded is change a Storyline variable... 'javascriptsLoaded' to True, to let Storyline now all my external JS files are loaded.

-On the first slide i have also a Variable Trigger. 'Execute Javascript' When 'javascriptsLoaded' changes.

So this Storyline trigger gets called once. Only when the variables changes. And then does some stuff where i need the external libraries for.

Basically this setup i have in most of my projects.

Joseph Diefenbach

Thank you Math, I did not realize you could create triggers on anything but user interactions. Sure enough, there it is "When the timeline start on" "this slide". Thanks for the guidance!

In the meantime I found a non-invasive way of doing it (non-invasive to the project content, but is a change in overall process), by adding a script tag for a default_include.js file to the template, right before user.js would be included, and then adding a project-specific default_include.js to the player. This allows all initialization code to be worked on outside the walls of SL, and leaves the API as a "read only" object for others (fewer moving parts exposed to non-coders)

Math Notermans

Sounds good Joseph, and indeed less things non-coders have access to, the better it is...thats Storyline's basic approach too..

What i miss though is how you add your external js-files...in the player you say ? As resources on a separate scene ? Or are you changing html and including those files there ? Then your back to the way Articulate suggests and thus need to redo that after any publish ?

I use Webobjects for  my external javascripts and include them during publish.
This is the code i have on timeline start...that loads external js files from a given folder in my Webobject...

var loadedCount = 0;
var player = GetPlayer();

function loadJSfile(filename, filetype){
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
fileref.onload = function() {
loadedCount++;
console.log(loadedCount+" / "+filename+' loaded.');
if(loadedCount >= 6){
player.SetVar("javascriptsLoaded", true);
}
};
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
}

/*
When we change anything in the scriptFolder this needs to change

*/
var scriptFolder ="story_content/WebObjects/6ga0RToD9D1/";

/*
And lastly we call the functions we want to run
*/

loadJSfile(scriptFolder+"JQUERY/jquery-1.10.2.min.js", "js");
loadJSfile(scriptFolder+"GSAP/TextPlugin.min.js", "js");
loadJSfile(scriptFolder+"GSAP/Draggable.min.js", "js");
loadJSfile(scriptFolder+"GSAP/MotionPathPlugin.min.js", "js");
loadJSfile(scriptFolder+"GSAP/InertiaPlugin.min.js", "js");
loadJSfile(scriptFolder+"SPECIFIC/generic_functions.js", "js");

 

Joseph Diefenbach

Right, in the immediate, I'm doing just that, appending them to the document, though I'm protecting my "loadJSfile" function in an IIFE because instead of a file countdown I do an array knock-out.

I will try my other more systemic method in the next project. I'm basically creating a secondary "user.js", but you have full control over its content. It gives visibility to the code in a way everyone can understand (Resources in the Player), anyone else can deploy, but no one can mess up.

It means creating a generic script anchor in the template html, and then adding a project-specific implementation of that script to the Resources in the Player. The script anchor always points to the same place, it's up to the project to use a real implementation (if any) by including it in the Player's Resources.

BTW I've learned a lot about SL since my original post, I've been working with it for a day now.