Javascript: call a function defined in a variable without eval - javascript

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);

Related

Overriding Canvas / DOM object properties dynamically

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!

Polygon mouseover event position

There is a problem on my website. I need to capture "mousemove" event and after that change the position of hint window according to the position of cursor at the moment of event. Here is the code:
google.maps.event.addListener(country, 'mousemove', function(e) {
this.setOptions({fillOpacity: 1});
$('div.info-win').css('top', e.kb.clientY + 20 + 'px');
$('div.info-win').css('left', e.kb.clientX -50 + 'px');
$('div.info-win').show()
});
At first everything was ok, but soon browser started to show the error, that e.kb is not defined. I checked the event via console and realized that "e.kb" really does not exist. Instead there was "e.lb" so I changed it in my code. But now the problem is the same, and console shows me that "e.lb" does not exist. I cant explain it, but now the captured event has ".gb" property instead of ".lb". can someone help me and explain why this happens every week again , so I need always to check the name of event property?? this is the first time I see problem like that but at the same time I cannot find any solution!
The Google Maps MouseEvent doesn't give the screen X and Y values.
Instead, it seems to give only Latitude/Longitude of the mouse position.
Reference:
https://developers.google.com/maps/documentation/javascript/reference#MouseEvent
https://developers.google.com/maps/documentation/javascript/reference#PolyMouseEvent
You need to convert the Latitude/Longitude value to the screen mouse X/Y position which is explained here (similar question):
Get Position of Mouse Cursor on Mouseover of Google Maps V3 API Marker
The reason your map worked for a while and later started failing is that you are using undocumented properties of the MouseEvent object. As you discovered, these may change at any time.
Think about it: do kb and lb and gb sound like names you would use in your code to describe a mouse position? And would you change them willy-nilly like that? :-)
So why does Google use gibberish names like these, and why do they change them all the time?
In the original source code that their developers work on, of course they use readable and consistent names. But then they compile the code with a "minifier" that shortens the private variable/property/method names for faster loading. When they revise the source and recompile it, those short names get reassigned arbitrarily.
The three different property names you found over the weeks clearly all refer to the same object in the original source, but the minifier changed the name you see in the browser.
Never assume you can use a property or method you discover in your browser's developer tools - especially if it has a name that makes no sense. Check it first in the Maps API documentation, and if it's not there, don't use it.
See Seyong's answer for the details of how to change your code to use the documented methods.

iOS-style formatting callout using Rangy

I'm looking at Rangy (http://code.google.com/p/rangy/) and it seems it has a bunch of DOM utilities but I don't understand them without examples. So I'm turning to SO with my ideas and hopefully you guys can show me how this can be done:
What I need to do with Rangy is use it to find the position and dimensions of the selection. I want to get the frame or Rect of the selection, whether relative to the document or parent element. Then I can position my callout accordingly.
I believe the demo that comes with Rangy already illustrates what you want. specifically http://rangy.googlecode.com/svn/trunk/demos/position.html inside showSelectionPosition function
Considering the fact that selection may be spread across multiple elements, it'd be best to use the coordinates of either startSelEl or endSelEl to anchor your callout to the beginning or end of a selection.
There is an embryonic, unreleased Rangy module I wrote for getting pixel coordinates of a selection or range. Unfortunately the difficulty of getting this working properly in all browsers and all situations has put me off completing it and I have essentially abandoned it. However, if you add a bit more detail about what you're trying to do I may be able to suggest something.

Restrict an object to stay within another object

This is related to SVG, I have a large SVG object made of paths which stays static. With a press of a button, I can create another SVG object inside the larger object. The second object can be dragged with the mousedown.
PROBLEM:
Now I want to add a restriction so that the second object created cannot venture outside of the main object.
I tried using 'mouseup' for restriction but that does not work because the restriction is applied according to the cursor point on the second object, rather than the border of the second object.
Hope someone can help.
UPDATE:
#Phrogz : We have been trying to get Kevin's code to work but are struggling to get any results. We have a function attached to onmouseup to find out the intersected points of the object on the underneath path.
The function is suppose to give the results of the intersection & give an alert that the function has been exected. Its not giving anything in response, leading us to wonder whether the function is being executed at all.
Here is the main code:
var path=svgDoc.getElementById("path");
var str=intersectPathShape(path,DragTarget);
alert(str)
Phrogz, any thoughts on this?
You will need to use an intersection library like this one by Kevin Lindsey to detect when the paths overlap and prevent it. (He also provides demos of his code.)
Depending on how you implement your dragging, you may also need to check the bounding box of the two items to ensure that one is contained in the other (since dragging the child completely outside the parent would cause them not to intersect, but also not be a legal position).
The simplest code would be to store the last position of the child and return it to that position when an intersection is detected. Under fast dragging, though, this might cause the child to stop being dragged at a spot that is not actually touching. For a better user experience, you may want to try a binary search of intermediary offsets between the last known-good position and the current position to find the closest point along that path that is legal.
Kayote,
I think I answered this in another question. The short version is have a look at this github project:
https://github.com/thelonious/js-intersections
In particular, the loadShapes function in this file:
https://github.com/thelonious/js-intersections/blob/master/samples/IntersectionUtilities.js
You will need to instantiate a specific object per node type then pass those to Intersection.intersectShapes.
HTH,
Kevin

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