Assignment causing program to behave strangely - javascript

I'm making a chess app for practice and I have a function called movePiece that gets called after a user selects a square to move a piece to. Before I call it I have a line that gets the destination piece's attribute (queen, pawn, etc..) using a dictionary:
secondAttr = pieceDict[posStr][0];
Strangely, when I comment out this line my movePiece() function gets called correctly, but when I leave it in there nothing happens until I click for a third time. I'm super confused why this random line before my function call that has nothing to do with the function itself is causing such behavior.
Here's a jsFiddle:
https://jsfiddle.net/eqmbk0u1/
, the pieces png's are stored on my computer so they won't show up, but they're all in their normal starting spots and when you select one it's square turns blue. See what happens when you comment out line 67 vs it normally.

You seem to have an error in you else block, of you $('square').click() event.
} else {
$("#"+firstID).removeClass('selected');
$("#"+firstID).addClass(lastSqrColor);
destRow = parseInt($(this).attr('row'));
destCol = parseInt($(this).attr('col'));
destID = parseInt($(this).attr('id'));
secondAttr = pieceDict[posStr][0]; //<--- Swap
posStr = positions[destRow][destCol]; //<--- Swap
movePiece();
firstTouch = false;
}
When you first click on a square, you save the name of the piece into posStr. Then, on your second click, you overwrite that value with an undefined value, because there is no piece in positions[destRow][destCol]. If you swap the lines 66 and 67, the code will work, because you set the secondAttr before overwriting posStr.

Related

HTML5Canvas, remembering the last instance made visible

I would like to do a simple task in Adobe Animate under HTML5Canvas environment. There are a couple of buttons on stage and corresponding circle symbol instances beside them that are made invisible at the beginning. When I click a button, an adjacent circle is visible. Then if I click on another button randomly, its adjacent circle is visible, but the previously visible circle must become invisible again as only one circle should be visible at any given time.
As a simple solution, I started with 4 instances: button_1, button_2, circle_1, circle_2. I planned to store the circle instance's name in a variable called 'store' when I first click on any button. Then pass that information to the next button's mouse click event to make the previous circle instance invisible again. My rookie code looks like this...
/*Made circles invisible at the beginning*/
this.circle_1.visible = false;
this.circle_2.visible = false;
/*button's click events*/
var _this = this;
_this.button_1('click', function(){
_this.cicle_1.visible = true;
store.visible = false; /*make the previous circle invisible if any*/
var store = this.circle_1; /*updating current circle's name in variable 'store'*/
});
var _this = this;
_this.button_2.on('click', function(){
_this.circle_2.visible = true;
store.visible = false; /*make the previous circle invisible if any*/
var store = this.circle_2; /*updating current circle's name in variable 'store'*/
});
/* It also works if I can make all circles instances invisible and then show the intended one during every click event, but how can I get and set 20+ circle instances invisible in one step? */
However, the code didn't work. I have no programming experience so my logic could be laughable but this is the easiest solution I can think of. Maybe I should have declared my variable globally? Can anyone kindly improve this code or make it work, please? Please no For-i or Array solution because it makes my head spin :) Thanks in advance.
Welcome to Javscript! I can tell what you're trying to do, but there are a few errors you made that are easy for someone just starting out to make.
You are accessing store variable before actually declaring the variable with var store = this.circle_1. Using the var keyword will declare a variable at the top of it's "scope," no matter which line of that scope it's declared, and let will declare it on the line you specified. Either way, the existence of any variable will not exist outside of it's scope. A scope consists of a set of curly braces {} meaning you are declaring store but then it's immediately getting deleted when you leave the curly braces. Something like the following will fix that:
/*
A variable declared in one scope is available to all scopes inside it.
By declaring 'store' outside of any scope/curly braces, it will be accessible from anywhere in the code
*/
var store = this.circle_1; // you store either circle here. I'm just using circle_1 as a placeholder
//rest of code
this.button_1('click', function(){
store.visible = false;
store = this.circle_1;
this.circle_1.visible = true; //make circle 1 visible
});
I feel you are overthinking it a bit (which is ok, it happens), and if you just have 2 circles, there is a much easier way of doing it which I'll post below.
It also seems like you forgot the on keyword in this.button_1 event declaration.
You are re-declaring _this which shouldn't be necessary the first time, much less twice. The code var _this = this; stores a reference of this in a new variable named _this basically just renaming it, and doesn't do anything else.
I don't know much about Adobe Animate's style of JavaScript, but I'll try to modify your original JavaScript in a way that I assume should work with Animate.
the following code is the simplest way if you only have 2 circles
//Made circles invisible at the beginning
this.circle_1.visible = false;
this.circle_2.visible = false;
//button's click events
this.button_1.on('click', function(){
this.circle_1.visible = true; //make circle 1 visible
this.circle_2.visible = false; //make the other circle invisible
});
this.button_2.on('click', function(){
this.circle_2.visible = true;
this.circle_1.visible = false;
});
Let me know if it works, or if you run it in a browser press F12 > Click Console and let me know if there are any errors.
If you wanted an arbitrary amount of circles an "array solution," as you put it would be the best. Arrays and loops are pretty fundamental, and easy to understand once you start (even though the syntax looks scary). If you plan to proceed in learning programming, that should probably be the next thing you learn.
You likely have there a variable visibility problem. Declaring a var inside a function should not allow other pieces of code to reach that var's value. Instead, you should declare that var outside and store there a null, then call like if (store) store.visible=false;. So, just move the var store outside of both functions, and refer to store inside both of them, should do.

Code Mirror Get Current Line Number every time content changes

I am trying to use Code Mirror to create a text editor. I want to show the current line number to the user at the bottom of the display, as text editors do.
So far I have tried this:
function updateInfo(){
var lines = editor.lineCount();
document.getElementById('line-no.').innerText = lines;
editor.refresh();
}
editor.on("change", updateInfo());
The line-no. is a span where I want to display the Line Number. This works for the first Line Number but then when I go to some other line, it doesn't do anything. The console shows this error:
codemirror.js:2154 Uncaught TypeError: Cannot read property 'apply' of undefined
at codemirror.js:2154
at fireCallbacksForOps (codemirror.js:2111)
at finishOperation (codemirror.js:2125)
at endOperation (codemirror.js:3747)
at HTMLTextAreaElement.<anonymous> (codemirror.js:3884)
Update
To follow updates in the editor, register a handler on the cursorActivity event. This event is fired when there is a change in the cursor or selection. The handler is called with the instance of CodeMirror; on this you can call the getCursor method to be the an object that contains the current line number that the cursor is active on.
Note that the line number is zero-based, so you may or may not increment it by 1.
const STATUS_CURRENT_LINE = document.getElementById('line-no.');
const onCursorActivity = (instance) => {
const cursor = cm.getCursor();
STATUS_CURRENT_LINE.textContent = cursor.line + 1;
}
editor.on("cursorActivity", onCursorActivity);
Setting the current line number when there is a doc change in the editor
The error happens because the updateInfo callback is called even before you register it. So that the value registered as the callback is undefined.
editor.on('change', updateInfo()) // -> editor.on('change', undefined)
This can be resolved by registering the function.
editor.on('change', updateInfo)
However, the signature for the callback should follow what is documented.
The change callback is passed the instance of CodeMirror and a changeObject from which you can retrieve the current line.
"change" (instance: CodeMirror, changeObj: object)
Fires every time the content of the editor is changed. The changeObj is a {from, too, text, removed, origin} object containing information about the changes that occurred as the second argument. from and to are the positions (in the pre-change coordinate system) where the change started and ended (for example, it might be {ch:0, line:18} if the position is at the beginning of line #19). text is an array of strings representing the text that replaced the changed range (split by line). removed is the text that used to be between from and to, which is overwritten by this change. This event is fired before the end of an operation before the DOM updates happen.
const STATUS_CURRENT_LINE = document.getElementById('line-no.');
function updateInfo(instance, changeObj){
STATUS_CURRENT_LINE.innerText = changeObj.to.line;
}
editor.on("change", updateInfo);

How to write a test that validates whether a function has successfully printed out a list of strings (specified in an array) to the screen?

Background/Context
I am busy learning how to write modules (in my case: files containing a single module.exports function) and running them in the terminal (linux) using node.js.
Part of this process also requires that I write my own tests using the 'assert' module and run them using Mocha.
What I need to accomplish
I need to test whether a module taking in an array (list) of strings as a parameter is successfully printing out each string to the screen.
Printing out each string to the screen is not complicated.
My code : module 'draw.js'
// draw.js
------------------
module.exports = function(arrayOfStrings) {
arrayOfStrings.forEach(function(string) {
console.log(string);
});
};
This successfully prints out each string to the screen. However, even though I know it works I am required to write test for it.
The code should look something like this:
//draw_test.js
------------------
var draw = require('../draw');
var assert = require('assert');
describe('draw', function(){
it("should pass through parameter ['hey', 'there'] and print out 'hey' and 'there' to the screen on separate lines.", function() {
var result = draw(['hey', 'there']); // PROBLEM
assert.equal(result, /*insert expected result here*/);
});
});
The problem
I have commented in where the problem lies in the code above.
The trouble comes in where I need to declare a variable called 'result' which will serve as the 'test' result against what I expect.
There are two problems:
a) 'result' will be undefined because draw(arrayOfStrings) prints to the screen using console.log and does not/should not return anything.
=> How do I solve this? I have spent hours researching and thinking but I have come up with no obvious solution.
b) I can't compare 'result' against any one thing because it needs to validate that all strings are printed to the screen. But in order for them to be printed to the screen individually requires a loop.
=> So how can the test verify that each string is printing to the screen?
I am sure that my two 'problems' above can be solved with one fix, but I can't seem to discover what that fix should be.
Some more context : there must be something more
I am certain I am missing something.
a) I was required to name this module 'draw.js' because I am supposed to use it to 'draw' things using characters to the screen. I have the feeling that I am supposed to do something more than 'console.log' here to print to the screen (for that is what 'printing to the screen' means to me).
b) I am assuming that when they say the function "takes a list of strings as a parameter and prints each entry in the list to the screen" that they mean each entry (or string) should be printed on a separate line.
c) I am supposed to be using this function (or module) called 'draw' in the next few tasks. Here are the names of the modules that I have to create with given directions:
square_stars.js
"Can draw a 4x4 square using the * character. Use the draw function you created above to display the results of the function on the screen. All subsequent functions that need to draw something on the screen should use the draw function. Why do you think we need the draw function?"
// Please edit this to provide hint on how to approach!
square.js
"Takes a character as a parameter and draws a 4x4 square using it. Remember to use the draw function."
square_param.js
"Takes a character and dimensions as parameters, and can draw a square using them. Remember the draw function."
tri.js
"Can draw a triangle with a base of 4 using *."
//Please edit this to provide hint!
tri_char.js
"Takes a character as a parameter, and can draw a triangle with a base of 4 using it."
square_param_base.js
"Takes a character and a base size as parameters, and can draw a square using them."
//Please edit this to provide hint!
diamond.js
"Can draw a diamond shape using *. The center of the diamond should be 5 characters wide."
// Please edit this to provide hint!
diamond_base.js
"Takes a character as a parameter, and can draw a diamond shape using it. The base of the diamond should be 5."
diamond_char_base.js
"Takes a character and a base size as parameters, and can draw a diamond shape using them."
//Please edit this to provide hint!
The reason I am providing you with this list is not only because I foresee similar issues with those tasks as with this one, but also to give context. One of you might be able to tell me if what I am doing is appropriate for the tasks that lie ahead. And I truly do hope you will tell me whether I am on the right path or not.
I would also appreciate some hints (not answers) as to how I could approach the modules above.
But before you guide me in the right direction to accomplish the tasks, please tell me how I am supposed to configure a test using the 'assert' module that follows a similar layout as shown above to validate a function that prints to the screen (i.e. console.log) and doesn't return anything.
First of all, your question about the draw() function. There's no code to check if the thing was printed to console, because almost everytime console.log() prints the text (except errors etc). If you really want to check if the variable printed out was an array, check if it's an array:
function draw(params) {
// Don't continue if params are not an array
if (!Array.isArray(params)) return;
// If it's an array, do something here
}
To print a text to the page simply do your_element.innerHTML = your_text. To get through every part of the array use .map() function.
function draw(params) {
if (!Array.isArray(params)) return;
var inner = '';
params.map(function(param) {
inner += param + '<br>';
});
document.body.innerHTML += inner;
}
draw(['hey', 'there']);
Fiddle
Why do I use inner variable? Because if you use document.body.innerHTML += param + '<br>'; inside .map() function, remove the line above and below it and use draw function with array of for example 10000 elements, it will slow down the page, because JS sets innerHTML 10000 times, and with my code it sets the innerHTML of body only once.
About the square() function, I made one:
function square(character, size) {
// If character is not a string, then set it's default value to '*'
var ch = typeof character == 'string'
? character : '*',
// If size is not a number, then set it's default value to 4
s = typeof size == 'number' ? size : 4,
inner = '', i, j;
// Make a square
for (i = 0; i < s; i++) { // Rows
for (j = 0; j < s; j++) // One-line for
inner += ch; // Prepare the one line
inner += '<br>'; // After the end of the line add a linebreak
}
document.body.innerHTML += inner; // Set the innerHTML
}
square(); // Will print 4x4 square filled with '*'s
// Other examples:
square(-1, 10); // Will print 10x10 square filled with '*'s
square('&'); // Will print 4x4 square filled with '&'s
square('&', 16); // Will print 16x16 square filled with '&'s
Fiddle
About the triangle and diamond functions... Unfortunately it's not easy to do in my opinion.
Cheers
PDKnight

For loop index undefined in IE9 for initial pass

On this page: http://koncordia.marketingassociates.com/19892arc/
I have a slideshow that I created custom prev/next links for. Each selection you make on the page advances it one slide forward. The progress bar at the top allows you to click a previous slide, and jump more than one back if you want (you can go from step 4 or step 1 for example).
This multi-step jump works fine in all the current major browsers, but the client uses IE9, and this is where I do not understand the source of the issue.
The following are the relevant methods in this issue. To mimic a user jumping back one or more slides I have a for loop iterate over simulatePrevClick() as many times as necessary; it's not sexy but it works.
The issue arises on the initial pass in IE9. The console spits out "undefined" for the first pass, but it says 0 for all other browsers (including IE 10 and 11) which is correct. If I remove the method call within the loop the iteration works perfectly, so it has something to do with the .click() event or way the method is called, but I don't know what.
No matter what, IE9 will show the immediate previous slide no matter how many they click back; the progress bar be out of sync if they click back more than one in this instance. The undefined result is not showing as an error, either.
//Highlight the right number of progress buttons
highlightProgressBar: function( slideNumber ) {
$(".btn-progress").attr('disabled', 'disabled').removeClass('active'); //Disabled all
$("#progress-wrapper a:lt(" + slideNumber + ")").removeAttr('disabled'); //Disable select number
$("#progress-wrapper a:eq(" + (slideNumber - 1) + ")").addClass('active'); //Add active to the specified button clicked
},
simulateNextClick: function () {
//The value of this must match what the responsiveslides function creates for the prev/next buttons (seen when you inspect element)
$(".transparent-btns_nav.transparent-btns1_nav.next").click();
},
simulatePrevClick: function () {
//The value of this must match what the responsiveslides function creates for the prev/next buttons (seen when you inspect element)
$(".transparent-btns_nav.transparent-btns1_nav.prev").click();
},
toggleProgressBar: function( clickedSlideNumber, activeSlideNumber ) {
var numSlides = activeSlideNumber - clickedSlideNumber;
for (var i=0; i < numSlides; i++) { //Anticipate user may click more than one step back
this.simulatePrevClick();
console.log(i); // **shows "undefined" on first pass in IE9 only**
}
this.highlightProgressBar(clickedSlideNumber);
}
Try to move the var i = 0 declaration out of the loop.
var i = 0;
for (; i < numSlides; i++) {}
It's really strange that that should happen.
This is just a guess, but I looked through the rest of your source code, and its possible that the root of your problem could be due to whenever you actually implement your toggleProgressBar function, in this area:
$(".btn-progress").click(function() {
var currentSlideID = $("#progress-wrapper").find('a.active').attr('id').split("-");
var clickedSlideID = $(this).attr('id').split("-");
slideFn.toggleProgressBar( clickedSlideID[1], currentSlideID[1] );
});
If I see right, your toggleProgressBar wants to accepts two numbers. However, what you're passing in are string literals:
slideFn.toggleProgressBar( "2", "1" );
ID attributes are output as strings, not numbers. I just tested the following in Chrome, and it worked:
"2" - "1" === 1 //true
This is because I guess V8 (Chrome's JS engine) coerces the two string literals into numbers. However, (while I have not tested it), this tells me that it's possible that IE might not be coercing the two strings into numbers (like I said, I don't know this for a fact, but this is something you might try debugging). Try this and see if it has any effect:
//first option
slideFn.toggleProgressBar( +clickedSlideID[1], +currentSlideID[1] );
//the + sign will typecast your strings into numbers
//second option
slideFn.toggleProgressBar( parseInt(clickedSlideID[1]), parseInt(currentSlideID[1]) );
However, in my experience, parseInt runs a little bit slower than using + to typecast the strings into numbers.
IE uses the Chakra JS engine, which I believe follows the standards of ECMAScript 3, which is from 1999. I haven't read through the standard, but it's worth considering the possibility that it has something to do with the issue.
Edit
Here's your problem:
$("#progress-wrapper").find('a.active') ==> []
The first time, there are no a.active elements. Thus, whenever you try to call split on an empty array, it throws a TypeError.
You need to give your first .btn-progress the class active, because the first time around, your first .btn-progress looks like this:
1
There's no active class. Only subsequent .btn-progress elements receive the class active whenever you click the .btn-continue. Your first one never does. Therefore, clickedSlideID[1] and currentSlideID[1] are undefined the first go around. It probably breaks in IE9 because IE9 doesn't understand i < undefined, but it's possible that other more modern browsers go ahead and execute anyway.
Somewhere in the beginning of your code, then, you need to do something like this:
$('.btn-progress').eq(0).addClass('active');
I just tried this in the console on your page, and it worked just fine. After I added the class active to the fist .btn-progress, currentSlideID[1] was now 1, and not undefined.

Trigger event not working or suspected passing on index

.full-arrow is an arrow that selects the next page. .full-navigation is a navigation bar, quite simply boxes in a line that change colour when you select them. The rest of the function isn't on here but you get the general idea.
When I create a trigger event to the function below the first one, it goes through okay but I'm unsure whether it's not picking up the index() or whether it's just not working at all. Weirdly, it works the first time but I think that's because the same_page variable is declared as 0 in the beginning.
The reason I'm also doubting whether it's the index() not being passed on is because the alert("foo"); isn't coming up.
$(".full-arrow").click(function() {
$(".full-navigation li:eq(" + same_page+1 + ")").trigger("click");
});
$(".full-navigation li").click(function(event) {
//alert("foo");
//alert(same_page);
same_page = $(this).index();
if(same_page == $(this).index()) { return false; }
});
Where are you getting the same_page variable from? Try using parseInt( same_page, 10 )--I have a hunch it's actually a string.

Categories

Resources