CoffeeScript function parentheses - coffeescript

I'm just learning CoffeeScript and I'm trying to do something I would normally do in plain 'ol JavaScript.
Here's what I tried to do:
initializeWebGL = (canvas) ->
gl = canvas.getContext "webgl" or canvas.getContext "experimental-webgl"
Which compiles to what I kind of expect:
var initializeWebGL;
initializeWebGL = function(canvas) {
var gl;
return gl = canvas.getContext("webgl" || canvas.getContext("experimental-webgl"));
};
In order to get what I really want, I have to wrap the getContext arguments with parentheses:
initializeWebGL = (canvas) ->
gl = canvas.getContext("webgl") or canvas.getContext("experimental-webgl")
Which produces what I want:
var initializeWebGL;
initializeWebGL = function(canvas) {
var gl;
return gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
};
Is there a better way to do what I'm trying to achieve than to just add parentheses around the function calls like in the second example?

Is there a better way to do what I'm trying to achieve than to just add parentheses around the function calls like in the second example?
No, I don't think so. My rule of thumb is it's OK to omit parentheses when the function call and its arguments are the last thing on a line, otherwise include them.
OK
someFunction 1, 2, 3
Not OK
someFunction 1, someOtherFunction 2, 3
In general I try to avoid overly-concise, terse statements. They are harder to work with both mentally as well as stepping through in a debugger is trickier.

I wouldn't say it's a better way but you could do something like this:
initializeWebGL = (canvas) ->
gl = canvas.getContext "webgl"
gl = gl or canvas.getContext "experimental-webgl"
I personally prefer readability over writing less code:
initializeWebGL = (canvas) ->
gl = canvas.getContext "webgl"
if !gl? then gl = canvas.getContext "experimental-webgl"

Related

How can I change the breakpoint for MegaMenu in Magento 2?

I am using the MegaMenu in Magento 2. The default breakpoint for the menu to collapse is 1024px / 1025px. The smaller version is a simple list of categories, sliding in from the right side of the screen. The bigger version is a dropdown with categories to hover.
How can I change the breakpoint to 1194px / 1195px?
I tried modifying JS and CSS but it just broke the menu. I could also not find an option in the admin panel.
I don't own the extension but i read the javascript and in the file of demo website:
https://mega-menu-m2-ce.magento-demo.amasty.com/static/version1586867535/frontend/Magento/luma/en_US/Amasty_MegaMenu/js/open-type.js
Line number 35:
_create: function () {
var self = this,
isMobile = $(window).width() <= 1024 ? 1 : 0,
options = self.options,
openType = options.openType,
isHamburger = +options.hamburgerStatus;
There is the breakpoint (1024). Maybe if you change that to 1194 or 1195?
_create: function () {
var self = this,
isMobile = $(window).width() <= 1194 ? 1 : 0,
options = self.options,
openType = options.openType,
isHamburger = +options.hamburgerStatus;
Override that setting with js. or override the whole js file? Hope that helps.

chartJS - points overplotting, jittering, noise?

Hey I'm using chartJS and I have a graph that looks like this:
As you can see the data points overlap each other which makes it harder to read. In RStudio there is a solution for that you simply set position="jittering" and it will add a slight noise around the dots.
Unfortunately, there is no such capability in chart.js today. However, that doesn't mean you can't still get the functionality that you want. There are a couple of options that come to mind.
1) Implement your own jitter function and use it to pre-process your data before passing to chart.js. Here is an example.
var jitter = function(data) {
return data.map(function(e) {
var xJitter = Math.random() * (-1 - 1) + 1;
var yJitter = Math.random() * (-1 - 1) + 1;
return {
x: e.x + xJitter,
y: e.y + yJitter,
}
});
};
Here is a codepen example showing a side by side example of charts with original and jittered data.
2) Use the jitter function above and add it as a chart.js plugin such that you can enable/disable it automatically in any given chart. Here is an example (note that we are using the jitter function above.
Chart.plugins.register({
afterInit: function(chartInstance) {
if (chartInstance.config.options.jitter) {
var helpers = Chart.helpers;
var ctx = chartInstance.chart.ctx;
chartInstance.data.datasets.forEach(function (dataset) {
dataset.data = jitter(dataset.data);
});
}
}
});
Then just add this into your charts options config.
options: {
jitter: true,
}
Here is a codepen example showing a side by side example of charts with original and jittered data using the plugin.
Obviously, you would want to implement a much more robust jitter function that accepts some sort of jitter options so that it can be intelligently applied to any type of dataset. I'll leave that for you to implement if you decide to take this approach.

Protractor locator issues

My goal is to pull the css width of a particular element.
The actual jQuery command I use in Chrome console works well :
$('div-grid-directive[grid-name="SAT"] .qMinus1_resize_expand').first().css('width');
"0px"
However, running Protractor in a Win 7 cmd prompt, I'm getting the following error:
Message:
Failed: element(...).first is not a function
The error points to my objects page topNav.icons.page.js, in this.getQMinus1Column :
module.exports = function(){
this.clickExpandRowsIcon = function(){
$('.resize-btns > .glyphicon.glyphicon-resize-horizontal').click();
browser.driver.sleep(2000);
};
this.getQMinus1Column = function(){
return element(by.css('div-grid-directive[grid-name="SAT"] .qMinus1_resize_expand')).first();
};
};
and my SAT.spec.js file :
var IndexPage = require('./pageObjects/index.page.js');
var TopNavBar = require('./pageObjects/topNav.icons.page.js');
describe('Testing the Sales & Trading Top Nav grid icons', function(){
var indexPage = new IndexPage();
var topNavBar = new TopNavBar();
beforeEach(function () {
indexPage.get(); // Launches app !!
});
describe('Clicking on the Expand Rows icon', function () {
it('Should horizontally expand previous quarters qMinus1 thru qMinus6', function () {
topNavBar.clickExpandRowsIcon();
var elem = topNavBar.getQMinus1Column();
expect(elem).getCssValue().indexOf('width: 5px;').not.toBe('-1');
});
});
});
So before I even try to get the css width attribute, I'm struggling to return first(), using this format:
return element(by.css('MY-CSS')).first();
Do I have too much going on in this line, or perhaps the wrong syntax for Protractor ?
return element(by.css('div-grid-directive[grid-name="SAT"] .qMinus1_resize_expand')).first();
Again, the same syntax works using jQuery in console tools, so the CSS select is correct.
Advice is greatly appreciated here...
regards,
Bob
As per this , element does not return a collection of objects to call first method in it.
Probably you might want to use element.all(locator)

Destroy leaflet map trouble

I use several leaflet maps at one time. They can be created and deleted dynamically. But when a map is destroyed by using the map.remove() there is a memory leak. Detached DOM trees appears. You can see it in Chrome Dev Tools.
Screenshot with a leak.
Example function I use to recreate div and map:
var map, mapDiv;
recreateMap = function(){
// destroy previous map and div
if(map) map.remove();
if(mapDiv) mapDiv.parentNode.removeChild(mapDiv);
// create new map div
var randomDivId = 'mapId' + new Date().getTime();
mapDiv = document.createElement('div');
mapDiv.id = randomDivId;
mapDiv.style.height = '200px';
mapDiv.style.width = '200px';
document.getElementsByTagName('body')[0].appendChild(mapDiv);
// attach map to div
map = L.map(randomDivId).setView([51.505, -0.09], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map);
map.invalidateSize();
};
Working example here.
How to properly destroy the leaflet maps?
Assume you create a leaflet map with some thing like the following
var lat =39, long = 40;
var coords=[lat,long];
var zoomLevel=13;
var mapInstance = leafLet.map(mapContainerId).setView(coords, zoomLevel);
You can remove it using the following code
if (mapInstance && mapInstance.remove) {
mapInstance.off();
mapInstance.remove();
}
If you are working with non blocking javascript code or async calls, if required you can use a timer to ensure that your code does not error out. Following is a sample implementation of the same
var timeoutIndex=0;
var watcher=window.setInterval(function(){
timeoutIndex++;
if (mapInstance && mapInstance.remove) {
mapInstance.off();
mapInstance.remove();
window.clearInterval(watcher);
}
if(timeoutIndex >50) { //wait for 5 seconds before giving up
window.clearInterval(watcher);
}
},100);
It worked for me. Guess it helps you too
If you have several maps, you need to put each map into diffents var.
var map1 = L.map(mapDiv1);
var map2 = L.map(mapDiv2);
You can also create a function to control map initialisation and container :
function BoolMapInit(map, mapDiv) {
return (map != null && map._container.id == divMap);
}
And the fonction to remove existing map :
function RemoveExistingMap(map) {
if (map != null) {
map.remove();
map = null;
}
}
Hope this help ;)
I had the same problem, and after spending much time on it, the best solution for this problem is to put the map container in a div, and when you want to regenerate the map, remove all of the div's HTML and create a new map container:
<div id="map-bx">
<div id="map"></div>
</div>
<script type="text/javascript">
// Map init
</script>
and when you want to regenerate (destroy) the map like so:
<script type="text/javascript">
$("#map-box").html("");
$("#map-box").html('<div id="map"></div>');
// map init code
</script>
in my case, map.remove() or map.unload() do not work.
maybe try to unload map first? (documentation says that map is being unloaded automatically when using remove method but it's worth to give an additional shot and try unload it manually first)

Coffeescript dynamically create/call a function from a list on select box value change

I'm working on adding some image editing tools using the Pixastic library. The idea is that the user can choose an aspect of the image or tool they want from a select box, then the tool will show up below the select box (I'm using select2) and the user can edit via a slider. Here's what I have so far:
# This seeds the select2 options list
imageToolsList = [
{id: 'bl', text: 'Blur'}
{id: 'bc', text: 'Brightness/Contrast'}
{id: 'ca', text: 'Color Adjust (RGB)'}
...
]
#Creates a select box and calls imageTooler function when the value of the box is changed
$(".image_tools_select").each ->
$(#).select2
placeholder: "Select an adjustment tool."
data: imageToolsList
$(#).on("change", (i) ->
imageTooler JSON.stringify(
val: i.val
clipName: $(#).closest('.clip').attr('id')
)
)
# The function called on the value that the select box is changed to
imageTooler = (i) ->
imageData = jQuery.parseJSON(i)
iId = imageData.val
imageClipName = imageData.clipName
newTool = "<div id=#{iId}><label>#{iId}</label><div class='slider#{iId}'></div></div>"
$("##{imageClipName}").find(".imagetoolfields").append newTool
This succeeds in appending the name of the editing tool and the correct slider div beneath the select box when a tool is chosen, but what I'd really like is dynamically create a slider function for that particular tool and image (there are multiple images on a page, each with their own editing toolbelt). Here's a slider function that works for a the 'Blur' tool:
$('.sliderbl').slider
min: 0
max: 5
value: 0.5
step: 0.1
range: "min"
slide: (event, ui) ->
$("#img_snapshot_16").pixastic("blurfast", {amount:ui.value})
Is there a way to expand the imageToolsList so that it looks something like:
imageToolsList = [
{id: 'bl', text: 'Blur', tool: $("##{imageClipName}").pixastic("blurfast", {amount:ui.value}), sliderVals: {min: 0, max: 5, value: 0.5, step: 0.1, range: "min"} }
...
]
and then dynamically create the jQuery slider functions for each tool in imageTooler, as is being done with the div and slider div?
Comments get a little tedious for anything complicated so I'll just go ahead and map it all out. I've made a few assumptions about what is defined where and when but I don't think the assumptions matter that much.
We'll start with a simplified case: just one object similar to what you have in imageToolsList:
{
id: 'bl'
text: 'Blur'
sliderVals: { min: 0, max: 5, value: 0.5, step: 0.1, range: "min" }
tool: (imageClipName) ->
(event, ui) -> $("##{imageClipName}").pixastic("blurfast", {amount:ui.value})
}
I've tweaked the order a little bit and switched tool to a function which returns a function. We don't want the pixastic call to happen while you're defining the object literals in imageToolsList, making tool a function allows us to defer the pixastic execution until later. Since we (presumably) don't know what imageClipName should be when we define imageToolsList, we need another function to allow us to fill that in with, again, calling pixastic until even later; hence the function returning a function trick.
Given one of these, how do we build a slider call? All we need to do is copy sliderVals (to avoid changing imageToolsList) and fill in the slide function:
sliderDef = { id: 'bl', ... }
doTheSliderThing = (imageClipName) ->
slide = sliderDef.tool(imageClipName)
args = $.extend({ }, sliderDef.sliderVals, slide: slide)
$(".slider#{sliderDef.id}").slider(args)
# And make it all go and pixastic-ify `#pancakes`.
doTheSliderThing('pancakes')
tool is a function which returns a callback function so sliderDef.tool(imageClipName) gives us the appropriate
(event, ui) -> $(...).pixastic(...)
callback function.
If we have an id and we want the appropriate entry from imageToolList, then we have to go looking for it:
# If the list is short:
[sliderDef] = (o for o in imageToolList when o.id == id)
The for loop gives you an array back and then the [sliderDef] unwraps that array and leaves the single result in sliderDef. If the imageToolList is longer then you'd want to short-circuit the loop and bail out as soon as you have a result:
# Longer list, bail out as soon as we've found what we're looking for.
for o in imageToolList when o.id == 2
sliderDef = o
break
or better, rework the structure of imageToolList to allow direct access by id:
# Even longer list: allow direct access by `id`.
imageToolList =
bl: { text: 'Blur', sliderVals: { ... }, ... }
...
and then we can do things like this:
doTheSliderThing = (id, imageClipName) ->
sliderDef = imageToolList[id]
slide = sliderDef.tool(imageClipName)
args = $.extend({ }, sliderDef.sliderVals, slide: slide)
$(".slider#{id}").slider(args)
# And make it all go and pixastic-ify `#pancakes` using `'bl'`.
doTheSliderThing('bl', 'pancakes')
Or, if you prefer to be terse:
doTheSliderThing = (id, imageClipName) ->
$(".slider#{id}").slider($.extend({ }
imageToolList[id].sliderVals
slide: imageToolList[id].tool(imageClipName)
))
Update for the comments: If you have this:
sliderDefs =
bl: { text: 'Blur', sliderVals: { ... }, ... }
...
Then you can build the stuff that slider2 wants like this:
opts = ({id: k, text: v.text} for k,v of sliderDefs)