Casting for element access in Haxe for HTML-elements - javascript

If we want to draw something on canvas we need to get 2D context of it.
I have a canvas element in index.html of project:
<body>
<canvas id="canv" width="200" height="200"></canvas>
</body>
So now I need to get access for that element, ok, let's write code:
var cans:CanvasElement = Browser.document.getElementById("canv");
and in compile phase I get error:
src/Main.hx:32: characters 2-78 : js.html.Element should be js.html.CanvasElement
But if we use unsafe casting, already will be fine:
var cans:CanvasElement = cast Browser.document.getElementById("canv");
And everything works fine, I get access and can get 2D context or make some settings like:
cans.width = cans.height = 800;
cans.style.backgroundColor = 'rgba(158, 167, 184, 0.4)';
Yes, I know, "if it works - don't fix", and I roughly understand that everything is normal, in principle I get what I need, when get cast, but can anybody explain this process for me?
What does that mean - js.html.Element should be js.html.CanvasElement? I'm only started Haxe learning (and programming in particular), I'm glad, that I can do workable things, but I want to know, why it works when it works and why not when it doesn't.

js.html.Element should be js.html.CanvasElement
This just means that the compiler expected the type CanvasElement (because that's what the type hint for the cans variable tells it), but encountered something else (Element in this case). You're trying assign the value returned by getElementById() to cans, but getElementById() returns an Element.
Since Element is less specific than CanvasElement (CanvasElement extends Element), you can't just assign an Element to a CanvasElement - who's to say it's not a DivElement, or any of the other options? This can only be decided when the code is executed / the compiler can't know this, hence the error (runtime vs compile time).
This works fine in this case because you know that the element with the ID "canv" is in fact a CanvasElement, so you can silence the compiler by telling it that you know what you're doing with a cast. It will go wrong when the returned value actually has another type.

Gama11 answer is really good. Sometimes easier just to create the canvas in code, like this simple snake.
https://try.haxe.org/#D0324
Secondly sometimes you can use another variable and let the abstractions stuff auto cast it for you, but I think you might end up still using a cast in your code.
var canvas = document.createCanvasElement();
var dom = canvas;
var style = dom.style;
// HTMLDocument is needed here so dom is infered to
// HTMLDocument type and the canvas is auto cast,
// via the use of the secondary variable.
The problem here is not really Haxe, the problem is the way HTML5 has evolved or retrospecively re-designed, Haxe is just trying to wrap something that is sort of Dynamic and inconsistant with some attempt at providing proper compiler checking and consistancy, often when types come into Haxe like loading data, you have to cast them for Haxe, but you only have one risky point, from then on the compiler can help you. You can in some cases be more explicit with your cast:
var style = cast( canvas, HTMLDocument ).style;
telling Haxe how you are expecting it to be cast. For some targets that are less dynamic Haxe can do a better job with the type systems, but the fact that js is so slack means despite the odd complication the use of Haxe is even more powerful for js coding. Stick with Haxe it's good for your general code understanding of many languages.

Related

How to cut off function from global scope

I have an idea for a game where people can type in some simple instructions for their character like player.goLeft() or player.attackInFront() and for that I have people type their code into a text box and then I parse it into eval(). This works well but it also allows people to change their own character object by typing things like player.health = Infinity; or something similar. I have a list of functions I want to allow people to use, but I am unsure how to restrict it to only use them.
I understand that the whole point of not letting people use eval is to avoid accidental cross-site scripting but I am unsure on how else to do this. If you have a suggestion please leave a comment about that.
I asked some people around on what to do and most suggested somehow changing scope(which is something I was not able to figure out) or to add some odd parameter to each function in my code that would be required to be a specific string to execute any function, but that seems hacky and since I am making the game in browser with p5js it would be easy to just inspect element and see what the password is.
basically every character has variable called "instruction" which is just a string of javascript. Then every frame of the game I execute it by doing eval(playerList[i].instruction);
tl;dr, how can I only allow specific function to be executed and not allow any others?
EDIT: I forgot to mention that I also am planning to provide player with information so that people can made code that would adapt to the situation. For example there will be parameter called vision that has vision.front and vision.left etc. These variables would just say if there is an enemy, wall, flower, etc around them in a grid. Some people suggested that I just replace some functions with key words but then it compromises the idea of using if statements and making it act differently.
EDIT 2: Sorry for lack of code in this post, but because of the way I am making it, half of the logic is written on server side and half of it works on client side. It will be a little large and to be completely honest I am not sure how readable my code is, still so far I am getting great help and I am very thankful for it. Thank you to everybody who is answering
Do NOT use eval() to execute arbitrary user input as code! There's no way to allow your code to run a function but prevent eval() from doing the same.
Instead, what you should do is make a map of commands the player can use, mapping them to functions. That way, you run the function based on the map lookup, but if it's not in the map, it can't be run. You can even allow arguments by splitting the string at spaces and spreading the array over the function parameters. Something like this:
const instructions = {
goLeft: player.goLeft.bind(player),
goRight: player.goRight.bind(player),
attackInFront: player.attackInFront.bind(player)
};
function processInstruction(instruction_string) {
const pieces = instruction_string.split(' ');
const command = pieces[0];
const args = pieces.slice(1);
if (instructions[command]) {
instructions[command](...args);
} else {
// Notify the user their command is not recognized.
}
};
With that, the player can enter things like goLeft 5 6 and it will call player.goLeft(5,6), but if they try to enter otherFunction 20 40 it will just say it's unrecognized, since otherFunction isn't in the map.
This issue sounds similar to the SQL Injection problem. I suggest you use a similar solution. Create an abstraction layer between the users input and your execution, similar to using parameters with stored procedures.
Let the users type keywords such as 'ATTACK FRONT', then pass that input to a function which parses the string, looks for keywords, then passes back 'player.attackInFront()' to be evaluated.
With this approach you simplify the syntax for the users, and limit the possible actions to those you allow.
I hope this isn't too vague. Good luck!
From your edit, it sounds like you're looking for an object-oriented approach to players. I'm not sure of your existing implementation needs, but it would look like this.
function Player() {
this.vision = {
left: '',
// and so on
}
}
Player.prototype.updateVisibilities = function() {
// to modify the values of this.visibility for each player
}
Player.prototype.moveLeft = function() {
}
Don't give the user an arbitrary interface (such as an input textfield that uses eval) to modify their attributes. Make a UI layer to control this logic. Things like buttons, inputs which explicitly run functions/methods that operate on the player. It shouldn't be up to the player as to what attributes they should have.

use css property content on element to exceptionnaly store information for javascript

I use a variable.less config file to store all relevant information about a design.
One of the information is the breakpoint between mobile layout and PC layout.
I also need this information in javascript, and I didn't know how to get it (without storing it in a data-attribute, because I wished to keep all design variables in the same file).
So I finally found that :
I store my break point in the variables.less :
#bk-point: "500px";
I use the css property "content", but not on a pseudo-element, but on any tag (less file):
#any-div {
content: "#{bk-point}";
}
Like this, this doesn't affect the design ("content" property doesn't show on element, only on pseudo-element), and I can get it very easily with jQuery :
var bkPoint = $('#any-div').css('content');
And all my variables are in the less files.
This is perfect for what I want, but is there any side-effect that I don't see ? Is this a bad practice for reasons I cannot imagine ?
Thanks for your advices !
Sébastien.
PS:
1. works in firefox 21.0 and in chromium 27.0
2. and of course, if you've got a better solution …
The css 'content' property is only valid on pseudo elements:
https://developer.mozilla.org/en-US/docs/Web/CSS/content
As much as this seems like a cool idea, I wouldn't feel comfortable using it in production. I think that you should accept that your js variables and css variables will be in 2 different files and then just apply the values via data-attributes.
However, if you really want a creative way to do this only from the css files that can print to the html and thus interact with the javascript, what about using valid properties that will not affect the design?
All you are really doing is storing a string in the html, so you could use a rather obscure element such as counter-reset, and then grab the value via jquery.css()
variables.less: #bkpoint = 500;
css:
#any-div {
counter-reset: break-point #bkpoint;
}
jquery:
$('#any-div').css('counter-reset'); // returns: 'break-point 500'
simple regex function to get you rid of the 'break-point' part of the returned string and you've got what you're looking for.
btw, can do this on any other css property not in use, such as:
border-width: #bkpoint;
border: none;
specs on counter-reset in case you're interested:
http://www.w3.org/TR/CSS2/generate.html#propdef-counter-reset

Need help converting vbs code to jscript. Im getting a Runtime Error Type Mismatch

Ive got this labratory equipment that is connected to my PC. It uses special OCX file to communicate with the device (reading, setting parameters and such). I got this code from manual that seems to be working. I get a message box saying "Magnification =1272.814 Last error=API not initialized".
<HTML>
<HEAD>
<SCRIPT LANGUAGE="VBScript">
<!--
Sub window_onLoad()
Dim Value
Dim er
call Api1.Initialise("")
call Api1.Get("AP_MAG",Value)
call Api1.GetLastError(er)
call window.alert("Magnification = " + CStr(Value)+"Last error="+er)
call Api1.ClosingControl()
end sub
-->
</SCRIPT>
<TITLE>New Page</TITLE>
</HEAD>
<BODY>
<object classid="CLSID:71BD42C4-EBD3-11D0-AB3A-444553540000" id="Api1">
<PARAM NAME="_Version" VALUE="65536">
<PARAM NAME="_ExtentX" VALUE="2096">
<PARAM NAME="_ExtentY" VALUE="1058">
<PARAM NAME="_StockProps" VALUE="0">
</OBJECT>
</BODY>
</HTML>
So because I have 0% knowledge in vbs and about 10% in jscript I`m trying to rewrite the same thing in Javascript. And I also have some necessary code already written in js.
<script language="JScript">
var Api1=new ActiveXObject("ApiCtrl");
var value;
var er;
Api1.Initialise("");
Api1.Get("AP_MAG",value);
Api1.GetLastError(er);
window.alert("Magnification = " + value+"\n Last error="+er);
Api1.ClosingControl();
</script>
Unfortunately I get a type mismatch error in either .Get or .GetLastError methods either with var value; var er; or var value=""; var er="";
Here is what API manual has to say
long GetLastError(VARIANT* Error)
[out] Error is the error string
associated with the error code for the last error
Remarks: This call will return a VT_BSTR VARIANT associated with the last error. Return
Value: If the call succeeds, it returns 0. If the call fails, an error
code is returned from the function.
long Get(LPCTSTR lpszParam, VARIANT* vValue)
[in] lpszParam is the name of the parameter e.g. “AP_MAG”
[in][out] vValue is the value of the parameter Remarks: This call will get the
value of the parameter specified and return it in vValue. In C++,
before calling this functions you have to specify the variant type
(vValue.vt) to either VT_R4 or VT_BSTR. If no variant type is defined
for vValue, it defaults to VT_R4 for analogue parameters (AP_XXXX) and
VT_BSTR for digital parameters (DP_XXXX). If the variant type is VT_R4
for an analogue parameter, then the floating point representation is
returned in the variant. If a VT_BSTR variant is passed, analogue
values are returned as scaled strings with the units appended (e.g.
AP_WD would return “= 10mm”). For digital parameters, VT_R4 variants
result in a state number and VT_BSTR variants result in a state string
(e.g. DP_RUNUPSTATE would return state 0 or “Shutdown” or the
equivalent in the language being supported). In C++, if the variant
type was specified as VT_BSTR then the API will internally allocate a
BSTR which the caller has to de-allocate using the SDK call
::SysFreeString (vValue.bstrVal)
Welcome to StackOverflow!
Well, each language is made with purpose. Then come to deal with ActiveX objects in browser (or WSH) environment, VBScript is the best choice, while JavaScript is most worst.
JavaScript hasn't so-called out parameters. That mean all function arguments are passed by value (as copy). Lets show you this with examples.
' VBScript
Dim X, Y
X = 1
Y = 2
Foo X, Y
MsgBox "Outer X = " & X & ", Y = " & Y
'> Local args: 6, 8
'> Outer X = 1, Y = 8
Sub Foo(ByVal arg1, ByRef arg2)
arg1 = 6
arg2 = 8
MsgBox "Local args: " & arg1 & ", " & arg2
End Sub
By default in VBS the arguments are passed by reference, so ByRef prefix in function arguments declaration is optional. I include it for clarity.
What the example illustrate is the meaning of "by reference" or "out" parameter. It behave like return value because it modify referenced variable. While modifying "by value" variable has no effect outside of the function scope, because we modify a "copy" of that variable.
// JavaScript
function foo(arg1) {
arg1 = 2;
alert('Local var = ' + arg1);
}
var x = 0;
foo(x);
alert('Outer var = ' + x);
// Local var = 2
// Outer var = 0
Now take a look at this thread. Looks like there is a kind of partial solution by using empty objects. I'm not sure in which cases that will work, but for sure it's very limited hack.
If this not help in your case, then looks like it's time to go with VBScript. Starting with VBS is easy anyway. It's the most user friendly language I ever touch. I was need days, even weeks with other languages only to get started, while just after a few hours with VBS I was able to use it freely.
[EDIT] Well, I made a lot more efforts to reply as may looks like at the glance :) Starting with the language limitation you met. Afterwards going to explain the nature of that limitation (what's "in/out" parameter), and the best way to do that is via example, and this is what I did. Afterwards I show you the only workaround out there to deal with this limitation in JS. Can we consider this as complete answer?
You not mention whether you test this "empty-object-trick", but as you still asking I presume you did that and it's not work with your OCX, right? Then, in this case, you're just forced to deal with your OCX via VBScript, what was my answer from the beginning. And as you prefer to stay with JS then you need to integrate a piece of VB code in your solution.
And as you noted too, this VBs/Js integration is a whole new question. Yes, good question of course, but it's a metter of new topic.
Ok, lets say that the question you append below: "why it should work with passing objects as a function parameter", is still a part of the main question. Well, as you see, even people using JS daily (am not one of them) has no idea what happens "behind the hood", i.e. do not expect an answer on what the JS-engine do in this case, or how this cheat the JS-engine to do something that it's not designed to do. Personally, as I use JS very rarely and not for such tasks, am not even sure if this trick works at all. But as the JS-guys assert it works (in some cases) then we s'd trust them. But that's all about. If this approach fail then it's not an option.
Now what's remain is a bit of homework, you s'd research all available methods for VBs/Js integration, also test them to see which one is most applicable to your domain, and if by chance you meet with difficulties, just then come-back to the forum with new topic and the concrete issue you're trying to resolve.
And to become as helpful as possible, I'll facilitate you with several references to get started.
Here is the plan...
1. If it's possible to work without VBs/Js integration then use stay-alone .VBS files (in WSH environment), else ...
2. In case you work in browser environment (HTML or HTA) then you can embed both (VBs/Js), and your integration w'd be simple.
3. Or may integrate VBs/Js with Windows Script Files (.wsf).
4. Or use ScriptControl that allow running VBScript from within JScript (or backward/opposite).
Links:
Using the ScriptControl
How To Call Functions Using the Script Control
An example VBs/Js integration using ScriptControl via
Batch-Embeded-Script
What is Batch-Embeded-Script:
VBS/Batch Hybrid
JS/Batch Hybrid
5. Some other method (if you find, that am not aware of).
Well, after all this improvements I not see what I can append more, and as I think, now
my answer is more than complete. If you agree with my answer then accept it by clicking on the big white arrow. Of course, if you expect to get better reply from other users, you may still wait, but keep in mind that unanswered questions stay active just for awhile and then become closed.

less.js - get variable values inside parsers' callback

I am using less.js (1.3.0) to parse less to css on the client side. Inside the parsers' callback I want to get the value for each variable. i tried the following without success.
var data = "#colour: red; #example { background-color: #colour; }",
parser = new less.Parser({});
parser.parse(data, function (error, root) {
console.log( root.toCSS() );
var varsDef = root.variables();
for (k in varsDef) {
console.log(varsDef[k]);
// how to get the value for the var?
//not working
console.log(varsDef[k].eval());
//not working
console.log(varsDef[k].toCSS());
//is an object but looking for a string value
console.log(varsDef[k].value);
//returns an empty string
console.log(varsDef[k].value.toCSS());
}
});
Neither eval() nor the toCSS() gave me any results. I do not understand the less parsers' inner workings. Each variable object has a variable property varsDef[k].value which is an object itself. But I just need the string value of the variable.
Does anyone know how to get the variables' values as a string?
varsDef[k].value.toCSS()
should be the value
varsDef[k].name
should be the variable name
varsDef[k].toCSS()
returns nothing because it is a variable - in CSS variables do not output.
i ran into this problem recently and it bit me because, like you, i had the same instinct of running something like very much like the code you wrote above but for complex variables along the lines of
#redColor: #900; // responds to .toCSS()
#fooColor: desaturate(#redColor, 20%); // both of these error out
#barColor: lighten(#fooColor, 10%); // when calling .toCSS()
you'd get this nested tree.Value for #barColor which was this nested representation of the parse tree, so it would say, unhelpfully that barcolor: {[value: {value: [{lighten: {...}}]}]} or somesuch. my parsing-fu is pretty bad because i would always end up with some object at some point which would no longer respond to me invoking tree.toCSS on it, so i gave up on that route.
Instead, what i did was generated a nonsense .less file with an import rule and a nonsense rule, like so
#import "varfile.less";
.foo {
redColor: #redColor;
fooColor: #fooColor;
barColor: #barColor;
}
less will happily parse such a file, it doesn't care if redColor is a real css property or not, it just ignores it and does all the substitutions where it has to dutifully. And so you actually end up with a single rule css file that you can easily parse since it's very straightforwardly marked up. it looks like this:
.foo{
redColor: #990000;
fooColor: #8a0f0f;
barColor: #b81414;
}
this is, coincidentally, the easiest file to parse. it practically begs to be turned into json or what have you. granted, the path to here is pretty comical. i suspect it's because a variable without a rule is still fair game to be modified within the rule itself, but i could just be rationalizing that.
assuming you only want to extract the final values of your less vars and not the semantic values of your less vars, it's pretty handy. if you want semantics, it seems better to just parse the actual less file.
i ended up writing this in node and after i got past my own objections to how dodgy it felt, it worked quite well and fed me a json dict with my project's variables. you can take a look, it's on github at nsfmc/less-extractor which basically takes a basic config file and then writes to stdout a json dict. it's inelegant, but it totally works, even if it's a bit hackish.
your original question asked about doing this entirely client-side, so that would appear to rule out that github project, but the idea is very similar: you want to be able to access the original less file as part of some xhr request, parse it to get the variable names, build a less string, parse that, and then the rest of the code is just string building and run of the mill parsing.
hope that helps you!
I was also having issues with the less parser too; doing it that way was getting ridiculous with recursive checking of tree nodes.
If you wan't the actual values as opposed to the CSS generated (as per the above answer), the best way is to probably manually parse the file's text.
This function returns a key/value pair for each of the variables in a given less file. It wont work if the LESS file has multiple values per line, you could make it better with regex. I used it to parse bootstrap's variables file, which works nicely.
getLessVars = (lessStr) ->
lines = lessStr.split('\n')
lessVars = {}
for line in lines
if line.indexOf('#') is 0
keyVar = line.split(';')[0].split(':')
lessVars[keyVar[0]] = keyVar[1].trim()
return lessVars

Building a Raphael path object in piecemeal fashion

I'm trying to create a little free-hand drawing app, and to figure out a way to add path segments (e.g. "L10,10") to a Raphael path Element. This answer suggests that isn't possible.
I've tried doing something like:
var e = paper.path("M0,0L100,100")
e.attr("path").push(["L",50,100])
...which does alter the array returned by e.attr("path") but doesn't change the graphic, so I guess this isn't supported behavior.
It looks like you have to call the setter version of .attr() to update the display. The following seems to work:
var e = paper.path("M0,0L100,100");
e.attr("path").push(["L",50,100]);
e.attr("path", e.attr("path"));
although this does look pretty clumsy. I don't really see a better way to do it using push(), though.
After looking through the Raphael 2 source I figured out a method to create an incremental path efficiently, by:
initializing the path using the Raphael API w/ elem = paper.path()
attaching the mousemove handler to alter the SVG DOM path directly, via elem.node.setAttribute("d", elem.node.getAttribute("d")+newLineSegment); Raphael uses the 'd' attribute to set path string internally so this should be cross-browser compatible AFAICT (Update: actually I'm mistaken; this only works for the SVG-compatible browsers, not VML), while bypassing a whole mess of code we don't need to have run on an inner loop
when done drawing, set the path attribute for the path element explicitly through Raphael's API, so it can do all the proper housekeeping on the Element e.g.: elem.attr( {path: elem.node.getAttribute("d") })
This performs reasonably well on Chrome, and other modern browsers I tested on.
I've finished a jQuery UI widget for a sketchpad that uses this. Please leave a comment if you would find such a thing useful as open source. If there's interest I'll see if I can make that happen.
I can conform that this works:
var arr = somePath.attrs.path;
arr.push(["L", x, y]);
somePath.attr({path: arr});

Categories