d3.js CoffeeScript class execution context - coffeescript

I am creating a d3.js chart using a CoffeeScript class. I would like to attach a method to a click event, and then run another method depending on what was clicked:
class #Chart
drawChart: ->
...
dataArea
.enter()
.append("path")
.on("click", #onClick);
...
onClick: ->
if d3.select(this).attr("type") == 'video'
#runVideo(d3.select(this).attr("title"))
runVideo: ->
The problem is that in the onClick method the execution context ("this") is the selection and not the Chart class, so "runVideo is not a function." How can I access selection attributes from within the onClick method and also run the runVideo method?

What you want to do is somehow capture the this when you add the click callback.
You have a few options here:
// The Coffeescript way:
.on("click", (args...) => #onClick(args...));
// The jQuery way:
.on("click", $.proxy(#onClick, #))
// The ECMAscript5 way:
.on("click", #onClick.bind(#))
Then you need to fix your onClick to this:
onClick: (evt) ->
if d3.select(evt.target).attr("type") == 'video'
#runVideo(d3.select(evt.target).attr("title"))

Related

Make Upload tab the default in Insert/Edit Image dialog

Using TinyMCE 5.7.0
Is there a way to make the "Upload" tab the default tab displayed in the Insert/Edit Image dialog?
I'm looking for a configuration option or programmatic way to do this so we can continue to easily update TinyMCE when new versions come out.
In TinyMCE (5.7.0 in my case, not the minified version), open plugins/image/plugin.js.
Search for these lines (1462 to 1466):
tabs: flatten([
[MainTab.makeTab(info)],
info.hasAdvTab ? [AdvTab.makeTab(info)] : [],
info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : []
])
Reorder the lines like this:
tabs: flatten([
info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : [],
[MainTab.makeTab(info)],
info.hasAdvTab ? [AdvTab.makeTab(info)] : []
])
We had the same requirement and this is how we did it.
Instead of adding the "Upload Image" option to toolbar, create a keyboard shortcut for opening the image upload modal using addShortcut method. Something like this in reactjs:
editor.addShortcut('ctrl+shift+i', 'Open image upload window', function () {
editor.execCommand('mceImage')
});
Now that we have a code block that runs when pressing the shortcut keys, we can add logic inside that block to initiate a click action on the "Upload" button within the modal like this:
setTimeout(() => {
let element = document.querySelectorAll('.tox-dialog__body-nav-item')[1];
if (element) { element.click() }
}, 0)
The setTimeout is added to make sure that the modal is added to DOM before run the querySelectorAll method on the document object is executed. Timeout even with 0 will make sure the code block only executes after all the synchronous tasks are done, which includes the DOM update.
In the end, the final codeblock will look like this:
editor.addShortcut('ctrl+shift+i', 'Open image upload window', function () {
editor.execCommand('mceImage')
setTimeout(() => {
let element = document.querySelectorAll('.tox-dialog__body-nav-item')[1];
if (element) { element.click() }
}, 0)
});
Edit:
If you notice other elements in the DOM with the same class as "tox-dialog__body-nav-item", you can change the querySelectorAll method to make it more well defined and make sure it only selects the class within image upload modal if found. I haven't yet ran into this issue, so this was enough for my case.

CoffeeScript: How to use both fat arrow and this?

I have a coffeescript class that has some jquery event listeners. I would like to use the fat arrow => to avoid having to reference the class, but I still need a reference to the element that would usually be used with this. How can I use both?
class PostForm
constructor: ->
$('ul.tabs li').on 'click', =>
tab = $(this)
#highlight_tab(tab)
#set_post_type(tab.attr('data-id'))
highlight_tab: (tab)->
tab.addClass 'active'
set_post_type: (id) ->
$('#post_type_id').val(id)
CoffeeScript links both this and # to the outer context, therefore you cannot access the context that jQuery provided (aka the desired "this"). Use event.target instead:
class PostForm
constructor: ->
$('ul.tabs li').on 'click', (event) =>
tab = $(event.target)
#highlight_tab(tab)
Using evt.currentTarget
You should probably use evt.currentTarget (which is equivalent to this) instead of evt.target (which isn't). If the node that you are tapping for click notifications has child nodes, evt.target might be one of those child nodes instead of the node you added the click handler to.
See http://codepen.io/ddopson/pen/erLiv for a demo of this behavior. (click on the inner red box to see that currentTarget points at the red div while target points at outer blue div that the event handler is registered on)
$('ul.tabs li').on 'click', (event) =>
tab = $(event.currentTarget)
#highlight_tab(tab)
Answer to the question asked - getting both `=>` and `this`:
You can do the following...
$('ul.tabs li').on 'click', (event) =>
tab = $(` this `) # MAKE SURE TO ADD THE SPACES AROUND `this`
#highlight_tab(tab)
The spaces are critical as they prevent Coffee from munching this into _this.
Using `self` and `->`
Alternatively, do the following ...
self = this
$('ul.tabs li').on 'click', (event) ->
tab = $(this)
self.highlight_tab(tab)
This is similar to CQQL's answer, except that I prefer the idiomatic use of self as the variable name; my VIM syntax highlighting rules color self as a "special" variable just as it would for this, arguments, or prototype.
I prefer this version, because I can understand it more easily.
class PostForm
constructor: ->
post_form = this
$('ul.tabs li').on 'click', (event) ->
tab = $(this)
post_form.highlight_tab(tab)
You may want to access variables set in the constructor from your functions. This would be how you do it (the key is calling the function via self while first extracting this with a thin arrow):
class PostForm
constructor: ->
self = this
#some_contrived_variable = true
$('ul.tabs li').on 'click', ->
tab = $(this)
self.highlight_tab(tab)
self.set_post_type(tab.attr('data-id'))
highlight_tab: (tab) ->
# Because of the fat arrow here you can now access # again
if #some_contrived_variable
tab.addClass 'active'
set_post_type: (id) ->
$('#post_type_id').val(id)
BTW: This is a great explanation of when to use the fat and thin arrow.
Summary:
Do you use this (#) in the function?
Do you want to execute the function later, possibly from a different scope?

handler for a submit button

I want to use a save button with a form in extjs. This is what i have as a handler
{
xtype: 'button',
handler: function(button, event) {
var form = this.getForm();
if (form.isValid()) {
Ext.MessageBox.alert('Submitted Values', form.getValues(true));
}
},
height: 37,
id: 'configurationDriversSave',
text: 'Save'
}
All i get now in firebug is an error: this.getForm is not a function. What am i doing wrong?
in the handler this will be reference to the button itself. You can check that in firebug, button of course doesn't have method getForm(). You need to call something like 'this.up('form')`.
Second thing - you don't have to do manual validation like you are trying to do. ExtJs has built-in validation mechanism for the forms.
this.getForm
is not supported in Firefox,
use document.forms instead,
Or you can get any reference from this link too.
According to this blog post, you can simply use this.form to access the form element that contains the element that generated the event.
So, instead of
var form = this.getForm();
use
var form = this.form;

How to identify the ID or value of when the submit button is clicked

I am having trouble identifying the particular value or ID of a submit button after it has been clicked and submitted using AJAX.
If I place the following code as a global function, it properly alerts the value of the button clicked:
$(":submit").live('click', function() {
alert($(this).val());
})
However, when I attempt to define the variable, I am unable to use that variable from within the success callback function:
$(":submit").live('click', function() {
var whichButton = $(this).val();
})
...
$("#applicant-form").validate({
function(form) {
$(form).ajaxSubmit({
...
success: alert(whichButton);
I have also tried placing the code in the submitHandler, but that doesn't work either.
In a somewhat related post, a user had suggested I place the following code:
$("#accordion .edit").click(function(){
window.lastButtonClicked = this;
});
...
submitHandler: function(){
var index_origin = $(window.lastButtonClicked).attr("name");
}
But I was not able to get that to get the value of the button clicked (it said that the value was undefined).
Any suggestions?
UPDATE: It might help if I provide more information about why I need to know which button is pressed. I have two kinds of submit buttons for each form in a multi-part form. I would like to do different things based on which button was clicked.
$(":submit").live('click', function() {
var whichButton = $(this).val();
})
The scope of whichbutton is inside of this anonymous function; you can't access it from elsewhere. A quick fix might be to declare whichbutton as a global variable but there's probably very few cases where you should do that. More context as to what it is you're trying to do would help, right now it just looks like you're trying to alert the button text on success after an ajax form submit.

Basic CoffeeScript not firing events when run?

I'm having real problems writing a simple Backbone.js app using CoffeeScript and Zepto.js
This is the simplest Backbone view yet the events don't fire. I get no errors in the console either? Where am I going wrong?
#Main view
class AppView extends Backbone.View
constructor: ->
#el = $("#books")
#template = _.template("<div>New Item <a href='' id='addNew'> add new item</a></div>")
events: {
"click" : "createNew"
}
render: =>
#el.html(#template())
createNew : ->
console.log "new"
#Onload
$(document).ready ->
view = new AppView
view.render()
I've been following the only example I can find of CoffeeScript & Backbone together https://github.com/bnolan/Backbone-Mobile/blob/master/application.coffee
However if I add super into my view code above I get an undefined error, his code does not.
The class Backbone.View has its own constructor that does plenty of work, and you are overriding it and not calling super. Bad.
Instead, Backbone.View provides you the ability to define your own constructor-type function called initialize. Perform all your setup there. Backbone.View#constructor will call initialize.
#Main view
class AppView extends Backbone.View
initialize: ->
#el = $("#books")
#template = _.template(
"<div>New Item <a href='' id='addNew'> add new item</a></div>"
)
I had a similar problem (events not firing) and found that the problem was due to not setting #el. I set that:
#el: $("#content")
and it worked.