<!DOCTYPE html>
<html ng-app="myApp">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link href="https://vjs.zencdn.net/6.2.5/video-js.css" rel="stylesheet">
</head>
<body ng-controller="ayCtrl"> <!-- Controller for speech recognition -->
<div ng-controller="myCtrl">
<div>
<video id='vid' preload="auto" controls class="video-js vjs-default-skin" vjs-video>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" type="video/mp4">
</video>
<p>Say: "Play", "Pause", or "Stop" to test functionality.</p>
</div>
</div>
<script src="https://code.jquery.com/jquery-2.0.3.min.js"> </script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="https://vjs.zencdn.net/6.2.5/video.js"></script>
<script src="./vjs-video.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/annyang/2.5.0/annyang.min.js"></script>
<script src="./annyangService.js"></script>
<script src="./script.js"></script>
<script src="./ayCtrl.js"></script>
</body>
</html>
var myApp = angular.module('myApp');
myApp.controller('myCtrl', ['$scope', '$timeout',
function($scope, $timeout , AnnyangService)
{
// These are the commands for speech recognition. They are replicated in the
// speech recognition service.
/*
$scope.commands = {
'play': function() {
$scope.vid.play();
},
'pause': function() {
$scope.vid.pause();
},
'stop': function() {
$scope.vid.currentTime(0); $scope.vid.pause();}
};*/
$scope.$on('vjsVideoReady', function (e, data) {
//data contains `id`, `vid`, `player` and `controlBar`
//NOTE: vid is depricated, use player instead
console.log('video id:' + data.id);
console.log('video.js player instance:' + data.player);
console.log('video.js controlBar instance:' + data.controlBar);
$timeout(function(){
$scope.vid = data.player;
$scope.vm.play = function() {
$scope.vid.play();
};
$scope.vm.pause = function() {
$scope.vid.pause();
}
$scope.vm.stop = function() {
$scope.vid.currentTime(0);
$scope.vid.pause();
}
$scope.vm.init();
/*
$scope.ay = annyang;
$scope.ay.addCommands($scope.commands);
$scope.ay.debug();
$scope.ay.start();*/
}, 1250);
})
}]);
I'm trying to implement annyang speech recognition with videojs on AngularJS.
I have obtained a controller and service for implementing annyang on AngularJS,
but I have not found how to successfully install it for my purposes.
The service and controller can be found at: https://github.com/levithomason/angular-annyang/tree/master/js
My intent is to set a controller for the speech recognition, which will set up and launch the service.
I'm getting the error: Error: $controller:ctrlreg
A controller with this name is not registered.
I know I'm doing something wrong with the AyCtrl controller, but I'm not sure what it is, and other articles, while
wordy, don't exactly *show* me what I'm doing wrong.
Thank you for your help.
/**
* @ngdoc directive
* @name vjsVideoApp.directive:vjs.directive.js
* @description
* # vjs.directive.js
*/
(function (root, factory) {
//module loader detection derrived from http://tinyurl.com/hs2coz2
if ((typeof define).match(/^(object|function)$/) && define.amd) {
//AMD type module loader detected
define(['angular', 'video.js'], factory);
} else if (typeof module === 'object' && module.exports) {
//CommonJS type module loader detected
module.exports = factory(require('angular'), require('video.js'));
} else {
//we aren't using a module loader so angular and video.js
//should exist globally
//also, we don't need to add this module to global space
factory(root.angular, root.videojs);
}
}(this, function (angular, videojs) {
'use strict';
var module = angular.module('vjs.video', []);
function getVersion() {
return (videojs && videojs.VERSION) ?
videojs.VERSION : '0.0.0';
}
function isMediaElement(element) {
return element[0].nodeName === 'VIDEO' || element[0].nodeName === 'AUDIO';
}
module.controller('VjsVideoController', ['$scope', function ($scope) {
var self = this;
function getVidElement(element, isContainer) {
var vid = null,
videos;
if (!videojs) {
throw new Error('video.js was not found!');
}
if (isContainer) {
videos = element[0].querySelectorAll('video, audio');
if (videos.length === 0) {
throw new Error('video tag must be defined within container directive!');
} else if (videos.length > 1) {
throw new Error('only one video can be defined within the container directive!');
}
vid = videos[0];
} else {
if (isMediaElement(element)) {
vid = element[0];
} else {
throw new Error('directive must be attached to a video or audio tag!');
}
}
return vid;
}
function applyRatio(el, ratioVal) {
var ratio = ratioVal,
style = document.createElement('style'),
parseRatio = function (r) {
var tokens = r.split(':'),
tokenErrorMsg = 'the ratio must either be "wide", "standard" or ' +
'decimal values in the format of w:h';
//if invalid ratio throw an error
if (tokens.length !== 2) {
throw new Error(tokenErrorMsg);
}
//confirm that both tokens are numbers
if (isNaN(tokens[0]) || isNaN(tokens[1])) {
throw new Error(tokenErrorMsg);
}
//confirm that the width or height is not zero
if (Number(tokens[0]) === 0 || Number(tokens[1]) === 0) {
throw new Error('neither the width or height ratio can be zero!');
}
return (Number(tokens[1]) / Number(tokens[0])) * 100;
},
genContainerId = function (element) {
var container = element[0].querySelector('.vjs-tech'),
vjsId;
if (container) {
vjsId = 'vjs-container-' + container.getAttribute('id');
} else {
//vjsId = 'vjs-container-default';
throw new Error('Failed to find instance of video-js class!');
}
//add generated id to container
element[0].setAttribute('id', vjsId);
return vjsId;
},
containerId,
ratioPercentage,
css;
//if ratio isn't defined lets default to wide screen
if (!ratio) {
ratio = '16:9';
}
switch (ratio) {
case 'wide':
ratio = '16:9';
break;
case 'standard':
ratio = '4:3';
break;
}
containerId = genContainerId(el);
ratioPercentage = parseRatio(ratio);
css = ['#', containerId, ' ',
'.video-js {padding-top:', ratioPercentage,
'%;}\n', '.vjs-fullscreen {padding-top: 0px;}'].join('');
style.type = 'text/css';
style.rel = 'stylesheet';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
el[0].appendChild(style);
}
function generateMedia(ctrl, mediaChangedHandler) {
var errMsgNoValid = 'a sources and/or tracks element must be ' +
'defined for the vjs-media attribute',
errMsgNoSrcs = 'sources must be an array of objects with at ' +
'least one item',
errMsgNoTrks = 'tracks must be an array of objects with at ' +
'least one item',
div,
curDiv;
//check to see if vjsMedia is defined
if (!ctrl.vjsMedia) {
return;
}
//if sources and tracks aren't defined, throw an error
if (!ctrl.vjsMedia.sources && !ctrl.vjsMedia.tracks) {
throw new Error(errMsgNoValid);
}
//verify sources and tracks are arrays if they are defined
if (ctrl.vjsMedia.sources && !(ctrl.vjsMedia.sources instanceof Array)) {
throw new Error(errMsgNoSrcs);
}
if (ctrl.vjsMedia.tracks && !(ctrl.vjsMedia.tracks instanceof Array)) {
throw new Error(errMsgNoTrks);
}
//build DOM elements for sources and tracks as children to a div
div = document.createElement("div");
if (ctrl.vjsMedia.sources) {
ctrl.vjsMedia.sources.forEach(function (curObj) {
curDiv = document.createElement('source');
curDiv.setAttribute('src', curObj.src || "");
curDiv.setAttribute('type', curObj.type || "");
div.appendChild(curDiv);
});
}
if (ctrl.vjsMedia.tracks) {
ctrl.vjsMedia.tracks.forEach(function (curObj) {
curDiv = document.createElement('track');
curDiv.setAttribute('kind', curObj.kind || "");
curDiv.setAttribute('label', curObj.label || "");
curDiv.setAttribute('src', curObj.src || "");
curDiv.setAttribute('srclang', curObj.srclang || "");
//check for default flag
if (curObj.default === true) {
curDiv.setAttribute('default', "");
}
div.appendChild(curDiv);
});
}
//invoke callback
mediaChangedHandler.call(undefined, {element: div});
}
function initVideoJs(vid, params, element, mediaChangedHandler) {
var opts = params.vjsSetup || {},
ratio = params.vjsRatio,
isValidContainer =
(!isMediaElement(element) && !getVersion().match(/^5\./)) ? true : false,
mediaWatcher;
if (!videojs) {
return null;
}
//override poster settings if defined in vjsMedia
if (params.vjsMedia && params.vjsMedia.poster) {
opts.poster = params.vjsMedia.poster;
}
//generate any defined sources or tracks
generateMedia(params, mediaChangedHandler);
//watch for changes to vjs-media
mediaWatcher = $scope.$watch(
function () {
return params.vjsMedia;
},
function (newVal, oldVal) {
if (newVal && !angular.equals(newVal, oldVal)) {
//deregister watcher
mediaWatcher();
if (isValidContainer) {
videojs(vid).dispose();
$scope.$emit('vjsVideoMediaChanged');
} else {
$scope.$emit('vjsVideoMediaChanged');
}
}
}
);
//bootstrap videojs
videojs(vid, opts, function () {
if (isValidContainer) {
applyRatio(element, ratio);
}
//emit ready event with reference to video
$scope.$emit('vjsVideoReady', {
id: vid.getAttribute('id'),
vid: this,
player: this,
controlBar: this.controlBar
});
});
//dispose of videojs before destroying directive
$scope.$on('$destroy', function () {
videojs(vid).dispose();
});
}
//export public methods
self.initVideoJs = initVideoJs;
self.getVidElement = getVidElement;
}]);
module.directive('vjsVideo', ['$compile', '$timeout', function ($compile, $timeout) {
return {
restrict: 'A',
transclude: true,
scope: {
vjsSetup: '=?',
vjsRatio: '@',
vjsMedia: '=?'
},
controller: 'VjsVideoController',
controllerAs: 'vjsCtrl',
bindToController: true,
link: function postLink(scope, element, attrs, ctrl, transclude) {
var vid,
parentContainer,
origContent,
compiledEl,
mediaChangedHandler = function (e) {
//remove any inside contents
element.children().remove();
//add generated sources and tracks
element.append(e.element.childNodes);
},
init = function () {
vid = ctrl.getVidElement(element);
//check if video.js version 5.x is running
if (getVersion().match(/^5\./)) {
//if vjsRatio is defined,
//add it to the vjsSetup options
if (ctrl.vjsRatio) {
if (!ctrl.vjsSetup) {
ctrl.vjsSetup = {};
}
ctrl.vjsSetup.aspectRatio = ctrl.vjsRatio;
}
}
//attach transcluded content
transclude(function (content) {
element.append(content);
//now that the transcluded content is injected
//initialize video.js
ctrl.initVideoJs(vid, ctrl, element, mediaChangedHandler);
});
};
origContent = element.clone();
//we need to wrap the video inside of a div
//for easier DOM management
if (!element.parent().hasClass('vjs-video-wrap')) {
element.wrap('<div class="vjs-video-wrap"></div>');
}
parentContainer = element.parent();
scope.$on('vjsVideoMediaChanged', function () {
//retreive base element that video.js creates
var staleChild = parentContainer.children()[0];
//remove current directive instance
//destroy will trigger a video.js dispose
$timeout(function () {
scope.$destroy();
});
//compile the new directive and add it to the DOM
compiledEl = origContent.clone();
parentContainer.append(compiledEl);
//it is key to pass in the parent scope to the directive
compiledEl = $compile(compiledEl)(scope.$parent);
//remove original element created by video.js
staleChild.remove();
});
init();
}
};
}]);
module.directive('vjsVideoContainer', [function () {
return {
restrict: 'AE',
transclude: true,
template: '<div class="vjs-directive-container"><div ng-transclude></div></div>',
scope: {
vjsSetup: '=?',
vjsRatio: '@',
vjsMedia: '=?'
},
controller: 'VjsVideoController',
controllerAs: 'vjsCtrl',
bindToController: true,
link: function postLink(scope, element, attrs, ctrl, transclude) {
var vid,
origContent,
mediaChangedHandler = function (e) {
var vidEl = element[0].querySelector('video, audio');
if (vidEl) {
//remove any inside contents
while (vidEl.firstChild) {
vidEl.removeChild(vidEl.firstChild);
}
//add generated sources and tracks
while (e.element.childNodes.length > 0) {
vidEl.appendChild(e.element.childNodes[0]);
}
}
},
init = function () {
vid = ctrl.getVidElement(element, true);
//we want to confirm that the vjs-video directive or
//any corresponding attributes are not defined on the
//internal video element
if (vid.getAttribute('vjs-video') !== null) {
throw new Error(
'vjs-video should not be used on the video ' +
'tag when using vjs-video-container!');
}
//we also want to make sure that no vjs-* attributes
//are included on the internal video tag
if ((vid.getAttribute('vjs-setup') !== null) ||
(vid.getAttribute('vjs-media') !== null) ||
(vid.getAttribute('vjs-ratio') !== null)) {
throw new Error(
'directive attributes should not be used on ' +
'the video tag when using vjs-video-container!'
);
}
//check if video.js version 5.x is running
if (getVersion().match(/^5\./)) {
if (ctrl.vjsRatio) {
if (!ctrl.vjsSetup) {
ctrl.vjsSetup = {};
}
ctrl.vjsSetup.aspectRatio = ctrl.vjsRatio;
}
} else {
//set width and height of video to auto
vid.setAttribute('width', 'auto');
vid.setAttribute('height', 'auto');
}
//bootstrap video js
ctrl.initVideoJs(vid, ctrl, element, mediaChangedHandler);
};
//save original content
transclude(function (content) {
origContent = content.clone();
});
scope.$on('vjsVideoMediaChanged', function () {
//replace element children with orignal content
element.children().remove();
element.append(origContent.clone());
init();
});
init();
}
};
}]);
return module;
}));
(function(annyang) {
'use strict';
function AnnyangService($rootScope) {
var service = {};
// COMMANDS
service.commands = {};
service.addCommand = function(phrase, callback) {
var command = {};
// Wrap annyang command in scope apply
command[phrase] = function(args) {
$rootScope.$apply(callback(args));
};
// Extend our commands list
angular.extend(service.commands, command);
// Add the commands to annyang
annyang.addCommands(service.commands);
console.debug('added command "' + phrase + '"', service.commands);
};
service.start = function() {
annyang.addCommands(service.commands);
annyang.debug(true);
annyang.start();
};
return service;
}
angular.module('myApp', ['vjs.video'])
.factory('AnnyangService', AnnyangService);
}(window.annyang));
(function(angular) {
'use strict';
function MyController(AnnyangService, $scope) {
$scope.vm = this;
$scope.vm.init = function() {
AnnyangService.addCommand('play', function() {
$scope.vm.play();
});
AnnyangService.addCommand('pause', function() {
$scope.vm.pause();
});
AnnyangService.addCommand('stop', function() {
$scope.vm.stop();
});
AnnyangService.start();
};
}
angular.module('myApp') // Why is controller ayCtrl "not registered"? Isn't that what...
.controller('ayCtrl', MyController); // <-- ...this is doing? Do I need to set the module to a var?
}
(window.angular));