JavaScript Strange Array Scope Issue - javascript

All source code is available here: Simply view the page source. Half the code is on that page, the other half is in the 'deputyDepot.js' file which is linked in the source and should also be viewable.
Just a few things: this is obviously still a work in progress. I'm pretty disorganized when coding, and I tend to jump around and do a little of this and a little of that. So it's a bit messy. Also the background. We'll just pretend that doesn't exist, okay?
So now, on to the problem! It seems that I'm unable to access arrays in any functions.
Currently, the way I've been testing is by modifying my main function (called weTalkNow(input)). This is located in deputyDepot.js. I set it to return whatever values I'm testing to see what they're set to. These values get printed in my chat box thingy. So I use it like console.log(). Currently it's set to return the length of the user input, but that's completely irrelevant.
_inputArray is populated by taking the user input (a string) and splitting it on spaces. It is declared at the top of the page. This is all fine and dandy.
Where the problem arises is if an array is populated manually.
var x = [ [1,2], [3,4] ];
/* Main Function */
function weTalkNow(){
return x[0][0];
}
That code, as far as I can tell, SHOULD output 1. But it does not. When the code is modified to this, it works fine:
/* Main Function */
function weTalkNow(){
var x = [ [1,2], [3,4] ];
return x[0][0];
}
This isn't very helpful, however. I need a global array, not a local one.
What makes things really weird is that if I decide to declare
var x = [ [1,2], [3,4] ];
on my main page (the one with HTML and stuffs), and then do this
/* Main Function */
function weTalkNow(){
return x[0][0];
}
in the deputyDepot.js file, it works fine. So suddenly if I declare a global array on a different page it works okay.
Please let me know if I can clarify any points in any way. I tried to give all the info I could. Random side tips are welcome too, but I'm mostly just focusing on this.
Again, just so I'm clear: for whatever reason, I CANNOT USE ARRAYS if I populate them manually (I assume this is somehow related to the problem). _inputArray is doing it's job fine, so that array works okay. It's the only global array that does so. But it isn't populated manually, but by the split function. I can't seem to be able to make a global array that is accessible by functions.
EDIT:
Okay, I found the problem! All the code I was writing works fine, like it's supposed to. The problem is that at the very top of my .js file was a broken function. This very first line prevented all the code below it from running, and so my arrays were never being initialized in the first place. For this reason I was unable to access them.
Once I checked the web console I was able to fix everything. I didn't know there was a web console before posting this question.

If your function refers to an array that is declared below the line where the function is called, the array will not be in scope.
myFunc();
function myFunc () {
console.log(ra[0]); // won't work
};
var ra = ["a"];
This will work:
var ra = ["a"];
myFunc();
function myFunc () {
console.log(ra[0]); // will work
};
The other thing to keep in mind is that javascript includes are processed in order also. One more thing: while generally javascript is processed from top to bottom, in other words; a function cannot call or reference variables that are defined on a lower line in your file, there is an exception. The exception is named functions, which you are using.
This won't work.
var funcA = function () {
funcB(); // wont work
};
funcA();
var funcB = function () {
console.log("from funcB");
};
This will work:
funcC(); // works fine
function funcC () {
funcD(); // works fine
}
function funcD () {
console.log("from funcD");
};
These subtle differences may be perceived to represent less than perfect design, but they can work very well.

Related

Getting deformation object in spark ar environment

I am trying to get deformation object to change it properties by JS, but I cannot even get it by any Spark module.
Spark AR have sample project with face distortion deformation.
https://developers.facebook.com/docs/ar-studio/tutorials-and-samples/samples/face-distortion/
You can even see in tutorial, that there is some morph object attached, which called faceDistortionPack. This object is located in assets, and I tried to get it by different ways in script, but couldn't make it. I want to write some js logic to manipulate deformations.
const Scene = require('Scene');
const Diagnostics = require('Diagnostics');
const faceMesh = Scene.root.find('facemesh_distortion');
Diagnostics.log(faceMesh); // FaceMesh: https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.facemesh
Diagnostics.log(faceMesh.deformation); // null
Diagnostics.log(faceMesh.find('faceDistortionPack')); // Exception...
// ....
I want to get 'faceDistortionPack' object to access it properties, like 'nose_z', so I can change them by JS.
Although this is a pretty old question I thought I'd answer anyway if anyone struggles with this and comes across this thread.
First of all: There's an amazing collection of helpful tips, tutorials, snippets etc. called Spark AR Community. There you can find a GitBook with an alternative, better indexed and better working version of the official script object reference. I recommend using it if you're lost in the official reference or it's not working which happens quite often. There you can see, that the BlendShapesMesh, mentioned in the previous answer is deprecated as of Spark AR v85+, so that won't help anyone. ()
So, what you are trying to achieve, if I understand it correctly, is to access the faceMesh's Blendshapes and change their value through script. What you need to do is this:
Follow the instructions in this Tutorial: https://sparkar.facebook.com/ar-studio/learn/tutorials/face-distortion-retouching/
After you applied the Blendshapes, mess around a bit with adjusting the Blendshapes, so you understand what's happening.
Add a script to your assets. You're gonna want to access the faceMesh-Object's Blendshapes' weight Property through script. That can be done by using the getBlendshapes()-Method of the Mesh-Class.
Here's a code example:
//Require Modules
const Scene = require('Scene');
const Diagnostics = require('Diagnostics');
const Reactive = require('Reactive');
//Enable async/await [part 1]
(async function () {
//Load Assets as Promise
const loadAssets = await Promise.all(
[
Scene.root.findFirst("faceMesh0"), //Put name of your facemesh here, default is faceMesh0
]
).then(function(assets){
const faceMesh = assets[0]; //Assign the facemesh from the assets-Array of the promise to a variable you can work with better
const faceMeshShapes = faceMesh.getBlendShapes(); //Access all Blendshapes of the faceMesh-Object and store them in an Array
What you have now is a variable called faceMeshShapes, that's an object with an array of Blenshapes in it. You can console log it with Diagnostics.log(faceMeshShapes) and see that there's an array called "_value" in it, that's filled with all Blendshapes as objects, that have a weight-Property, which specifies the weight of the Blendshape with a scalarValue. You can consolelog this Value by converting it with the pinLastValue()-Method of the scalarValue-Class and you can assign different values by binding it to a custom value, that you convert to a scalarValue by using the val()-Method from the Reactive-Module.
Here's an example:
Diagnostics.log(faceMeshShapes._value[0].weight.pinLastValue()); //console log the Value of the weight property's scalarValue of the Blendshape that's stored at the index 0 of the _value-Array
faceMeshShapes._value[0].weight = Reactive.val(0.5) //set the weight to 0.5
Diagnostics.log(faceMeshShapes._value[0].weight.pinLastValue()); //console log the changed value of the weight property
So basically, that's how you can access every Blendshape's weight. The index should be according to the order in which they are listed in spar AR studio, beginning with 0 for the first BlendShape. Now you can do lots of things with this value, like binding it to an animation that animates it from 0-1 or to the mouth-openess using the face-tracking module and so on.
Last but not least, don't forget any semicolons and close all brackets.
});
})(); // Enable async/await [part 2]
P.S.: It can really help sometimes to understand what's happening and how to access stuff, by console logging a list of all properties of an object. Especially since the spark AR documentation is pretty weak on that part (and pretty weak in general). You can use the following function from MDN to do that. It's nothing fancy, but it does the job and has saved me a couple of times already :)
function listAllProperties(o) {
var objectToInspect;
var result = [];
for(objectToInspect = o; objectToInspect !== null;
objectToInspect = Object.getPrototypeOf(objectToInspect)) {
result = result.concat(
Object.getOwnPropertyNames(objectToInspect)
);
}
return result;
}
The link you posted isn't live any more.
I'm guessing the below links are more relevant now for others looking into this. I animated a series of blendShapes, and these links were useful. Basically cycling through the weights of each blendShape.
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.blendshapesmesh
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.blendshape

Decoding Javascript File

need to know wich kind of encoded is this:
$(_0x6b88[150])[_0x6b88[149]]();
$(_0x6b88[154])[_0x6b88[153]](function () {
$(this)[_0x6b88[151]](_0x6b88[14]);
})[_0x6b88[152]](function () {
$(this)[_0x6b88[151]](_0x6b88[12]);
});
I had encoded my js code some months ago and now I dont know where I saved the beautified version.
Can anyone help me? How can I decode this? It look like Hexadecimal or something (_0x6b88[14]) but the rect [] looks very strange too me.
Thanks.
0x6b88 is a number in hexadecimal.
so _0x6b88 is like writing _27528, or myinteger. It is a variable name, the name doesn't really matter.
(_27528 is a different variable to _0x6b88, though)
It looks like an array, so lets replace it with alist to make it easier to understand:
$(alist[150])[alist[149]](); // runs an element with id alist[149]
$(alist[154])[alist[153]]( // runs an element and gives it a function
function () {
$(this)[alist[151]](alist[14]);
}
)
[alist[152]]( // gets an element from what ever is returned by the function
function () { // Passes a function to the element gotten
$(this)[alist[151]](alist8[12]);
}
);
alist[0] is the first element in the array. Inorder to work out what this does, you need to know what is in the cells of those arrays.
It is probably best to rewrite your code; it will be easier that way. It has been obfuscated, so you'll have a hard time working it out.
You probably won't be able to get the original variables names, although that depends on the software you used for obfuscating the code.

How do I keep an array reference through closure?

Quick question that i seem unable to google...
I'm wondering how to keep an array reference through closure. I think.
Im using AngularJS but raw JS has the same behaviour in my trials.
Context: think of a memory game where you match two images. When two selected tiles mismatch, they flip back but only after showing the selected images. So I want to delay the flip.
I have an array that i .filter to a new array.
$scope.selectedTiles = $scope.tiles.filter($scope.filterSelected);
An aside: This new array; does it only contain references or copies of data? MDN says
constructs a new array of all the values for which callback returns a true value.
Not very clear imo. My conclusion is they are references.
Anyways...
This selectedTiles is sent to a closure in order to keep its references. Only it doesnt.
flipSelectedTiles = function (selection){
return function(){
selection.forEach($scope.flipTile);
}
};
function reset(selection){
return function(){
$scope.flipSelectedTiles(selection);
}
}
var resetTiles = reset(filteredTiles);
$timeout($scope.flipSelectedTiles($scope.selectedTiles), $scope.delay);
I need this because I intend to wait a while before executing the flipSelectedTiles.
I also need to clear the selectedTiles immediately in case someone clicks another image.
selectedTiles.splice(0, $scope.selectedTiles.length);
As the timeout fires the array is empty and nothing happens.
How do I keep an array reference through closure?
Edit: here is a fiddle in plain JS http://jsfiddle.net/Tobis/TkjEF/
Edit: added flipSelectedTiles function
You are keeping a reference. The problem is you're emptying the array too early, as you noted yourself. I believe you need a copy instead:
var resetTiles = reset(filteredTiles.slice(0));
$timeout(resetTiles, $scope.delay);
Also note that you weren't passing a function to $timeout, you were invoking flipSelectedTiles immediately (unless flipSelectedTiles returns a function).

Prototype injection, google maps api

I need to catch the event of getting back suggestions for google maps autocomplete. I know it is undocumented, but doing some research I found that it could be down via some prototype hacking.
<input type='text' id='myInput'>
<script src="http://maps.google.com/maps/api/js?libraries=places&sensor=false&language=en-EN"></script>
<script>
function catcher(key) { console.log(key); }
function MyProto() {}
MyProto.prototype = new google.maps.MVCObject();
MyProto.prototype.changed = catcher;
var gAuto = new google.maps.places.Autocomplete(
document.getElementById('myInput'), ['geocode']);
// one of two should be commented
//gAuto.__proto__.__proto__.changed = catcher; // every key, including 'predictions'
gAuto.__proto__.__proto__ = MyProto.prototype; // only 'types', '0', and 'place' when selected
</script>
JSFiddle link: http://jsfiddle.net/agentcooper/hRyTF/ (check the console)
Check the last two lines. When setting 'changed' function directly on MVCObject prototype (first one, commented), everything works great and I can catch key 'predictions' in the 'catcher' function. The problem is that catcher needs to be different if, for example, I need to have two instances of autocomplete on my page. So when I'm trying to inject custom object in autocomplete's prototype chain (last line) everything fails. Is there any way to solve this?
EDIT: working version, thanks to Sajid :-)
UPDATE: Completed the code, maybe it will be helpful to anyone
In the second line, you are replacing the entire prototype of the MVC object with an instance of the MVC object, and depending on how the thing is initialized, this will likely not work at all. The first like replaces one function, though in the process it completely breaks that function since you don't call its superclass version, so you are not extending, you are really clobbering. To not clobber, you need to do:
(function() {
var oldChanged = gAuto.__proto__.__proto__.changed;
function catcher(key) {
// call old version, and make sure to maintain this reference correctly
oldChanged.call(this, key);
// do your stuff here
}
gAuto.__proto__.__proto__.changed = catcher;
})();
One simple solution is that each object has an idea of this mentioned above. So changed has a reference to this, which will refer to the object being used as the caller's target (except in specific situations that are a bit out of scope here). But basically:
var x = new MVCObject();
x.changed('hi') // this === x
So if your two versions need to do different things, you can check which this the changed method was called from and react appropriately.

Can't empty JS array

Yes, I am having issues with this very basic (or so it seems) thing. I am pretty new to JS and still trying to get my head around it, but I am pretty familiar with PHP and have never experienced anything like this. I just can not empty this damn array, and stuff keeps getting added to the end every time i run this.
I have no idea why, and i am starting to think that it is somehow related to the way chekbox id's are named, but i may be mistaking....
id="alias[1321-213]",
id="alias[1128-397]",
id="alias[77-5467]" and so on.
I have tried sticking
checkboxes = []; and
checkboxes.length = 0;
In every place possible. Right after the beginning of the function, at the end of the function, even outside, right before the function, but it does not help, and the only way to empty this array is to reload the page. Please tell me what I am doing wrong, or at least point me to a place where i can RTFM. I am completely out of ideas here.
function() {
var checkboxes = new Array();
checkboxes = $(':input[name="checkbox"]');
$.each(checkboxes,
function(key, value) {
console.log(value.id);
alert(value.id);
}
);
checkboxes.length = 0;
}
I have also read Mastering Javascript Arrays 3 times to make sure I am not doing something wrong, but still can't figure it out....
I think there's a lot of confusion coming out of this because you are clearing the array -- just maybe not for the purpose you want, or at the wrong time, etc.
function () {
var checkboxes = new Array(); // local-scope variable
checkboxes = $(':input[name="checkbox"]'); // new instance
$.each(checkboxes, /* ... */); // (truncated for brevity)
checkboxes.length = 0; // this clears the array
// but, for what...?
// nothing happens now
}
As per your snippet, every call to the function recreates and clears the array. However, all work with the array is done while the array is full (nothing happens after it's cleared).
Also, checkboxes is a private variable to the function. The variable only exists during execution of the function and is forgotten once the function is done.
So, what is the big picture? Why are you trying to clear the array?
To take a guess, it sounds like you're intending on clearing it for the next call of the function.
i.e. (filling in doSomething for function name):
doSomething(); // log all array elements and clears the array
doSomething(); // does nothing, since the array is already empty
To accomplish this, you need to define checkboxes in a single location outside of the function, either as a global variable or using closures (the heavily more recommended, albeit more complex, option):
NOTE: If you haven't dealt with closures before, they may not make much sense after only a single example. But, there are thousands of resources available, including Stack Overflow, to help explain them better than I can here.
// closure (or instantly-called function)
// allows for defining private variables, since they are only known by
// code blocks within this function
(function () {
// private (closure-scoped) variable(s)
var checkboxes = $(':input[name="checkbox"]');
function doSomething() {
$.each(checkboxes, /* ... */);
checkboxes.length = 0;
}
})();
The closure will run once, defining checkboxes as the array of inputs while doSomething will iterate the array before clearing it.
Now, the last step is to expose doSomething -- cause, as with checkboxes, it is also private. You can accomplish exposing it by passing the function reference from the closure to a variable outside the closure:
var doSomething = (function () {
/* ... */
return doSomething; // note that you DO NOT want parenthesis here
})();
Is setting length even possible? ;)
Any way checkboxes is a jquery object not an array.
When You do checkboxes = $(':input[name="checkbox"]'); it's not an array any more and whatever there was before has no influence. It doesn't matter what was in a variable if You assign something new to it in any language I know.
You are making some jquery related error. Please elaborate more so that I can help
Are You sure You put name="checkbox" in all of them? It doesn't seem to have a lot of sense. Maybe You waned $(':input[type="checkbox"]'); ?
Edit: that's funny. the above selector isn't too good as well. It should be:
$('input:checkbox');
as for removing stuff:
delete varname
delete arrname[elem]
is the right way to do it.
assigning null does not change the length but just makes trouble.
What about doing this:
checkboxes = new Array();
You can also delete it.
delete checkboxes;
When you rerun the function it will search for all checkboxes again.
Consider this strategy:
function() {
var checkboxes = $(':input[name=checkbox]:not(.examined)');
checkboxes.addClass('examined');
$.each(checkboxes, function(key, value) {
// ... stuff
});
}
You could also use .data() if you don't want to "pollute" the DOM.

Categories

Resources