less.js - get variable values inside parsers' callback - javascript

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

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.

Casting for element access in Haxe for HTML-elements

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.

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

Replace comment in JavaScript AST with subtree derived from the comment's content

I'm the author of doctest, quick and dirty doctests for JavaScript and CoffeeScript. I'd like to make the library less dirty by using a JavaScript parser rather than regular expressions to locate comments.
I'd like to use Esprima or Acorn to do the following:
Create an AST
Walk the tree, and for each comment node:
Create an AST from the comment node's text
Replace the comment node in the main tree with this subtree
Input:
!function() {
// > toUsername("Jesper Nøhr")
// "jespernhr"
var toUsername = function(text) {
return ('' + text).replace(/\W/g, '').toLowerCase()
}
}()
Output:
!function() {
doctest.input(function() {
return toUsername("Jesper Nøhr")
});
doctest.output(4, function() {
return "jespernhr"
});
var toUsername = function(text) {
return ('' + text).replace(/\W/g, '').toLowerCase()
}
}()
I don't know how to do this. Acorn provides a walker which takes a node type and a function, and walks the tree invoking the function each time a node of the specified type is encountered. This seems promising, but doesn't apply to comments.
With Esprima I can use esprima.parse(input, {comment: true, loc: true}).comments to get the comments, but I'm not sure how to update the tree.
Most AST-producing parsers throw away comments. I don't know what Esprima or Acorn do, but that might be the issue.
.... in fact, Esprima lists comment capture as a current bug:
http://code.google.com/p/esprima/issues/detail?id=197
... Acorn's code is right there in GitHub. It appears to throw comments away, too.
So, looks like you get to fix either parser to capture the comments first, at which point your task should be straightforward, or, you're stuck.
Our DMS Software Reengineering Toolkit has JavaScript parsers that capture comments, in the tree. It also has language substring parsers, that could be used to parse the comment text into JavaScript ASTs of whatever type the comment represents (e.g, function declaration, expression, variable declaration, ...), and the support machinery to graft such new ASTs into the main tree. If you are going to manipulate ASTs, this substring capability is likely important: most parsers won't parse arbitrary language fragments, they are wired only to parse "whole programs". For DMS, there are no comment nodes to replace; there are comments associated with ASTs nodes, so the grafting process is a little trickier than just "replace comment nodes". Still pretty easy.
I'll observe that most parsers (including these) read the source and break it into tokens by using or applying the equivalent of a regular expressions. So, if you are already using these to locate comments (that means using them to locate *non*comments to throw away, as well, e.g., you need to recognize string literals that contain comment-like text and ignore them), you are doing as well as the parsers would do anyway in terms of finding the comments. And if all you want to do is to replace them exactly with their content, echoing the source stream with the comment prefix/suffix /* */ stripped will do apparantly exactly what you want, so all this parsing machinery seems like overkill.
You can already use Esprima to achieve what you want:
Parse the code, get the comments (as an array).
Iterate over the comments, see if each is what you are interested in.
If you need to transform the comment, note its range. Collect all transformations.
Apply the transformation back-to-first so that the ranges are not shifted.
The trick is here not change the AST. Simply apply the text change as if you are doing a typical search replace on the source string directly. Because the position of the replacement might shift, you need to collect everything and then do it from the last one. For an example on how to carry out such a transformation, take a look at my blog post "From double-quotes to single-quotes" (it deals with string quotes but the principle remains the same).
Last but not least, you might want to use a slightly higher-level utility such as Rocambole.

Javascript in LESS appears hobbled: workaround?

I am finding that LESS has a hobbled JavaScript evaluator, at least the way I am using it, which is to compile *.less files into *.css on a client before uploading them to the web server.
I'm aware that compilation may be more often done server-side, but for performance & simplicity we only want CSS files on the server. I'm compiling the LESS files on Fedora Linux with the lessc ruby gem installed into Node Package Manager as in these instructions.
The compiler is working perfectly, but as far as I can tell the JavaScript expression evaluation is sorely limited. I believe this also applies to server-side JavaScript expression evaluation based on this posting which suggests uncertainty about how the JavaScript engine is plugged into the LESS environment.
All I can use are simple, comma-separated expressions, like the following:
#bar: `
"ignored-string-expression"
,
5
`;
div.test-thing { content: ~"#{bar}"; }
which compiles into:
div.test-thing {
content: 5;
}
When I try to define a function, the compiler barfs (whether or not the semicolon in the expression is backslash-escaped):
[719] anjaneya% cat testfunc.less
#bar: `function foo() {return 5}; foo()`;
div.test-thing { content: ~"#{bar}"; }
[720] anjaneya% lessc testfunc.less
SyntaxError: JavaScript evaluation error: `function foo() {return 5}; foo()` ...
There also doesn't seem to be any way of looping, even if you try to trick it into evaluating a loop as you would the "ignored-string-expression" above, like:
#foo: `x = 0,
for (var n = 0; n <= 10; n++) { x++; },
x
`;
div.test-thing { content: ~"#{bar}"; }
which says:
ParseError: Syntax Error on line 1 ...
Why bother? To be able to compile this LESS:
#svgSource: '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><linearGradient id="g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#{start}" /><stop offset="1" stop-color="#{end}" /></linearGradient><rect x="0" y="0" width="100%" height="100%" fill="url(#g)" /></svg>';
into this CSS:
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxsaW5lYXJHcmFkaWVudCBpZD0iZyIgeDE9IjAiIHkxPSIwIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzU3OWRkYiIgLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMwMDAwMjIiIC8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIiAvPjwvc3ZnPg==);
using a program like this, whether the algorithm is implemented in JavaScript, PHP, Perl, UNIX shell, or anything else. This processing might be done without function definitions but not without looping, and without functions you can't even have recursion.
Given that both functions and looping are compound statements that probably aren't evaluated as expressions (it's not LISP), that is probably the basis for the failure... it's not really a full JavaScript interpreter. So I'm hoping someone who really knows the LESS compiler will:
clarify the limitation above, so I can use JavaScript & LESS portably for this task
say how this problem can be worked around (e.g., with a "shell escape" or any evaluation environment that can process strings iteratively) and/or
say how to extend the LESS compiler with such text processing capabilities, like a "real" JavaScript engine.
The data-uri() function is now built into LESS:
http://lesscss.org/functions/#misc-functions-data-uri
Will that help?
I think what you're looking for is both a mixture of a BUNCH of little frustrating things about using javascript in LESS.
All javascript has to be on one line. (I... don't even.. whatever.)
All javascript has to return something or you get the completely useless error "Cannot read property 'value' of undefined"
All javascript must be one statement. This comes from the original use case where they were thinking you would do something like #foo: ~'something.javascript.is.good.at({#bar});'
Number 1 is formatting, 2 just means you need to return something even if you don't use it, but number 3 catches a lot of people off guard. To get around it, just make you "one thing" a self-executing anonymous function.
Eg:
(function(){
do.some.cool.stuff();
other.cool.things();
return('Fry');
})();
So the LESS parser sees that (make sure it's all in one line instead of how I wrote it!), bundles it off to javascript land as a single execution, reads the return value, and calls it a day.
If you want to see it in action, I recently wrote LESS mixin to make RGBA 1x1 pixels for background fills, etc. that uses all of this madness.
Hope that helps!
I know this post is a couple of years old but javascript embedded in LESS can be very handy so I though I would post some tips:
//All javascript must
// a. be on the rhs of an assignment e.g. #x:` var x = #{val}+7`;
// b. must evaluate to a value
// c. cannot be terminated by a ';'
//
// To get around this, multiline code can be packed into an IIFE......
#val:7;
#loop:`(function(val){
var sum=0;
for(var i=0; i<val; i++){sum+=i;}
return sum;
} )(#{val})`;
.test{
content:#loop; // content: 21;
}
// console.log writes directly to the beginning of the output file
#x:`console.log('/*...... loop = #{loop}.......*/')`; // /*...... loop = 21.......*/
// you can use the global variable. Here we attach a library module to it.....
#x:`global.myLib={}`;
// Then add a method to the module..............
#btoa:`myLib.btoa=function(str){
var buffer = new Buffer((str).toString(), 'binary');
return buffer.toString('base64');
}`;
// And invoke the method to encode some text............................
#sometext:'LESS is more (more or less)';
.test2{
content:`myLib.btoa(#{sometext})`;// content: "TEVTUyBpcyBtb3JlIChtb3JlIG9yIGxlc3Mp";
}

Categories