How to properly structure class in Node.js - javascript

I have a class called TileStreamer that I am currently defining as follows:
function TileStreamer {
};
This class has constants, which I define as follows:
// Tiles are 256 x 256 pixels
TileStreamer.prototype.TILE_SIZE = 256;
// Header size in bytes
TileStreamer.prototype.HEADER_SIZE = 28;
// Various table entry sizes in bytes
TileStreamer.prototype.RESOLUTION_ENTRY_SIZE = 12;
TileStreamer.prototype.TILE_COUNT_SIZE = 4;
TileStreamer.prototype.TILE_ENTRY_SIZE = 12;
// Offsets within header
TileStreamer.prototype.WIDTH_OFFSET = 3;
TileStreamer.prototype.HEIGHT_OFFSET = 4;
TileStreamer.prototype.NUM_TABLES_OFFSET = 7;
TileStreamer.prototype.UNPOPULATED_OFFSET = 12092;
There also other variables. These variables are important because they need to be accessible from other classes. They get their values within the methods of this class. This is what I am unsure of as far as structure. What I'm currently trying is:
TileStreamer.prototype.header;
TileStreamer.prototype.resolutionEntry;
TileStreamer.prototype.resolutionTable;
TileStreamer.prototype.filepath;
TileStreamer.prototype.s3;
TileStreamer.prototype.level;
TileStreamer.prototype.ncols;
TileStreamer.prototype.nrows;
TileStreamer.prototype.nlevels;
TileStreamer.prototype.toffset;
TileStreamer.prototype.tsize;
TileStreamer.prototype.modifiedTime;
TileStreamer.prototype.tile;
TileStreamer.prototype.host;
TileStreamer.prototype.bucket;
This class also has methods such as:
TileStreamer.prototype.Init = function(filepath, index, s3config){
var retval = false;
AWS.config.update({accessKeyId: s3config.access_key, secretAccessKey: s3config.secret_key});
var blc = new BlockLibraryConfigs();
var awsConfig = blc.awsConfig;
AWS.config.update({region: awsConfig.region});
var aws = new AWS.S3();
var params = {
Bucket: s3config.bucket,
Key: s3config.tile_directory + filepath,
Range: 'bytes=0-' + (this.HEADER_SIZE - 1)
};
aws.getObject(params, function(err, data){
if(err == null){
TileStreamer.modifiedTime = data.LastModified;
var header = bufferpack.unpack('<7I', data.Body);
TileStreamer.header = header;
TileStreamer.nlevels = header[TileStreamer.NUM_TABLES_OFFSET];
if(TileStreamer.nlevels == 5){
TileStreamer.level = 0;
TileStreamer.ncols = Math.ceil((header[TileStreamer.WIDTH_OFFSET] * 1.0) / TileStreamer.TILE_SIZE);
TileStreamer.nrows = Math.ceil((header[TileStreamer.HEIGHT_OFFSET] * 1.0) / TileStreamer.TILE_SIZE);
}
}
});
};
The method above should set some of the values of the variables, such as modifiedTime so that I can access it in another class such as:
TileStreamer = require('tilestreamer.js');
var ts = new TileStreamer();
ts.Init(parPath, index, config);
var last_modified = ts.modifiedTime;

Just put any public properties you want to initialise when the object is created, directly in the init function. Here's a small example...
function TileStreamer() {
};
TileStreamer.prototype.Init = function() {
this.modifiedTime = new Date();
};
var ts = new TileStreamer();
ts.Init();
console.log(ts);
jsfiddle example
https://jsfiddle.net/v6muohyk/
To get around the issue you're having with setting the object properties in a callback from an asynchronous function, just create a locally accessible variable to reference the object that you are creating at that time...
TileStreamer.prototype.Init = function() {
var thisTileStreamer = this;
asynchFunction(function(err, data) {
thisTileStreamer.modifiedTime = data.lastModified;
});
};
To take it one step further, if you need to execute some code after the init function has completed, then that will require waiting for the asynchronous function to complete, as well. For that, pass a further parameter to init, that is a function to be executed after all the work is done...
TileStreamer.prototype.Init = function(callback) {
var thisTileStreamer = this;
asynchFunction(function(err, data) {
thisTileStreamer.modifiedTime = data.lastModified;
callback();
});
};
var ts = new TileStreamer();
ts.Init(function() {
// put code here that needs to be executed *after* the init function has completed
alert(ts.modifiedTime);
});

Related

How to access variable from a function in Javascript

I want to access variables ie. distance, vertex2Position, path which in two seperate function, inside main function called getResult. How can I achieve this without altering my code or altering my code in minimum way.
function getResult() {
document.getElementById("vertex1").onchange = function() {
var vertex1 = document.getElementById("vertex1").value;
var vertex1Position = graph.node.findIndex(e => e.id == vertex1) + 1;
document.getElementById("output").textContent = vertex1Position;
var distance = execute(vertex1Position); // How can I access distance in my result variable
};
var vertex2Position = 0;
console.log("whats here");
document.getElementById("vertex2").onchange = function() {
var vertex2 = document.getElementById("vertex2").value;
vertex2Position = graph.node.findIndex(e => e.name == vertex2)+ 1; // I also want to access vertex2Position in my result variable which is in outer function
document.getElementById("secondOutput").textContent = vertex2Position;
var path = getPath(vertex2Position); //How can I access path in var result
};
var result = distance.vertex2Position; // I want to store distance and vertex2Position in result variable
document.getElementById("searchResult").innerHTML = "test" + result + "" + path + "."; // I also want to access path
}
You should use something like this :
var container = (function(){
var distance;
var vertex2P;
return {
setDistance: function(distance){
this.distance = distance;
},
getDistance: function(){return this.distance;},
setVertex2P: function(vertex2P){
this.vertex2P = vertex2P;
},
getVertex2P: function(){return this.vertex2P;},
}}());
And then you can get and set the values in other functions like this
var result = function(){
container.setDistance(2);
container.setVertex2P(3);
console.log(container.getDistance() + container.getVertex2P());
}
result(); // 5
These are(maybe ) the best practices you can use in Javascript with this you avoid the global variables and added privacy to your variables, hope it helps you.
P.S you can short this with ECMASCRIPT 6
In javascript, you need understand about scopes. In your code, the
main scope is the getResult() function, so if you want to access
variables inside sub functions (functions inside the getResult()
function), you'll need declare the variables at beginning of this main
scope.
Example:
function getResult() {
var distance,
path,
vertex1,
vertex2,
vertex1Position,
vertex2Position = 0;
document.getElementById("vertex1").onchange = function() {
vertex1 = document.getElementById("vertex1").value;
vertex1Position = graph.node.findIndex(e => e.id == vertex1) + 1;
document.getElementById("output").textContent = vertex1Position;
distance = execute(vertex1Position);
}
document.getElementById("vertex2").onchange = function() {
vertex2 = document.getElementById("vertex2").value;
vertex2Position = graph.node.findIndex(e => e.name == vertex2)+ 1;
document.getElementById("secondOutput").textContent = vertex2Position;
path = getPath(vertex2Position); //How can I access path in var result
};
result = distance.vertex2Position;
document.getElementById("searchResult").innerHTML = "test" + result + "" + path + ".";
}
Note: You're using functions triggered by "onchange" event, so your variables will initiate as undefined, except for "vertex2Position"

Dynamic Javascript Variable?

I am not very sure how to name the question. What i am trying to achieve is this..
I have a set of Global Variable, they will need to be replicated over and over, but assigned with different set's name example. For example
var start
var end
var time
And i have many set/model that i have to create and change, so i am wondering if it is possible to create 1 set and i just have a var modelnumber which then i can just copy and paste them and change the modelnumber so i wont have to change thousands of variable names?
Example
var modelnumber = "1";
var modelstart = modelnumber + "modelstart";
var modelend = modelnumber + "modelend";
var modeltime = modelnumber + "modeltime";
Edit: To provide more info
So i have model1.js , model2.js model3.js and so on....and all the variable names function names are the same, and to save me time, i want to write 1 set of code that i can just change the var modelname at the top of each field so i wont have to change the thousands of variable names and function names..
You can always write a function:
function createVariables(modelNumber) {
window[modelNumber + 'modelstart'] = 1;
window[modelNumber + 'modelend'] = 2;
window[modelNumber = 'modeltime'] = 3;
}
createVariables(1);
Or change it to however you want. :)
UPDATE: (use global in place of window for NodeJS).
I think you're looking for a normal object literal. You can specify the property keys of the object with strings, which will give you the dynamic effect you're looking for.
Here's an example, using a for loop to populate the object.
var models = {};
var number_of_keys = 1000;
for(var i = 1; i < number_of_keys; i++) {
var keyName = 'model' + i;
var model = {
'start': i + 'modelstart',
'end': i + 'modelend',
'time': i + 'modeltime'
}
models[keyName] = model;
}
console.log(models);
Update:
As an example of how you could access your populated models, consider the following:
// You can effectively replace the `1` in this example with any number:
var model1 = models['model1'];
// model1 would be:
// {
// 'start': '1modelstart',
// 'end' : '1modelend',
// 'time': '1modeltime'
// }
var start1 = model1.start;
var end1 = model1.end;
var time1 = model1.time;
// Pseudo-code
var modelN = models['modelN'];
var startN = modelN.start;
var endN = modelN.end;
var timeN = modelN.time;
HTH
You could (should?) use an object or an array of objects.
For example:
// The "Model"
var Model = function(start,end,time) {
this.start = start;
this.end = end;
this.time = time;
}
// One option.
// Assign "Model" to the models
var models = {
'm1': new Model(x,y,z),
'm2': new Model(a,b,c)
}
// Access values
if (models.m1) {
alert("m1 end:["+ models.m1.end +"]");
}
// Add a "new" model
models['ace'] = new Model(r,s,t);
// or even
models.club = new Model(e,f,g);
You could also extend it like so:
Model.prototype.debug = function(id) {
if (id) {
console.log("model id:["+ id +"]");
}
console.log("start:["+ this.start +"]");
console.log("end:["+ this.end +"]");
console.log("time:["+ this.time +"]");
}
Which you would call like so:
models.m1.debug();
Or even:
for(x in models) {
models[x].debug(x);
}
Here is a code snippet example.
var Model = function(start,end,time) {
this.start = start;
this.end = end;
this.time = time;
}
Model.prototype.debug = function(id) {
if (id) {
console.log("model id:["+ id +"]");
}
console.log("start:["+ this.start +"]");
console.log("end:["+ this.end +"]");
console.log("time:["+ this.time +"]");
}
var models = {
'm1' : new Model('x','y','z'),
'm2' : new Model('a','b','c')
};
models.ace = new Model('r','s','t');
for(x in models) {
models[x].debug(x);
}

AS3: How do I get dynamic loader URL from LoaderInfo in Event Listener Function?

I'm loading many pictures, and am using an array to do so.
loader[i].load(new URLRequest(picture[i]));
My Event Listener function is enabled like this:
loader[i].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
My onComplete event handler shows this:
trace(e.target); //OUTPUT: [object LoaderInfo]
I've looked for properties in LoaderInfo that might identify which loader initiated the listener (the value of "i") so that I can putz around with each one specifically, like this:
bitmapDataArr[i] = e.target.content.bitmapData;
bmVisArr[i] = new Bitmap(bitmapDataArr[i]);
But cannot determine which "i" initiated the specific instance of the listener.
Any ideas? I tried giving a name to LoaderInfo to no avail. I still can't extract the pesky little identifying number.
EDIT showing loop for loaders and onComplete function:
for (i = 0; i < 10; i++) {
loader[i] = new Loader();
loader[i].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
loader[i].load(new URLRequest(letter[i]));
}
private function onComplete(e:Event):void {
trace("e.target",e.target); //OUTPUT: e.target [object LoaderInfo]
var LI:LoaderInfo = e.target as LoaderInfo;
var eNum:int = (????);
bitmapDataArr[eNum] = e.target.content.bitmapData;
bmVisArr[eNum] = new Bitmap(bitmapDataArr[eNum]);
}
You'll somehow need to bring i value to onComplete function. For example, in the this context or thru an argument.
P.S.: It's easier to use weak ref. Dictionaries instead of deleting properties, though I don't know much about AS3.
Here's an example that also shows how to remove the event listeners (including their callback functions):
/* An object containing callback
* functions used along with event listeners.
*/
const callbacks: Object = {};
/* This function will re-declare and hoist i
* in itself. */
private function loop(i: uint): void {
loader[i] = new Loader;
const wrapped =
callbacks[i] = function wrapper(...args) {
// Pass all arguments (respectively event and i)
onComplete.apply(null, args);
// Function#apply(thisContext, arguments)
// Rest exp. isn't implemented yet, else we could just do:
// onComplete(...args);
};
loader[i].contentLoaderInfo
.addEventListener(Event.COMPLETE, wrapped, false,
0, true);
loader[i].load(new URLRequest(letter[i]));
};
for (var i: uint = 0; i < 10; ++i) loop(i);
private function onComplete(e: Event, i: uint): void {
const loaderInfo: LoaderInfo = e.target as LoaderInfo;
bitmapDataArr[i] = e.target
.content.bitmapData;
bmVisArr[i] = new Bitmap(bitmapDataArr[i]);
loader[i].contentLoaderInfo
.removeEventListener(
Event.COMPLETE, callbacks[i]
);
// Deletes the property that stores
// the function inside callbacks
delete callbacks[i];
}
Since posting this question, I've been utilizing the following class. It takes in an integer (number pictures to load) and gives public access to an array of Sprites in the Array "ShapeArr."
Each sprite's name property is derived from its URL name. (name:"pic1" from loaded url "assets/pic1.png")
I was having trouble with the whole concept/implementation of inline functions, and have been using this approach instead.
package {
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.Sprite;
import flash.events.Event;
public class MultipleImageLoader extends Sprite {
{private var pic:Array = [
"assets/pic1.png", "assets/pic2.png", "assets/pic3.png", "assets/pic4.png",
]}
private var loader:Array = [];
public var ShapeArr:Array = [];
public var bitmapDataArr:Array = [];
public var bmVisArr:Array = [];
private var shapeText:Array = [];
private var picArray:Array = [];
private var count:int = 0;
private var loaderCounter:int = 0;
private var numPicsToLoad:int;
private var a:String;
public var loaded:Boolean = false;
public function MultipleImageLoader(numPics:int):void {
numPicsToLoad = numPics;
loaded = false;
init();
}
private function init(e:Event = null):void {
if (hasEventListener(Event.ADDED_TO_STAGE)) {
removeEventListener(Event.ADDED_TO_STAGE, init);
}
picArray = new Array;
for (var i:int = 0; i < numPicsToLoad; i++) {
picArray.push(i);
}
initiateLoaders();
}
private function initiateLoaders():void{
loader[loaderCounter] = new Loader;
loader[loaderCounter].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
a = pic[picArray[loaderCounter]];
//trace("shapecolor load:", a);
shapeText[loaderCounter] = (a.substr(16, a.length - 20));
loader[loaderCounter].load(new URLRequest(a ) );
}
private function onComplete(e:Event):void {
//trace("sssssssssssssssssssssssssshapecolor");
bitmapDataArr[loaderCounter] = e.target.content.bitmapData;
bmVisArr[loaderCounter] = new Bitmap(bitmapDataArr[loaderCounter]);
bmVisArr[loaderCounter].scaleX = .1;
bmVisArr[loaderCounter].scaleY = .1;
bmVisArr[loaderCounter].x =-bmVisArr[loaderCounter].width / 2;
bmVisArr[loaderCounter].y =-bmVisArr[loaderCounter].height / 2;
ShapeArr[loaderCounter] = new Sprite();
ShapeArr[loaderCounter].name = a.substr(7,4);
trace("Name",loaderCounter,ShapeArr[loaderCounter].name );
ShapeArr[loaderCounter].addChild(bmVisArr[loaderCounter]);
loader[loaderCounter].contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
if (loaderCounter <numPicsToLoad-1) {
loaderCounter += 1;
initiateLoaders();
}
//trace("gonna count",count);
counting();
count += 1;
}
private function counting():void {
trace("tile count", count,numPicsToLoad);
if (count < numPicsToLoad-1) {
return;
}
else{
removeEventListener(Event.ENTER_FRAME, counting);
loaded = true;
count = 0;
trace("All Images LOADED");
}
}
}//end Class
}//end Package

oop - How to get this of this javascript

I'm changing some code to function more like a class in c# so I don't have to make a new script for each occurrence. I've hit problems with my scope on my constructors, I have this constructor
function Game(canvas) {
this.Polygon = function(size, pointCount){
this.size = size;
this.pointCount = pointCount;
this.corners = [];
this.palette = [];
this.render = function (GameObject) {
this.makePolygon(GameObject, this.size, this.corners);
};
};
this.makePolygon = function(GameObject, size, corners){//other code...}
}
My problem is in this.render, makePolygon is inside the class so this means something different. I have tried using .bind(this); but I can't get it to work.
I'm positive that this has been asked before but none of the answers I found would work for me.
A convention that I have used on different teams is to alias this at the top of javascript functions, to avoid this exact problem.
Example:
this.Polygon = function(size, pointCount){
var my = this;
my.size = size;
my.pointCount = pointCount;
my.corners = [];
my.palette = [];
my.render = function (GameObject) {
my.makePolygon(GameObject, my.size, my.corners);
};
};
this.makePolygon = function(GameObject, size, corners){//other code...}
Another option, depending on where this function lies, is to do it as follows.
// Somewhere at the top of this code snippet
var my = this;
//...
my.Polygon = function(size, pointCount){
my.size = size;
my.pointCount = pointCount;
my.corners = [];
my.palette = [];
my.render = function (GameObject) {
my.makePolygon(GameObject, my.size, my.corners);
};
};
my.makePolygon = function(GameObject, size, corners){//other code...}

Creating a simple constructor for a grouping of Web Audio API nodes

I have a batch of Web Audio API nodes that look like the code below. I want to abstract this into a simple constructor but I'm having trouble. I'm not sure what I'm doing wrong. The end result should look something like
function filterTemplate(name,freqVal){
this.name = context.createBiquadFilter();
this.name.type = 5;
this.name.gain.value = null;
this.name.Q.value = 1;
this.name.frequency.value = this.freqVal; // freqVal is here
}
When I call the function:
var filter = new filterTemplate("theName",200); //Uncaught TypeError: Cannot call method 'createBiquadFilter' of undefined
I changed the method to look like this and the error is removed
this.name = function(){return context.createBiquadFilter()};
but then I get another error for the various property values
//Uncaught TypeError: Cannot set property 'value' of undefined
I'm really just confused as to the proper way to create a vanilla constructor using built in browser methods and properties.
I want to abstract the code below into looking something like the code above
filter1 = context.createBiquadFilter();
filter1.type = 5;
filter1.gain.value = null;
filter1.Q.value = 1;
filter1.frequency.value = 80; // Changes
filter2 = context.createBiquadFilter();
filter2.type = 5;
filter2.gain.value = 0;
filter2.Q.value = 1;
filter2.frequency.value = 240; // Changes
filter3 = context.createBiquadFilter();
filter3.type = 5;
filter3.gain.value = 0;
filter3.Q.value = 1;
filter3.frequency.value = 750; // Changes
filter4 = context.createBiquadFilter();
filter4.type = 5;
filter4.gain.value = 0;
filter4.Q.value = 1;
filter4.frequency.value = 2200; // Changes
filter5 = context.createBiquadFilter();
filter5.type = 5;
filter5.gain.value = 0;
filter5.Q.value = 1;
filter5.frequency.value = 6000; // Changes
The builder pattern is very nice for this situation. Especially when you can set a lot of properties.
http://jsfiddle.net/yw8Fm/
You can create a simple FilterTemplate class like this.
function FilterTemplate(builder) {
this.context = builder._context;
this.context.type = builder._type;
this.context.gain.value = builder._gainValue;
this.context.Q.value = builder._qValue;
this.context.frequency.value = builder._frequencyValue;
}
It takes a builder object as constructor argument. Here is the Builder.
FilterTemplate.Builder = function () {
this._context = context.createBiquadFilter();
this._type = 5;
this._gainValue = null;
this._qValue = 1;
this._frequencyValue = 80;
this.context = function (val) {
this._context = val; return this;
};
this.type = function (val) {
this._type = val; return this;
};
this.gainValue = function (val) {
this._gainValue = val; return this;
};
this.qValue = function (val) {
this._qValue = val; return this;
};
this.frequencyValue = function (val) {
this._frequencyValue = val; return this;
};
};
You can further extend this example as you like.
Now you can create FilterTemplates with ease.
var filter1 = new FilterTemplate(
(new FilterTemplate.Builder()).frequencyValue(80)
);
var filter2 = new FilterTemplate(
(new FilterTemplate.Builder()).frequencyValue(80).qValue(2)
);
Your problem is with the scope of your context variable.
var filter = new filterTemplate("theName",200); //Uncaught TypeError: Cannot call method 'createBiquadFilter' of undefined
... means that the context variable isn't available from where you're trying to reach it (which is within the filterTemplate constructor). When you do...
this.name = function(){return context.createBiquadFilter()};
... you're assigning the function to this.name instead, and it won't try to access the context until the function is run, and thus the error is removed. What happens instead is that you don't have a filter in this.name, but rather a function, and a function doesn't have a gain property and therefore you get an error when you try to set this.name.gain.value.
What you should look for is where you define the context, and make sure it's possible to access that variable from within filterTemplate.

Categories