I have this code:
var history = {
stack : [],
counter : -1,
add : function(item){
this.stack[++this.counter] = item;
this.doSomethingWith(item);
// delete anything forward of the counter
this.stack.splice(this.counter+1);
},
undo : function(){
this.doSomethingWith(this.stack[--this.counter]);
},
redo : function(){
this.doSomethingWith(this.stack[++this.counter]);
},
doSomethingWith : function(item){
// show item
}
};
When I try use that on this way it's not working;
var item = $('#myDiv');
history.add(item);
I have this error:
Uncaught TypeError: undefined is not a function
How can I solve the problem? Thanks!
The problem is that you picked a bad name for your object. There is build in History object available as window.history. So rename yours to something else and it should work. For example:
var appHistory = {
stack : [],
counter : -1,
...
};
You could also fix it by wrapping your code into IIFE for example, so that history would become a local variable. But I would still recommend picking less confusing name.
It works well if you put this behind the other code:
var item = $('myDiv');
history.add(item);
If you put it in front the function is not yet defined.
Working example: http://jsfiddle.net/LqrpW/
$('myDiv') replace with $('.myDiv') or $('#myDiv')
Related
The following bit of code is troubling me:
define('TodoItem', function()
{
var Component = require('Component')
var app = require('application').app
var merge = $.extend
var TodoItem = Component.extend(
{
template : 'todo-item-template',
events :
{
'button.completed click' : 'markAsCompleted',
'button.delete click' : 'remove'
},
getInitialState : function()
{
var initial =
{
uuid : undefined,
text : '',
completed : false
}
return initial
},
markAsCompleted : function(evt)
{
if (this.root)
{
var test = merge({}, this.getInitialState(), { whatever : 'whatever' })
app.completeTodo(this.model.uuid)
}
}
}
}
By the time markAsCompleted() is executed, merge is called without surprises, but somehow, the call to app.completeTodo() throws a Uncaught TypeError: Cannot read property 'completeTodo' of undefined(and app is indeed undefined).
app being a reference to an object, the only way its value changed is if the referenced object changed in the meantime. And yet require('application').app still evaluates to the object it should.
It seems like a simple JS issue I'm misunderstanding but I can't figure it out.
Thanks in advance for any help!
I make a simple quiz game. Here is some relevan methods that i have inside one object.
But doesn't work. I always get an error within 'rightAnswerGot' function. Console drops
"uncaught typeerror undefined is not a function for object methods" for this.addVariantsHtml(this.updateCharacter());
BasicGame.Game.prototype = {
actionOnClick: function (button) {
var log;
if(button.value==this.char_bubble.text) {
setTimeout(this.rightAnswerGot,1000);
} else {
// wrong
swoshsound.play();
}
console.log(log);
},
rightAnswerGot: function (){
this.addVariantsHtml(this.updateCharacter());
},
addVariantsHtml: function(id) {
this.answer = this.getAnswersVariants(id);
for (var i = 0; i < 6; i++) {
this.button[i].value = this.answer[i]['trans'];
this.button[i].char_id = this.answer[i]['id'];
this.ans_text[i].setText(this.answer[i]['trans']);
}
},
updateCharacter: function() {
var i = this.getRandomCharacter();
console.log("updateCharacter: "+i + " " +this.chars[i]);
this.char_bubble.setText(this.chars[i].getPath());
return i;
}
}
The aim is to froze the game for a second, when user choose the right answer, and than go to next question. Any ideas why does it happens?
Thanks
Looks like a classic JavaScript scope issue to me. However as you've tagged this question as using Phaser, I would suggest you use a Phaser Timer event to avoid scope problems. Specifically:
setTimeout(this.rightAnswerGot,1000);
replace it with:
this.game.time.events.add(Phaser.Timer.SECOND, this.rightAnswerGot, this);
Which will create a single 1 second timer that fires only once, calling your function at the end of it. You can use 1000 instead of Phaser.Timer.SECOND of course.
I would image that whats happening is that its trying to call the this.addVariantsHtml method, before its calling this.updateCharacter and getting the ID.
So your probably expecting that when it runs, for it to be something like:
this.addVariantsHtml(1);
But its actually trying to run
this.addVariantsHtml(this.updateCharacter());
So just do this:
var id = this.updateCharacter();
this.addVariantsHtml(id);
Either that or you need to look into method chaining/piping, which is just complicated and doesnt need to be used for this situation, but is interesting :)
Ok I found something that made it work!!
Here is a solution:
actionOnClick: function (button) {
var log;
if(button.value==this.char_bubble.text) {
var context=this;
setTimeout(function() {
context.addVariantsHtml(context.updateCharacter());
},1000);
} else {
// wrong
swoshsound.play();
}
console.log(log);
},
For a previous question I answered here on SO, I made the following library.
(function(){
var LS = function(){
return new LS.fn.init();
};
LS.fn = LS.prototype ={
//Check to see if the browser suports LocalStorage
init : function(){
this.ls = (typeof(Storage)!=="undefined") ? false : true;
return this;
}
}
LS.fn.init.prototype = LS.fn;
LS.fn.init.prototype = {
set : function(name, val){
if(this.ls) localStorage.setItem(name, val);
},
get : function (name){
if(this.ls) return localStorage.getItem(name);
},
remove : function(name){
if(this.ls) localStorage.removeItem(name);
},
test: function(){
alert("hi")
}
}
window.LS = window.ls = LS;
})();
This was working fine until recently.
I am getting errors like:
LS.set("foo", "bar"); // Error: undefined is not a function...
Even the following code gives an error:
LS.test();
Why has this stopped working?
Thank you.
NOTE: Previously, I had a typo, but now I have fixed that and it still doesn't work.
Your window.LS is initialized to a function, and you seem to use it as if you expect it to be initialized to the return value of that function.
Change this line:
window.LS = window.ls = LS;
to:
window.LS = window.ls = LS();
Also, try to simplify your library code. It seems very convoluted. At the very least comment it out a bit to indicate what the various parts do/are used for. The part with the typo (LS.protorype) isn't used anywhere for example.
I'm having a very hard time understanding how to setup an object that allows me to test my jQuery calls. I don't need to mock any Async calls or anything, just basic use. So let me set out my function that I want to test (truncated for simplicity):
listGamesCallback : function(data) {
var gameList = $("#gameList select");
gameList.empty();
$.each(data, function() {
var newOption = $('<option>', {value : this.gameId });
newOption.text(string);
newOption.data("isJoinable", isJoinable);
// Add it to the list
gameList.append(newOption);
});
}
I need to mock the jQuery here to unit test this method, but I'm unable to figure out how to do this in javascript. Even without jsMockito, I don't know how to create an object with the properties that jQuery has in this situation. Any help with this would be appreciated.
I am using jsTestDriver, jsHamcrest, jsMockito and jQuery. However a generalized approach to create a $ object that has these properties would be awesome as well. Thank you!
For those that asked, here is what I came up with that seemed to kinda work..but I don't understand why.
var saved$ = $;
var mockContruct = mockFunction();
var mockedGamelist = mock(jQuery);
var mockedOption = mock(jQuery);
mocked$ = (function() {
var test = function(name) {
var args = jQuery.makeArray(arguments);
return mockContruct.call(test, args);
};
$.extend(test, $);
// This is what confuses me. This worked, but it's wierd
// It allows me to use the regular jQuery functions like
// $.each, while returning mocked objects when selectors are used.
test.prototype.constructor = test;
return test;
})();
$ = mocked$;
when(mockContruct).call(anything(), hasItem(containsString("#gameList")))
.thenReturn(mockedGamelist);
when(mockContruct).call(anything(), hasItems(containsString("<option>"), both(object()).and(hasMember("value"))))
.thenReturn(mockedOption);
headerFunctions.listGamesCallback([ {
gameId : 1,
isWhitesTurn : false,
isGameOver : false,
whiteUserName : "foobar",
blackUserName : "barfoo"
} ]);
JsMockito.verify(mockedGamelist).empty();
JsMockito.verify(mockedGamelist).append(mockedOption);
$ = saved$;
Ok, here what I came up with that does the job with minimal setup. The .extend is completely necessary here so that the jQuery object is setup correctly. This allows you to mock the constructor to return mocked jQuery objects that you can use to run your tests on. As a spy, jQuery will work as expected in all situations except when you want it to do something else. Here it is:
TestCase("HeaderTest", {
testListGamesCallback : function () {
var saved$ = $;
$ = $.prototype.construct = jQuery.extend(spy(jQuery), jQuery);
var mockGameList = mock(jQuery);
when($)(containsString("#gameList")).thenReturn(mockGameList);
headerFunctions.listGamesCallback([ {
gameId : 1,
isWhitesTurn : false,
isGameOver : false,
whiteUserName : "foobar",
blackUserName : "barfoo"
} ]);
verify(mockGameList).empty();
verify(mockGameList).append(object());
$ = saved$;
}
});
The caveat to this solution is that mocking anything other than the constructor is a bit tricky. You will have to set each individual function that you want to mock, then program the behavior. So:
$.each = mockFunction();
when($.each)(...matchers...).thenReturn(...);
But it still allows for testing what you need to.
As an extension to alpian's answer, you can create DOM elements without having to add them to the page. Make your JS functions take the relevant elements as parameters:
listGamesCallback : function(data, gameListSelectElem) {
var gameList = $(gameListSelectElem);
...
and test them like so:
var fakeSelect = $('<select>'),
data = ...;
listGamesCallback(data, fakeSelect[0]);
equal(fakeSelect.find('option').length, 1, 'must have exactly 1 option');
...
The last line of code above is for qUnit. Take whatever you need, the point is to say you can pass a DOM element that was never added to the page and afterwards investigate that DOM element using jQuery to find whether it was manipulated right.
No sure if i understand what you mean but if you want to create 'data' for you example , this is the method i know:
var data = [ { id : 1 , name : 'foo' } , { id : 2, name : 'bar' ]
but - if you wanted to create a list of options, than you code needs a couple of fixes:
see http://jsfiddle.net/7MMap/
var data = [ { gameId : 1 , name : 'foo' ,isJoinable:true} , { gameId : 2, name : 'bar' ,isJoinable:false}]
listGamesCallback = function(data) {
var gameList = $("#gameList select")
.empty();
$.each(data, function(i,d) {
var newOption = $('<option>', {value : d.gameId })
.text(d.name)
.data("isJoinable", d.isJoinable);
// Add it to the list
gameList.append(newOption);
})
};
listGamesCallback(data);
Mocking jQuery is not what mocking is for. You should only ever be mocking your object's collaborators. jQuery is providing you with some utilities - it's not a collaborator and hence should not be mocked.
What you are collaborating with here is the DOM, or some intermediate object between your code and the DOM. data is a value object and can simply be created in your test as Avi suggests.
In my JS tests, i don't mock the DOM, i use the real DOM and am sure to tear down anything i created between tests and this seems to work pretty well.
My problem is pretty easy to understand. I have a JSON object (see code) and I will automatically call all functions of this object in the order that those appears. .
var installer = {
a : function() {
...
}
b : function() {
...
}
};
for(var func in installer) {
fn.call(document);
};
Have you any idea why the previous code doesn't work ? I'm sorry, I'm a beginner in javascript.
Thanks in advance !
Regards.
You don't have a variable called fn, and you are also missing commas at the end of your function definitions.
Additionally, your functions will not be called in order because JavaScript orders your object properties arbitrarily. You may want to consider using an array or, as I have done below, specify an array that determines the order.
var installer = {
a : function() {
...
},
b : function() {
...
},
};
var order = [ "a", "b" ];
for(var i = 0; i < order.length; i++) {
installer[order[i]].call(document);
}
You declare var func as the variable to loop through the members of installer, yet you use fn.call(...). Where did fn come from?
Should you be able to do: installer[func].call(document) instead of fn.call(document).
Also your functions declared in the installer object don't take any arguments, yet you're passing document as an argument.
[updated code to add missing .call to installer[func](document)]