Phaser: Instantiate class by string reference - javascript

I am working on a "manager" for selecting what crop shall be placed on a certain plot. Every crop has a completely different design and therefor their own class/object. However instead of writting >40 different lines that will instantiate that class I would like 1 line that simply contains the string that matches the exact name of a class and then run it. That way my code will stay clean. I have tried some stuff but never managed to get it done. Usually resulting in an the following error:
TypeError: this.crop is not a constructor
The code I am attempting to run
export default class CropManager extends Phaser.Group {
constructor (game, className, plotId) {
super(game)
this.x = 0
this.y = 0
this.plotId = plotId
this.className = className
this.cropHandler(this.className)
}
// Defines which class to call
cropHandler (className) {
const ActualClass = 'plot' + className
this.cropclasses = { ActualClass: ActualClass}
this.crop = this.cropclasses[ActualClass]
this.classRun = new this.crop(this.game, this.x, this.y, this.plotId)
this.add(this.classRun)
}
}
Note every crop their classname = crop+cropname (cropCarrots, cropCows, etc)

Rethink the way you're storing key-value pairs in this.cropclasses. The way it's done now, it's going to have 'ActualClass' as the key and 'plotNameOfTheClass' (or whatever 'plot' + className produces) as the value, thus, when accessing it later as an array, this.crop comes out undefined since there isn't a 'plotNameOfTheClass' key in the map.

Related

Calculate Height/Depth Of Binary Tree Javascript

I am just a beginner, so if the error is something too obvious, I apologize .
My two questions are:
What is this.root in our school's provided code;
How can I implement the .height method in order to measure the depth of a Tree.
The explanation:
We were provided with this code in the class:
function BinarySearchTree(value) {
this.value = value;
this.right = null;
this.left = null;
}
BinarySearchTree.prototype.add = function(value) {
let newLeaf = new BinarySearchTree(value)
if(value > this.value){
this.right === null? this.right = newLeaf : this.right.add(value)
} else {
this.left === null? this.left = newLeaf : this.left.add(value)
}
};
And we were supposed to write a method to calculate the height/depth of a binary tree. Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty, while it proceeds to create a new node on the left side of the first empty one. Well, not empty, but whose value is undefined. Is this a desired behavior?
let newTree = new BinarySearchTree
>undefined
newTree.add(7)
>undefined
newTree.add(3)
>undefined
newTree.add(5)
>undefined
newTree
>BinarySearchTree {value: undefined, right: null, left: BinarySearchTree}
left: BinarySearchTree {value: 7, right: null, left: BinarySearchTree}
right: null
value: undefined
[[Prototype]]: Object
Now, considering the tests are passing for .add method, obviously I may be wrong in this situation, since this is the code provided to us by the teacher in the class.
This is the code I keep finding online and the reason I am not getting far with my code for .heigth method is because I am unable to implement this.root:
function Node(val){
this.value = val;
this.left = null;
this.right = null;
}
function BinarySearchTree(){
this.root = null;
}
How should I proceed with the .height method?
If it helps, here are the tests:
describe('Binary Search Tree', function() {
var binarySearchTree;
beforeEach(function() {
binarySearchTree = new BinarySearchTree(5);
});
it('should have methods named "add", "contains", "depthFirstPre", "depthFirstIn", "depthFirstPost", "breadthFirst"', function() {
expect(binarySearchTree.add).to.be.a("function");
});
it('should add values at the correct location in the tree', function(){
binarySearchTree.add(2);
binarySearchTree.add(3);
binarySearchTree.add(7);
binarySearchTree.add(6);
expect(binarySearchTree.left.right.value).to.equal(3);
expect(binarySearchTree.right.left.value).to.equal(6);
});
it('height method should return correct height', function() {
binarySearchTree.left = new BinarySearchTree(3);
binarySearchTree.left.left = new BinarySearchTree(1);
expect(binarySearchTree.height()).to.eql(2);
binarySearchTree.left.left.right = new BinarySearchTree(2);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.left.left.left = new BinarySearchTree(0);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.right = new BinarySearchTree(8);
expect(binarySearchTree.height()).to.eql(3);
});
}
Again, I apologize for a long question. I was trying to write all the relevant information regarding my problem.
Happy holidays!
What is this.root in our school's provided code
Your school's template code does not manage what is the root of the tree, so this must be managed in a variable by the driver code. In the testing code this variable is named binarySearchTree, and it really is what would be called this.root in the second (2-class) implementation.
Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty [...] Is this a desired behavior?
No it is not desired behavior. The template code does not provide the concept of an empty binary tree. It expects you to create the tree with at least one value, which should be provided as argument to the constructor. It is not intended to leave out the argument when calling the constructor.
The 2-class implementation provides the idea of an empty tree. But the school's template code does not; you would just have to state binarySearchTree = null if you want an empty tree. But the downside is clear: you cannot use the methods of the class to add a value to that. The only way to get the first value in a tree is to call the constructor and assign the constructed object to your binarySearchTree variable. So adding the very first value to the tree requires a different approach than adding the other values. This is also what you see in the testing code: the first value is added as argument to the constructor -- which is always called with an argument -- while the other values are added by calling the add method. This is a pity and really shows the limitations of the template code.
How can I implement the .height method in order to measure the depth of a Tree.
The idea is that you use recursion:
If there is a left child, get the height of the left subtree through recursion. If there is none, use -1 as default, as it is an empty subtree, and empty trees have a height of -1. Do the same at the right side. Get the maximum of these two values, since only the higher subtree of the two determines what is the height of the tree. Finally add one to this result so to account for the current node.
BinarySearchTree.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
Again, you can only run the height method an a tree that has at least one node, because of the limitations of the school's template code.
For completeness sake, the 2-class equivalent would place the above code on the Node class, and would add a wrapper method on the BinarySearchTree class, like this:
Node.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
BinarySearchTree.prototype.height = function() {
return this.root === null ? -1 : root.height();
}

Weird behavior when declaring class property with semicolon

class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
alert(c.x + ' : ' + c.y);
Why is the code compilable but the value of c.x is undefined?
What is the effect of declaring a class property with :?
Regarding the x: 5 part, although this is a valid javascript code, there is no much use for it.
This is a javascript label and it used (if any) mostly within loops context.
So to answer your questions:
Why is the code compilable
Because technically this is a valid javascript code (yet not a valid class field).
but the value of c.x is undefined
Because the x is a label and not a class field.
What is the effect of declaring a class property with :
You get a label instead of a class field.
Addendum
Another common mistake, is this code of block:
class SomeClass {
z = () => {
x: 5;
};
}
You would think that z() will return an object with an x key:
`{x:5}`
But actually you have a function with a label of x that just run an expression of 5.
Just for completeness sake, the fix will be either to add an explicit return and another set of curly braces
() => {return {x: 5}}
Or just wrap the whole thing with parentheses
() => ({x: 5})
Edit
As a followup to the comments below:
Just to be clear, your code compiles on several environments that i tested as well as stack-snippets as can be seen below:
class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
console.log(c.x + ' : ' + c.y);
The code is not valid ES6.
You seem to be "compiling" with babel, and have inadvertently enabled the flow syntax extension (and also class properties for the second line). In flow, x: 5 is a class field type annotation. Of course, 5 as a type doesn't make sense, but apparently they allow pretty arbitrary expressions.

I'm trying to use jquery to create a div containing columns but I can't get my array to format correctly

I have an array that contains dates. and for some reason I can't get it to show on my screen I've been debugging for a few days now and I've tracked it down to a single line, but the line has worked before and I can't figure out what the issue might be.
The array looks like this:
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"...];
It's passed as an argument from another function, but that's how it's showing in console.log().
I might be going about this the wrong way, maybe even a lot further around then I need to but this is what I've come up with:
1. function setTHead(selectItems) {
2 var formatString;
3. for (var x = 0; x < 12; x++) {
4. formatString = selectItems[x].replace(/[^0-9/-]/g, "").toString();
5. console.log(selectItems);
6. $('#datTab').append("<div id='col" + x + "' class='column'>'" + formatString + "'</div>");
7. }
8. }
the array up top is what's showing from the console.log 5 lines down.
the sixth line is what is seeming to give me issues. Nothing is put on the page at all.
I'm getting a console error saying:
jQuery.Deferred exception: selectItems is undefined setTHead#http://localhost/mySite/script.js:136:9
startUp2#http://localhost/mySite/script.js:146:5
#http://localhost/mySite/table.php:19:9
mightThrow#http://localhost/mySite/lib/jquery.js:3586:52
resolve/</process<#http://localhost/mySite/lib/jquery.js:3654:49
setTimeout handler*resolve/<#http://localhost/mySite/lib/jquery.js:3692:37
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
fire#http://localhost/mySite/lib/jquery.js:3458:21
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
ready#http://localhost/mySite/lib/jquery.js:3923:13
completed#http://localhost/mySite/lib/jquery.js:3933:9
EventListener.handleEvent*#http://localhost/mySite/lib/jquery.js:3949:9
#http://localhost/mySite/lib/jquery.js:39:9
#http://localhost/mySite/lib/jquery.js:17:3
undefined
followed by:
TypeError: selectItems is undefined
and thats pointing to line 6.
if anyone has any advice I would be very much appreciative. Thank you in advance.
EDIT: A little more code:
function startTblView(defSel) {
if (defSel === true) {
setCookie('defSel', true, 7);
} else{
setCookie('defSel', false, 7);
}
saveSelected();
window.open('table.php', '_self');
defSel = getCookie('defSel');
if (defSel) {
selectItems = getDefDates();
}else {
selectItems = reGetSelected();
}
setTHead(selectItems);
}
defSel, is a boolean passed from my last page stating whether I'm doing a default view or a custom view, the custom view is passed from saveSelected();
saveSelected is a function for just saving the selected global value as a cookie so I can pull it out on the next page.
getDefDates pulls the default values for the array
reGetSelected, gets the selected array from the cookie.
I apologize for wonky naming conventions. I'm the only one working on this site and I'm just making sure the names don't overlap.
You can do this :
HTML code
<div id="datTab"></div>
JS code
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"];
function setTHead(selectItems) {
var formatString;
$.each( selectItems, function( index, value ){
formatString = value.replace(/[^0-9/-]/g, "").toString();
$('#datTab').append("<div id='col" + index + "' class='column'>'" + value + "'</div>");
});
};
You can use $.each, its better than 'for' with javascript.
The .each() method is designed to make DOM looping constructs concise
and less error-prone. When called it iterates over the DOM elements
that are part of the jQuery object. Each time the callback runs, it is
passed the current loop iteration, beginning from 0. More importantly,
the callback is fired in the context of the current DOM element, so
the keyword this refers to the element.
I did a JsFiddle
Here.

How to set goog.ui.Autocomplete minimum input to 0

I would like the autocomplete to show the entire list when the input box gets focused (no input is given). Would also like the auto complete to match substrings without having to fiddle with private variables.
At the moment the code is:
autocomplete = goog.ui.ac.createSimpleAutoComplete(
gsa.Game.gameData.teams, team2, false);
matcher=autocomplete.getMatcher();
matcher.useSimilar_=true
autocomplete.setMatcher(matcher);
Similar matches work but have to set a private variable for that (no getter or setter available).
The other one I have not been able to find out; how to show all data when no input is given (like a smart select input). So when the textbox receives focus it'll show all data since there is no filter text given. These are basic things that one would like to configure but can't find it in the API documentation.
You need to create descendants of goog.ui.ac.AutoComplete, goog.ui.ac.ArrayMatcher and goog.ui.ac.InputHandler. Also you will directly create the instance of auto complete object instead of calling goog.ui.ac.createSimpleAutoComplete.
In goog.ui.ac.AutoComplete descendant you assign custom input handler and matcher.
goog.provide('my.ui.ac.AutoComplete');
goog.require('goog.ui.ac.Renderer');
goog.require('my.ui.ac.ArrayMatcher');
goog.require('my.ui.ac.InputHandler');
my.ui.ac.AutoComplete = function(data, input, opt_multi, opt_useSimilar) {
var renderer = new goog.ui.ac.Renderer();
var matcher = new my.ui.ac.ArrayMatcher(data, !opt_useSimilar);
var inputhandler = new my.ui.ac.InputHandler(null, null, !!opt_multi, 300);
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(my.ui.ac.AutoComplete, goog.ui.ac.AutoComplete);
In goog.ui.ac.ArrayMatcher descendant you need to override getPrefixMatches() method, since the default behaviour discards empty strings. So if there is an empty string, we just return the first x rows from the data.
goog.provide('my.ui.ac.ArrayMatcher');
goog.require('goog.ui.ac.ArrayMatcher');
my.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
goog.ui.ac.ArrayMatcher.call(this, rows, opt_noSimilar);
};
goog.inherits(my.ui.ac.ArrayMatcher, goog.ui.ac.ArrayMatcher);
my.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function(token, maxMatches) {
if (token == '')
{
// for empty search string, return first maxMatches rows
return this.rows_.slice(0, maxMatches);
}
else
{
return goog.base(this, 'getPrefixMatches', token, maxMatches);
}
};
In goog.ui.ac.InputHandler descendant you need to override processFocus() method, and force to show the autocomplete popup. This can be done by calling update() method with first parameter set to true.
goog.provide('my.ui.ac.InputHandler');
goog.require('goog.ui.ac.InputHandler');
my.ui.ac.InputHandler = function(opt_separators, opt_literals, opt_multi, opt_throttleTime) {
goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals, opt_multi, opt_throttleTime);
};
goog.inherits(my.ui.ac.InputHandler, goog.ui.ac.InputHandler);
my.ui.ac.InputHandler.prototype.processFocus = function(target) {
goog.base(this, 'processFocus', target);
// force the autocomplete popup to show
this.update(true);
};

Duplicating script and incrementing id names to dynamically add input content

I am currently working on a website to easily modify css properties and animate using jquery. The page will then output the code for the user to copy and paste into their work.
I am wanting to dynamically add input fields so users can enter their own parameters in "keyframes" for the animation.
I was wondering if there is a way to have a button to create the new elements and the jquery to have it operate. Currently I have tried setting a variables value to increment with the button then have a "for" loop to create the new keyframe elements.
Is there a way to append the previous keyframes with new keyframes
this is a sample of the things I need to set when creating the DOM nodes for the input fields, but the main issue I am having is incrementing the value of "i" and duplicating the code only once.
//////////TOP TEXT INPUT///////////
var newTop = document.createElement('input')
newTop.type = "text"
newTop.id ="animation_" + i + "_top" // i = 1
newTop.size="10"
newTop.value="100"
newTop.name="animation_" + i +"_top" // i = 1
newTop.textContent = "top";
var refTopSibling = document.getElementById('frame_1')
var refTopParent = refTopSibling.parentNode
refTopParent.appendChild(newTop)
//////////TOP TEXT INPUT///////////
var newLeft = document.createElement('input')
newLeft.type = "text"
newLeft.id ="animation_" + i + "_left" // i = 2
newLeft.size="10"
newLeft.value="100"
newLeft.name="animation_" + i +"_left" // i = 2
newLeft.textContent = "left";
var refTopSibling = document.getElementById("animation_" + i + "_top")
var refTopParent = refTopSibling.parentNode
refTopParent.appendChild(newLeft)
Regards,
Andrew
EDIT--------------------------
Thanks for the replies. Unfotunately it's a little large for JS fiddle. I'm only a noob so this may not be the most elegant way to do it, but I used an if statement to increment the variables current value on each press... unfortunately it's not dynamic but I have enough options to allow 11 keyframes of animation that can be dynamically added to the DOM.
function addFrame(){
var n;
if (document.getElementById('frame_1')){n=2};
if (document.getElementById('frame_2')){n=3};
if (document.getElementById('frame_3')){n=4};
if (document.getElementById('frame_4')){n=5};
if (document.getElementById('frame_5')){n=6};
if (document.getElementById('frame_6')){n=7};
if (document.getElementById('frame_7')){n=8};
if (document.getElementById('frame_8')){n=9};
if (document.getElementById('frame_9')){n=10};
if (document.getElementById('frame_10')){n=11};
//rest of the code here
}
Now to figure out a way to draw svg dynamically and animate it with user inputs ;-)
This isn't necessarily the best implementation but I believe it meets your requirements.
function SampleObject(position, i)
{
this.posString = "new" + position;
$(this[this.posString] = document.createElement("input"))
.css({ type: "text",
id: "animation_" + i + "_" + position,
size: "10" ,
value: "100" ,
name: "animation_" + i +"_" + position,
textContent: position
});
$("#frame_1").parent().append(el);
}
//Public methods
SampleObject.prototype.getInput = function()
{
return this[this.posString];
}
//Static members
SampleObject.set = [];
//Static methods
SampleObject.new = function(position);
{
this.set.push(new SampleObject(position, this.set.length + 1));
}
//Quick test
SampleObject.new("top");
jQuery rewrite aside this does three actions, albeit the last might be surplus to requirement.
1) Rather than write a new function for every position the constructor of the object takes a position string (any string would be fine but for your objective position seems apt). The string is appended to "new" to form the "new" that is used to create an object member named in accordance with the current multi-function approach in the post that stores the new input object - personally I think the member naming is redundant but I don't know the project. The object has a get method for returning the input form.
2) Using the static "Sample.new" method (which takes a position parameter) a new SampleObject which has an index value of 1 + (static member) SampleObject.set, the set variable is used to store the new objects as they are created. This might not be necessary but I imagine you'll want to interact with them later or attach event.
As for the "frame_" if statements
$("[id^=frame_]").length() + 1
Assuming that frames are always created iteratively this will return the appropriate value for n.

Categories

Resources