draggable on ScalaJS removeEventListener does not work - scala

based on that js demo: http://jsfiddle.net/wfbY8/737/
class Draggable(element: HTMLDivElement) {
var offX: Double = 0
var offY: Double = 0
val divMove = (e:MouseEvent) => {
//element.style.position = 'absolute';
element.style.top = (e.clientY-offY) + "px"
element.style.left = (e.clientX-offX) + "px"
}
val mouseDown = (e:MouseEvent) => {
offY = e.clientY - element.offsetTop
offX = e.clientX - element.offsetLeft
window.addEventListener("mousemove", divMove, useCapture = true)
println("added")
}
val mouseUp = (e:MouseEvent) => {
window.removeEventListener("mousemove", divMove, useCapture = true)
println("removed")
}
def addListeners(){
element.addEventListener("mousedown", mouseDown, useCapture = false)
window.addEventListener("mouseup", mouseUp, useCapture = false)
}
addListeners()
}
From the client code I use it like:
val panelElem = document.getElementById(COMPONENT_ID).asInstanceOf[HTMLDivElement]
if (draggable == null) {
draggable = new Draggable(panelElem)
}
I see "added" and "removed" on my log. But the element still movable (when I move mouse without pressing it) for me as if I did not removed mousemove event from the listener (on mouseUp).
I wonder why..

This happens because you're effectively converting the Scala function (the lambda) into a JS function separately for add and remove. In Scala.js, Scala functions are implicitly converted to JS functions as needed. However, the conversion yields a different JS function every time (it does not have the same identity). Therefore, the function you're trying to remove is not the same as the one you added, and of course that has no effect.
You can fix this by forcing the conversion to happen early, so that then you add and remove the same function. To do so, simply add an explicit type to your function vals as JS functions:
val divMove: js.Function1[MouseEvent, Unit] = (e:MouseEvent) => {
...
}
This way, the conversion from Scala function to JS function happens only once, and it is the same JS function that is given to add- and removeEventListener.

Related

How can I reduce the redundancies of the fields handling of feed handler

I am subscript to a message feed for a number of fields, I need to set the values from the feed to the domain object and have code like below:
if (map.contains(quoteBidPriceAcronym)) {
quote.bid.price = Some(map.get(quoteBidPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteBidSizeAcronym)) {
quote.bid.size = Some(sizeMultipler() * map.get(quoteBidSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteBidNumAcronym)) {
quote.bid.num = Some(map.get(quoteBidNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteAskPriceAcronym)) {
quote.ask.price = Some(map.get(quoteAskPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteAskSizeAcronym)) {
quote.ask.size = Some(sizeMultipler() * map.get(quoteAskSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteAskNumAcronym)) {
quote.ask.num = Some(map.get(quoteAskNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteExchTimeAcronym)) {
quote.exchtime = getExchTime(String.valueOf(map.get(quoteExchTimeAcronym).get));
}
It look pretty redundant, any suggestion to improve it?
You can do something like:
map.get(quoteBidPriceAcronym).map { item =>
quote.bid.price = item.map(_.asInstanceOf[Number].doubleValue())
quote.changed = true
}
Other issues might be better to fix outside. E.g. why map[quoteBidPriceAcronym] is storing an Option, if your code assumes it's not going to be None?
Something like this perhaps?
val handlers = Map[String, Number => Unit] (
quoteBidPriceAcronym -> { n => quote.bid.price = Some(n.doubleValue) },
quoteBidSizeAcronym -> { n => quote.bid.size = Some(sizeMultipler() * n.intValue },
etc. ...
)
for {
(k,handler) <- handlers
values <- map.get(k).toSeq
quote.chanded = true
_ = handler(n.asInstanceof[Number])
}
Personally, I don't like code changing an object state (quote) but this is a question on Scala, not functional programming.
That said I would reverse the way you are using you map map keys. Instead of checking whether a value exists to perform some action, I'd have a map from your keys to actions and I'd iterate over your map elements.
e.g (assuming map is of the type Map[String, Any]):
val actions: Map[String, PartialFunction[Any, Unit]] = Map(
(quoteBidPriceAcronym, {case n: Number => quote.bid.price = Some(n.doubleValue())}),
(quoteBidSizeAcronym, {case n: Number => quote.bid.size = Some(sizeMultipler() * n.doubleValue())}),
...
...
)
for((k,v) <- map; action <- actions.get(k); _ <- action.lift(v))
quote.changed = true;
The for construct here iterates over map key-values, then (next level of iteration, over the possible action available for the key. If an action is found, which is a partial function, it gets lifted to make it a function from Any to Option[Unit]. That way, you can iterate in an additional inner level so quote.changed = true is only run when the action is defined for v.

Specify Variable Initialization Order in Scala

I have a special class Model that needs to have its methods called in a very specific order.
I tried doing something like this:
val model = new Model
new MyWrappingClass {
val first = model.firstMethod()
val second = model.secondMethod()
val third = model.thirdMethod()
}
The methods should be called in the order listed, however I am seeing an apparently random order.
Is there any way to get the variable initialization methods to be called in a particular order?
I doubt your methods are called in the wrong order. But to be sure, you can try something like this:
val (first, second, third) = (
model.firstMethod(),
model.secondMethod(),
model.thirdMethod()
)
You likely have some other problem with your code.
I can run 100 million loops where it never gets the order wrong, as follows:
class Model {
var done = Array(false,false,false);
def firstMethod():Boolean = { done(0) = true; done(1) || done(2) };
def secondMethod():Boolean = { done(1) = true; !done(0) || done(2) };
def thirdMethod():Boolean = { done(2) = true; !done(0) || !done(1) };
};
Notice that these methods return a True if done out of order and false when called in order.
Here's your class:
class MyWrappingClass {
val model = new Model;
val first = model.firstMethod()
val second = model.secondMethod()
val third = model.thirdMethod()
};
Our function to check for bad behavior on each trial:
def isNaughty(w: MyWrappingClass):Boolean = { w.first || w.second || w.third };
A short program to test:
var i = 0
var b = false;
while( (i<100000000) && !b ){
b = isNaughty(new MyWrappingClass);
i += 1;
}
if (b){
println("out-of-order behavior occurred");
println(i);
} else {
println("looks good");
}
Scala 2.11.7 on OpenJDK8 / Ubuntu 15.04
Of course this doesn't prove it impossible to have wrong order, only that correct behavior seems highly repeatable in a fairly simple case.

Calling class function within a constructor isn't being recognised

Answer:
It turns out I had neglected to use the new keyword when creating the class instance. The code in the question itself is fine.
Question:
I have a fairly simple class where the constructor calls another method on the class (editor_for_node). The call happens inside a jQuery each() loop, but I've also tried moving it outside.
define ['jquery'], ($) ->
class Editor
constructor: (#node, #data, #template) ->
#node.widgets().each (i, elem) =>
data = if #data then #data[i] else null
node = $(elem)
#editor_for_node node, data
editor_for_node: (node, data) ->
console.log 'hello!'
return {
'Editor': Editor,
}
When the line #editor_for_node node, data gets called, I get an error (in Firebug) saying this.editor_for_node is not a function.
I really can't see why this isn't working properly, the only possible source of weirdness that I can see is my use of require.js's define function at the start.
Edit: Generated output
(function() {
define(['jquery'], function($) {
var Editor;
Editor = (function() {
Editor.name = 'Editor';
function Editor(node, data, template) {
var _this = this;
this.node = node;
this.data = data;
this.template = template;
this.node.widgets().each(function(i, elem) {
data = _this.data ? _this.data[i] : null;
node = $(elem);
return _this.editor_for_node(node, data);
});
}
Editor.prototype.editor_for_node = function(node, data) {
return console.log('hello!');
};
return Editor;
})();
return {
'Editor': Editor
};
});
}).call(this);
First: Which version of CoffeeScript are you using? The fat arrow has been a source of bugs in certain previous releases.
If you're using the latest (1.3.1), then I'm going to go ahead and say that this is an indentation issue. When I copy and paste your code, it works fine. Are you mixing tabs and spaces? Verify that the compiled output contains the line
Editor.prototype.editor_for_node = ...
Update: See the comments on this answer. Turns out the problem was that the new keyword wasn't being used when invoking the constructor.

Scala Swing GUI as Lift Webapp

I'm playing around with a ConnectFour Game written in Scala. I want to make a Lift Webapp using the logic from the game (model and controller).
My problem is, how should I start? I have got the pure HTML Template, but how can I use the logic from the game for example the creation of the board like this?
var board = new Board()
for(y <- 0 to 5; x <- 0 to 6) contents += new CoinComp(x, y)
Thanks!
Best regards,
John
First, you need to know how to generate HTML from your Board class.
There are two choices:
from server, scala.XML
from client, javascript
Then, create an ajax server-side function which you can call from client:
def render = {
import JsCmds._
var board = initBoard()
def moveAction(s: String): JsCmd = {
val moveXY = parseXY(s) // s should be like "{x: 1, y: 2}"
board = move(board, moveXY) // your logic here
val newBoardToSend = boardToCmd(board) // generate board HTML or json here
Call("renderBoard", newBoardToSend) // call the client-side render function
}
val script = S.fmapFunc(S.contextFuncBuilder(moveAction _)) { funcName => {
val func = JsRaw("'" + funcName + "='") + Call("encodeURIComponent", LiftRules.jsArtifacts.jsonStringify(JsRaw("{x:x,y:y}")))
JsCmds.Function(
"moveIt", // the js function name you will call
"x" :: "y" :: Nil,
SHtml.makeAjaxCall(func).cmd
)
}} // generate a client ajax function: function moveIt(x, y)
yourRenderXml ++ <tail>{JsCmds.Script(script)}</tail>
}
At last, you should have a client function "renderBoard(b)" to render you board on your page. and when the client click to move, call "moveIt(x, y)"

tinymce.dom.replace throws an exception concerning parentNode

I'm writing a tinyMce plugin which contains a section of code, replacing one element for another. I'm using the editor's dom instance to create the node I want to insert, and I'm using the same instance to do the replacement.
My code is as follows:
var nodeData =
{
"data-widgetId": data.widget.widgetKey(),
"data-instanceKey": "instance1",
src: "/content/images/icon48/cog.png",
class: "widgetPlaceholder",
title: data.widget.getInfo().name
};
var nodeToInsert = ed.dom.create("img", nodeData);
// Insert this content into the editor window
if (data.mode == 'add') {
tinymce.DOM.add(ed.getBody(), nodeToInsert);
}
else if (data.mode == 'edit' && data.selected != null) {
var instanceKey = $(data.selected).attr("data-instancekey");
var elementToReplace = tinymce.DOM.select("[data-instancekey=" + instanceKey + "]");
if (elementToReplace.length === 1) {
ed.dom.replace(elementToReplace[0], nodeToInsert);
}
else {
throw new "No element to replace with that instance key";
}
}
TinyMCE breaks during the replace, here:
replace : function(n, o, k) {
var t = this;
if (is(o, 'array'))
n = n.cloneNode(true);
return t.run(o, function(o) {
if (k) {
each(tinymce.grep(o.childNodes), function(c) {
n.appendChild(c);
});
}
return o.parentNode.replaceChild(n, o);
});
},
..with the error Cannot call method 'replaceChild' of null.
I've verified that the two argument's being passed into replace() are not null and that their parentNode fields are instantiated. I've also taken care to make sure that the elements are being created and replace using the same document instance (I understand I.E has an issue with this).
I've done all this development in Google Chrome, but I receive the same errors in Firefox 4 and IE8 also. Has anyone else come across this?
Thanks in advance
As it turns out, I was simply passing in the arguments in the wrong order. I should have been passing the node I wanted to insert first, and the node I wanted to replace second.