Adding multiple instances of an object to another object's array; - javascript

I am trying to add multiple instances of different objects to another object's array. I'm having trouble however.
*Creates the player*
function Player(){
this.name = "";
this.status = "Alive";
this.primaryClass = null;
this.secondaryClass = null;
this.strength = 0;
this.stamina = 0;
this.mystica = 0;
this.health = 0;
this.primaryWeapon = null;
this.offHand = null;
this.accuracy = 0;
this.block = 0;
this.baseDamage = 0;
this.maxDamage = 0;
this.attackSpeed = 0;
this.shield = null;
this.armor = null;
this.armorRating = 0;
this.exp = 0;
}
*Creates the sword weapon*
function LongSword(){
this.name = "";
this.attackSpeed = 1;
this.baseDamage = 10;
this.maxDamage = 15;
this.durability = 100;
this.block = 5;
this.exp = 0;
}
*Creates the Long Sword skills*
function Stab(){
this.name = "Stab";
this.status = "Unlocked";
this.damage = 0;
this.damageModifier = 0.75;
this.cooldown = 5;
this.exp = 0;
this.desc = "Stabs your opponent for an additional " +parseInt(this.damageModifier * 100) +"% of your base damage.";
}
*Equips the Player weapon(s)*
Player.prototype.equipWeapon = function equipWeapon(main, offHand){
if(this.primaryClass.dualWield){
this.primaryWeapon = main;
if(offHand){
this.offHand = offHand;
this.baseDamage += (this.strength + (main.baseDamage + (offHand.baseDamage / 2))) / 10;
this.maxDamage += (this.strength + (main.maxDamage + (offHand.maxDamage / 2))) / 5;
this.attackSpeed += main.attackSpeed + (offHand.attackSpeed / 2);
this.block += main.block + offHand.block;
}
}
else{
this.primaryWeapon = main;
this.offHand = null;
this.baseDamage += (this.strength + main.baseDamage) / 10;
this.maxDamage += (this.strength + main.maxDamage) / 5;
this.attackSpeed += main.attackSpeed;
this.block += main.block;
}
if(!this.primaryClass.dualWield && offHand){
console.log("Your class can not wield dual weapons.");
}
}
*Equips the Weapon skills*
LongSword.prototype.skills = function skills(skill){
this.skills = [];
skill.damage = parseFloat((this.baseDamage / skill.damageModifier).toFixed(1));
this.skills.push(skill);
}
These objects construct the basic elements of what I'm trying to do. So when I go to instantiate each one,
var Robert = new Player();
Robert.equipWeapon(new LongSword());
Robert.primaryWeapon.skills(new Stab());
I am getting the results I want. However, if I were to try to add another instance of Stab() so that it looks like this
var Robert = new Player();
Robert.equipWeapon(new LongSword());
Robert.primaryWeapon.skills(new Stab());
Robert.primaryWeapon.skills(new Stab());
I get the TypeError: Robert.primaryWeapon.skills is not a function. Why would it work correctly once, but not a second time. The end result of which I'm trying to achieve is that if consoled out Robert.primaryWeapon.skills, I should see two instances of the Stab object.

There are two issues in your Longsword's prototype.
First, you are replacing your function with your storing skill array, which have the same name :
LongSword.prototype.skills = function skills(skill){
this.skills = []; //Overrides your function
...
Which leads to your error, Robert.primaryWeapon.skills is not a function, cause once you call it, it is an array indeed.
To fix it, just change the name of one of the function or of the array.
Secondly, you are initializing your skills array to an empty array each time you call the function, resetting it every time. You should initialize it in Longsword's protoype.
Here's a an example with these fixes (fiddle with it if you want):
function LongSword(){
this.skills = [];
...
LongSword.prototype.addSkill = function skills(skill){
...
Then, you'll be able to add multiple skills :
var Robert = new Player();
Robert.equipWeapon(new LongSword());
Robert.primaryWeapon.addSkill(new Stab());
Robert.primaryWeapon.addSkill(new Stab());

Related

Accessing class property always returns false in P5JS

I have a game in P5.JS that i want to pause. Most of my code is in a class. This class has a isPaused field that is set to false in the constructor. Now, i have a button that flips this value, like this:
this.buttonP.mousePressed(function () {this.isPaused = !this.isPaused;});. When i print the value from inside the class, the value is flipped appropriately. However, when I try to access the isPaused property from the draw function, which is in the primary.js file, the value is always false.
Here's part of the code:
let game;
function setup() {
createCanvas(scale * factor + 1000, scale * factor);
game = new CellularAutomaton(RULESETS.DAY_AND_NIGHT, 90, 0);
game.setupButton();
game.initField();
}
function draw() {
if (!game.isPaused){
game.updateField();
}
}
class CellularAutomaton
{
generation = 0;
field = [];
colorfield = [];
born = [];
survive = [];
scale = 0;
active = 0;
factor = 15;
movement = 0;
isPaused;
buttonR;
buttomP;
constructor(rule, scale, movement)
{
var tempborn = rule.split('/')[0];
var tempsurvive = rule.split('/')[1];
this.born = tempborn.split('');
this.survive = tempsurvive.split('');
this.born.shift();
this.survive.shift();
for (var i = 0; i < this.born.length; i++) this.born[i] = +this.born[i];
for (var i = 0; i < this.survive.length; i++) this.survive[i] = +this.survive[i];
this.scale = scale;
this.movement = movement;
this.isPaused = false;
}
setupButton(){
this.buttonP = createButton('Pause');
this.buttonP.position(this.scale * this.factor + 100, 500)
this.buttonP.size(width/8 + 30, height/8);
this.buttonP.mousePressed(function () {this.isPaused = !this.isPaused;});
this.buttonP.style('background-color', color(0,0,255));
this.buttonP.style('font-size', 100)
this.buttonR = createButton('Restart');
this.buttonR.position(this.scale * this.factor + 500, 500)
this.buttonR.size(width/8 + 30, height/8);
this.buttonR.mousePressed(this.initField);
this.buttonR.style('background-color', color(0,0,255));
this.buttonR.style('font-size', 100)
}
Using function create a new scope with its proper context so the this used in the function is not referring the class context anymore. You should use an anonymous function since they operate in the enclosing scope context (the class here) or save the class context inside a variable like this :
// create a new context, not accessing this.isPaused from class anymore
this.buttonP.mousePressed(function () {this.isPaused = !this.isPaused;});
// dos not create a new context then can access isPaused from current class context
this.buttonP.mousePressed() => this.isPaused = !this.isPaused;);
// or
const that = this;
this.buttonP.mousePressed(function () {that.isPaused = !that.isPaused;});
More reading

JS missing ) in parenthetical (line #88)

I'm writing a program in JS for checking equal angles in GeoGebra.
This is my first JS code, I used c# formerly for game programming.
The code is:
var names = ggbApplet.getAllObjectNames();
var lines = new Set();
var angles = new Set();
var groups = new Set();
for(var i=0; i<names.length; i++)
{
if(getObjectType(names[i].e)==="line")
{
lines.add(names[i]);
}
}
for(var i=0;i<lines.size;i++)
{
for(var j=0;j<i;j++)
{
var angle = new Angle(i,j);
angles.add(angle);
}
}
for(var i=0;i<angles.size;i++)
{
var thisVal = angles.get(i).value;
var placed = false;
for(var j=0;j<groups.size;j++)
{
if(groups.get(j).get(0).value===thisVal)
{
groups.get(j).add(angles.get(i));
placed = true;
}
}
if(!placed)
{
var newGroup = new Set();
newGroup.add(angles.get(i));
groups.add(newGroup);
}
}
for(var i=0;i<groups.size;i++)
{
var list="";
for(var j=0;j<groups.get(i).size;j++)
{
list = list+groups.get(i).get(j).name;
if(j != groups.get(i).size-1)
{
list = list+",";
}
}
var comm1 = "Checkbox[angle_{"+groups.get(i).get(0).value+"},{"+list+"}]";
ggbApplet.evalCommand(comm1);
var comm2 = "SetValue[angle_{"+groups.get(i).get(0).value+"}+,0]";
ggbApplet.evalCommand(comm2);
}
(function Angle (i, j)
{
this.lineA = lines.get(i);
this.lineB = lines.get(j);
this.name = "angleA_"+i+"B_"+j;
var comm3 = "angleA_"+i+"B_"+j+" = Angle["+this.lineA+","+this.lineB+"]";
ggbApplet.evalCommand(comm3);
var val = ggbApplet.getValue(this.name);
if(val>180)
{val = val-180}
this.value = val;
ggbApplet.setVisible(name,false)
});
function Set {
var elm;
this.elements=elm;
this.size=0;
}
Set.prototype.get = new function(index)
{
return this.elements[index];
}
Set.prototype.add = new function(object)
{
this.elements[this.size]=object;
this.size = this.size+1;
}
It turned out that GeoGebra does not recognize Sets so I tried to make a Set function.
Basically it collects all lines into a set, calculates the angles between them, groups them and makes checkboxes to trigger visuals.
the GeoGebra functions can be called via ggbApplet and the original Workspace commands via ggbApplet.evalCommand(String) and the Workspace commands I used are the basic Checkbox, SetValue and Angle commands.
The syntax for GeoGebra commands are:
Checkbox[ <Caption>, <List> ]
SetValue[ <Boolean|Checkbox>, <0|1> ]
Angle[ <Line>, <Line> ]
Thank you for your help!
In short, the syntax error you're running to is because of these lines of code:
function Set {
and after fixing this, new function(index) / new function(object) will also cause problems.
This isn't valid JS, you're likely looking for this:
function Set() {
this.elements = [];
this.size = 0;
}
Set.prototype.get = function(index) {
return this.elements[index];
};
Set.prototype.add = function(object) {
this.elements[this.size] = object;
this.size = this.size + 1;
};
Notice no new before each function as well.
I'm not sure what you're trying to accomplish by creating this Set object though - it looks like a wrapper for holding an array and its size, similar to how something might be implemented in C. In JavaScript, arrays can be mutated freely without worrying about memory.
Here's an untested refactor that removes the use of Set in favour of native JavaScript capabilities (mostly mutable arrays):
var names = ggbApplet.getAllObjectNames();
var lines = [];
var angles = [];
var groups = [];
for (var i = 0; i < names.length; i++) {
if (getObjectType(names[i].e) === "line") {
lines.push(names[i]);
}
}
for (var i = 0; i < lines.length; i++) {
for (var j = 0; j < i; j++) {
angles.push(new Angle(i, j));
}
}
for (var i = 0; i < angles.length; i++) {
var thisVal = angles[i].value;
var placed = false;
for (var j = 0; j < groups.length; j++) {
if (groups[j][0].value === thisVal) {
groups[j].push(angles[i]);
placed = true;
}
}
if (!placed) {
groups.push([angles[i]]);
}
}
for (var i = 0; i < groups.length; i++) {
var list = "";
for (var j = 0; j < groups[i].length; j++) {
list += groups[i][j].name;
if (j != groups[i].length - 1) {
list += ",";
}
}
var comm1 = "Checkbox[angle_{" + groups[i][0].value + "},{" + list + "}]";
ggbApplet.evalCommand(comm1);
var comm2 = "SetValue[angle_{" + groups[i][0].value + "}+,0]";
ggbApplet.evalCommand(comm2);
}
function Angle(i, j) {
this.name = "angleA_" + i + "B_" + j;
var comm3 = "angleA_" + i + "B_" + j + " = Angle[" + lines[i] + "," + lines[j] + "]";
ggbApplet.evalCommand(comm3);
var val = ggbApplet.getValue(this.name);
if (val > 180) {
val -= 180;
}
this.value = val;
ggbApplet.setVisible(name, false);
}
Hopefully this helps!
Your function definition is missing the parameter list after the function name.
Also, you're initializing the elements property to an undefined value. You need to initialize it to an empty array, so that the add method can set elements of it.
function Set() {
this.elements=[];
this.size=0;
}

2D Javascript Array TypeError

[EDIT] Full application available at: http://bit.ly/1CGZzym
I'm receiving the error:
Uncaught TypeError: Cannot set property '0' of undefined
with the following code. I believe it is due to my not declaring the child array of the 2D array properly but I am really confused as to where I should be declaring this. Any ideas would be excellent.
// Create an array for the tiles we're about to draw
var tileArray = []
is declared out side of the function.
I assume it is because I am trying to create child elements within each [col] so I guess I need to declare each col number somewhere but nothing I attempt seems to be working.
function drawGrid()
{
// Draw diamond grid
var col = 0;
var row = 0;
topTileX = (viewWidth/2);
topTileY = 0;
var nextX = 0;
var nextY = 0;
var getCols = 0;
while (topTileX > -1)
{
tileArray[col][row] = new DiamondTile(topTileX, topTileY, tileWidth, true, col, row);
tileArray[col][row].draw();
while (tileArray[col][row].xPos + tileArray[col][row].tileWidth < (viewWidth) + tileWidth)
{
col++;
nextX = tileArray[col-1][row].xPos + tileArray[col-1][row].tileWidth / 2;
nextY = tileArray[col-1][row].yPos + tileArray[col-1][row].tileHeight / 2;
tileArray[col][row] = new DiamondTile(nextX, nextY, tileWidth, true, col, row);
tileArray[col][row].draw();
if (col == getCols)
{
break;
}
}
row++;
getCols = col;
col = 0;
topTileX = topTileX - tileWidth/2;
topTileY = topTileY + tileHeight/2;
}
};
For the purpose of demonstration, the DiamondTile function is as follows:
function DiamondTile(xPos,yPos,width,interactive,myCol,myRow)
{
// Set x and y position for this sprite
this.xPos = xPos;
this.yPos = yPos;
this.myRow = myRow;
this.myCol = myCol;
// Used for AI pathfinding
this.isObstacle = false;
this.isStart = false;
this.isEnd = false;
this.gValue = 0;
this.hValue = 0;
this.fCost = 0;
this.tileWidth = width;
this.tileHeight = this.tileWidth/2;
var self = this;
// Create sprite
this.spriteObj = new PIXI.Sprite(grass);
this.spriteObj.interactive = interactive;
this.spriteObj.anchor = new PIXI.Point(0.5,0);
this.spriteObj.hitArea = new PIXI.Polygon([
new PIXI.Point(0,0),
new PIXI.Point(100,50),
new PIXI.Point(0,100),
new PIXI.Point(-100,50)
]);
this.spriteObj.mouseover = function()
{
if (self.spriteObj.tint == 0xFFFFFF)
{
self.spriteObj.tint = 0xA7E846;
}
text2.setText(self.myCol + "," + self.myRow + " Start: " + self.isStart);
}
this.spriteObj.mouseout = function()
{
if (self.spriteObj.tint == 0xA7E846)
{
self.spriteObj.tint = 0xFFFFFF;
}
}
this.spriteObj.click = function()
{
if (startStage === true)
{
startStage = false;
self.isStart = true;
self.spriteObj.tint = 0x1AFF00;
text.setText("Now select an end point");
endStage = true;
return true;
}
if (endStage === true)
{
endStage = false;
self.isEnd = true;
self.spriteObj.tint = 0xFF0000;
text.setText("Now place some obstacles");
obsStage = true;
return true;
}
if (obsStage ===true)
{
self.isObstacle = true;
self.spriteObj.tint = 0x3B3B3B;
text.setText("Press 'C' to calculate path");
return true;
}
}
};
That is a multi-dimensional array and you have not initialized the first dimension array correctly. In the while loop you have to initialize the first dimension to be able to access a second dimension element with an index:
while (topTileX > -1)
{
if (tileArray[col] == null)
tileArray[col] = [];
tileArray[col][row] = new DiamondTile(topTileX, topTileY, tileWidth, true, col, row);
tileArray[col][row].draw();
// omitted other code for brevity
}
Javascript arrays are dynamic and it's enough to initialize the first dimension array elements in this case. You don't have to initialize the individual items in the second dimension.
Update: here is a fiddle with working code http://jsfiddle.net/0qbq0fts/2/
In addition your semantics is wrong. By the book, the first dimension of a 2-dimensional array should be rows, and the second dimension should be columns.
You have to explicit create the elements representing the second dimension, e.g.:
function make2DArray(rows, cols) {
var r = Array(rows);
for (var i = 0; i < rows; ++i) {
r[i] = Array(cols);
}
return r;
}
If you don't know in advance how many columns, just use this:
function make2DArray(rows) {
var r = Array(rows);
for (var i = 0; i < rows; ++i) {
r[i] = [];
}
return r;
}
The individual rows can each have independent lengths, and will grow as you add values to them.
Similarly, if you just need to add a new (empty) row, you can just do:
tileArray.push([]);
This should probably me a comment, but SO has crashed on my side. JavaScript might be throwing an exception on your stated line, but the problem may be with the 'DiamondTile' function.

Javascript error: object is not a function in a video poker script

So I have been writing a script to play a video (or really text-based) poker game as an exercise in learning Javascript. I have everything working to play through an instance of the game once, but on trying to run it a second time, it develops an error: "Uncaught TypeError: object is not a function"
This error comes up when trying to create a new hand.
Here is the relevant code, I left a few functions out that don't seem to be causing any issues:
//object constructor for card
function card(suite, faceValue) {
this.suite = suite,
this.faceValue = faceValue
}
//object constructor for hand
function hand(cards, handName, score, docHandName) {
this.cards = cards,
this.handName = handName,
this.score = score,
this.docHandName = docHandName
}
var deck = new Array;
var buildDeck = function() {
for (var i = 0; i <= 52; i++) {
if (i < 13) {
deck[i] = new card("Spades", i + 2);
}
else if (i < 26) {
deck[i] = new card("Clubs", i - 11);
}
else if (i < 39) {
deck[i] = new card("Hearts", i - 24);
}
else if (i < 52) {
deck[i] = new card("Diamonds", i - 37);
}
}
}
//pulls a card from location in deck specified by randomSpot()
var pullCard = function(spot) {
var newCard = deck[spot];
deck.splice(spot, 1);
return newCard;
}
//takes away a card each time
//passes into pullCard(spot) as spot
var pullCount = 0;
var randomSpot = function() {
var x = Math.floor(Math.random() * (52 - pullCount));
pullCount++;
return x;
}
var dealFiveCards = function() {
var card1 = pullCard(randomSpot());
var card2 = pullCard(randomSpot());
var card3 = pullCard(randomSpot());
var card4 = pullCard(randomSpot());
var card5 = pullCard(randomSpot());
var fiveCards = [card1, card2, card3, card4, card5];
return fiveCards;
}
function createNewHand() {
newHand = new hand();
newHand.cards = dealFiveCards();
return newHand;
}
var playOneGame = function() {
buildDeck();
hand = createNewHand();
hand.cards.sort(compare);
assignHandScore();
wager = prompt("How much do you bet?");
printHandValue();
dealAgain();
hand.cards.sort(compare);
assignHandScore();
payout = pays(wager);
printHandValue();
printPayout();
}
playAgain = "Y";
while (playAgain === "Y") {
playOneGame();
playAgain = prompt("Would you like to play again? Y/N").toUpperCase();
}
So the error occurs when trying to run the playOneGame() function a second time. The first time runs fine and a hand is created. The second time when it gets to hand = createNewHand(); it gives the object is not a function error.
To be clear, I have the hand created as an object, which contains properties cards, handName, score, docHandName where cards is an array of card objects, themselves containing properties of suite, faceValue.
The error gives the line newHand = new hand(); in function createNewHand() as the reference line.
Help?
The second line of playOneGame is overriding your global hand function with an instance of hand. So when createNewHand runs again hand it is no longer the same thing.
You should probably rename the function hand to Hand.

Extending a class in javascript

I have two almost identically classes written in js. I would like to make one of them extend the other one, in order to have less code. I'm a novice in javascript and I need a little help to make this.
I'm posting the classes here. Can anybody help?
//============================================================================================================================================
//Class1==================================================================================================================================
//============================================================================================================================================
function Class1(config){
var targetObj;
var copycanvas = null;
var copy = null;
var outputcanvas = null;
var draw = null;
var direction = config.direction || "lr";
var TILE_WIDTH = config.tileWidth || 100;
var TILE_HEIGHT = config.tileHeight || 100;
var SOURCERECT = {x:0, y:0, width:0, height:0};
var interval;
var tiles2 = [];
var cols = 0;
var rows = 0;
createTiles = function(){
tiles = [];
tiles2 = [];
var y=0;
while(y < SOURCERECT.height){
var x=0;
cols = 0;
while(x < SOURCERECT.width){
cols++;
x += TILE_WIDTH;
}
rows++;
y += TILE_HEIGHT;
}
var i, j;
if (direction == "tl"){
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++){
x = j * TILE_WIDTH;
y = i * TILE_HEIGHT;
var tile = new Tile();
tile.imageX = x;
tile.imageY = y;
tiles2.push(tile);
}
}
arrangeSquares();
};
arrangeSquares = function(){
var i, j, k;
var M, N;
M = rows;
N = cols;
i = j = 0;
var cnt = 0;
for (i = 0; i < N + M - 1; i++)
for (j = 0; j <= i; j++)
if (j < M && (i - j) < N){
tiles.push(tiles2[j * N + (i - j)]);
}
}
processFrame = function(){
copycanvas.width = outputcanvas.width = targetObj.width;
copycanvas.height = outputcanvas.height = targetObj.height;
copy.drawImage(targetObj, 0, 0, targetObj.width, targetObj.height);
for(var i=0; i < tiles.length; i++) {
var tile = tiles[i];
tile.alpha += 0.05;
var TH = Math.max(0, Math.min(TILE_HEIGHT, targetObj.height - tile.imageY));
var TW = Math.max(0, Math.min(TILE_WIDTH, targetObj.width - tile.imageX));
draw.save();
draw.translate(tile.imageX, tile.imageY);
draw.globalAlpha = Math.max(0, tile.alpha);
draw.drawImage(copycanvas, tile.imageX, tile.imageY, TW, TH, 0, 0, TW, TH);
draw.restore();
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].alpha < 1) {
ok = false;
break;
}
}
if (ok)
{
clearInterval(interval);
showComplete();
}
};
function showComplete() {
$target.trigger("showComplete");
$img.show();
$(copycanvas).remove();
$(outputcanvas).remove();
if ($hideTarget)
$hideTarget.hide();
};
this.hide = function(target) {
};
var $target = null;
var $img = null;
var $hideTarget = null;
this.show = function(target, hideTarget){
$target = $("#" + target).show();
align($target);
if (hideTarget != undefined) {
$target.before($hideTarget = $("#" + hideTarget).show());
align($hideTarget);
}
$img = $("#" + target + " > img").filter(":first").hide();
$("<canvas/>").attr("id", "sourcecopy")
.css("position", "absolute")
.appendTo($target)
.hide();
copycanvas = document.getElementById("sourcecopy");
copy = copycanvas.getContext('2d');
$("<canvas/>").attr("id", "output")
.css("position", "absolute")
.appendTo($target);
outputcanvas = document.getElementById("output");
draw = outputcanvas.getContext('2d');
targetObj = document.getElementById($img.attr("id"));
clearInterval(interval);
SOURCERECT = {x:0, y:0, width: targetObj.width, height: targetObj.height};
createTiles();
for(var i=0; i<tiles.length; i++){
var tile = tiles[i];
tile.alpha = 0 - (i * (2 / tiles.length));
}
var intervalDelay = (config.duration * 1000) / (40 + rows + cols);
interval = setInterval(function() { processFrame(); }, intervalDelay);
};
function Tile(){
this.alpha = 1;
this.imageX = 0;
this.imageY = 0;
};
};
//============================================================================================================================================
//Class2===================================================================================================================================
//============================================================================================================================================
function Class2(config){
var targetObj;
var copycanvas = null;
var copy = null;
var outputcanvas = null;
var draw = null;
var direction = config.direction || "lr";
var TILE_WIDTH = config.barWidth || 50;
var TILE_HEIGHT = 100;
var SOURCERECT = {x:0, y:0, width:0, height:0};
var interval;
var tiles = [];
createTiles = function(){
tiles = [];
var y=0;
while(y < SOURCERECT.height){
var x=0;
while(x < SOURCERECT.width){
var tile = new Tile();
tile.imageX = x;
tile.imageY = y;
tiles.push(tile);
x += TILE_WIDTH;
}
y += TILE_HEIGHT;
}
};
processFrame = function(){
copycanvas.width = outputcanvas.width = targetObj.width;
copycanvas.height = outputcanvas.height = targetObj.height;
copy.drawImage(targetObj, 0, 0, targetObj.width, targetObj.height);
for(var i=0; i < tiles.length; i++) {
var tile = tiles[i];
tile.alpha += 0.05;
var TH = Math.max(0, Math.min(TILE_HEIGHT, targetObj.height - tile.imageY));
var TW = Math.max(0, Math.min(TILE_WIDTH, targetObj.width - tile.imageX));
draw.save();
draw.translate(tile.imageX, tile.imageY);
draw.globalAlpha = Math.max(0, tile.alpha);
draw.drawImage(copycanvas, tile.imageX, tile.imageY, TW, TH, 0, 0, TW, TH);
draw.restore();
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].alpha < 1) {
ok = false;
break;
}
}
if (ok)
{
clearInterval(interval);
showComplete();
}
};
function showComplete() {
$target.trigger("showComplete");
$img.show();
$(copycanvas).remove();
$(outputcanvas).remove();
if ($hideTarget)
$hideTarget.hide();
};
this.hide = function(target) {
};
var $target = null;
var $img = null;
var $hideTarget = null;
this.show = function(target, hideTarget){
$target = $("#" + target).show();
align($target);
if (hideTarget != undefined) {
$target.before($hideTarget = $("#" + hideTarget).show());
align($hideTarget);
}
$img = $("#" + target + " > img").filter(":first").hide();
$("<canvas/>").attr("id", "sourcecopy")
.css("position", "absolute")
.appendTo($target)
.hide();
copycanvas = document.getElementById("sourcecopy");
copy = copycanvas.getContext('2d');
$("<canvas/>").attr("id", "output")
.css("position", "absolute")
.appendTo($target);
outputcanvas = document.getElementById("output");
draw = outputcanvas.getContext('2d');
targetObj = document.getElementById($img.attr("id"));
clearInterval(interval);
if (direction == "tb" || direction == "bt")
{
TILE_WIDTH = targetObj.width;
TILE_HEIGHT = config.barWidth;
}
else
{
TILE_WIDTH = config.barWidth;
TILE_HEIGHT = targetObj.height;
}
SOURCERECT = {x:0, y:0, width: targetObj.width, height: targetObj.height};
createTiles();
if (direction == "lr" || direction == "tb")
{
for(var i=0; i<tiles.length; i++){
var tile = tiles[i];
tile.alpha = 0 - (i * (1 / tiles.length));
}
}
else
{
for(var i=tiles.length - 1; i >= 0 ; i--){
var tile = tiles[i];
tile.alpha = 0 - ((tiles.length - i - 1) * (2 / tiles.length));
}
}
var intervalDelay = (config.duration * 1000) / (40 + tiles.length);
interval = setInterval(function() { processFrame(); }, intervalDelay);
};
function Tile(){
this.alpha = 1;
this.imageX = 0;
this.imageY = 0;
};
};
Try declaring the class like this.
var theClass = function theClass() {
....
to extend this class you can use the prototype method:
theClass.prototype.newMethodName = function () {
....
You have a couple of choices. You can isolate the common functionality into a third object that Class1 and Class2 share (aggregation), or you can actually create a hierarchy of objects (inheritance). I'll talk about inheritance here.
JavaScript doesn't have classes, it's a prototypical language. An object instance is "backed" by a prototype object. If you ask the instance for a property it doesn't have (and functions are attached to objects as properties), the JavaScript interpreter checks the prototype behind the object to see if it has the property (and if not, the prototype behind that object, etc., etc.). This is how prototypical inheritance works.
JavaScript is an unusual prototypical language in that, until recently, there was no way to create an object and assign its prototype directly; you had to do it through constructor functions. If you're using class-based terminology, you're probably going to be more comfortable with constructor functions anyway. :-)
Here's a basic inheritance setup (this is not how I would actually do this, more on that below):
// Constructs an Vehicle instance
function Vehicle(owner) {
this.owner = owner;
}
// Who's this Vehicle's owner?
Vehicle.prototype.getOwner = function() {
return this.owner;
};
// Constructs a Car instance
function Car(owner) {
// Call super's initialization
Vehicle.call(this, owner);
// Our init
this.wheels = 4;
}
// Assign the object that will "back" all Car instances,
// then fix up the `constructor` property on it (otherwise
// `instanceof` breaks).
Car.prototype = new Vehicle();
Car.prototype.constructor = Car;
// A function that drives the car
Car.prototype.drive = function() {
};
Now we can use Car and get the features of Vehicle:
var c = new Car("T.J.");
alert(c.getOwner()); // "T.J.", retrived via Vehicle.prototype.getOwner
The above is a bit awkward and it has a couple of issues with when things happen that can be tricky. It also has the problem that most of the functions are anonymous, and I don't like anonymous functions (function names help your tools help you). It's also awkward to call your prototype's version of a function if you also have a copy of it (e.g., a "supercall" — not an uncommon operation with hierarchies). For that reason, you see a lot of "frameworks" for building hierarchies, usually using class-based terminology. Here's a list of some of them:
The Class feature of Prootype, a general-purpose JavaScript library
Dean Edwards' base2
John Resig's Simple JavaScript Inheritance (Resig being the person who created jQuery)
Er, um, mine — which as far as I know is being used by about three people. I did it because I had issues with decisions each of the above made. I will be updating it to not use class terminology (and actually releasing it as a tiny library, rather than just a blog post), because none of these adds classes to JavaScript, and acting as though they do misses the point of JavaScript prototypical model.
Of those four, I'd recommend Resig's or mine. Resig's uses function decompilation (calling toString on function instances, which has never been standardized and doesn't work on some platforms), but it works even if function decompilation doesn't work, it's just slightly less efficient in that case.
Before jumping on any of those, though, I encourage you to look at the true prototypical approach advocated by Douglas Crockford (of JSON fame, also a big wig at YUI). Crockford had a great deal of input on the latest version of ECMAScript, and some of his ideas (most notably Object.create) are now part of the latest standard and are finding their way into browsers. Using Object.create, you can directly assign a prototype to an object, without having a constructor function.
I prefer constructor functions (with my syntactic help) for places where I need inheritance, but Crockford's approach is valid, useful, and gaining popularity. It's something you should know about and understand, and then choose when or whether to use.

Categories