I'm using client-side less.js. Is there a way to get all variables from my less file. I know how to modify variables:
less.modifyVars({
"#var": "#fff"
});
But I want to get all of them, like (don't work):
var variables = less.getVars();
This is going to be an unconventional answer as it seems that this data isn't publicly exposed as part of the browser facing API.
tl;dr
Save a local copy of the less.js file.
Add this function definition somewhere
function exposeVars(root) {
var r=root._variables,n=Object.keys(r),m={}
for(var k of n){m[k]=r[k].value}
less.variables = m;
}
Add the following call exposeVars(evaldRoot) just before return result on line ~2556.
Now less.variables contains all the variables from your file.
Disclaimer: Doing this is not a good idea! It's fine if you're just playing around, debugging or testing something, but don't depend on this hack for anything serious!
The basic aim here was to find the point in the source where the .less files are turned into abstract syntax trees (or some other formal structure).
Jumping straight into the source, I found a ParseTree class. It's a reasonable assumption to guess that it will contain the result of parsing the less file.
I wrote a quick test file and added it to the browser with the appropriate tag. It looks like this:
#myvar: red;
#othervar: 12px;
body {
padding: #othervar;
background: #myvar;
}
I've downloaded a local copy of less.js and added a breakpoint added to line 2556.
I had a poke around in the local scope to see what was available and found the variables in an object called evaldRoot.
evaldRoot = {
_variables: {
#myvar: {
name: '#myvar',
value: Color
},
#othervar: {
name: '#othervar',
value: Dimension
}
}
}
Next job was to work out where this data goes. It seems like the evaldRoot variable is used to generate the resulting CSS (which would make sense, as it contains information such as variables).
if (options.sourceMap) {
sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
} else {
result.css = evaldRoot.toCSS(toCSSOptions);
}
Whatever happens, the variables goes out of scope after it is used to generate a string of CSS as result.css.
To expose of these variables, the script needs a small modification. You'll have to expose the variables publicly somehow. Here's an example of doing that.
function exposeVars(root) {
var varNames = Object.keys(root._variables);
var variables = varNames.reduce(function(varMap, varName) {
varMap[varName] = root._variables[varName].value;
}, {});
less.variables = variables;
}
This can be added just before the return statement with the breakpoint.
exposeVars(evaldRoot);
return result;
Now the variables will be available in a name: value object on the global less object.
You could even modify the expose function to return the variables from a call to less.getVars(). Just like your initial suggestion.
function exposeVars(root) {
// ...
less.getVars = function() {
return variables;
};
}
Related
When setting a breakpoint on the console.log statement, why would this be undefined in the debugger but the statement print with no issues? Am I missing something in regards to scope here?
export class OptionsSidebarComponent {
...
public filters: Object = new Object();
...
public generateFilters() {
for (let property in this.terms) {
if (this.terms.hasOwnProperty(property) && this.terms[property] !== null) {
if (!this.filters.hasOwnProperty(this.productGroup.id)){
this.filters[this.productGroup.id] = new Filters();
}
this.terms[property].forEach((term) => {
console.log(this.filters);
});
}
}
}
}
With typescript While debugging, keep in mind that transpilers can rename variables. Using the original name in the console without sourcemaps that include renaming will show you undefined even if the original value isn't. Make sure you use the transpiled name in watches or console commands.
When you're referencing this with your console.log statement inside its own class, you're using it in its relevant scope. Depending on the framework you are using, different terms are used to reference your class... AngularJS used $scope- Angular 2+ uses this to reference the current class (or current scope/context).
When you use this in your debugger you're using it outside of its scope. It no longer references the class you intend it to.
Each one of your components in Angular uses this to reference itself. Here's an article to explain in further detail: https://angular-2-training-book.rangle.io/handout/features/refresher_on_this.html
If we simplify it to basic javascript and look at your class as just a function a good example would be this:
function example(){
var anything = 10;
console.log(anything); //output: 10
}
console.log(anything); //undefined! we're out of scope :)
So looking at it from this perspective, this is nothing magical. It's just provided to us by Angular to make our references to other things inside our components easier.
So, I have the need for a singleton. It really is a rather large "do something" object. Processes information etc.. it could be extended, and some methods could or might even be inherited, but overall, there doesn't need to exist more than one of them. So, I read a bit here which I love the concept: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
I am thinking more in terms of leveraging the sub module behavior.
But, I'd like to break my obj into sub-modules. But I am not seeing the need to pass in the parent sub-module as the "return" on that parent gives me access anyways. ala. Perhaps I am missing the "robustness" or real usage here.
For example.
var a = {};
a.m = function(){
var conf = {
a: 'aaa',
b: 'bbb'
}
var funcs = {
func1: function(){
console.log('a.m sub object func1');
}
}
return { // doing this gives me access
conf: conf,
funcs: funcs
};
}()
// this sub module obj WILL need some behaviors/methods/vals in a.m
a.anothersub = (function(m){
var anotherSub = m;
anotherSub.funcs.func1(); // access to a.m methods do I even need to pass it in?
a.m.funcs.func1(); // also access to a.m methods
}( a.m || {}))
// is a better approach to extend a.anothersub with a.m?
// jQuery.extend(a.anothersub, a.m);
If both "m" and "anothersub" are part of object 'a'. Is there a need for loose or tight augmentation here and for sake of keeping code compartmentalized and of same function behavior, I am creating these "sub objects".
I read that article and felt I could leverage its power. But not really sure this is the best approach here, or even needed. Your thoughts?
This all comes down to how tightly-coupled your modules/submodules actually are, and how much you can expect them to exist in all places around your application (ie: every page of a site, or at the global level of an application, et cetera).
It's also broaching a couple of different topics.
The first might be the separation of concerns, and another might be dependency-inversion, while another, tied to both, might be code organization/distribution.
Also, it depends on how cohesive two submodules might be...
If you had something like:
game.playerFactory = (function () {
return {
makePlayer : function () { /*...*/ }
};
}());
game.playerManager = (function (factory) { return {/*...*/}; }(game.playerFactory));
It might make sense to have the factory passed into the manager as an argument.
At that point, attaching both to game is really just a convenient place to make both accessible to the global scope.
Calling game from inside of one or the other, however, is problematic, in large systems, systems with lots of submodules, or systems where the interface is still in flux (when are they not?).
// input-manager.js
game.inputManager = (function () {
var jumpKey = game.playerManager.players.player1.inputConfig.jump;
}());
If all of your buttons are mapped out and bound to in that way, for every button for every player, then all of a sudden you've got 40 lines of code that are very tightly bound to:
The global name of game
The module name of playerManager
The module-interface for playerManager (playerManager.players.player1)
The module-interface for player (player.inputConfig.jump)
If any one of those things changes, then the whole submodule breaks.
The only one the input-manager should actually care about is the object that has the .inputConfig interface.
In this case, that's a player object... ...in another case, it might be completely decoupled or stuck on another interface.
You might be half-way through implementing one gigantic module, and realize that it should be six smaller ones.
If you've been passing in your objects, then you really only need to change what you're passing in:
game.inputManager = (function (hasInput) {
var jumpKey = hasInput.inputConfig.jump;
}(game.playerManager.players.player1));
Can easily become
game.inputManager = function (hasInput) {
/*...*/
}(game.playerManager.getPlayer("BobTheFantastic").config));
and only one line of code changed, rather than every line referencing game. ......
The same can be said for the actual global-reference:
// my-awesome-game.js
(function (ns, root) {
root[ns] = { };
}( "MyAwesomeGame", window ));
// player-factory.js
(function (ns, root) {
root[ns] = {
make : function () { /*...*/ }
};
}("playerFactory", MyAwesomeGame));
// player-manager.js
(function (ns, root, player) {
var manager = {
players : [],
addPlayer : function () { manager.players.push(player.make()); }
};
}("playerManager", MyAwesomeGame, MyAwesomeGame.playerManager));
Your code isn't impervious to change, but what you have done is minimize the amount of change that any one submodule needs to make, based on external changes.
This applies directly to augmentation, as well.
If you need to override some piece of some software, in a completely different file, 20,000 lines of code down the page, you don't want to have to suffer the same fate as changing interfaces elsewhere...
(function (override, character) {
override.jump = character.die;
}( MyAwesomeGame.playerManager.get(0), MyAwesomeGame.playerManager.get(1) ));
Now, every time player 1 tries to jump, player 2 dies.
Fantastic.
If the interface for the game changes in the future, only the actual external call has to change, for everything to keep working.
Even better.
I use UglifyJS to minify a concatenated set of files, which works fine but not good enough. The built lib uses namespaces, so classes, functions and constants are stored in a root namespace variable:
(function() {
var root = { api:{}, core:{}, names:{} };
/* util.js file */
root.names.SOME_LONG_NAMED_CONST='Angel';
/* Person.js file */
root.core.Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root.api.perform;
})();
which is minified to the not-so-minimal version
(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();
I understand uglify probably thinks that root var is a data structure that must be kept as-is and can't be changed. Is there a way to let UglifyJS mangle the nested names in the root namespace?
When you minimize Javascript you can only change names of variables, the api, core and names are not variables but properties of an object. If these were changed by the minimizer, you would potentially get unexpected results. What if in your code you would call
root["api"].perform = function()...
or even something like
function doIt(section, method, argument) {
root[section][method](argument);
}
doIt('api','perform', 101);
All perfectly legal JS, but a minimizer could never figure out what's going on.
I have been trying to use --mangle-props of UglifyJS2 and can tell you: 'it makes a mess'.
As someone pointed out: 'Developer should decide what properties to mangle, not uglifyjs'
I am approaching the problem using this options:
--mangle-props
--mangle-regexp="/_$/"
The regex matches any property with a underscore at the end.
You asked to mangle nested names in the root namespace. So, your code:
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST_='Angel';
root.core.Person_ = function(name) { this.name = name };
root.api.perform_ = function(param_for_api) { }
window.lib_name.perform = root.api.perform;
})();
Would result in this:
(function() {
var n = {
api: {},
core: {},
names: {}
};
n.names.a = "Angel";
n.core.b = function(n) {
this.name = n;
};
n.api.c = function(n) {};
window.lib_name.perform = n.api.c;
})();
Command:
uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js
If you want to mangle first level of root namespace (api, core, names) just put a underscore on them (api_, core_, names_), you are in control ;)
Just a side note: when you are mangling properties usable by other js files, you should mangle all files together with the same command, so the same identifier will be used over all files.
Aside from #JanMisker 's point (which is completely valid), rewriting properties is unsafe because they can be exposed to code outside the scope of the minification.
Although the self executing function has a scope, and if the code is only
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST='Angel';
alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something
})();
It is true that outside of the function, there is no way to access the root object, so rewriting the property names is safe, and the following code would result in the same:
(function() {
var a = { b:{}, c:{}, d:{} };
a.d.e='Angel';
alert(a.d.e);
})();
But even if you are inside your private scope you can access, and more importantly assign to variables from an outer scope! Imagine this:
(function() {
var root = { api:{}, core:{}, names:{} };
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name = root.api;
})();
You are not only exposing a function but an object with a function on it. And the function will be visible from any place where window is visible.
So, for example writing the following in the javascript console would yield different results with and without minification:
window.lib_name.perform(asdf);
With minification you would have to write:
window.lib_name.f(asdf);
Or something similar.
Remember that there can always be code outside your minification.
It is not that crucial to have the absolute minimal JS, but if IT IS that crucial for some reason (for example: aliens abducted your stepdaughter, and the only way to have her back is to minify this below 100 characters or so), you can manually replace an undesirably long property name to a shorter one, just be sure that it will not be exposed anywhere, and isn't be accessed through associative array notation (root['api']).
as #Jan-Misker explained in his answer, property name mangling is NOT an good idea because it could potentially break your code.
However, you can workaround it by define the property names as local variables, and modify all .properties to [keys], to make smaller file size:
(function() {
var API = 'api';
var CORE = 'core';
var NAMES = 'names';
var SLNC = 'SOME_LONG_NAMED_CONST';
var root = {};
root[API]={};
root[CORE]={};
root[NAMES]={};
/* util.js file */
root[NAMES][SLNC] ='Angel';
/* Person.js file */
root[CORE].Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root[API].perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root[API].perform;
})();
Because now all the properties became a local variable, uglify js will mangle/shorten the variable names and as consequence you overall file size reduced:
!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}();
However, reduced file size doesn't mean you will get shorter downloading time on real server, because usually our http transport is gzipped, most of the repetitions will be compressed by your http server and it does a better job than human.
The latest release of uglify (today) has object property mangling, see v2.4.18. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
Use the --mangle-props option and --reserved-file filename1.json filename2.json etc....
So, I just noticed today that you can apparently run javascript in the chrome console. I had no idea you could do this. It's actually really cool.
In my rails app, I have an external javascript page. Some of the variables on that page I would like to be global so that all the functions in the JS file can access them. for example I have a map, and I would like the map object to be global in the javascript file because that way all my functions access the one map variable instead of creating their own, and I can break complex operations down into smaller functions.
This is all well and good I know how to do that and it's working perfectly. My problem now, can I protect the variables from outside? For example you can change the values of all the javascript class variables from the chrome console.. as well methods from for example the map are accessible and excecutable.. I have locked the map settings on one of the pages so it is not zoomable or movable, however from the console I can simply say map.setZoom(11) and the map will zoom to 11.. I can type map.dragable = true and bam u can drag the map.. I don't like this really..
It's not too terribly bad yet like the user enabling map drag and zoom isnt the worst thing in the world.. but still I'd like to disable this. Any ideas?
EDIT
Thanks all for the answers and comments. I guess I will just resort to not putting anything that can be turned malicious into my javascript, and do thing like pass my map variable to functions where necessary to slow people down.
You can use an immediately-invoked function (IIFE) expression to prevent your variables and functions from being exposed in the global scope:
var a = 10;
(function() {
var b = 20;
})();
window.a lets you view and modify a, but you cannot do that with b:
Try it out here
I'm more than sure that there's a way to edit b with Inspector, but I haven't taken the time to figure it out. Don't waste your time trying to prevent your users from modifying code that they can view.
You can't. Even if you wrap them into anonymous functions, user can get to them through debugger. As last resort he can simply intercept your traffic to his own machine and replace your JavaScript with something else.
Bottom line: JavaScript in browser is client-side. Client can do whatever he pleases with it.
Try doing something like this:
(function(){
//All of your current code
})();
One thing to still be aware of - Chrome developer tools also lets you edit the javascript (not the javascript file on the server, just currently running copy.) Go to Chrome Dev Tools->Sources and you can edit the javascript files.
You can't. Your saying you need to define your map globally, this means it's accessible for everyone.
You could define your map in a different scope and then only define the "public" things:
(function() {
var map = new Map();
window.myMap = {
goTo: function(lat, lng) {
map.goTo(lat, lng);
}
};
})();
Depending on your architecture, there are a few ways to accomplish this. Use this method to create a reusable component that has public and private properties:
var protectedScope = function () {
var protected_var = 'protected';
this.showProtected = function () {
return protected_var;
}
this.public = 'public';
};
var myObject = new protectedScope();
console.log('Public var: '+myObject.public); // outputs "public"
console.log('Protected via accessor: '+myObject.showProtected ()); // outputs "private"
console.log('Protected var: '+myObject.protected); // outputs undefined
Any variable or function declared with a var keyword will be, in effect, private. Any variable or function that uses the this.name mechanism will be "public".
Understand that this structure is not truly public or private, such concepts are not a part of the language. There are still ways to get at those variables, and one can always view source. Just be clear; this is a code organization concept rather than a security concept. Chrome has had this developer console for a while, and other major user agents are moving to include similar tools (or already have done so). There are also tools like Firebug which allow a user full access to your javascript runtime environment. This isn't a realm that the developer can control at all.
Try it here: http://jsfiddle.net/cf2kS/
More Reading
"Private Members in JavaScript" by Douglas Crockford* - http://www.crockford.com/javascript/private.html
"OOP in JS, Part 1 : Public/Private Variables and Methods" on http://phrogz.net - http://phrogz.net/JS/classes/OOPinJS.html
Javascript Object Management on MDN - https://developer.mozilla.org/en-US/docs/XUL_School/JavaScript_Object_Management
Closures on MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures
Object.defineProperty(map, 'zoom', {value:1});
or
Object.defineProperty(map, 'zoom',{
set: function(){console.warn('Access denied!');},
get: function(){return 1;}
});
demo
or
Object.defineProperty(Object.prototype, 'protect', {
value: function(ignore){
var childObjects = [], ignore = ignore || [];
ignore.push(this);
if(this instanceof MimeType)return; //Chrome Fix //window.clientInformation.mimeTypes[0].enabledPlugin[0] !== window.clientInformation.mimeTypes[0]
for(var prop in this){
if(typeof this[prop] === "unknown")continue; //IE fix
if(this[prop] instanceof Object){
var skip = false;
for(var i in ignore)
if(ignore[i]===this[prop]){
skip = true;
break;
}
if(!skip)childObjects.push(prop);
}
var d = Object.getOwnPropertyDescriptor(this, prop);
if(!d || !d.configurable || !d.writable)continue;
var that = this;
(function(){
var temp = that[prop];
delete that[prop];
Object.defineProperty(that, prop,{
set: function(){console.warn('Access denied!');},
get: function(){return temp;}
});
})();
}
for(var i = 0;i<childObjects.length;i++)
this[childObjects[i]].protect(ignore);
}
});
this.onload=function(){this.protect();} //example
demo
The problem I have is that there are a set of variable values / properties in one file and a library in another file. I have started refactoring the code but still need to keep variable values(dynamic) and library(static) differently.
I am using namespacing and overall want only one global namespace.
The problems I have at the moment:
1. How can I still keep one global namespace
2. What is the best way to read the values from one file and use it in the library present in another file.
e.g I came up with something like
//File ONE with values
var main.dynamicvalues = (function(){
var a = 10,
b = 20,
c = 30;
return {
a:a,
b:b,
c:c
}
}());
//File TWO with core Library
var main.library = (function(){
//Various Private functions that need to use a,b,c variables from above main.dynamicvalues namespace
return {
//Public functions again need to use a,b,c from above namespace.
}
}());
Is there a way I can have a pattern so that I keep only one global namespace and can refer to variables directly without having to use maincode.values.a, maincode.values.b, maincode.values.c or something like this in maincode.library.functions
Thanks
Sparsh Gupta
This approach is a little better, but it's not exactly what you're looking for.
var main = {};
main.dynamicvalues = (function() {
// same as before
})();
main.library = (function(dyn){
// use dyn.a, dyn.b etc
return {
// same in here
}
}(main.dynamicvalues));
Create a new file (maybe name it something like "common.js") and put the values there.
You can try RequireJS. This will let you do what you want with no global namespace at all (if you'd like to). In addition it will give you non-blocking script loading, easy way to handle dependencies and a build tool.
On the other hand, it can deprive you of the joy of investigating things for your own and better understanding of js architectural patterns.
Your code with RequireJs could have looked like this:
// File one with values, let's name it values.js
define([], function() {
var a = 10,
b = 20,
c = 30;
return {
a: a,
b: b,
c: c
}
})
// File two with library
define([
// load values.js as a dependency
'values'
// what is returned in values.js can be passed as an argument to the callback
], function( values ) {
values.a === 10 // true
})