I would like to be able to show more than one video on the page - tin-can-api

I took the Github xAPI script for "playing a youtube video" and tried to modify it to show two videos instead of one. Ultimately I would like to list five or six videos in this page. Unfortunately I cannot get it to show more than one video at a time. Instead it only shows one video and that is the second one that I have listed. Can someone tell me how I can modify this code to list more than one video? Also, I changed my LRS credentials before posting this question for obvious reasons. Many thanks.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=2.0">
<meta name="description" content="A shorthand syntax for communicating xAPI Statements">
<meta name="author" content="ADL">
<link rel="icon" href="favicon.ico">
<title>xAPI Youtube Video Tracking</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
</head>
<body>
<section class="container">
<div class="page-header">
<h1 class="text-primary"><i class="fa fa-youtube"></i> xAPI Youtube Video Tracking</h1>
<h3 class="text-muted">Send Youtube Video interactions to an LRS with xAPI</h3>
</div>
<div class="row">
<div class="form-group col-md-12">
<div id="player"></div>
<div id="player2"></div>
<p>This example uses minimal javascript and the youtube iframe API.</p>
<p>Statements are built with xapi-youtube-statements.js and dispatched to an LRS with xapiwrapper.min.js using a custom ADL.XAPIYoutubeStatements.onStateChangeCallback function.</p>
<p>You can view statements with the statement viewer.</p>
</div><!-- .col-md-12 -->
</div><!-- .row -->
</section><!-- .container -->
<script type="text/javascript" src="lib/xapiwrapper.min.js"></script>
<script type="text/javascript" src="src/xapi-youtube-statements.js"></script>
<script>
var video = "6hwHKOYCYL4"; // Change this to your video ID
var videoName = "Microlearning vs Traditional Learning";
var video2 = "SUJkBCHB4vQ"; // Change this to your video ID
var videoName2 = "Micro Learning is a BIG deal";
// "global" variables read by ADL.XAPIYoutubeStatements
ADL.XAPIYoutubeStatements.changeConfig({
"actor": {"mbox":"mailto:john.menken#syniverse.com", "name":"John M."},
"videoActivity": {"id":"https://www.youtube.com/watch?v=" + video, "definition":{"name": {"en-US":videoName}} }
});
ADL.XAPIYoutubeStatements.changeConfig({
"actor": {"mbox":"mailto:john.menken#syniverse.com", "name":"John M."},
"videoActivity": {"id":"https://www.youtube.com/watch?v=" + video2, "definition":{"name": {"en-US":videoName2}} }
});
function initYT() {
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: video,
playerVars: { 'autoplay': 0 },
events: {
'onReady': ADL.XAPIYoutubeStatements.onPlayerReady,
'onStateChange': ADL.XAPIYoutubeStatements.onStateChange
}
});
}
var player2;
function onYouTubeIframeAPIReady() {
player2 = new YT.Player('player2', {
height: '390',
width: '640',
videoId: video2,
playerVars: { 'autoplay': 0 },
events: {
'onReady': ADL.XAPIYoutubeStatements.onPlayerReady,
'onStateChange': ADL.XAPIYoutubeStatements.onStateChange
}
});
}
initYT();
// Auth for the LRS
var conf = {
"endpoint" : "https://www2.test.com/test/ScormEngineInterface/TCAPI/",
"auth" : "Basic " + toBase64("test:test"),
};
ADL.XAPIWrapper.changeConfig(conf);
/*
* Custom Callbacks
*/
ADL.XAPIYoutubeStatements.onPlayerReadyCallback = function(stmt) {
console.log("on ready callback");
}
// Dispatch Youtube statements with XAPIWrapper
ADL.XAPIYoutubeStatements.onStateChangeCallback = function(event, stmt) {
console.log(stmt);
if (stmt) {
stmt['timestamp'] = (new Date()).toISOString();
ADL.XAPIWrapper.sendStatement(stmt, function(){});
} else {
console.warn("no statement found in callback for event: " + event);
}
}
</script>
</body>
</html>

You are overwriting the onYouTubeIframeAPIReady callback immediately upon setting the first one, so that when the iframe is ready the first callback is no longer the value of that function. (Have to think asynchronously.) That function should only ever get called once, and should only have a single definition. To make this work, you need to move the instantiation of player2 into the onYouTubeIframeAPIReady function. (This portion is a duplicate of onYouTubeIframeAPIReady called once but multiple videos needed on a page)
Also note that ADL's wrapper is effectively using a singleton for communications with the LRS, so you are going to get all of your statements for both videos with the activity as the object from the second video (because it is the later call to changeConfig). I don't see a way around this other than to wrap their state change handler with your own function that calls changeConfig each time the event occurs, and even then you'd have the potential for a race condition.

Related

one-time payment with Laravel Cashier without subscription

I'm Planning to use Laravel Cashier for one-time payments with Stripe. It says in the documentation:
If you're only performing "one-off" charges and do not offer subscriptions, you should not use Cashier.
However, I was able to charge the user without subscription using the following code:
$amount = $request['amount];
Stripe::setApiKey(Config::get('stripe.secret_key'));
$token = $input['stripeToken'];
$user = Auth::user();
return $user->charge($amount * 100, ['source' => $token]);
And it worked! I'm wondering is there a problem with this approach? Why they suggested to not use Cashier? Is it gonna cause problems along the way? Please let me know what do you think?
As currently implemented, it's safe to do this. You can see the code for the charge method here: https://github.com/laravel/cashier/blob/822b6535e755fd36dec8ecceb52cc8909c8a953e/src/Billable.php#L37
That said, given the explicit warning that Cashier isn't intended for this sort of use, it's your own fault if the charge function gets modified in a way that breaks your app somewhere down the line.
There's not much reason to use Cashier in this situation, either. Using the SDK's Stripe\Charge class directly will be cleaner code, and you don't take on the risk of misusing a different library.
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<script src="https://js.stripe.com/v3/"></script>
<br><br>
<h1 align="center">One Time Charge</h1>
<form method = "post" action = "{{route('charge')}}" id="form">
#csrf
<div class="form form-cb" align="center">
Name on Card: <input type="text" id="card-holder-name" required>
<br> <br>
<div id="card-element" style="width: 300px;">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div><br>
<button disabled style="background-color:skyblue;color:black">$1.00 USD</button><br><br>
<button id="card-button" style="background-color: green;color:white;font-size:20px;" />Pay
</button>
</div>
</form>
<script>
var stripe = Stripe('Your Stripe-key');
var elements = stripe.elements();
var cardElement = elements.create('card');
cardElement.mount('#card-element');
// Add an instance of the card UI component into the `card-element` <div>
var cardHolderName = document.getElementById('card-holder-name');
var cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
var { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
console.log('error');
} else {
var payment_id = paymentMethod.id;
createPayment(payment_id);
}
});
var form = document.getElementById('form');
form.addEventListener('submit', function(event) {
event.preventDefault();
});
// Submit the form with the token ID.
function createPayment(payment_id) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'payment_id');
hiddenInput.setAttribute('value',payment_id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script>
</body>
</html>

VideoJs not playing on Browser and Devices

I wrote an app for Video Player using video.js, but its not playing when I clicked on play.
I tried to inspect on the browser and I saw this error:
Uncaught TypeError: The element or ID supplied is not valid. (videojs)
at videojs (video.js:21689)
Then when I checked (video.js:21689) I have this:
if (!tag || !tag.nodeName) {
// re: nodeName, could be a box div also
throw new TypeError('The element or ID supplied is not valid. (videojs)'); // Returns
}
<link href="https://vjs.zencdn.net/5.10.2/video-js.css" rel="stylesheet">
#if(strstr($media->mime_type, "video/"))
<div oncontextmenu=”return false;”>
<video controls controlsList="nodownload" id="videoElementID" playsinline>
<source src="{{str_replace('http://localhost:8000','http://example.com/public',$media->getUrl())}}" type="video/mp4" >
</video>
</div>
#elseif(strstr($media->mime_type, "audio/"))
<div style="magin:0 auto;">
<audio controls controlsList="nodownload" style="width:100%;">
<source src="{{str_replace('http://localhost:8000','http://example.com/public',$media->getUrl())}}" type="audio/mp3">
</audio>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script src="https://vjs.zencdn.net/5.10.2/video.js"></script>
<script src="//cdn.sc.gl/videojs-hotkeys/0.2/videojs.hotkeys.min.js"></script>
<script type="text/javascript">
const player = videojs('vid1', {});
document.addEventListener('contextmenu', event => event.preventDefault());
function takeTest() {
$('.lectureArea').hide();
$('.testMarker').hide();
$('.testArea').show();
}
$(document).ready(function(){
$('#videoElementID').bind('contextmenu',function() { return false; });
});
</script>
I expect the video to play when I clicked but not happen
you need to pass video control id
const player = videojs('videoElementID', {});

json print required data with ng-repat

i am new to coding and i am starting with angular js....
i have a json like below
$scope.data={
"items":
{
"yamaha":
{
"title":"R15",
"description":"sports mode",
"speed":"180kmph"
},
"Tvs":
{
"title":"apache",
"description":"sports mode",
"speed":"150kmph"
}
}
};
now my requirement is to show each value one after other in html.....
Note
As i said i am new,i have googled this and found some info like ng-repeat but i didnt understand how to use this in while implementing.
thanks for your response
ng-repeat will iterate over the data in the given array....here i used key,vakue concept to print object keys and values
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css">
<script src="http://code.angularjs.org/1.1.4/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="(k,v) in data.items">
<div><h2>{{k}}</h2>
<p>{{ v.title }}</p>
<p>{{v.description }}</p>
<p>{{v.speed }}</p>
</div>
</div>
</body>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data={
"items":
{
"yamaha":
{
"title":"R15",
"description":"sports mode",
"speed":"180kmph"
},
"Tvs":
{
"title":"apache",
"description":"sports mode",
"speed":"150kmph"
}
}
};
});
</script>
</html>

kendo ui autocomplete - json file datasource - template - 404 Not Found error - /undefined URL

Below code has been thankfully provided by machun for toggling between RTL and LTR directions in Kendo UI widgets.
The code consists of:
HTML:
kendo autocomplete form plus a button to activate support for RTL and LTR language.
Script:
k-rtl class container
datasource (json file)
kendo autocomplete widget initializing + template to show image beside data and to open data links in the same tab
k-rtl class
The problem is that links don't open correctly. It shows a 404 Not Found error plus a /undefined at the end of the URL.
Live demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled</title>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.mobile.all.min.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/angular.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/jszip.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/kendo.all.min.js"></script>
</head>
<body>
<div id="container">
<input type="button" id="toggleRTL" value="Activate RTL Support" class="k-button" />
<input id="autocomplete" type="text" />
</div>
</body>
</html>
<script>
/*------k-rtl class container----------*/
function createAutoComplete(){
if($("#autocomplete").data("kendoAutoComplete") != null){
$("#autocomplete").parent().remove();
$("#container").append("<input id='autocomplete' type='text' />")
}
/*------datasource (json file)---------*/
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "json.txt",
dataType: "json",
data: {
q: "javascript"
}
}
},
schema: {
data: "results"
}
});
/*------kendo autocomplete widget initializing + template to show image beside data and to open data links in the same tab----------*/
$("#autocomplete").kendoAutoComplete({
dataSource: dataSource,
dataTextField: "name",
template: '<span><img src="/kendo-autocomplete-test/img/#: id #.jpg" /></span>' + '<span data-href="#:link#">#:name#</span>',
select: function(e) {
var href = e.item.find("span").data("href");
location.assign(href);
}
});
}
/*------k-rtl class----------*/
createAutoComplete();
$('#toggleRTL').on('click', function(event) {
var form = $('#container');
console.log(form);
if (form.hasClass('k-rtl')) {
console.log("test1");
form.removeClass('k-rtl')
} else {
console.log("test2");
form.addClass('k-rtl');
}
createAutoComplete();
})
</script>
I advice to debug your function first then simply check the variable and make sure it contain the right thing. You overlooked a simple thing that your jquery dom selector isn't quite right resulting var href contain "undefined".
Change
var href = e.item.find("span").data("href");
To
var href = e.item.find("span[data-href]").attr("data-href");
Take a look here

jQuery multiple tabs + validation select does not fire (MVC3/Razor/VB)

I've used a solution here (which I've fiddled here) for restricting submission of a form until all required fields are filled.
The problem is when I run on my MVC3 project, as I've observed on Firebug, it stops starting at the third line of the following block. Because of it, the solution doesn't work.
var validator = $("#myForm").validate();
var tabs = $("#tabs").tabs({
select: function(event, ui) { // Here.
// The following lines are not executed.
var valid = true;
var current = $(this).tabs("option", "selected");
var panelId = $("#tabs ul a").eq(current).attr("href");
Can somebody tell me why my project won't run the rest of the script and what I should do to fix it? Thanks!
In my View file
#ModelType SLC.Models.RegisterModel
// downloaded with NuGet
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
var validator = $("#myForm").validate();
console.log(validator);
var tabs = $("#tabs").tabs({
select: function (event, ui) {
var valid = false;
var current = $(this).tabs("option", "selected");
var panelId = $("#tabs ul a").eq(current).attr("href");
$(panelId).find("input").each(function () {
console.log(valid);
if (!validator.element(this) && valid) {
valid = false;
}
});
return valid;
}
});
});
</script>
<link rel="stylesheet" href="#Url.Content("~/Content/themes/base/jquery-ui.css")" />
#Using Html.BeginForm("", "", Nothing, New With {.id = "myForm"})
#Html.ValidationSummary(True, "Account creation was unsuccessful. Please correct the errors and try again.")
#<div>
<div id="tabs">
<ul>
<li>1. Information</li>
<li>2. Information</li>
<li>3. Confirm</li>
</ul>
<div id="tab-1">
// Some fields...
</div>
<div id="tab-2">
// Some fields...
</div>
<div id="tab-3">
// Some fields...
<input type="submit" value="Register" />
</div>
</div>
</div>
End Using
In my Layout template file
<head>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
// downloaded with NuGet
<script src="#Url.Content("~/Scripts/jquery-1.9.1.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.10.0.js")" type="text/javascript"></script>
</head>
I found this:
The jQuery UI select method, in particular, is deprecated as of 1.10.0, so it's either back to UI 1.9.2 or a code rewrite.

Resources