I have a function that sends a request to a server and handles the response in a function. I then want to return the response, but I want it to return it to the parent function (getChannelId) so that for example getChannelId('someUsername') would return "someChannelId":
function getChannelId(username) {
function onSearchResponse(response) {
console.log(response);
var channelId = response.items[0].id;
return channelId; /* This is the value I want to return, but I want to return it within the getChannelId function */
console.log({channelId: channelId });
}
var request = window.gapi.client.youtube.channels.list({
part: 'snippet',
forUsername: username,
});
request.execute(onSearchResponse);
}
How do I get the response back to the parent function?
In getchannelid, you should have a variable that the inside function sets. Instead of just returning the result there...
var tmp;
function onSearchResponse(....) {
....
tmp = channelId;
}
Then you can return tmp in the outer function.
Solved it by continuing the schedule in the onSearchRespone callback.
Full code:
requirejs.config({
baseUrl: '//cdnjs.cloudflare.com/ajax/libs',
paths: {
jquery: "jquery/2.0.3/jquery.min",
jqueryColor: "jquery-color/2.1.2/jquery.color.min",
annyang: "annyang/1.0.0/annyang.min",
prefixfree: "prefixfree/1.0.7/prefixfree.min",
gapi: "https://apis.google.com/js/client.js?onload=onGAPILoaded",
gjsapi: "http://www.google.com/jsapi?noext",
ytpapi: "https://www.youtube.com/iframe_api?noext"
/*,
ytdapi: ""*/
}
});
var player, Ice;
function shuffle(o) {
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function onGAPILoaded() {
console.log('Google Client API Loaded, Loading YouTube Data API..');
gapi.client.load('youtube', 'v3', onYTPAPILoaded);
}
function onYTPAPILoaded() {
console.log('YouTube Data API Loaded');
gapi.client.setApiKey('AIzaSyBt4dZrv29ut8U0xeYTRFrgH_rB8zil9_M');
}
function onYouTubeIframeAPIReady() {
player = new YT.Player('video-player', {
height: '720',
width: '1280',
suggestedQuality: 'hd720',
events: {
onStateChange: function(e) {
Ice.state = e.data;
console.log({ STATE: Ice.state });
}
}
});
}
requirejs(['jquery', 'jqueryColor', 'annyang', 'prefixfree', 'gapi', 'ytpapi', 'gjsapi'], function() {
Ice = new ice();
if(annyang) {
var commands = {
'Ice search youtube for *exp': function(param) {
Ice.execute('watchSearchList', param);
},
'Ice watch (youtube) channel': function(param) {
Ice.execute('watchChannel', param);
},
'Ice play': function(param) {
Ice.execute('togglePlayback', param);
},
'Ice fit': function(param) {
Ice.execute('fit', param);
},
'test *exp': Ice.test
};
annyang.init(commands);
annyang.start();
}
function ice() {
/*
* Variables of Ice
*/
this.props = {
order: 'viewCount',
part: 'snippet',
type: 'video',
maxResults: 50,
q: '',
channelId: ''
}
this.state = -1;
this.intelligence = 9001;
/*
* Functions of Ice
*/
/* Debug Functions */
this.execute = function(func, param) {
Ice.flash();
Ice.log(func);
Ice[func](param);
};
this.test = function(term) {
Ice.log(term);
};
this.flash = function() {
var oldColor = $('h1').css('color');
$('h1').animate({ 'color': 'black' }, function() {
$(this).animate({ 'color': oldColor });
});
}
this.log = function(log) {
$('h1 span').text(log);
};
/* Video Functions */
this.togglePlayback = function() {
var func = (Ice.state == 1 ? 'pause':'play')+'Video';
console.log(func);
player[func]();
};
this.fit = function() {
var W = $(window).innerWidth(),
H = $(window).innerHeight();
console.log({ W: W, H: H });
player.setSize(W, H);
$('.video-player').addClass('full').css({
'width': W+'px',
'height': H+'px'1
});
};
this.watchChannel = function() {
Ice.props.channelId = Ice.getChannelId(prompt('Write Channel:', 'EthosLab'));
console.log({ channelId: Ice.props.channelId });
Ice.props.order = 'date';
Ice.playVideos();
}
this.watchSearchList = function(q) {
Ice.props.q = q;
Ice.playVideos();
}
this.playVideos = function() {
function onSearchResponse(response) { showResponse(response); }
function showResponse(response) {
console.log(response);
videoList = [];
for(var i = 0; i < response.items.length; i++) {
videoList.push(response.items[0].id.videoId);
}
// videoList = shuffle(videoList);
console.log({ videoList: videoList });
player.loadPlaylist(videoList);
$('.video-player').animate({
'height': '720px',
'width': '1280px',
});
}
console.log(Ice.props);
var request = window.gapi.client.youtube.search.list(Ice.props);
// Send the request to the API server,
// and invoke onSearchRepsonse() with the response.
request.execute(onSearchResponse);
};
/* YouTube functions */
this.setChannelIdFromUsername = function(username) {
Ice.props.channelId = Ice.getChannelId(username);
};
this.getChannelId = function(username) {
var channelId = '';
function onSearchResponse(response) {
console.log(response);
Ice.props.channelId = response.items[0].id;
Ice.playVideos();
}
var request = window.gapi.client.youtube.channels.list({
part: 'snippet',
forUsername: username,
});
request.execute(onSearchResponse);
console.log({channelId: channelId });
return channelId;
};
}
this.returnChannelId = function() {
};
});
Related
I'm having issues with the new CAF receiver not registering the YouTube iframe player as a player, and that it is playing. After 5 minutes connected to the receiver, the connection is broken because it thinks that the player is idle.
This is the sender-code
var metadata = new chrome.cast.media.GenericMediaMetadata();
metadata.title = "Foo - Bar";
metadata.image = 'https://img.youtube.com/vi/IXNrHusLXoM/mqdefault.jpg';
metadata.images = ['https://img.youtube.com/vi/IXNrHusLXoM/mqdefault.jpg'];
var mediaInfo = new chrome.cast.media.MediaInfo();
mediaInfo.contentType = "video/*";
mediaInfo.contentId ="IXNrHusLXoM";
mediaInfo.duration = 300;
var request = new chrome.cast.media.LoadRequest();
request.media = mediaInfo;
request.customData = customData;
request.metadata = metadata;
castSession.loadMedia(request).then(
function() {
console.log('Load succeed');
},
function(errorCode) {
console.log('Error code: ' + errorCode);
});
The receiver code can be found here: https://github.com/zoff-music/zoff-chromecast-receiver/blob/feature/v3/receiver.js
Is there any way of having the new CAF receiver hook into the YouTube iframe player, or "manually" dispatch LOADED, BUFFERING, PLAYING, PAUSED, STOPPED events so that the receiver doesn't disconnect from the sender?
Edit: with the above code, the PlayerState gets to the BUFFERING stage, but stops there. The promise with the log "Load succeed" is never triggered.
I managed to trick the receiver with fake mediaEelement. You can see the code in pastebin
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
var yt_events = {};
var pause_request = false;
var yt_player;
var yt_video_fake = {
removeAttribute: function(attr) {
},
setAttribute: function(attr, val) {
},
getCurrentTimeSec: function() { return yt_player && yt_player.getCurrentTime ? yt_player.getCurrentTime() : 0; },
getDurationSec: function() { return yt_player ? yt_player.getDuration() : 0; },
getVolume: function() {
if(!yt_player || !yt_player.getVolume) {
return 0;
}
var volume = new cast.framework.messages.Volume();
volume.level = yt_player.getVolume() / 100;
volume.muted = yt_player.isMuted() ? true : false;
return volume;
},
setVolume: function(vol) { yt_player && yt_player.setVolume(vol.level * 100); },
getState: function() {
if(!yt_player || !yt_player.getPlayerState) {
return 'IDLE';
}
var state = yt_player.getPlayerState();
var _state;
if(pause_request) {
pause_request = false;
state = YT.PlayerState.PAUSED;
}
switch(state) {
default: case YT.PlayerState.UNSTARTED:
_state = 'IDLE';
break;
case YT.PlayerState.PLAYING:
_state = 'PLAYING';
break;
case YT.PlayerState.PAUSED:
_state = 'PAUSED';
break;
case YT.PlayerState.BUFFERING:
_state = 'BUFFERING';
break;
case YT.PlayerState.ENDED:
_state = 'ENDED';
break;
}
return _state;
},
addEventListener: function(e, func) { },
load: function() {},
play: function() { yt_player && yt_player.playVideo(); },
pause: function() { if(yt_player && yt_player.pauseVideo) {pause_request = true; yt_player.pauseVideo(); }},
seek: function(timeTo) { yt_player && yt_player.seekTo(timeTo, true);},
reset: function() {
if(yt_player) {
try { yt_player.destroy && yt_player.destroy(); } catch(e) {
//console.trace(e);
};
delete yt_player;
}
},
registerErrorCallback: function(func) { yt_events['error'] = func; },
registerEndedCallback: function(func) { yt_events['ended'] = func; },
registerLoadCallback: function(func) { yt_events['load'] = func; },
unregisterErrorCallback: function () { delete yt_events['error'] },
unregisterEndedCallback: function () { delete yt_events['ended']},
unregisterLoadCallback: function () { delete yt_events['load']}
};
Object.defineProperty(yt_video_fake, 'currentTime', {
val1: null,
get: function() { return yt_player && yt_player.getCurrentTime ? yt_player.getCurrentTime() : this.val1; },
set: function(newValue) {
yt_player && yt_player.seekTo(newValue, true);
this.val1 = newValue;
},
enumerable : true,
configurable : true
});
Object.defineProperty(yt_video_fake, 'volume', {
val1: null,
get: function() { var vol = this.getVolume(); if(vol) return vol['level']; return 1; },
set: function(newValue) {
yt_player && yt_player.setVolume && yt_player.setVolume(newValue * 100);
this.val1 = newValue;
},
enumerable : true,
configurable : true
});
Object.defineProperty(yt_video_fake, 'duration', {
val1: null,
get: function() { return this.getDurationSec(); },
set: function() {},
enumerable : true,
configurable : true
});
function YoutubePlayMedia(videoid) {
var yt_container = $('#yt_container');
if(!yt_container.length) {
yt_container = $('<div id="yt_container" style="position:absolute;top:0;left:0;width:100%;height:100%;"></div>');
$('body').append(yt_container);
}
yt_container.html('<iframe id="youtube_container" style="width:100%;height:100%;" frameborder="0" allowfullscreen="1" allow="autoplay; encrypted-media" title="YouTube video player" src="//www.youtube.com/embed/' + videoid +'?autoplay=1&enablejsapi=1&modestbranding=1&controls=0&fs=0&iv_load_policy=3&rel=0&cc_load_policy=1&cc_lang_pref=bg"></iframe>');
yt_player = new YT.Player('youtube_container', {
events: {
'onReady': function(e) {
yt_player.is_loaded = true;
yt_player.playVideo();
},
'onStateChange': function(e) {
switch(e.data) {
case YT.PlayerState.PLAYING:
if(yt_player.is_loaded) {
if(yt_events['load']) {
yt_events['load']();
}
}
break;
case YT.PlayerState.ENDED:
//yt_events['ended'] && yt_events['ended'](e);
break;
}
},
'onError': function(e) {
//yt_events['error'] && yt_events['error'](e);
}
}
});
}
function YoutubeLoadMedia(url) {
current_media_type = 'Youtube';
window.onYouTubeIframeAPIReady = function() {
window.youtube_loaded = true;
YoutubePlayMedia(url);
}
if(!window.youtube_script) {
window.youtube_script = document.createElement('script');
window.youtube_script.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(window.youtube_script, firstScriptTag);
} else {
// Вече имаме api направо действаме
YoutubePlayMedia(url);
}
}
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
loadRequestData => {
if (loadRequestData.media && loadRequestData.media.contentId) {
YoutubeLoadMedia(loadRequestData.media.contentId);
playerManager.setMediaElement(yt_video_fake);
return false;
}
return loadRequestData;
}
);
const options = new cast.framework.CastReceiverOptions();
options.disableIdleTimeout = true;
context.start(options);
The YouTube iframe player is not designed to be a player for a Cast receiver.
I want the use set method of factory but both of return default how can I fix that problem?
app.factory("DualListShareFactory", function(){
var selectedArray=[];
return{
getSelectedArray: function () {
return selectedArray;
},
setSelectedArray: function (array){
selectedArray=array;
}
}
});
Using ng-dual List from https://github.com/tushariscoolster/ng-duallist
I'm tkining it not working: DualListShareFactory.setSelectedArray(vm.rightValue);
I use other method for and push but I am received same error .
app.controller("duallist2", function($scope,DualListShareFactory){
var vm=this;
vm.property='duallist2';
activate();
function activate() {
vm.leftValue = [];
vm.rightValue = [];
vm.addValue = [];
vm.removeValue = [];
function loadMoreLeft() {
for (var i = 0; i < $scope.incomingItem.length; i++) {
vm.leftValue.push({
'name': $scope.incomingItem[i]
});
}
};
function loadMoreRight() {
}
vm.options = {
leftContainerScrollEnd: function () {
},
rightContainerScrollEnd: function () {
},
leftContainerSearch: function (text) {
console.log(text)
vm.leftValue = $filter('filter')(leftValue, {
'name': text
})
},
rightContainerSearch: function (text) {
vm.rightValue = $filter('filter')(rightValue, {
'name': text
})
},
leftContainerLabel: 'Gelen Parçalar',
rightContainerLabel: 'Seçilen Parçalar',
onMoveRight: function () {
console.log('right');
console.log(vm.addValue);
},
onMoveLeft: function () {
console.log('left');
console.log(vm.removeValue);
}
};
loadMoreLeft();
var leftValue = angular.copy(vm.leftValue);
var rightValue = angular.copy(vm.rightValue);
} console.log(vm.rightValue);
DualListShareFactory.setSelectedArray(vm.rightValue);
});
I am not this will work but try "this",
app.factory("DualListShareFactory", function(){
this.selectedArray=[];
return{
getSelectedArray: function () {
return this.selectedArray;
},
setSelectedArray: function (array){
this.selectedArray = array;
}
}
});
var app = angular.module("testapp", ["ng-duallist"]);
app.factory("DualListShareFactory", function(){
var selectedArray = [];
return{
getSelectedArray: function () {
return selectedArray
},
setSelectedArray: function (array){
angular.copy(array, selectedArray);
}
}
});
using for this add the other code block before ending function.
onMoveRight: function () {
DualListShareFactory.setSelectedArray(vm.rightValue);
},
onMoveLeft: function () {
DualListShareFactory.setSelectedArray(vm.rightValue);
}
};
loadMoreLeft();
var leftValue = angular.copy(vm.leftValue);
var rightValue = angular.copy(vm.rightValue);
I have a controller that works fine on initial load. It calls user [0] data and everything processes fine. When I change dropdown, I want to call the same function, but I cannot get the entire controller to reload. It starts from the function call and leaves undefined in places where it pulls correct information (linkToken, etc) on initial load. Is there a way I can get it to reload all data from the controller instead of just from the function?
After calling the testchange() from the view to pass in the new data, I get :
TypeError: Cannot read property 'locations' of undefined
at h.$scope.updateLocations (refillController.js:261)
at refillController.js:73
But, when I call the original getRefills() that is ran on the initial page load I get the same error. How can I define these so the load after the onchange(0)?
angular.module('FinalApp.Controllers').controller('refillController', function ($rootScope, $scope, $location, $modal, userService, dependentsService, accountService, sharedCollections, configurationService, refillsService) {
$scope.user = userService.GetUserInformation();
$scope.userInfoArr = [];
//$scope.tests.push({'Name':$scope.user.FirstName, 'LinkToken':$scope.user.LinkToken});
$scope.userInfoArr = $scope.user.Dependants.map(function(item){return {'Name':item.FirstName, 'LinkToken':item.LinkToken}});
$scope.userInfoArr.splice(0, 0, {'Name': $scope.user.FirstName, 'LinkToken': $scope.user.LinkToken});
console.log($scope.userInfoArr);
$scope.finUserInfoArr = $scope.userInfoArr[0].LinkToken;
$scope.billingInfo = null;
$rootScope.showNavbar = true;
$scope.selectedMethod = null;
$scope.location = null;
$scope.payment = null;
$scope.refills = [];
$scope.deliverTypes = [];
$scope.locations = [];
$scope.payments = [];
$scope.allSelected = false;
$scope.loadingBillingInfo = false;
$scope.isMailOrder = false;
//Detect Mobile Switch Refill List To Grid
if(window.innerWidth <= 800) {
$scope.view = "Grid";
} else {
$scope.view = "List";
}
$scope.changeViewToList = function () {
$scope.view = "List";
};
$scope.changeViewToGrid = function () {
$scope.view = "Grid";
};
$scope.testchange = function(selectedTest) {
$scope.getRefills(selectedTest);
console.log(selectedTest);
};
$scope.getRefills = function (linkToken) {
$scope.allSelected = false;
$scope.loading = true;
refillsService.GetRefills(
linkToken,
$scope.selectedMethod,
$scope.location,
$scope.payment
).then(function (data) {
$scope.refills = [];
_.each(data.Prescriptions, function (item) {
fillRefills(item);
});
fillDeliverTypes(data.DeliveryTypes);
if (!$scope.selectedMethod)
$scope.selectedMethod = data.DeliveryTypeId;
if (!$scope.location)
$scope.location = data.PickupLocationId;
if (!$scope.payment)
$scope.payment = data.PaymentTypeId;
$scope.loading = false;
$scope.updateLocations();
})["catch"](function (error) {
console.log(error);
$scope.loading = false;
alertify.alert(configurationService.ErrorMessage("getting your refills", error.Message));
});
};
var fillRefills = function (item) {
//TODO-CallDoc temp fix
if (item.RefillClass == "CALL_DOCTOR") {
item.NextRefillDate = '1900-01-01T00:00:00'
}
var parsedDate = checkDate(moment(item.NextRefillDate).format('L'));
var lastrefill = checkDate(moment(item.LastDispenseDate).format('L'));
var expireDate = checkDate(moment(item.ExpirationDate).format('L'));
var status = (item.RefillStatus.indexOf("After") == -1) ? item.RefillStatus : "Refill " + item.RefillStatus;
$scope.refills.push({
selected: false,
rx: item.ScriptNo,
name: item.DrugName,
dose: item.UnitsPerDose,
dir: item.Instructions,
nextfill: parsedDate,
lastfill: lastrefill,
refillsLeft: item.NumRefillsLeft,
status: status,
msg: item.RefillMessage,
canSelect: item.IsRefillable,
refillClass: item.RefillClass,
lastDispenseQty: item.LastDispenseQty,
DaysSupply: item.DaysSupply,
expireDate: expireDate,
copayAmt: item.CopayAmt,
drFirstName: item.DoctorFirstName,
drLastName: item.DoctorLastName,
writtenQty: item.WrittenQty
});
};
var checkDate = function (date) {
if (date == "01/01/1900") return "N/A";
if (date == "Invalid Date") return "";
return date;
};
var fillDeliverTypes = function (deliverTypes) {
$scope.deliverTypes = [];
_.each(deliverTypes, function (item) {
$scope.deliverTypes.push({
id: item.DeliveryTypeId,
name: item.DeliveryTypeName,
locations: item.PickupLocations,
payments: item.PaymentTypes
});
});
};
var getBillingInfo = function () {
$scope.loadingBillingInfo = true;
accountService.GetCreditCardInfo().then(function (data) {
$scope.billingInfo = data;
$scope.loadingBillingInfo = false;
})["catch"](function (error) {
$scope.loadingBillingInfo = false;
alertify.alert(configurationService.ErrorMessage("getting account information", error.Message));
});
};
var getAccountInfo = function () {
accountService.GetAccountInfo().then(function (data) {
if (data.StatusCode == "SUCCESS") {
$scope.user = data;
userService.SaveUserInformation(data);
if ($scope.user.IsLinked) {
$rootScope.enableMyRefills = true;
$rootScope.enableMyReports = true;
window.location.hash = "#/refills";
} else {
$rootScope.enableMyRefills = false;
$rootScope.enableMyReports = true;
}
} else {
alertify.alert(configurationService.ErrorMessage("getting account information", data.StatusMessage));
}
})["catch"](function (error) {
alertify.alert(configurationService.ErrorMessage("getting account information", error.Message));
});
};
var openModal = function (viewUrl, controllerUrl, size, payload) {
var modalInstance = $modal.open({
templateUrl: viewUrl,
controller: controllerUrl,
size: size,
resolve: {
data: function () {
return payload;
}
}
});
return modalInstance;
};
$scope.toggleRxSelection = function(rx) {
if (rx.canSelect) {
rx.selected = !rx.selected;
$scope.evaluateAllSelected();
}
};
$scope.selectAll = function (data) {
// $scope.allSelected = allSelected;
_.each($scope.refills, function (x) {
if (x.canSelect) x.selected = data;
});
};
$scope.evaluateAllSelected = function () {
var count = _.countBy(_.where($scope.refills, {canSelect:true}), function(refill) {
return refill.selected ? 'selected' : 'not';
});
$scope.allSelected = (count.not === undefined);
};
$scope.openEditCreditCardInfo = function () {
var payload = ($scope.billingInfo != null && $scope.billingInfo != undefined)
&& $scope.billingInfo.CardNumber != "" ? $scope.billingInfo : {};
if (payload != {}) {
payload.ExpMonth = {id: parseInt(payload.ExpMonth)};
payload.ExpYear = {id: parseInt(payload.ExpYear)};
}
openModal('app/views/editAccount/billingInformation.html', "billingInformationController", "xlg", payload).result.then(function () {
getAccountInfo();
getBillingInfo();
}, function () {
getBillingInfo();
});
};
$scope.openConfirmOrder = function () {
var refillsSelected = _.where($scope.refills, {selected: true});
var location = _.findWhere($scope.locations, {PickupLocationId: $scope.location});
var payment = _.findWhere($scope.payments, {PaymentTypeId: $scope.payment});
var deliver = _.findWhere($scope.deliverTypes, {id: $scope.selectedMethod});
if (refillsSelected.length == 0) {
alertify.error("You need to select at least one refill");
return;
}
if (deliver.id == 10001 && !$scope.user.IsCreditCardOnFile) {
alertify.error("Need credit card on file for mail order");
return;
}
sharedCollections.setRefills(refillsSelected);
sharedCollections.setLocation(location);
sharedCollections.setPayment(payment);
sharedCollections.setDeliver(deliver);
openModal('app/views/refills/confirmOrder.html', "confirmOrderController", "xlg").result.then(function () {
$scope.billingInfo = accountService.GetCreditCardInfo();
$scope.getRefills();
}, function () {
//$scope.billingInfo = accountService.GetCreditCardInfo();
//getRefills();
});
};
$scope.showRefill = function (rx) {
var data = {rx: rx, refills: $scope.refills};
openModal('app/views/refills/showRefills.html', "refillsCarrousel", "xlg", data).result.then(function () {
$scope.evaluateAllSelected();
}, function () {
$scope.evaluateAllSelected();
});
};
$scope.updateLocations = function () {
$scope.locations = _.findWhere($scope.deliverTypes, {id: $scope.selectedMethod}).locations;
$scope.payments = _.findWhere($scope.deliverTypes, {id: $scope.selectedMethod}).payments;
setLocationAndPayment();
};
var setLocationAndPayment = function () {
if ($scope.locations.length == 1) {
$scope.location = $scope.locations[0].PickupLocationId;
}
if ($scope.payments.length == 1) {
$scope.payment = $scope.payments[0].PaymentTypeId;
}
//check for mail order
($scope.selectedMethod == 10001 && !$scope.payment) ? $scope.isMailOrder = true : $scope.isMailOrder = false;
};
$scope.getRefills($scope.finUserInfoArr);
getBillingInfo();
});
Check if your refillsService returns correct data, it could be that $scope.refills remains empty array.
Thanks to other people's questions, I have gotten AJAX links to work for individual songs when clicked in the DOM. My problem is that I need to find a way to load all of the tracks under a given user's account on soundcloud. I edited the stratus.js file to make it listen for the click by using .delegate(), but I can't figure out how to call the track listing from soundcloud and load it through the given iframe. This is the stratus.js file.
(function() {
var $;
$ = jQuery;
(function($){var g,d,j=1,a,b=this,f=!1,h="postMessage",e="addEventListener",c,i=b[h]&&!$.browser.opera;$[h]=function(k,l,m){if(!l){return}k=typeof k==="string"?k:$.param(k);m=m||parent; if(i){m[h](k,l.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))}else{if(l){m.location=l.replace(/#.*$/,"")+"#"+(+new Date)+(j++)+"&"+k}}};$.receiveMessage=c=function(l,m,k){if(i){if(l){a&&c(); a=function(n){if((typeof m==="string"&&n.origin!==m)||($.isFunction(m)&&m(n.origin)===f)){return f}l(n)}}if(b[e]){b[l?e:"removeEventListener"]("message",a,f)} else{b[l?"attachEvent":"detachEvent"]("onmessage",a)}}else{g&&clearInterval(g);g=null;if(l){k=typeof m==="number"?m:typeof k==="number"?k:100; g=setInterval(function(){var o=document.location.hash,n=/^#?\d+&/;if(o!==d&&n.test(o)){d=o;l({data:o.replace(n,"")})}},k)}}}})(jQuery);
$.fn.stratus = function(settings) {
return $.stratus(settings);
};
$.stratus = function(settings) {
var root_url, src;
root_url = settings.env === 'development' ? 'http://example.com:3000' : 'http://stratus.sc';
$('head').append("<link rel='stylesheet' href='" + root_url + "/stratus.css' type='text/css'/>");
if (settings.align === 'top') {
$('head').append("<style>#stratus{ top: 0; }</style>");
}
if (settings.position === 'absolute') {
$('head').append("<style>#stratus{ position: absolute; }</style>");
}
if (settings.offset) {
$('head').append("<style>#stratus{ " + settings.align + ": " + settings.offset + "px !important; }</style>");
}
$('body').append("<div id='stratus'><iframe allowtransparency='true' frameborder='0' scrolling='0'></div>");
src = root_url + '/player?' + $.param(settings, true) + '&link=' + encodeURIComponent(document.location.href);
$('#stratus iframe').attr({
src: src
});
$('#stratus iframe').load(function() {// /resolve?url=
return $(this).css({
visibility: 'visible'
});
});
$('#stratus').show();
$(document).delegate("a.stratus","click", function () {
$.postMessage($(this).attr('href'), src, $('#stratus iframe')[0].contentWindow);
return false;
});
return $.receiveMessage(function(e) {
return $('#stratus').toggleClass('open');
}, root_url);
};
}).call(this);
I believe that a call through the Soundcloud API will be necessary, then a run through the user data for individual track additions through the iframe. This is the player.js file, which I'm not sure is necessary for this post, but it helped me begin to understand what was happening with the initial adding of tracks from the customisable Links field:
console.log("Stratus loading...");
$(function() {
var b, booleans, getScaledImageData, link, params, scApiUrl, source_origin, strtobool, timecode, _i, _len;
window.Track = Backbone.Model.extend({
initialize: function() {
var that, track;
that = this;
track = this.attributes;
track.timecode = timecode(track.duration);
return soundManager.createSound({
id: "sound_" + track.id,
multiShot: false,
url: track.stream_url + (/\?/.test(track.stream_url) ? '&' : '?') + 'consumer_key=' + Stratus.options.key,
volume: Stratus.options.volume,
whileplaying: function() {
Stratus.$('.played').width((this.position / track.duration * 100) + '%');
return Stratus.$('#player .duration').text(timecode(this.position) + ' / ' + timecode(track.duration));
},
whileloading: function() {
return Stratus.$('.loaded').width((this.bytesLoaded / this.bytesTotal * 100) + '%');
},
onplay: function() {
if (this.loaded) {
return Stratus.$('.loaded').width('100%');
}
},
onresume: function() {
if (this.loaded) {
return Stratus.$('.loaded').width('100%');
}
},
onfinish: function() {
return Stratus.nextTrack();
}
});
},
sound: function() {
return "sound_" + this.id;
},
play: function() {
return soundManager.play(this.sound());
},
pause: function() {
return soundManager.pause(this.sound());
},
seek: function(relative) {
return soundManager.setPosition(this.sound(), this.get('duration') * relative);
},
getWave: function(callback) {
var that;
that = this;
return $.getJSON('http://wave64.heroku.com/w?callback=?', {
url: this.get('waveform_url')
}, function(data) {
var waveform;
waveform = new Image();
waveform.src = data.data;
return waveform.onload = function() {
var waveform_data;
waveform_data = getScaledImageData(waveform);
that.set({
'waveform_data': waveform_data
});
return callback();
};
});
},
comment: function(text) {
return SC.post("/tracks/" + this.id + "/comments", {
"comment[body]": text
}, function() {
Stratus.$('#comment input').val('');
Stratus.toggleComment();
return alert("Comment posted!");
});
},
favorite: function() {
if (Stratus.$('.love').hasClass('loved')) {
return SC["delete"]("/me/favorites/" + this.id, function() {
return Stratus.$('.love').removeClass('loved');
});
} else {
return SC.put("/me/favorites/" + this.id, function() {
return Stratus.$('.love').addClass('loved');
});
}
},
isFavorite: function() {
return SC.get("/me/favorites/" + this.id, function(data) {
if (!(data.errors != null)) {
return Stratus.$('.love').addClass('loved');
}
});
}
});
window.TrackList = Backbone.Collection.extend({
model: Track,
select: function(track) {
this.stop();
this.current = track;
return this.trigger('player:select');
},
toggle: function(track) {
if (track && this.current !== track) {
this.select(track);
}
if (this.playing) {
return this.pause();
} else {
return this.play();
}
},
play: function(track) {
if (track && this.current !== track) {
this.select(track);
}
this.playing = true;
this.current.play();
return this.trigger('player:toggle');
},
pause: function() {
this.playing = false;
this.current.pause();
return this.trigger('player:toggle');
},
stop: function() {
this.playing = false;
return soundManager.stopAll();
},
prev: function() {
var i;
i = this.indexOf(this.current) - 1;
if (i > -1) {
return this.at(i);
} else {
return this.last();
}
},
next: function() {
var i;
i = this.indexOf(this.current) + 1;
if (i < _.size(this)) {
return this.at(i);
} else {
return this.first();
}
},
random: function() {
var i;
i = Math.round(Math.random() * _.size(this));
return this.at(i);
}
});
window.Tracks = new TrackList();
window.TrackView = Backbone.View.extend({
tagName: "li",
events: {
"click": "toggleTrack"
},
render: function() {
return $(this.el).html(ich.track(this.model.toJSON()));
},
toggleTrack: function() {
return Tracks.toggle(this.model);
}
});
window.AppView = Backbone.View.extend({
el: $('#stratus'),
defaults: {
align: 'bottom',
animate: 'slide',
auto_play: false,
buying: true,
color: 'F60',
download: true,
env: 'production',
key: 'ybtyKcnlhP3RKXpJ58fg',
links: ['http://soundcloud.com/qotsa/sets/test'],
random: false,
redirect: 'http://stratus.sc/callback.html',
user: true,
stats: true,
volume: 50
},
events: {
"dblclick": "showDrawer",
"click .prev": "prevTrack",
"click .toggle": "toggleCurrent",
"click .next": "nextTrack",
"click #time": "seekTrack",
"mousemove #time": "movePosition",
"click .share": "toggleShare",
"click .close.sharing": "toggleShare",
"click .comment": "toggleComment",
"click .close.commenting": "toggleComment",
"keypress #add input": "commentTrack",
"click .love": "favoriteTrack",
"click #avatar": "logout",
"click .popup": "popupPlayer"
},
initialize: function() {
var options, that;
console.log("Stratus initializing...");
that = this;
this.options = options = _.extend(this.defaults, this.options);
Tracks.bind('add', this.add, this);
Tracks.bind('player:select', this.render, this);
Tracks.bind('player:toggle', this.toggle, this);
SC.initialize({
client_id: options.key,
redirect_uri: options.redirect
});
return SC.whenStreamingReady(function() {
return that.loadTracks(options.links, function() {
Tracks.select(options.random ? Tracks.random() : Tracks.first());
if (options.auto_play) {
Tracks.play();
}
if (options.align === 'top') {
options.top = true;
}
options.color = {
base: tinycolor(options.color).toHexString(),
light: tinycolor.lighten(options.color).toHexString(),
dark: tinycolor.darken(options.color).toHexString()
};
$('head').append(ich.theme(options));
if (SC.isConnected()) {
that.updateUser();
}
return that.animate(function() {
return that.resize();
});
});
});
},
loadTracks: function(links, callback) {
var index, loadURL;
index = 0;
loadURL = function(link) {
var url;
console.log("Loading " + link + "...");
url = scApiUrl(link);
return SC.get(url, function(data) {
index += 1;
if (data.tracks) {
Tracks.add(data.tracks);
} else if (data.username || data.creator) {
links.push(data.uri + '/tracks');
} else {
Tracks.add(data);
}
if (links[index]) {
return loadURL(links[index]);
} else {
return callback();
}
});
};
return loadURL(links[index]);
},
render: function() {
var artwork, data, el, that, track;
that = this;
track = Tracks.current;
data = Tracks.current.toJSON();
el = this.$('#tracks .track_' + data.id);
this.$('#player .track').html(ich.current(data));
this.$('#buttons').html(ich.buttons(data));
this.$('#stats').html(ich.stats(data));
this.$('#share').html(ich.share(data));
artwork = data.artwork_url ? data.artwork_url : data.user.avatar_url;
this.$('#artwork img').attr({
src: artwork.replace('-large', '-t300x300')
});
el.addClass('current').siblings().removeClass('current');
if (track.has('waveform_data')) {
this.updateWave(track);
} else {
track.getWave(function() {
return that.updateWave(track);
});
}
if (SC.isConnected()) {
track.isFavorite();
}
return this.resize();
},
add: function(track) {
var view;
view = new TrackView({
model: track,
className: 'track track_' + track.id
});
return this.$("#tracks").append(view.render());
},
toggle: function() {
return this.$('#player').toggleClass('playing', Tracks.playing);
},
toggleCurrent: function() {
Tracks.toggle();
return false;
},
prevTrack: function() {
Tracks.play(Tracks.prev());
return false;
},
nextTrack: function() {
Tracks.play(Tracks.next());
return false;
},
seekTrack: function(e) {
var relative;
if (!Tracks.playing) {
Tracks.play();
}
relative = Math.min(this.$('.loaded').width(), (e.pageX - this.$('#time').offset().left) / this.$('#time').width());
Tracks.current.seek(relative);
return false;
},
movePosition: function(e) {
return this.$('.position').css({
"left": e.pageX - this.$('#time').offset().left
});
},
updateWave: function(track) {
var canvas, context;
canvas = this.$('#waveform').get(0);
context = canvas.getContext('2d');
canvas.setAttribute('width', 180);
canvas.setAttribute('height', 40);
context.clearRect(0, 0, 180, 40);
return context.putImageData(track.get('waveform_data'), 0, 0);
},
animate: function(callback) {
if (this.options.popup) {
this.$('#player, #drawer').fadeIn('slow');
}
switch (this.options.animate) {
case 'slide':
return this.$('#player').slideDown('slow', function() {
return callback();
});
case 'fade':
return this.$('#player').fadeIn('slow', function() {
return callback();
});
default:
return this.$('#player').show(0, function() {
return callback();
});
}
},
resize: function() {
this.$('#share').css({
"margin-right": this.$('#buttons').width() - 30
});
return this.$('#comment').css({
"margin-right": this.$('#buttons').width() - 60
});
},
showDrawer: function() {
this.$('#drawer').toggle();
return $.postMessage(true, source_origin, parent);
},
popupPlayer: function() {
Tracks.stop();
this.toggle();
return $.popupWindow($.url().attr('source') + '&popup=true', {
height: 199,
width: 800,
location: false
});
},
toggleShare: function() {
this.$('#share').toggle();
this.$('#share input').select();
return false;
},
toggleComment: function() {
var that;
that = this;
if (SC.isConnected()) {
this.$('#comment').toggle();
this.$('#comment input').select();
} else {
this.login(function() {
return that.toggleComment();
});
}
return false;
},
commentTrack: function(e) {
var text;
text = this.$('#comment input').val();
if (e.keyCode === 13) {
return Tracks.current.comment(text);
}
},
favoriteTrack: function() {
if (SC.isConnected()) {
Tracks.current.favorite();
} else {
this.login(function() {
return Tracks.current.favorite();
});
}
return false;
},
login: function(callback) {
var that;
that = this;
return SC.connect(function(user) {
that.updateUser();
return callback();
});
},
updateUser: function() {
var that;
that = this;
return SC.get("/me", function(user) {
return that.$('#avatar').attr({
src: user.avatar_url
});
});
},
logout: function() {
SC.disconnect();
return alert("Logged out.");
}
});
link = decodeURIComponent($.url().param('link'));
source_origin = $.url(link).attr('base');
$.receiveMessage(function(e) {
var result, url;
url = e.data;
result = Tracks.find(function(track) {
return track.get('permalink_url') === url;
});
if (result) {
return Tracks.toggle(result);
} else {
return SC.get("/resolve", {
url: url
}, function(track) {
Tracks.add(track);
return Tracks.play(Tracks.get(track.id));
});
}
}, source_origin);
scApiUrl = function(url) {
if (/api\./.test(url)) {
return url;
} else {
return "/resolve?url=" + url;
}
};
timecode = function(ms) {
return SC.Helper.millisecondsToHMS(ms);
};
strtobool = function(str) {
switch (str) {
case 'true':
return true;
case true:
return true;
default:
return false;
}
};
getScaledImageData = function(image) {
var color, height, isImageData, lastIndex, orig, origCtx, origImageData, origWidth, populateScaledImagedData, precise, scaleX, scaleY, scaled, scaledCtx, scaledImageData, width, x, y;
color = Stratus.$('#player').css('background-color');
color = tinycolor(color).toRgb();
precise = function(number, precision) {
precision = Math.pow(10, precision || 0);
return Math.round(number * precision) / precision;
};
populateScaledImagedData = function(x, y, srcImageData, indexOffset) {
var alpha, index, indexScaled, isOpaque;
indexOffset = indexOffset || 0;
index = (Math.floor(y / scaleY) * origWidth + Math.floor(x / scaleX)) * 4;
indexScaled = indexOffset + (y * width + x) * 4;
alpha = srcImageData.data[index + 3];
isOpaque = alpha === 255;
scaledImageData.data[indexScaled] = isOpaque ? color['r'] : 0;
scaledImageData.data[indexScaled + 1] = isOpaque ? color['g'] : 0;
scaledImageData.data[indexScaled + 2] = isOpaque ? color['b'] : 0;
scaledImageData.data[indexScaled + 3] = alpha;
return indexScaled;
};
height = 40;
width = 180;
origWidth = image.width;
scaleX = precise(width / image.width, 4);
scaleY = precise(height / image.height, 4);
try {
isImageData = !(image instanceof Image);
} catch (e) {
isImageData = image.hasOwnProperty("data") && image.data.hasOwnProperty("length");
}
orig = document.createElement("canvas");
orig.width = image.width;
orig.height = image.height;
origCtx = orig.getContext("2d");
if (!isImageData) {
origCtx.drawImage(image, 0, 0, image.width, image.height, 0, 0, image.width, image.height);
} else {
origCtx.putImageData(image, 0, 0);
}
origImageData = origCtx.getImageData(0, 0, image.width, image.height);
scaled = document.createElement("canvas");
scaled.width = width;
scaled.height = height;
scaledCtx = scaled.getContext("2d");
scaledImageData = scaledCtx.getImageData(0, 0, width, height);
y = 0;
while (y < height) {
x = 0;
while (x < width) {
lastIndex = populateScaledImagedData(x, y, origImageData, 0);
x++;
}
y++;
}
return scaledImageData;
};
params = $.url().param();
if (params.links) {
params.links = decodeURIComponent(params.links);
params.links = params.links.split(',');
}
if (params.redirect) {
params.redirect = decodeURIComponent(params.redirect);
}
booleans = ['auto_play', 'buying', 'download', 'random', 'user', 'stats', 'popup'];
for (_i = 0, _len = booleans.length; _i < _len; _i++) {
b = booleans[_i];
if (params[b]) {
params[b] = strtobool(params[b]);
}
}
return window.Stratus = new AppView(params);
});
I'm aware that this is rather intensive, and I apologize for that. Just throwing up a prayer that I won't have to radically alter my website user's experience in a negative way.
I don't know about the stratus player, but in general - if you want to retrieve a list of tracks of a specific user - given that you know the user's id, you indeed have to make a call to the soundcloud API. You can do it like this:
SC.initialize({
client_id: "YOUR_CLIENT_ID",
redirect_uri: "http://example.com/callback.html",
});
/**
Once that's done you are all set and ready to call the SoundCloud API.
**/
/**
Call to the SoundCloud API.
Retrieves list of tracks, and displays a list with links to the tracks showing 'tracktitle' and 'track duration'
**/
var userId = 39090345; // user_id of Prutsonic
SC.get("/tracks", {
user_id: userId,
limit: 100
}, function (tracks) {
for (var i = 0; i < tracks.length; i++) {
console.log(tracks[i].title);
}
});
You can try my fiddle here: http://jsfiddle.net/tobiasbeuving/26pHX/5/
Cheers,
T
here are lines from a built in directive(blueimp)
.controller('FileUploadController', [
'$scope', '$element', '$attrs', '$window', 'fileUpload',
function ($scope, $element, $attrs, $window, fileUpload) {
alert('incontroller');
var uploadMethods = {
progress: function () {
return $element.fileupload('progress');
},
active: function () {
return $element.fileupload('active');
},
option: function (option, data) {
return $element.fileupload('option', option, data);
},
add: function (data) {
return $element.fileupload('add', data);
},
send: function (data) {
return $element.fileupload('send', data);
},
process: function (data) {
return $element.fileupload('process', data);
},
processing: function (data) {
return $element.fileupload('processing', data);
}
};
$scope.disabled = !$window.jQuery.support.fileInput;
$scope.queue = $scope.queue || [];
$scope.clear = function (files) {
var queue = this.queue,
i = queue.length,
file = files,
length = 1;
if (angular.isArray(files)) {
file = files[0];
length = files.length;
}
while (i) {
i -= 1;
if (queue[i] === file) {
return queue.splice(i, length);
}
}
};
$scope.replace = function (oldFiles, newFiles) {
var queue = this.queue,
file = oldFiles[0],
i,
j;
for (i = 0; i < queue.length; i += 1) {
if (queue[i] === file) {
for (j = 0; j < newFiles.length; j += 1) {
queue[i + j] = newFiles[j];
}
return;
}
}
};
$scope.applyOnQueue = function (method) {
var list = this.queue.slice(0),
i,
file;
for (i = 0; i < list.length; i += 1) {
file = list[i];
if (file[method]) {
file[method]();
}
}
};
$scope.submit = function () {
this.applyOnQueue('$submit');
};
$scope.cancel = function () {
this.applyOnQueue('$cancel');
};
// Add upload methods to the scope:
angular.extend($scope, uploadMethods);
// The fileupload widget will initialize with
// the options provided via "data-"-parameters,
// as well as those given via options object:
$element.fileupload(angular.extend(
{scope: function () {
return $scope;
}},
fileUpload.defaults
)).on('fileuploadadd', function (e, data) {
data.scope = $scope.option('scope');
}).on('fileuploadchange', function (e, data) {
data.scope = $scope.option('scope');
data.scope.extend({
mycustomfield:$element
//i added above line, is it illegal, or too ugly to get element,or needless,some other way?
});
}).on([
'fileuploadadd',
'fileuploadsubmit',
'fileuploadsend',
'fileuploaddone',
'fileuploadfail',
'fileuploadalways',
'fileuploadprogress',
'fileuploadprogressall',
'fileuploadstart',
'fileuploadstop',
'fileuploadchange',
'fileuploadpaste',
'fileuploaddrop',
'fileuploaddragover',
'fileuploadchunksend',
'fileuploadchunkdone',
'fileuploadchunkfail',
'fileuploadchunkalways',
'fileuploadprocessstart',
'fileuploadprocess',
'fileuploadprocessdone',
'fileuploadprocessfail',
'fileuploadprocessalways',
'fileuploadprocessstop'
].join(' '), function (e, data) {
if ($scope.$emit(e.type, data).defaultPrevented) {
e.preventDefault();
}
}).on('remove', function () {
// Remove upload methods from the scope,
// when the widget is removed:
var method;
for (method in uploadMethods) {
if (uploadMethods.hasOwnProperty(method)) {
delete $scope[method];
}
}
});
// Observe option changes:
$scope.$watch(
$attrs.fileUpload,
function (newOptions) {
if (newOptions) {
$element.fileupload('option', newOptions);
}
}
);
}
])
.directive('fileUpload', function () {
return {
controller: 'FileUploadController',
scope: true
};
})
i can see reaction on my template controller
$scope.$on('fileuploadchange',function(e,data){
//here i want to get element which is the source of event
//if you can look at above code piece, i think i can get the element by
//data.scope.mycustomfield
}
some nicer way?