setTimeout and clearTimeout interruption - javascript

I'm trying to make like a drop alert queue.
So far i've achieved it. The problem comes when I try to implement interruptions.
Let's say that an offline event fires one alert, and before it end's it's timeout, online event fires. I want it to kill offline alert, and show online alert instead.
The thing is that timeout's aren't working as I expect them to, in that case.
Here's the Angular service that handles the alerts:
.factory('AlertDrop', ['$rootScope', '$q', function ($rootScope, $q) {
var timeout = 2000;
$rootScope.DropAlerts = [];
var hideonclick = false;
var listenerOn = false;
var t = this;
this.currentAlert = {};
var getColor = function (clase) {
switch (clase) {
case 'error':
return 'rgba(201,48,44,0.9)';
break;
case 'warning':
return 'rgba(240,173,78,0.9)'
break;
case 'success':
return 'rgba(68,157,68,0.9)'
break;
case 'info':
return 'rgba(49, 176, 213,0.9)'
break;
default:
console.log('Código de color incorrecto. Cargo color por defecto.');
return 'rgba(152,26,77,0.9)';
break;
}
};
this.requestTypes = [
{
type: 'offline',
class: 'error'
},
{
type: 'online',
class: 'success'
}
];
this.bgcolor = 'rgba(201,48,44,0.9)';
this.showAlert = function (params) {
this.currentAlert = params;
delete t.ctout;
t.ctout = {};
var deferred = $q.defer();
if (params.timeout) {
this.timeout = params.timeout
}
if (params.hideonclick) {
hideonclick = params.hideonclick;
}
if (params.class != '')
this.bgcolor = getColor(params.class);
if (params.class != '') {
$('.alertDrop').attr('style', 'background: ' + this.bgcolor + '; top:44px;');
} else {
$('.alertDrop').attr('style', 'top:44px;');
}
if (hideonclick) {
addListener(this.bgcolor);
}
$('.alertDrop').html(params.message);
t.ctout = setTimeout(function () {
timeoutDone();
}, this.timeout);
};
this.hideAlert = function (color) {
$('.alertDrop').css('top', '0');
rmListener();
};
var rmListener = function () {
if (listenerOn) {
$('.alertDrop').unbind('click');
}
};
var addListener = function (color) {
if (!listenerOn) {
// $('.alertDrop').on('click', t.hideAlert(color));
}
};
var timeoutDone = function(){
var deferred = $q.defer();
t.hideAlert(this.bgcolor);
var tt = setTimeout(function(){
t.changeStatus(false);
deferred.resolve(true);
},2000); // 2 segundos, tiempo establecido en style.css
return deferred.promise;
};
var removeOnlines = function(){
$rootScope.DropAlerts = $rootScope.DropAlerts.filter(function(el){
return el.type != 'online';
});
};
var removeOfflines = function(){
$rootScope.DropAlerts = $rootScope.DropAlerts.filter(function(el){
return el.type != 'offline'
});
};
var interrupcion = function(params){
var load = true;
if(params.type == 'offline'){
removeOnlines(params);
load = false;
}else if(params.type == 'online'){
removeOfflines(params);
load = false;
}
if(load){
$rootScope.DropAlerts.push(params);
}else{
clearTimeout(t.ctout);
timeoutDone().then(function(){
t.showAlert(params);
});
}
};
this.checkPush = function(params){
var deferred = $q.defer();
if( $rootScope.AlertDropActive ){
if( params.type == this.currentAlert.type ){
clearTimeout(this.ctout);
setTimeout(function(){
timeoutDone();
},this.timeout)
}else{
interrupcion(params);
}
deferred.resolve(true);
}else{
$rootScope.DropAlerts.push(params);
deferred.resolve(true);
}
return deferred.promise;
};
this.pushAlert = function (params) {
this.checkPush(params);
if (!$rootScope.AlertDropActive) {
this.changeStatus(true);
var alert = $rootScope.DropAlerts.shift();
this.showAlert(alert);
}
};
this.changeStatus = function(v){
$rootScope.AlertDropActive = v;
if (!$rootScope.AlertDropActive && ( $rootScope.DropAlerts.length > 0 )) {
this.changeStatus(true);
var alert = $rootScope.DropAlerts.shift();
this.showAlert(alert);
}
};
return {
showAlert: this.showAlert,
hideAlert: this.hideAlert,
pushAlert: this.pushAlert,
changeStatus: this.changeStatus,
currentAlert: this.currentAlert,
checkPush: this.checkPush
};
}]);
So, when I fire interrupcion function, only in the case that connection is reestablished or lost, I need to stop the current timeout and, once the alert is hidden, show the new event.
I'm displaying this on a div, with a transition of 2 seconds.
Any ideas?

Actually the error wasn't on the code i posted, but when I called pushAlert.
I added a timeout in order to try to simulate fast event changes, and that was messing around with the timeouts.

Related

AngularJS TypeError: updateCompany is undefined

Resource:
This is the Resource I use for the Update function. I am unable to invoke the Update method in my Resource (written in Java)
c.adminCompaniesResource = $resource("api/admin/companies/:id",{"id":"#id"},{
"updateCompany": {method:"PUT"}
});
Update Company Function:
First I put all companies in an Array and then I'm trying to invoke an update action on a specific company in the Array
Problem with line: "updateCompany.$updateCompany(company, function() {"
TypeError: updateCompany is undefined
c.allCompanies = [];
c.updateCompanyForm = function(company) {
const updateCompany = company;
c.allCompanies = c.adminAllCompaniesResource.query(c.companyFields, function() {
c.allCompanies.forEach(function(company){
if (c.companyFields.id == company.id) {
company.id = c.companyFields.id;
company.password = c.companyFields.password;
company.email = c.companyFields.email;
updateCompany.$updateCompany(company, function() {
//problem here
c.companyFields.id = company.id;
c.companyFields.compName = company.compName;
c.companyFields.password = company.password;
c.companyFields.email = company.email;
c.updateCompanyTableDiv = true;
c.updateCompanyExceptionDiv = false;
}, function() {
c.error("Request could not be completed");
c.updateCompanyExceptionDiv = true;
c.updateCompanyTableDiv = false;
});
}
})
}, function() {
c.error("Request could not be completed")
c.getAllCompaniesExceptionDiv = true;
c.getAllCompaniesTableDiv = false;
});
}
There seems to be no problem with your code. The error message is pretty much for the resolution of the issue. The call to function c.updateCompanyForm() is sending the undefined value to it. Which leads to failure. If you can provide the complete code fragment, than it is possible to find the source of error.
I tried to mock up your code with dummy function.
<html>
<head>
<title>Test</title>
</head>
<body>
<script>
var c = {
companyFields: {
id: 10
},
adminAllCompaniesResource: {
query: function(fields, callback) {
callback();
return [{
id: 10
}];
}
}
};
c.allCompanies = [
{id: 10}
];
c.updateCompanyForm = function(company) {
const updateCompany = company;
c.allCompanies = c.adminAllCompaniesResource.query(c.companyFields, function() {
c.allCompanies.forEach(function(company){
if (c.companyFields.id == company.id) {
company.id = c.companyFields.id;
company.password = c.companyFields.password;
company.email = c.companyFields.email;
updateCompany.$updateCompany(company, function() {
//problem here
c.companyFields.id = company.id;
c.companyFields.compName = company.compName;
c.companyFields.password = company.password;
c.companyFields.email = company.email;
c.updateCompanyTableDiv = true;
c.updateCompanyExceptionDiv = false;
}, function() {
c.error("Request could not be completed");
c.updateCompanyExceptionDiv = true;
c.updateCompanyTableDiv = false;
});
}
})
}, function() {
c.error("Request could not be completed")
c.getAllCompaniesExceptionDiv = true;
c.getAllCompaniesTableDiv = false;
});
}
//Test 1: This should show message "Here"
c.updateCompanyForm({id: 10, $updateCompany: function() {alert('Here')}});
//Test 2: This will produce error
c.updateCompanyForm(undefined);
</script>
</body>
</html>
Hopefully this gives you the idea from where the issue started propagation.

Chained promises and prototype `this`

I'm having a hard time to get promises to work with the right this scope inside prototypes.
Here is my code:
'use strict';
angular.module('testApp').factory('UrlSearchApi',
function($resource, URL_SEARCH_API, PAGE_SIZE, $q){
var resource = $resource(URL_SEARCH_API);
resource.Scroll = function () {
return this.reset();
};
resource.Scroll.prototype.reset = function () {
this.visibleItems = [];
this.allItems = [];
this.busy = null;
return this;
};
resource.Scroll.prototype.fetch = function(query){
var params = {};
if(query) { params.q = query; }
return resource.query(params).$promise;
};
resource.Scroll.prototype.loadAllItems = function (results) {
var d = $q.defer();
angular.forEach(results, function (result, i) {
this.allItems.push(result);
if(i === results.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.loadVisibleItems = function () {
var length = this.visibleItems.length,
offset = parseInt(length / PAGE_SIZE),
start = PAGE_SIZE * offset,
end = start + PAGE_SIZE,
subset = this.allItems.slice(start, end),
d = $q.defer();
angular.forEach(subset, function (item, i) {
this.visibleItems.push(item);
if(i === subset.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.nextPage = function (query) {
if(this.busy) { return; }
console.log('nextPage ', query);
var tasks = [],
that = this;
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems)
.then(this.loadVisibleItems)
.finally(function () {
this.busy = false;
});
} else {
this.busy = true;
return this.loadVisibleItems().finally(function () {
this.busy = false;
});
}
};
return resource;
});
Whenever I run the tests I get
describe('#nextPage', function () {
var scroll;
describe('when there is NO search term (show all)', function () {
beforeEach(function (done) {
scroll = new UrlSearchApi.Scroll();
$httpBackend.expectGET('/policy/search')
.respond(200, arrayGenerator(123));
scroll.nextPage().then(done);
$httpBackend.flush();
$rootScope.$apply();
});
it('should load all the items in all items variable', function () {
expect(scroll.allItems.length).toBe(123);
});
});
});
I get the following error:
TypeError: 'undefined' is not an object (evaluating 'this.allItems')
I now that $q in strict mode sets the this inside then to undefined. I tried using bind(this) in multiple places but not luck... Any ideas?
I've already answered a question like this here.
Just let me know in comments if you still have questions.
Upd. Try to update your resource.Scroll.prototype.nextPage method like this:
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems.bind(this)) //bind here
.then(this.loadVisibleItems.bind(this)) // here
.finally(function () {
this.busy = false;
}.bind(this)); //and here
But keep in mind - when you pass a function as a callback to a then or to forEach e.t.c it'll lose this context. So, use bind exactly when you pass the function which uses this syntax as a callback.

Cordova media, how to get callback for loop playing?

I have following Angular JS service which is accessing to the cordova media plugin.
MediaSrv.loadMedia(filePath, mediaSuccess, null, status).then(function(media, status, test, status1){
media.play({ numberOfLoops: 999 });
media.setVolume(volume);
$scope.selectedSounds[index].state = 1;
$scope.selectedSounds[index].mediaInstance = media;
$scope.someSoundsArePlaying = true;
});
I would like to ask, how can i do loop playing of the selected file which can be stopped after passing mediaInstance to stop function?
I tried mediaSuccess Callback and status CallBack but it does not work properly.
Service is following:
'use strict';
angular.module('MaxRelax')
.factory('MediaSrv', function($q, $ionicPlatform, $window){
var service = {
loadMedia: loadMedia,
getStatusMessage: getStatusMessage,
getErrorMessage: getErrorMessage
};
function loadMedia(src, onError, onStatus, onStop){
var defer = $q.defer();
$ionicPlatform.ready(function(){
var mediaSuccess = function(){
if(onStop){onStop();}
};
var mediaError = function(err){
_logError(src, err);
if(onError){onError(err);}
};
var mediaStatus = function(status){
console.log(status);
if(onStatus){onStatus(status);}
};
if($ionicPlatform.is('android')){src = '/android_asset/www/' + src;}
defer.resolve(new $window.Media(src, mediaSuccess, mediaError, mediaStatus));
});
return defer.promise;
}
function _logError(src, err){
console.error('media error', {
code: err.code,
message: getErrorMessage(err.code)
});
}
function getStatusMessage(status){
if(status === 0){return 'Media.MEDIA_NONE';}
else if(status === 1){return 'Media.MEDIA_STARTING';}
else if(status === 2){return 'Media.MEDIA_RUNNING';}
else if(status === 3){return 'Media.MEDIA_PAUSED';}
else if(status === 4){return 'Media.MEDIA_STOPPED';}
else {return 'Unknown status <'+status+'>';}
}
function getErrorMessage(code){
if(code === 1){return 'MediaError.MEDIA_ERR_ABORTED';}
else if(code === 2){return 'MediaError.MEDIA_ERR_NETWORK';}
else if(code === 3){return 'MediaError.MEDIA_ERR_DECODE';}
else if(code === 4){return 'MediaError.MEDIA_ERR_NONE_SUPPORTED';}
else {return 'Unknown code <'+code+'>';}
}
return service;
});
Many, many thanks for any help.
EDIT:
Playing of the item is processed by the following method:
$scope.playSelectedItem = function(index) {
try {
var fileName = $scope.selectedSounds[index].file;
var volume = $scope.selectedSounds[index].defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
console.log(filePath);
MediaSrv.loadMedia(
filePath,
function onError(err){ console.log('onError', MediaSrv.getErrorMessage(err)); },
function onStatus(status){ console.log('onStatus', MediaSrv.getStatusMessage(status)); },
function onStop(){ console.log('onStop'); myMedia.play(); }
).then(function(media){
myMedia = media;
media.play({ numberOfLoops: 999 });
media.setVolume(volume);
$scope.selectedSounds[index].state = 1;
$scope.selectedSounds[index].mediaInstance = media;
$scope.someSoundsArePlaying = true;
});
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Error during the playing item");
}
};
Stopping:
$scope.stopSelectedItem = function(index) {
try {
var leng = 0;
if($scope.selectedSounds[index].state == 1) {
var mediaInstance = $scope.selectedSounds[index].mediaInstance;
mediaInstance.stop();
$scope.selectedSounds[index].state = 0;
$scope.selectedSounds[index].mediaInstance = "";
myMedia.stop();
}
angular.forEach($scope.selectedSounds, function loadMedia(selectedSound, idx){
if($scope.selectedSounds[idx].state == 1) {
leng ++;
}
});
if(leng <= 0) {
$scope.someSoundsArePlaying = false;
console.log("No sound are playing");
}
if(leng > 0) {
$scope.someSoundsArePlaying = true;
console.log("Some sound are playing");
}
console.log("Leng is:");
console.log(leng);
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Cannot stop playing of item");
}
};
EDIT2:
I finally solved it using storing myMedia instance in the simple array.
$scope.playSelectedItem = function(index) {
try {
var fileName = $scope.selectedSounds[index].file;
var volume = $scope.selectedSounds[index].defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
console.log(filePath);
MediaSrv.loadMedia(
filePath,
function onError(err){ console.log('onError', MediaSrv.getErrorMessage(err)); },
function onStatus(status){ console.log('onStatus', MediaSrv.getStatusMessage(status)); },
function onStop(){
console.log('onStop');
if($scope.selectedSounds[index].state == 1) {
console.log('For index ' +index+' is state '+$scope.selectedSounds[index].state);
myMedia[index].play();
}
}
).then(function(media){
myMedia[index] = media;
media.play({ numberOfLoops: 999 });
media.setVolume(volume);
$scope.selectedSounds[index].state = 1;
$scope.selectedSounds[index].mediaInstance = media;
$scope.someSoundsArePlaying = true;
});
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Error during the playing item");
}
};
I'm pleased that you find my angular service usefull.
In your sample you seems to mess up with parameter order :
MediaSrv.loadMedia(filePath, mediaSuccess, null, status) vs
function loadMedia(src, onError, onStatus, onStop)
BTW, play parameter numberOfLoops does not seems to work (at least on my nexus4). If you want to loop, you will need to call play() every time the mp3 ends.
Here is a short example :
var myMedia = null;
MediaSrv.loadMedia(
'sounds/1023.mp3',
function onError(err){ console.log('onError', MediaSrv.getErrorMessage(err)); },
function onStatus(status){ console.log('onStatus', MediaSrv.getStatusMessage(status)); },
function onStop(){ console.log('onError'); myMedia.play(); },
).then(function(media){
myMedia = media;
myMedia.play();
});
With this code, your sound should play, forever... To control when your sound should stop, I suggest you to add a control parameter, like this :
var myMedia = null;
var shouldPlay = false;
MediaSrv.loadMedia(
'sounds/1023.mp3',
function onError(err){ console.log('onError', MediaSrv.getErrorMessage(err)); },
function onStatus(status){ console.log('onStatus', MediaSrv.getStatusMessage(status)); },
function onStop(){ console.log('onError'); if(shouldPlay){myMedia.play();} },
).then(function(media){
myMedia = media;
});
function playStart(){
shouldPlay = true;
myMedia.play();
}
function playStop(){
shouldPlay = false;
myMedia.stop();
}
To play multiples files in a loop, you have to store all media references and play them successively. See there :
var shouldPlay = false;
var playingMedia = null;
var soundFiles = ['sounds/1.mp3', 'sounds/2.mp3', 'sounds/3.mp3'];
var mediaInstances = [];
var onPlayStop = function(){
if(shouldPlay){
if(playingMedia === null){
playingMedia = 0;
} else {
playingMedia = (playingMedia+1) % mediaInstances.length;
}
mediaInstances[playingMedia].play();
}
};
for(var i in soundFiles){
MediaSrv.loadMedia(soundFiles[i], null, null, onPlayStop).then(function(media){
mediaInstances.push(media);
});
}
function playStart(){
shouldPlay = true;
onPlayStop();
}
function playStop(){
shouldPlay = false;
mediaInstances[playingMedia].stop();
}
I hope this will helps :D

… is not a function in javascript {custom code}

please, could somebody tell me, what he heck I am doing wrong in my syntax?
The problem starts in the statement this.form.onsubmit, where I get this.initData is not a function.
Thanks.
var Contact_Form = function(element){
this.form = element;
this.errors = new Array();
this.invalid = new Array();
this.inSent = false;
this.name = new String();
this.email = new String();
this.message = new String();
this.initData = function()
{
this.name = this.getElementValue('contact-name');
this.email = this.getElementValue('contact-email');
this.message = this.getElementValue('contact-message');
}
this.form.onsubmit = function(event)
{
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}
this.verifyData = function()
{
if(!this.isNameLength())
this.setError('name', 'Zadejte, prosím, jméno dlouhé maximálně 30 znaků.');
if(this.isProperEmail())
{
if(!this.isEmailLength())
this.setError('email', 'Váš e-mail smí obsahovat maximálně 50 znaků.');
}
else
this.setError('email', 'Zadejte, prosím, email v korektním formátu.');
if(!this.isMessageLength())
this.setError('name', 'Zadejte, prosím, zprávu v rozsahu 1-999 znaků.');
this.doInvalidFields();
if(0 == this.errors.length)
return true;
return false;
}
this.doInvalidFields = function()
{
if(this.invalid.length > 0)
{
for(var invalid in this.invalid)
this.getElement(invalid).setAttribute('aria-invalid', true);
}
}
this.setError = function(field, message)
{
this.errors.push(message);
this.invalid.push(field);
}
this.getElementValue = function(element) {
return this.getElement(element).value;
}
this.getElement = function(element) {
return document.getElementById(element);
}
this.getElementName = function() {
return this.getElement('contact-name');
}
this.getElementEmail = function() {
return this.getElement('contact-email');
}
this.getElementMessage = function() {
return this.getElement('contact-message');
}
this.isNameLength = function(){
return this.isLength(this.name, 1, 30);
}
this.isEmailLength = function(){
return this.isLength(this.email, 1, 50);
}
this.isMessageLength = function(){
return this.isLength(this.email, 1, 999);
}
this.isProperEmail = function() {
return this.email.match(/^(?:\w){1,100}#{1}(?:\w){1,100}(?:.){1}(?:\w){1,10}$/ig);
}
this.isLength = function isLength(string, _min, _max) {
if(string.length >= _min && string.length <= _max)
return true;
return false;
}
}
window.onload = function()
{
new Contact_Form(document.forms[0]);
}
The problem is that this is not inherited, and has a different value inside each function.
Then, use
var Contact_Form = function(element){
/* ... */
var that = this;
/* Here this===that */
this.form.onsubmit = function(event)
{
/* Here this===that.form */
event.preventDefault();
that.initData();
if(that.verifyData())
this.send();
}
/* ... */
}
this is referring to the form in the onsubmit handler. You could assign this to a local variable, or bind the handler to the correct this with Function.prototype.bind, ie:
this.form.onsubmit = function(event) {
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}.bind(this)
or with jQuery.proxy
this.form.onsubmit = $.proxy(function(event) {
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}, this);
Both examples are forcing the this context of the function to be the instance of a Contact_Form whenever the handler is called

Javascript - Object not a collection

i try to create an video object with activex but i take this error : "Object not a collection". This is my code and error begins on line "this.parts = null;". There may be other things which causes error before this line. I search on the Net about this error but there is no example to solve it.
function detailKeyPress(evt) {
var evtobj=window.event? event : evt;
switch (evtobj.keyCode) {
case KEYS.OK:
if (player.isFullScreen == false)
player.makeFullScreen();
else
player.makeWindowed();
break;
case KEYS.PLAY:
player.isPlaying = true;
player.object.play(1);
break;
case KEYS.PAUSE:
player.pause();
break;
case KEYS.STOP:
player.makeWindowed();
player.stop();
break;
}
}
function Player(id) {
this.id = id;
this.object = document.getElementById(id);
this.isFullScreen = false;
this.isPlaying = false;
this.parts = null;
return this;
}
Player.prototype.play = function () {
this.isPlaying = true;
return this.object.play(1);
}
Player.prototype.playByUrl = function (url) {
this.object.data = url;
return this.play();
}
document.onkeydown = function (evt) {
detailKeyPress(evt);
}
window.onload = function () {
player = new Player('playerObject');
player.playByUrl($mp4Link);
}
Player.prototype.makeFullScreen = function () {
try {
this.object.setFullScreen(true);
this.isFullScreen = true;
}
catch (ex) {//If philips
this.object.fullScreen = true;
this.isFullScreen = true;
}
}
Player.prototype.makeWindowed = function () {
try {
this.object.setFullScreen(false);
this.isFullScreen = false;
}
catch (ex) { //If philips
this.object.fullScreen = false;
this.isFullScreen = false;
}
}
Player.prototype.pause = function () {
this.isPlaying = false;
this.object.play(0);
}
Player.prototype.stop = function () {
this.isPlaying = false;
this.object.stop();
}
This may caused by your registry. If you clean it, you can solve or probably a bug. I have searched also a lot about this error. There is no another thing to say.

Categories

Resources