Adding a Print Button to the Player and other SL3 / 360 Player Hacks

Similar to my post about adding a logo to the player, you can add a print button directly to the player too!  Here is how:

  1. Add a T/F variable to control running the JavaScript only once. Set its initial value to True.
  2. Add your JavaScript trigger to run When timeline starts if your control variable is equal to true. (See JavaScript explanation below)
  3. Set your control variable equal to false When timeline starts and be sure it is in the trigger stack below your JavaScript trigger
  4. Be sure the Volume Control has been selected in the Player Properties Window. (You can hide this later if you want to, but for now you will need it to use as a location target.)

Here is the JavaScript:
//First we are going to create and add a Print Button to the webpage
//Create a new input element and set its attributes
var printButton = document.createElement('input');
printButton.setAttribute('type', 'button');
printButton.setAttribute('id', 'CustomPrint');
printButton.setAttribute('value', 'Print Page');
printButton.setAttribute('onClick', 'window.print()');

//Add your button to the page
document.body.insertBefore(printButton, document.body.firstChild);

//This next part uses a bit of JQuery to find the volume control in the StoryLine player and then move the button you just created to be right after it.
$(".volume").after($("#CustomPrint"));
//Format the button with the same style as other player buttons.
$("#CustomPrint").addClass('btn cs-button')

//If you want to hide the volume control, add the following line.
$(".volume").hide();

The StoryLine file I'm including demonstrates adding a Logo, adding a Print button, hiding various player elements, and showing elements that you've previously hidden.

Credits: Thanks to James Kingsley for the inspiration. His "Hacking the Player" webinar was very useful. You can view it here: Webinar Recording

36 Replies
Lisa Anderson

Hey James and Owen,

First, thank you for championing these Java and jQuery  fun things to work through! In addition to following much of your work, James, I also recently signed up for an account on reviewmyelearning.com.  Thank you!!!!!

I'm working on incorporating what both you and Owen have provided, to add a custom "script" button in the player controls (Owen's idea) and your idea to show the fancy toggle. I think I'm close, but I know I'm missing quite a bit. Here's my ideal goal:

On every screen, I'd like the user to be able to determine if/when they'd like to view the script/notes. Instead of using the CC button, or having the notes section always visible, (and since the toggle feature from Presenter has been removed in SL, I'm using 3) I'd like a button on the player to toggle the .area-secondary, as you refer it, to be visible, or not throughout the entire course, per slide. I've managed on slide 2 of my upload to make the custom script button appear and the volume button to hide. I've manged to get the mouse "click" image to appear when hovering over the new button, but not execute anything. The jQuery for the fancy toggle works when selecting the fancy button on the screen, but not on the custom script button. And, when the user clicks to the next screen, then selects previous screen, another script button appears. 

I think I'm close, but maybe so far away. Is what I'm trying to do do-able and am I heading down the right path? I'm neither a Java nor jQuery expert, quite the opposite, but with much research, and following forums with you, Owen and Mathew, I get my self in just enough trouble!

Any input you could or would be willing to provide is much appreciated. And, as I'm now using my reviewmyelearing.com with my first client...I'm grateful!

Cheers,

Lisa

onEnterFrame (James Kingsley)

Hi Lisa!

The main problem is with this line:

scriptButton.setAttribute('onClick', '.area-secondary.script()');

I am not 100% sure which code you want to fire when they click that button. But.. In JS you can't start a function/variable name with a dot. Normally what you would see there is something like:

scriptButton.setAttribute('onClick', 'myScript()')

And then somewhere else in the file you would have created a function called "myScript". 

But since our script is going to be wrapped in another function (that is just how SL works) we are scoped inside that function. So we have to define that function at the window level (just like you are with your variable window.mw 

You also have some unneeded brackets in that function {}. I believe that is code you want run when the button is clicked? Assuming that is the case I believe this will work for you:

 var scriptButton = document.createElement("input");
scriptButton.setAttribute("type", "button");
scriptButton.setAttribute("id", "CustomScript");
scriptButton.setAttribute("value", "Script");
scriptButton.setAttribute("onClick", "myScript()");
document.body.insertBefore(scriptButton, document.body.firstChild);

$(".volume").after($("#CustomScript"));
$("#CustomScript").addClass("btn cs-button");
$(".volume").hide();

window.myScript = function() {
if (!window.mw) window.mw = $(".area-secondary").width();
if ($(".area-secondary").width() > 0) {
$(".area-secondary").animate({
width: "-=" + mw,
o pacity: 0
});
} else {
$(".area-secondary").animate({
width: "+=" + mw,
opacity: 1
});
}
};

 

 

Lisa Anderson

Hi James!

I am grateful for your response. I've followed your logic, made the updates, and republished. Now, the button "script" has not been added, the volume button is not hidden, and no functionality occurs.

It is my intent to create a button titled "script" in the player. When clicked, the menu/notes sidebar (area-secondary) would fancy toggle show, if hidden, hide, if shown. The volume would be hidden and the "script" button would be in its place.

Originally, the first part of that would work (the button would appear in the player), but it wouldn't execute the fancy toggle. 

I've also tried a few other things, also following your logic, with no results.

Any ideas? I've attached an updated .story file for review.

By the way...I love the reviewmyelearning.com! I'm about to acquire another client, and I'll be able to upgrade my account. What an awesome site!

Lisa

Lisa Anderson

Hey James,

After a couple of hours, I figured it out! You probably inferred this in your post and I may have missed it.

I replaced the line above highlighted       scriptButton.setAttribute("onClick", "myScript()");

With:    scriptButton.setAttribute("onClick", "areaSecondary()");    (the actual area I want shown on click).

I replace the line above highlighted     window.myScript = function() {

With:     window.areaSecondary = function()   moving the curly bracket to the next line on its own.

Then, I replaced all the code that follows with the original code I inserted, and it worked.

Making these changes also got the fancy and toggle buttons working as original.

I added an extra slide to see how this code works over several slides and it seems it only needs to be place on first slide and it works throughout the scene.

A HUGE thanks to Owen for starting this thread, the others who contributed to it, and to James for responding to my plea! Because of all your help, I was able to solve this and hopefully I can pay it forward to anyone who may want to try something similar.

Cheers to you all...and here's an updated .story file.

Lisa

Here's the final code that works for me:


var scriptButton = document.createElement('input');
scriptButton.setAttribute('type', 'button');
scriptButton.setAttribute('id', 'CustomScript');
scriptButton.setAttribute('value', 'Script');
scriptButton.setAttribute('onClick', 'areaSecondary()');
document.body.insertBefore(scriptButton, document.body.firstChild);

$(".volume").after($("#CustomScript"));
$("#CustomScript").addClass('btn cs-button')
$(".volume").hide();

window.areaSecondary = function()
{
if(!window.mw) window.mw = $('.area-secondary').width()
if($('.area-secondary').width() > 0){
$('.area-secondary').animate({
'width': '-='+mw,
opacity: 0
})
}else{
$('.area-secondary').animate({
'width': '+='+mw,
opacity: 1
})
}
}
Melissa Pittman

Here is an example of how I incorporated a print button into an interactive self-reflection tool for our company's competency model.  I have condensed this to only one of the competencies for sharing.  

You can see the functionality by clicking on Instills Trust and choosing at least the first less skilled behavior "Lacks consistent follow-through on commitments."

I also include a printing tip for users. 

Lisa Anderson

Hi Melissa,

Your sample looks good!

I used the print feature a little differently. I combined Owen's code and James' code from a different post to add a button on the actual player, called "script". Then added an animation when clicked to either show or hide the menu/notes section. I personally don't like the way the player looks when the menu/notes is open and I wanted to user to be able to toggle between hiding, or showing that section at their discretion. I also got a little weary of having to create buttons on the actual screen to do those very same things. I think screen landscape is more important than having too many buttons.

Nice work on your use! And, thanks for sharing.

Lisa

Lisa Anderson

Hey James!

I really like the CoursePortfolios site. It's great! And, I really, really love the RMeL site!

I started wondering, after our code discussion above, if the area-secondary, aka the notes/menu area, could initially be hidden when the screen loads, rather than the default of shown.Then, when the user clicks the script button, the above code executes, and the notes/menu will be shown. I've been working with the code, and I can't quite get it to fire.

I've tried as a separate execute java with trigger and including it in the above code, with no luck.

Just curious...any ideas? 

Thanks, again James!

Lisa

onEnterFrame (James Kingsley)

To accomplish that I believe you will need to run some JS as soon as the player loads. That can get a bit tricky. There are some ways to force the player into a "developer" mode and then it will broadcast an event when it is loaded. But as I said it gets into some complicated edge of norm kind of code. 

It might be possible to run a loop (every 100ms) that checks to see if the player is loaded and then hides it.