HowlerJS playlist: checking if a specific song is playing - javascript

I'm trying to experiment with a HowlerJS playlist code. I would like to make a button that checks if a specific song in the playlist is playing, and if so, hide a line of text when clicked. I'm not very knowledgeable with these things, so I've been shooting in the dark trying to get it working. Is it possible to use this playlist code and make the type of button I need? This is the playlist and the code to hide the line of text:
var player = new Player([
{
title: 'Rave Digger',
file: 'rave_digger',
howl: null
},
{
title: '80s Vibe',
file: '80s_vibe',
howl: null
},
{
title: 'Running Out',
file: 'running_out',
howl: null
}
]);
function checksound() {
if(rave_digger.playing()){
$(".note").hide();
}
}
<div class="button" onclick="checksound();">Click here</div>
<div class="note">remove this text if Rave Digger is playing</div>
This would be easy if the songs were individually defined, but I can't figure it out due to how the songs are retrieved and defined:
sound = data.howl = new Howl({
src: ['./audio/' + data.file + '.webm', './audio/' + data.file + '.mp3'],
html5: true,});}
sound.play();
Here is the full code I am using: https://jsfiddle.net/vfozrn9d/1/
Is there any way I could specify that the file "rave_digger" should be checked for ".playing()"?

Had a look at the Howler player.js code on github and it looks like the player object will expose a playlist array and an index property
so you could write a function that checks the playlist to see if a certain track is playing
function isTrackPlaying(filename) {
// assume the track is not playing
let playing = false;
// Retrieve the currently active track listing from the playlist
const track = player.playlist[player.index];
// is it the track we are looking for?
if(track.file == filename) {
// has it been initialised, and is it playing?
playing = track.howl && track.howl.playing();
}
return playing;
}
Then your checksound function would become
function checksound() {
if(isTrackPlaying('rave_digger')){
$(".note").hide();
}
}

Related

How do I loop through a list of songs without all the songs playing simultaneously (JavaScript)?

I have a list of audio tags containing songs in my HTML document:
<div class="pop">
<p>Pop</p>
<!--Pop Songs-->
<audio src="./Pop/We Don't Talk Anymore - Charlie Puth feat. Selena Gomez.mp3"></audio>
<audio src="./Pop/Senorita - Camila Cabello feat. Shawn Mendes.mp3"></audio>
<audio src="./Pop/Sucker - Jonas Brother.mp3"></audio>
<audio src="./Pop/Sunflower - Post Malone feat. Swae Lee.mp3"></audio>
<audio src="./Pop/No Brainer - DJ Khaled feat. Justin Bieber, Quavo, Chance The Rapper.mp3"></audio>
</div>
My objective is to loop through them (play each individual song) using Javascript. The issue I keep running into is that all the songs are being played simultaneously, and not each track being played individually, one after the other, like I want it to. How can I fix this?
This the JS code I previously tried, but as I mentioned, all the songs in the list are played simultaneously, which I don't want:
const popSongs = document.querySelectorAll(".pop audio");
popSongs.forEach(song => {
song.play();
})
You don't need to figure out what the length of all songs are and set a timer or anything. Simply use the ended event.
// Get All Songs
const popSongs = document.querySelectorAll(".pop audio");
// For Each Song
popSongs.forEach((song, songIndex) => {
// Get the Next Song
let nextSong = popSongs[ songIndex === popSongs.length - 1 ? 0 : ++songIndex ];
// When Current Song Ends
song.addEventListener("ended", function() {
//Play the Next Song
nextSong.play();
});
});
// Play first song
popSongs[0].play();

Advice on scaling this code from querySelector to querySelectorAll

I have a custom video player with JS, html and css. Crux of my issue here is I didn't anticipate scaling this from one video, to two videos and I'm looking to refactor this so I can play multiple videos on one page. I've tried rewriting everything into a forEach and haven't been able to crack it. Really just need someone to nudge me in the right direction here:
Fiddle
My thinking was to simply change const player = document.querySelector('.custom-video-player'); to const players = document.querySelectorAll('.custom-video-player'); and then scope something like:
players.forEach((player) => {
// declare all the consts here... and event listeners
})
However, this approach isn't really working. Ideally I wanted to be lazy and not rewrite each instance of player. At this point I'm pretty stuck...
HTML
<div class="cs__video">
<div class="custom-video-player">
<video class="player__video viewer" src="http://techslides.com/demos/sample-videos/small.mp4"></video>
</div>
<div class="custom-video-player">
<video class="player__video viewer" src="http://techslides.com/demos/sample-videos/small.mp4"></video>
</div>
</div>
JS
/* custom video player javascripts */
// declaring elements
const player = document.querySelector('.custom-video-player');
const video = player.querySelector('.viewer');
/* Build out functions */
function togglePlay() {
console.log('playing');
const method = video.paused ? 'play' : 'pause';
video[method]();
}
/* event listeners */
video.addEventListener('click', togglePlay);
video.addEventListener('play', updateButton);
video.addEventListener('pause', updateButton);
toggle.addEventListener('click', togglePlay);
You may find it easier to manage the multiple players if you create each one from a class that includes all the relevant setup and methods.
Once you create the class for all players it's easy to create as many as you like.
Here's an example that creates an array of two players from an array of video sources (also available as a fiddle).
class Player {
// We call `new Player()` with two arguments, the id
// and the video source
constructor(id, src) {
// We assign both id and src to the class
this.id = id;
this.src = src;
// Then we call two functions, one to generate the
// video HTML, and one to add it to the page
const html = this.generateHTML(id);
this.addHTMLToDOM(html);
}
// We use a template literal to build our HTML
// using the id and src we passed into the class earlier
generateHTML() {
return (
`<div data-player=${this.id}>Player ${this.id}</div>
<video controls width="250">
<source src="${this.src}" type="video/mp4" />
Sorry, your browser doesn't support embedded videos.
</video>`
);
}
// This method simply adds the player HTML
// to the document body
addHTMLToDOM(html) {
document.body.insertAdjacentHTML('beforeend', html);
}
// play and pause are a couple of example methods for
// player control. `return this` allows for the methods
// to be chained (see below)
play() {
console.log(`Playing video ${this.id}`);
return this;
}
pause() {
console.log(`Pausing video ${this.id}`);
return this;
}
}
// An array of video sources
const srcs = [
'http://techslides.com/demos/sample-videos/small.mp4',
'http://techslides.com/demos/sample-videos/small.mp4'
]
// `map` over the srcs array to create an array of new players
const players = srcs.map((src, i) => new Player(++i, src));
// An example to show how we can call the player instance methods
players[0].play().pause();
players[1].play().pause();

Typescript Parent/Child

Angular2/Typescript - Parent/Child Directive(?)
I'm new to, and very much still learning, Angular2/Typescript/javascript. As a result, I'm not entirely sure how to title my question. The basis of my app is a card game. The premise (relative to my struggle) is that the game has 2 players and each player has a hand of 5 cards. I have API calls to build/return the hand of cards.
In my app.component template, I have 2 div blocks; one for each players' hand of cards. Currently, I have it working by building two distinct arrays of cards (named p1cards and p2cards). Here is the relative code for that:
<div class="player1Cards" id="player1Cards">
<ul class="list-group">
<div draggable *ngFor="let card of p1cards" [dragData]="card"
class="list-group-item">
<img src="{{cardBluePath + card.fileName}}">
</div>
</ul>
</div>
<div class="player2Cards" id="player2Cards">
<ul class="list-group">
<div draggable *ngFor="let card of p2cards" [dragData]="card"
class="list-group-item">
<img src="{{cardBluePath + card.fileName}}">
</div>
</ul>
</div>
And here is the actual export class of the entire AppComponent:
#Injectable()
export class AppComponent implements OnInit
{
#ViewChild(ModalComponent) errorMsg: ModalComponent;
errorMessage: string;
gameBoard: GameBoard[];
name: {};
mode = 'Observable';
//we need a gameboard (maybe not)
//we need an array of players
player:Player;
players:Player[] = [];
p1cards:Card[] = [];
p2cards:Card[] = [];
droppedItems = [];
//This tells us where the card images can be found
cardBluePath = "/assets/deck/Blue/";
cardRedPath = "/assets/deck/Red/";
//The boardService will handle our API calls
boardService;
//Initialize the API service
constructor(boardService:BoardService) {
this.boardService = boardService;
}
//On load...
ngOnInit()
{
//Create the game
this.boardService.createGame()
.subscribe(
error => this.errorMessage = <any>error);
//Create the players
this.createPlayer(0);
this.createPlayer(1);
}
createPlayer(player: number)
{
var playerName;
if (player == 0) {playerName = "Player1"} else {playerName = "Player2"};
//We'll make a call to the API to build the hand of cards
this.boardService.buildHand(player)
.subscribe(
cardList =>
{
var cardData = [];
cardData = JSON.parse(cardList.toString());
var i, itemLength, card
itemLength = cardData.length;
for(i=0;i<itemLength;i++)
{
let card = new Card();
Object.assign(card,
{
"cardNum":i,
"id": cardData[i].id,
"displayName": cardData[i].displayName,
"fileName": cardData[i].fileName,
"left": cardData[i].left,
"top": cardData[i].top,
"right": cardData[i].right,
"bottom": cardData[i].bottom,
"level": cardData[i].level,
"native": cardData[i].native
});
if (player == 0) {this.p1cards.push(card)} else {this.p2cards.push(card)};
////this.cards.push(card);
}
//Now we will create the player and feed it the hand
this.player = new Player(playerName);
if (player ==0) {this.player.cardHand = this.p1cards} else {this.player.cardHand = this.p2cards};
this.players.push(this.player);
}
);
}
//When a card is dropped...
onItemDrop(e: any, slot: any)
{
e.dragData.slot = slot;
//Update the object
this.boardService.playCard(slot, e.dragData.card)
.subscribe(result => {
//If the slot is open and the card is played, physically move the item
if (result == "true" )
{
this.droppedItems.push(e.dragData);
this.removeItem(e.dragData, this.p1cards);
}
else{
window.alert("Slot already occupied.");
//this.modalWindow.show()
//this.errorMsg.showErrorMessage("Slot already occupied.");
//this.errorMsg.show();
}
});
}
//Remove the card from the hand
removeItem(item: any, list: Array<any>)
{
let index = list.map((e) => {
return e.cardNum
}).indexOf(item.cardNum);
list.splice(index, 1);
}
}
The createPlayer function is really where the question begins. Currently, it will make the API call and parse the JSON back into an array of cards. Right now, the array of cards lives locally in the AppComponent (as p1cards or p2cards).
What I want to do instead is create a player objects (component) for each player, assign their respective hand of cards, and then put those players in an array. I had that part working (pieces of the code still exist above, but not all of it), but I hit a wall in my *ngFor to display the cards. In pseudocode, I understood what I needed to do, but in practice I couldn't figure it out.
I knew that div class player1Cards needed to be something like "let player of Players where name = player1", and then I needed to iterate over the player.cardHand[] array to display each of the cards. I tried quite a few things, but nothing worked.
So then, after a few hours of Google searching, I came to the conclusion that I needed a child view for the player to handle it. I currently have the following for that:
My player.html is:
<div draggable *ngFor="let card of cardHand" [dragData]="card" class="list-group-item">
<img src="{{cardBluePath + card.fileName}}">
</div>
And my player.ts is:
import { Component, Input, OnInit } from '#angular/core';
import { Card } from './card';
#Component({
selector: 'player',
templateUrl: './player.html',
})
export class Player implements OnInit
{
public cardHand: Card[];
cardBluePath = "/assets/deck/Blue/";
constructor
(
public name: string
)
{}
ngOnInit()
{
}
}
Then in my AppComponent template, I added the block (and Imported the player.ts)
I get an error message on App.Component "inline template:69:16 caused by: No provider for String!". Of all the Google research I performed and all of the changes I tried (ViewChild, Input/Output, Reference), I could not get it to work. I don't recall exactly what I did, but at one point I was able to eliminate the error, but the card array was not getting passed to the player (I wish I had committed or stashed that code).
In my mind, I understand the task at hand, I just can't make it happen. I know I need to create the Player object and feed it the respective cardHand in order for the player html to be able to parse it. I can do that fine in AppComponent, but once I try to do it as a parent/child, I get stuck.
Can someone help get me going in the right direction?
I know I need to create the Player object and feed it the respective
cardHand in order for the player html to be able to parse it. I can do
that fine in AppComponent, but once I try to do it as a parent/child,
I get stuck.
I agree that it makes sense to create an array of player objects, each with an array of cards in their hand. Something like this:
let player1 = {name:'Player 1',hand:[]}
let player2 = {name:'Player 2',hand:[]}
this.players = [player1, player2]
player1.hand.push(this.dealCard())
...
player2.hand.push(this.dealCard())
...
You can then create a player component to show the players (and even a card component to show their hand). In your root template you'll loop through the players, creating your player component and passing in the player data, including their hands.
<player-component *ngFor="let player of players" [player]="player"></player-component>
Make sure your player component has an input to receive the player data:
export class PlayerComponent implements OnInit {
#Input() player: Player;
constructor() { }
ngOnInit() { }
}
Then in the <player-component> template loop through the player's hand and render the cards:
<p>I am {{player.name}}. My hand is:</p>
<ul>
<li *ngFor="let card of player.hand">{{card}}</li>
</ul>
Here is a plunker showing a working demo that's a simplified version of this setup:
https://plnkr.co/edit/5Hz8P7poCb9Ju5IR6MWs?p=preview
You should be able to configure it to the specific setup of your game. Good luck!
in your player component, if you want to access another component:
1. that component needs a import statement on top
2. within the #component section, you need to include it in Providers
3. also include it in the constructor
For more information, visit here: https://angular.io/guide/dependency-injection

How do I detect the first time the user logs in and the first time a specific page is loaded?

I would like to Trigger some JS only the first time a user logs in, and only the first time a specific page is loaded.
I believe I can deal with the first time they log in, by simply checking user.sign_in_count < 2, but I don't know how to specify just on the first page load only.
i.e. I don't want the JS to be triggered after the user logs in for the first time and refreshes the page without logging out.
I am using Turbolinks and $(document).on('turbolinks:load', function() { to trigger it.
Edit 1
So what I am trying to do is execute Bootstrap Tour on a number of pages. But I only want that tour to be automatically executed, on the first page load. The tour itself will lead the user to other specific pages within my app, but each of those pages will have page-specific tour JS on each page.
Right now, in my HTML I have something like this:
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
// Initialize the tour
tour.init();
// Start the tour
tour.start();
});
</script>
So all I really want to do is the following:
Not bombard the user with executing a new tour, on their first login, whenever they reload the page.
Allow them to be able to manually execute the tour at a later date if they want, by simple pressing a link.
I don't want to store anything in my DB if I don't have to -- so preferably this should be a cookie-based approach or localStorage
Assume that I will use Rails to track the number of sign-ins they have done. So once they sign in more than once, I can not trigger this JS.
The real problem is just within that first sign in, if they refresh the main page 10 times, this tour gets executed 10 times. That's what I am trying to stop.
I hope that provides some more clarity.
Preface
It's my understanding that you have:
multiple pages that contain a single tour (each page's tour is different)
a way to detect first signin to an account (ruby login count)
ability to add a script value based upon first signin
Solution Overview
The solution below uses localStorage to store a key value pair of each tour's identifier and if it has been seen or not. localStorage persists between page refreshes and sessions, as the name suggests, localStorage is unique to each domain, device, and browser (ie. chrome's localStorage cannot access firefox's localStorage even for the same domain, nor can chrome's localStorage on your laptop access chrome's localStorage on your mobile even for the same domain). I raise this to illustrate the reliance upon Preface 3 to toggle a JS flag for if the user has logged in previously.
For the tour to start, the code checks localStorage for if its corresponding key value pair is not set to true (representing having been "seen"). If it does exist and is set to true, the tour does not start, otherwise it runs. When each tour begins, using its onStart method, we update/add the tour's identifier to localStorage and set its value to true.
Manual execution of the tour can be performed by either manually calling the tour's start method if you would like only the current page's tour to execute, otherwise, you can clear out all of the localStorage related to the tour and send the user back to the first page/if you're on the first page, again just call the start method.
JSFiddle (HTML based off other question's you've asked regarding touring)
HTML (this could be any element with the id="tourAgain" attribute for the following code to work.
<button class="btn btn-sm btn-default" id="tourAgain">Take Tour Again</button>
JS
var isFirstLogin = true; // this value is populated by ruby based upon first login
var userID = 12345; // this value is populated by ruby based upon current_user.id, change this value to reset localStorage if isFirstLogin is true
// jquery on ready function
$(function() {
var $els = {}; // storage for our jQuery elements
var tour; // variable that will become our tour
var tourLocalStorage = JSON.parse(localStorage.getItem('myTour')) || {};
function activate(){
populateEls();
setupTour();
$els.tourAgain.on('click', tourAgain);
// only check check if we should start the tour if this is the first time we've logged in
if(isFirstLogin){
// if we have a stored userID and its different from the one passed to us from ruby
if(typeof tourLocalStorage.userID !== "undefined" && tourLocalStorage.userID !== userID){
// reset the localStorage
localStorage.removeItem('myTour');
tourLocalStorage = {};
}else if(typeof tourLocalStorage.userID === "undefined"){ // if we dont have a userID set, set it and save it to localStorage
tourLocalStorage.userID = userID;
localStorage.setItem('myTour', JSON.stringify(tourLocalStorage));
}
checkShouldStartTour();
}
}
// helper function that creates a cache of our jQuery elements for faster lookup and less DOM traversal
function populateEls(){
$els.body = $('body');
$els.document = $(document);
$els.tourAgain = $('#tourAgain');
}
// creates and initialises a new tour
function setupTour(){
tour = new Tour({
name: 'homepage', // unique identifier for each tour (used as key in localStorage)
storage: false,
backdrop: true,
onStart: function() {
tourHasBeenSeen(this.name);
$els.body.addClass('is-touring');
},
onEnd: function() {
console.log('ending tour');
$els.body.removeClass('is-touring');
},
steps: [{
element: "div.navbar-header img.navbar-brand",
title: "Go Home",
content: "Go home to the main page."
}, {
element: "div.navbar-header input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}, {
element: "span.num-players",
title: "Number of Players",
content: "This is the number of players that are in our database for this Tournament"
}, {
element: '#page-wrapper div.contact-box.profile-24',
title: "Player Info",
content: "Here we have a quick snapshot of the player stats"
}]
});
// Initialize the tour
tour.init();
}
// function that checks if the current tour has already been taken, and starts it if not
function checkShouldStartTour(){
var tourName = tour._options.name;
if(typeof tourLocalStorage[tourName] !== "undefined" && tourLocalStorage[tourName] === true){
// if we have detected that the tour has already been taken, short circuit
console.log('tour detected as having started previously');
return;
}else{
console.log('tour starting');
tour.start();
}
}
// updates localStorage with the current tour's name to have a true value
function tourHasBeenSeen(key){
tourLocalStorage[key] = true;
localStorage.setItem('myTour', JSON.stringify(tourLocalStorage));
}
function tourAgain(){
// if you want to tour multiple pages again, clear our localStorage
localStorage.removeItem('myTour');
// and if this is the first part of the tour, just continue below otherwise, send the user to the first page instead of using the function below
// if you just want to tour this page again just do the following line
tour.start();
}
activate();
});
PS. the reason we dont use onEnd to trigger the tourHasBeenSeen function is that there is currently a bug with bootstrap tour where if the last step's element doesnt exist, the tour ends without triggering the onEnd callback, BUG.
You could try using Javascript's sessionStorage, which is deleted when the user closes the tab, but survives through refreshes. Just use sessionStorage.setItem(key, value and sessionStorage.getItem(key). Remember that sessionStorage can only store strings!
Using your code:
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
if(sessionStorage.getItem("loggedIn") !== "yes"){//Remember that sessionStorage can only store strings!
//Initialize the tour
tour.init();
// Start the tour
tour.start();
}
else{
//Set item "loggedIn" in sessionStorage to "yes"
sessionStorage.putItem("loggedIn", "yes");
}
var goBackToTour = function(e){
//You can also make a "fake" link, so that it looks like a link, but is not, and you don't have to put the following line:
e.preventDefault();
tour.init();
tour.start();
};
document.getElementById("goBackToTourLink").addEventListener("click", goBackToTour);
});
//On the logout
var logout = function(){
sessionStorage.setItem("loggedIn", "no");
};
</script>
You can store if user has seen the tour or not in the cookie. You can maintain a "TrackingCookie" which has all the user tracking information (eg. tour_shown, promotion_shown etc, which is accessed by your javascript
code. Following TrackingCookie code is to maintain all such tracking information in one cookie. I am calling it tracking_cookie.
Cookies can be accessed server-side using
cookies[:tracking_cookie]
tracking_cookie.js
var TrackingCookie = (function() {
function TrackingCookie() {
this.name = 'tracking_cookie';
this.expires = new Date(new Date().setYear(new Date().getFullYear() + 1));
}
TrackingCookie.prototype.set = function(name, value) {
var data={};
if(!this.readFromStore()) {
data = this.readFromStore();
}
data[name] = value;
return this.writeToStore(data);
};
TrackingCookie.prototype.set_if_unset = function(name, value) {
if (!this.get(name)) {
return this.set(name, value);
}
};
TrackingCookie.prototype.get = function(name) {
return this.readFromStore()[name];
};
TrackingCookie.prototype.writeToStore = function(data) {
return $.cookie(this.name, JSON.stringify(data), {
path: '/',
expires: this.expires
});
};
TrackingCookie.prototype.readFromStore = function() {
return $.parseJSON($.cookie(this.name));
};
return TrackingCookie;
})();
In your HTML
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
//Instantiate the cookie
var tracking_cookie = new TrackingCookie();
//Cookie value not set means, it is a new user.
if(!tracking_cookie.get("tour_shown")){
//Set the value to be true.
tracking_cookie.set("tour_shown",true)
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
// Initialize the tour
tour.init();
// Start the tour
tour.start();
};
});
</script>
The cookie class is verbose. You can just use $.cookie to achieve simple one toggle behavior. The above code works for all first time users, logged-in as well as logged-out. If you just want it for logged-in user, set the flag on user log-in on server-side.
To use local storage:
if (typeof(Storage) !== "undefined") {
var takenTour = localStorage.getItem("takenTour");
if (!takenTour) {
localStorage.setItem("takenTour", true);
// Take the tour
}
}
We use this solution because our users don't log in, and it is a bit lighter than using cookies. As mentioned above it doesn't work when users switch machines or clear the cache, but you have that covered off by your login count.
Based on your comment, I think you're going to want to track this in your data (which is effectively what you're doing with the user.sign_in_count > 1 check). My recommendation would be to use a lightweight key-value data store like Redis.
In this model, each time a user visits a page that has this feature, you check for a "visited" value associated with that user in Redis. If it doesn't exist, you trigger the JS event and add "visited": true to Redis for that user, which will prevent the JS from triggering in the future.
Local storage is not a cross browser solution. Try this cross browser SQL implementation which uses different methods (including localstorage) to store 'databases' on the users hard drive indefinitely.
var visited;
jSQL.load(function(){
// create a table
jSQL.query("create table if not exists visits (time date)").execute();
// check if the user visited
visited = jSQL.query("select * from visits").execute().fetchAll("ASSOC").length;
// update the table so we know they visited already next time
jSQL.query("insert into visits values (?)").execute([new Date()]);
jSQL.persist();
});
This should work if what you want to do is gate the page for its life. If you need to prevent re-execution for longer periods, consider localStorage.
var triggered;
$(document).on('turbolinks:load', function() {
if (triggered === undefined) {
triggered = "yes";
...code...
}}
You're going to have to communicate with the backend somehow to get sign-in count. Either in a injected variable, or as json route you hit with ajax, do logic like:
if !session[:seen_tour] && current_user.sign_in_count == 1
#show_tour = true
session[:seen_tour] = true
else
#show_tour = false
end
respond_to do |format|
format.html {}
format.json { render json: {show_tour: #show_tour } }
end
Values in session will persist however you've configured your session store, by default that is stored in cookies.

Write plugin for VideoJS 5

When I trying to use the newest version of videojs 5, the following code is no longer worked. I am trying to write a videojs plugin, but videojs 5 use ecmascript 6, which is new to me. Any helps are appreciated.
videojs.SharingButton = videojs.Button.extend({
/** #constructor */
init: function(player, options){
videojs.Button.call(this, player, options);
this.player = player;
}
});
videojs.SharingButton.prototype.createEl = function(tagName,options) {
return videojs.Component.prototype.createEl(tagName,{
className: this.buildCSSClass(),
innerHTML: '',
role: 'button',
'aria-live': 'polite', // let the screen reader user know that the text of the button may change
tabIndex: 0
});
}
videojs.SharingButton.prototype.buttonText = 'Share Video';
videojs.SharingButton.prototype.options_ = {};
videojs.SharingButton.prototype.buildCSSClass = function(){
return 'vjs-sharing-control ';
};
Hi i had the same problem, Replace this Code
videojs.SharingButton = videojs.Button.extend({
by
var SharingButton = videojs.getComponent('Button');
videojs.SharingButton = videojs.extend(SharingButton , {...});
videojs.registerComponent('SharingButton', SharingButton);
var myButton = myPlayer.addChild('SharingButton');
If you want to add a Component that is not a direct child of the player element you will have to climb the child elements and add the Component.
Like:
parentComponent = myPlayer.getChild('component1').getChild('component2')...
parentComponent.addChild('SharingButton')
Beware that the player components have to start lowercase like e.g. controlBar.
Find the component tree in this link.
A lot of changes has being made as version 5.0 is built (see this link), and unfortunately most videojs plugins didn't make an update of their codes! one of theme is Social button sharing

Categories