Overriding Canvas / DOM object properties dynamically - javascript

I am trying to override a few properties in at the Object level for native JavaScript objects but I am struggling to figure out how to do this globally / correctly. This is done for privacy purposes to avoid fingerprinting.
For example I would like to override the UNMASKED_VENDOR_WEBGL, UNMASKED_RENDERER_WEBGL, and getSupportedExtensions() values from a query as below.
let e = document.createElement("canvas").getContext('webgl');
var t = e.getExtension("WEBGL_debug_renderer_info");
e.getParameter(t.UNMASKED_VENDOR_WEBGL);
e.getParameter(t.UNMASKED_RENDERER_WEBGL);
e.getSupportedExtensions();
For most properties for example at the navigator or screen levels I can just use Object.defineProperty or even directly rewrite the functions to just return what I want. However for the example above it's a bit more tricky because it needs to apply for any canvas created. I cannot access the specific canvas DOM element because I don't think there is a on createElement trigger as far as I know and the crafty creator of this extremely invasive fingerprinting code does this dynamically from WASM...
I have the same issue overriding the canPlayType on (new Audio) instance.
If anyone knows a trigger event I can use for on creteElement or better yet how I can spoof this it would be a huge help!

Related

Animate CC - getChildByName canvas vs WebGL

Afternoon. I have an FLA with a single MovieClip on the stage - the clip is named myThing in the Library and also has an instance name of myThing. On another layer I have the following script:
this.myThing=this.getChildByName("myThing");
console.log(this.myThing);
When I run this in a WebGL project it works as I'd expect and returns a JS object but when I run the same thing in a canvas project (which is what I need to use) it comes back null.
Initially, can anyone explain what the difference is between a WebGL and a canvas project in Adobe Animate CC and how I can get a reference to child clips to control their timelines?
Along with that, can anyone point me to any decent resources on scripting these projects? It seems like no matter what I search for I always end up back at that *!#%£¡ rabbit tutorial that manages to cram very little info into an awful lot of words.
Thanks all, your help is always appreciated :)
So I was being a numpty.
The name property of any asset defaults to null. This is not a problem because the getChildByName() method is not really necessary (for me at least) once I realise that you can just call this.someChild.someMethod().
I got hooked up on the wrong approach because it was the only one I could find examples of. I'm still finding documentation very sketchy and not very helpful when compared to AS3 or even competing JS libraries like Greensock
Also not sure why my first approach worked in WebGL but not canvas. Ah well, onwards and upwards...
WebGL and HTML5 Canvas documents work somewhat differently in Animate CC.
In WebGL, symbols having instance names are accessible as follows:
var mySymbol = this.getChildByName("instance-name");
In Canvas, the same can be done as follows:
var mySymbol = this.instance-name;
Unnamed instances can be referenced using this.getChildAt(index) in both canvas and WebGL.
Once a reference to the required instance is obtained, you can easily control it as desired. (gotoAndPlay()/Stop() etc.)
PS: In canvas, Symbol-instance names are not explicitly set as name properties of corresponding symbols in the output - hence the name property is returned as null.

What is the difference between Container and DisplayObjectContainer?

I am using a JavaScript library called PIXI and am looking for a way to "zoom" in my game. A search on the internet suggested that I put everything inside a DisplayObjectContainerand then resize it to simulate a zoom-effect.
The thing is that I already have Container (aka stage), and I think that is resizable too. So I don't understand the reason behind using a DisplayObjectContainerwhen you have a Containeralready. And frankly, I don't even see the difference between them.
This page says the following about DisplayObjectContainer:
A DisplayObjectContainer represents a collection of display objects. It is the base class of all display objects that act as a container for other objects.
This other page says the following about Container:
A Container represents a collection of display objects. It is the base class of all display objects that act as a container for other objects.
The only possible scenario I can imagine is that one of these container-objects is outdated and belongs to an older version of PIXI, which isn't too unrealistic since PIXI is rather new and could change a lot. But this is just a guess.
The guess is correct. DisplayObjectContainer is outdated and replaced with Container.

Javascript: call a function defined in a variable without eval

I'm writing a jQuery plugin that hooks to scroll and drag events, and rotates an element to point towards another element when a relevant event is triggered. Basically my plugin just detects the events, calculates the correct angle, and rotates the element accordingly using a third-party plugin. For a better explanation, you can see the demos of my plugin.
I don't want to force a certain rotation plugin to be used, so the rotation function's name may vary. At the moment I'm using eval to allow for custom function names, but I'd like to find another way of doing this. I've tried googling, but so far haven't found a solution.
So, is there a way to get around this? The rotation function's name is stored in settings.rotateFunction:
eval("$(this)." + settings.rotateFunction + "(angle)");
So far I've tried the "new Function(codeToEval)" method, but this for some reason breaks the use of $(this).
In Javascript object members (properties or functions) can be accessed using these two notations:
object.memberName
object["memberName"]
The second one is especially useful when member isn't known during design-time but rather during runtime. That's why you can call your rotate function this way:
$(this)[settings.rotateFunction](angle);
Use following syntax:
$(this)[settings.rotateFunction](angle);

What is the Proper Way to Destroy a Map Instance?

I recently developed an html5 mobile application. The application was a single page where navigation hash change events replaced the entire DOM. One section of the application was a Google Map using API v3. Before the map div is removed from the DOM, I want to remove any event handlers/listeners and free up as much memory as possible as the user may not return to that section again.
What is the best way to destroy a map instance?
I'm adding a second answer on this question, because I don't want to remove the back and forth we had via follow-up comments on my previous answer.
But I recently came across some information that directly addresses your question and so I wanted to share. I don't know if you are aware of this, but during the Google Maps API Office Hours May 9 2012 Video, Chris Broadfoot and Luke Mahe from Google discussed this very question from stackoverflow. If you set the video playback to 12:50, that is the section where they discuss your question.
Essentially, they admit that it is a bug, but also add that they don't really support use cases that involve creating/destroying successive map instances. They strongly recommend creating a single instance of the map and reusing it in any scenario of this kind. They also talk about setting the map to null, and explicitly removing event listeners. You expressed concerns about the event listeners, I thought just setting the map to null would suffice, but it looks like your concerns are valid, because they mention event listeners specifically. They also recommended completely removing the DIV that holds the map as well.
At any rate, just wanted to pass this along and make sure it is included in the stackoverflow discussion and hope it helps you and others-
The official answer is you don't. Map instances in a single page application should be reused and not destroyed then recreated.
For some single page applications, this may mean re-architecting the solution such that once a map is created it may be hidden or disconnected from the DOM, but it is never destroyed/recreated.
Since apparently you cannot really destroy map instances, a way to reduce this problem if
you need to show several maps at once on a website
the number of maps may change with user interaction
the maps need to be hidden and re-shown together with other components (ie they do not appear in a fixed position in the DOM)
is keeping a pool of map instances.
The pool keeps tracks of instances being used, and when it is requested a new instance, it checks if any of the available map instances is free: if it is, it will return an existing one, if it is not, it will create a new map instance and return it, adding it to the pool. This way you will only have a maximum number of instances equal to the maximum number of maps you ever show simultaneously on screen.
I'm using this code (it requires jQuery):
var mapInstancesPool = {
pool: [],
used: 0,
getInstance: function(options){
if(mapInstancesPool.used >= mapInstancesPool.pool.length){
mapInstancesPool.used++;
mapInstancesPool.pool.push (mapInstancesPool.createNewInstance(options));
} else {
mapInstancesPool.used++;
}
return mapInstancesPool.pool[mapInstancesPool.used-1];
},
reset: function(){
mapInstancesPool.used = 0;
},
createNewInstance: function(options){
var div = $("<div></div>").addClass("myDivClassHereForStyling");
var map = new google.maps.Map(div[0], options);
return {
map: map,
div: div
}
}
}
You pass it the starting map options (as per the second argument of google.maps.Map's constructor), and it returns both the map instance (on which you can call functions pertaining to google.maps.Map), and the container , which you can style using the class "myDivClassHereForStyling", and you can dinamically append to the DOM.
If you need to reset the system, you can use mapInstancesPool.reset(). It will reset the counter to 0, while keeping all existing instances in the pool for reuse.
In my application I needed to remove all maps at once and create a new set of maps, so there's no function to recycle a specific map instance: your mileage may vary.
To remove the maps from the screen, I use jQuery's detach, which doesn't destroy the map's container .
By using this system, and using
google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);
and running
google.maps.event.clearInstanceListeners(divReference[0]);
divReference.detach()
(where divReference is the div's jQuery object returned from the Instance Pool)
on every div I'm removing, I managed to keep Chrome's memory usage more or less stable, as opposed to it increasing every time I delete maps and add new ones.
I would have suggested removing the content of the map div and using delete on the variable holding the reference to the map, and probably explicitly deleteing any event listeners.
There is an acknowledged bug, though, and this may not work.
As google doesnt provide gunload() for api v3 better use iframe in html and assign map.html as a source to this iframe. after use make src as null. That will definitely free the memory consumed by map.
When you remove the div, that removes the display panel and the map will disappear. To remove the map instance, just make sure that your reference to the map is set to null and that any references to other parts of the map are set to null. At that point, JavaScript garbage collection will take care of cleaning up, as described in: How does garbage collection work in JavaScript?.
I guess you're talking about addEventListener. When you remove the DOM elements, some browsers leak these events and doesn't remove them. This is why jQuery does several things when removing an element:
It removes the events when it can using removeEventListener. That means it's keeping an array with the event listeners it added on this element.
It deletes the attributes about events (onclick, onblur, etc) using delete on the DOM element when addEventListener is not available (still, it has an array where it stores the events added).
It sets the element to null to avoid IE 6/7/8 memory leaks.
It then removes the element.

How to properly use HTML5's canvas within JavaScript classes?

First off, I use the term "classes" to mean functions with prototypes that might be in a separate file from the main initializing file for what I'm working on.
Now for my question/issue:
I'm working on building something in JavaScript/HTML5, and trying to program it "properly" (ie. using prototypes with formats that are, I hope, standard). In my main JavaScript file, I have methods that have create use an instance (basically the root instance of my OOP/prototype based script) that sets up the canvas.
I also have another file that is loaded which contains a 'class' for creating clickable buttons. As of right now, I'm just trying to get the buttons to draw on the canvas, however, I can't access the instance of the canvas because I believe the instance is out of scope (which is fine, I don't want everything I do to contain a long dot-notation for referencing instances). However, this is giving me trouble when trying to draw a rectangle on the canvas, considering the 'class' the button is in, isn't able to reference the context outside of it.
I found a way around this by using something along the lines of:
function CreateButton(btn_x, btn_y, btn_width, btn_height, btn_txt) {
// ... (check and define parameters)
CreateButton.prototype.custom_canvas = document.getElementById('root_canvas');
CreateButton.prototype.ctxt = this.custom_canvas.getContext('2d');
CreateButton.prototype.ctxt.fillStyle = '#666666';
CreateButton.prototype.ctxt.fillRect(this.x, this.y, this.width, this.height);
}
Basically, it's writing on top of the canvas with a canvas of the same name? I'd assume that I can still manipulate the regular canvas afterwards and it would just act as if it was a single canvas element. I worried that redrawing on the canvas might use up a lot of memory when many things are added, however, no matter the method, writing on top of the canvas can't be avoided (even when in the same scope).
Any suggestions, or help? Or is my method of using the same canvas within a different class acceptable?
Thanks for any feedback.
[UPDATE]
Hmm, maybe I should try passing the context as a parameter and just using that.
...Or should I just make the canvas a global object? Any suggestions?
I guess you could try to implement some sort of "WidgetManager" that retains reference to canvas and your widgets. It will use a callback mechanism to render widgets. Each widget (ie. in this case Button) will have certain kind of rendering states (pressed, released) and some kind of internal state. Other widgets might have more complicated states.
Note that "WidgetManager" should probably keep track of widget "z" and user presses (which widget was hit). Based on this it should be able to trigger handlers bound to widgets. In a way you have to reinvent what basic UI libs do already. :)
I think you are better off by working out your design this way before moving into the implementation phase. A lot depends on what you really want to with it. :)
Note that you can simplify rendering and checks a lot by using multiple canvasii instead of just one. In this case you'll have to deal with z-index and absolute positioning but at least you get to piggyback on some of the existing stuff without having to implement it yourself.

Categories

Resources