Specify a target for less compilation within browser - javascript

I have a page with a short form to input some less values. I then want to take those values and recompile the less (from within the browser, with less.modifyVars(); ), but be able to specify the target file. This is so that I can access it from an iframe from within the page.
Does anyone know how I can specify a target for less compilation in the browser?

I'm not too familiar with in-browser "less.js" usage so this is quite rough answer.
Basically, no, you can't specify a target for automatic compilation.
#1
So you will have to explicitly parse a less file of interest into a string containing the result CSS styles and then apply these result styles to whatever HTML target you need. E.g. using javascript:
var parser = new(less.Parser)(options);
parser.parse(less_src_string, function (err, tree) {
var css_result_string;
if (err) {
// handle errors
} else {
css_result_string = tree.toCSS(env);
}
// ...
});
I'm not aware of any good examples/demos of such in-browser LESS usage so I'm afraid the less.js and lessc sources themselves are the only documentation for this stuff.
#2
The other way around is to do it in reverse, i.e. instead of including "less.js" into the frame where you set your variables, include it into your target frame and then set your config frame script to use modifyVars() of the less object defined in the target frame. That could be much more simple than #1.

Related

Convert css styles to inline styles with javascript keeping the style units

We have a corporate content management system that allows for rich text editing/html markup, but does not allow for head elements or style sheets to be uploaded, attached, or used in any way. It provides some rich text editing controls and also access to the source html, but just for the html fragment -- there is no head, no body. We also have no access the whole system that presents these bits of markup on the page. The only way to style the content is through inline style attributes on the elements. It is best, it isn't pretty, but that is what we have and I'm trying to make the best of a bad situation.
We also have high standards for visual presentation and would like to be able to quickly produce and modify/update content and keep it looking nice. It is difficult to correctly apply formatting using the system. For anybody who has tried to markup anything more than a paragraph or two with an RTE, you probably know what I mean. It seems like we should have a different system, but has anybody worked for a large company before? Just sayin.
We do have access to another location where we could "author" and "store" actual styled content and then "compile it" for copypasta into the other system. In other words, we could author/design using css and best practices and then we could run some code that could convert those element, class, and id formatting into inline styles.
I did my research and found this thread which also lead me to this code.
These both are very helpful in exploring solutions, but I've run into an issue. These solutions use the javascript getComputedStyle() method. There are some other options for properties to only look at other properties or to be recursive on the children of the element provide, but basically it boils down to this. (Since getComputeStyle returns an object and not an array, there is also a prototype/polyfill to allow iterating over an object with forEach, but none of that is part of the issue I'm facing.)
const computedStyle = getComputedStyle(element);
computedStyle.forEach(property => {
element.style[property] = computedStyle.getPropertyValue(property);
});
This works well for css attributes like font-size:24px or margin:0 15px. The issue I'm running into are when I'm using units other than px. For example, if I'm trying to make something that has width:50%. getComputedStyle() converts the 50% to the actual number of pixels that 50% is currently using.
In the notes section of the MDN web docs I see that this is expected behavior. Although I'm not quite clear on what that last line means.
...An example difference between pre- and post-layout values includes the
resolution of percentages for width or height, as those will be
replaced by their pixel equivalent only for used values.
So what I'm trying to do is convert something like this
.container{width:50%;}
<div class="container">
into something like this
<div class="container" style="width:50%">
Does anyone know of a way to complete this type of transformation?
PS: If it matters we'll be using the more basic attributes in our css -- no transitions, grid, prefixing, etc. We still need to support IE 11 -- if that tells you anything. We won't need to account for every edge case or browser. Just some basic stuff so that all our H1 look the same.
Couldn't find any way to do this using the built in getComputedStyle(). It also returned too many properties that I wasn't interested in. So I came up with a different approach. Basically to use the same function to loop through an element (and maybe all its children elements) and the use Element.matches() to get all the css rules that apply to the element and apply the properties as they were specified in the stylesheet.
I modified this answer a bit to get the rules from the stylesheet.
Has the added benefit that we can pull either from all the document stylesheets or just from a specific one that is needed for preparing the code to go into our content management systems's rich text editor.
function applyInline(element, recursive = true) {
if (!element) {
throw new Error("No element specified.");
}
const matches = matchRules(element);
// we need to preserve any pre-existing inline styles.
var srcRules = document.createElement(element.tagName).style;
srcRules.cssText = element.style.cssText;
matches.forEach(rule => {
for (var prop of rule.style) {
let val = srcRules.getPropertyValue(prop) || rule.style.getPropertyValue(prop);
let priority = rule.style.getPropertyPriority(prop);
element.style.setProperty(prop,val,priority);
}
});
if (recursive) {
element.children.forEach(child => {
applyInline(child, recursive);
});
}
}
function matchRules(el, sheets) {
sheets = sheets || document.styleSheets;
var ret = [];
for (var i in sheets) {
if (sheets.hasOwnProperty(i)) {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
if (el.matches(rules[r].selectorText)) {
ret.push(rules[r]);
}
}
}
}
return ret;
}

Measuring pollution of global namespace

Background
I'm trying to refactor some long, ugly Javascript (shamefully, it's my own). I started the project when I started learning Javascript; it was a great learning experience, but there is some total garbage in my code and I employ some rather bad practices, chief among them being heavy pollution of the global namespace / object (in my case, the window object). In my effort to mitigate said pollution, I think it would be helpful to measure it.
Approach
My gut instinct was to simply count the number of objects attached to the window object prior to loading any code, again after loading third-party libraries and lastly after my code has been executed. Then, as I refactor, I would try to reduce the increase that corresponds to loading my code). To do this, I'm using:
console.log(Object.keys(window).length)
at various places in my code. This seems to work alright and I see the number grow, in particular after my own code is loaded. But...
Problem
Just from looking at the contents of the window object in the Chrome Developer console, I can see that its not counting everything attached to the object. I suspect it's not including some more fundamental properties or object types, whether they belong to the browser, a library or my own code. Either way though, can anyone think of a better and more accurate way to measure global namespace pollution that would help in refactoring?
Thanks in advance!
So after some of the comments left by Felix Kling and Lèse majesté, I have found a solution that works well. Prior to loading any libraries or my own code, I create the dashboard global object (my only intentional one) and store a list of objects attached to window via:
var dashboard = {
cache: {
load: Object.getOwnPropertyNames(window)
}
};
Then, after I load all of the libraries but prior to loading any of my own code, I modify the dashboard object, adding the pollution method (within a new debug namespace):
dashboard.debug = {
pollution: (function() {
var pollution,
base = cache.load, // window at load
filter = function(a,b) { // difference of two arrays
return a.filter(function(i) {
return !(b.indexOf(i) > -1);
});
},
library = filter(Object.getOwnPropertyNames(window), base),
custom = function() {
return filter(Object.getOwnPropertyNames(window),
base.concat(library));
};
delete cache.load;
pollution = function() {
console.log('Global namespace polluted with:\n ' +
custom().length + ' custom objects \n ' +
library.length + ' library objects');
return {custom: custom().sort(), library: library.sort()};
};
return pollution;
}())
};
At any point, I can call this method from the console and see
Global namespace polluted with:
53 custom objects
44 library objects
as well as two arrays listing the keys associated with those objects. The base and library snapshots are static, while the current custom measurement (via custom) is dynamic such that if I were to load any custom javascript via AJAX, then I could remeasure and see any new custom "pollution".
The general pattern you've selected works OK from experience. However, there are two things you might need to consider (as additions or alternatives):
Use JsLint.com or JSHint.com with your existing code and look at the errors produced. It should help you spot most if not all of the global variable usage quickly and easily (you'll see errors of 'undefined' variables for example). This is a great simple approach. So, the measurement in this case will be just looking at the total number of issues.
We've found that Chrome can make doing detection of leaking resources on the window object tricky (as things are added during the course of running the page). We've needed to check for example to see if certain properties returned are native by using RegExs: /\s*function \w*\(\) {\s*\[native code\]\s*}\s*/ to spot native code. In some code "leak detection" code we've written, we also try to (in a try catch) obtain the value of a property to verify it's set to a value (and not just undefined). But, that shouldn't be necessary in your case.

Assign all vendor-prefixed variants to style

Modernizr provides a testAllProps() method which conveniently tests all the vendor prefixed styles of the one given to see if the style is supported by the currently running browser.
However I have no come to a point where I need to actually assign these properties from javascript because of various reasons that boil down to it being too cumbersome to conditionally link CSS files.
So for instance I could build an array and a routine which assigns each vendor specific style to the style of my target element:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration'].map(function(s){ element.style.s = "style_setting"; });
Well, this will probably generate a bunch of errors because I will try to assign "style_setting" to 4 or 5 undefined values.
Does anybody know anything to make this a bit less painful?
Probably best to use an existing library that knows all about this stuff:
Prefix Free will let you assign styles from CSS without vendor-prefixing. There is also a jQuery Plugin for it that will allow you to do the same from JavaScript.
Before setting the value, check whether the property is undefined:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration']
.map(function(s) {
if (element.style[s] != undefined) element.style[s] = "style_setting";
});

Contextualizing jQuery

I've got a fairly large site, with a lot of jQuery code for lots of different pages. We're talking about 1000 lines of fairly well optimized code (excluding plugins).
I know jQuery is fairly good at ignoring listeners for page elements that don't exist, but it still has to test their existence when the page loads. I'm also creating a load of vars (including decent sized arrays and objects), but only a few of them are used on each page.
My Question is: What's the best method of cutting down the amount of work each page has to do?
However, I do NOT want to split up the code into separate files. I want to keep all my code in 1 place and for the sake of efficiency I only want to call the server to download JS once (it's only 30kb, smaller than most images).
I've thought of several ways so far:
Put all my code chunks into named functions, and have each page call the functions it needs from inline <script> tags.
Have each page output a variable as the pageID, and for each chunk of have an if statement: if (pageID = 'about' || pageID = 'contact') {code...}
Give each page (maybe the body tag) a class or ID that can be used to identify the chunks that need executing: if ($('.about').length || $('.contact').length) {code...}
Combine 1 and 2 (or 1 and 3), so that each page outputs a variable, and the if statements are all together and call the functions: if (pageID = 'about') {function calls...}
Any other ideas? Or which is the best/most efficient of those?
Your first option will be fastest (by a minute margin).
You'll need to remember to call the functions from the correct pages.
However, don't bother.
Unless you've measured a performance impact in a profiler, there is no need to optimize this much.
I would argue that you are taking more of a performance hit for downloading the 30k then you will ever see from the code execution. That said, you could always test your url to determine the page and run all setup methods through a bootloader that determines the correct functions to run/ events to bind at load time. Something like the following maybe:
$(function(){
var page_methods = {
home : [meth_1, meth_2],
about : [meth_3, meth_2]
},
page = location.pathname.match(/\/(\w)$/)[1],
i = 0,
meth;
for ( ; meth = page_methods[ page ][ i++ ] ; ){
meth();
}
});

JavaScript to test for an object and child and grandchild objects (e.g.: HTML Frames)

I have a piece of JavaScript that's part of a library that I want enabled only if a certain frameset is available.
What I wanted to do was just do:
if (typeof(top.TableOfContents.frames.content)!=='undefined') {
// stuff I want to do only if there's a frame named
// TableOfContents with a frame named content
}
Now, I get errors with that if the frames are not available, so what I'm doing now is:
if (typeof(top.TableOfContents)!=='undefined'
&& typeof(top.TableOfContents.frames)!=='undefined'
&& typeof(top.TableOfContents.frames.content)!=='undefined'){
// stuff I want to do only if there's a frame named
// TableOfContents with a frame named content
}
But it feels like there might be a better way to do this (beyond losing the frames, haha)... is there a more efficient or less verbose way to test for an object and the child objects and the grandchild objects?
Now, these are all frames referenced with relative urls, so there should be no "same origin policy" issues that I can think of (I've not run into anything in my testing).
I like to use a sieve in this case
if (!!((((top || {}).TableOfContents || {}).frames || {}).content)) {
// stuff you want to do only if there's a frame named
// TableOfContents with a frame named content
}
the double not (!!) makes an explicit boolean casting.
this is the same kind of control you are already doing (nothing more) but is more efficient

Categories

Resources