Delayed trigger for Backbone on Nodejs - javascript

B = require 'backbone'
U = require 'underscore'
o = {}
U.extend o, B.Events
o.on 'e', console.log
setTimeout o.trigger, 5000, 'e', 'Hi!'
Why did Nodejs console not log for the delayed Backbone #trigger above?
#Edit
Q = require 'q'
d = Q.defer()
d.promise.then console.log
setTimeout d.resolve, 5000, 'Hi!'
This worked flawlessly. But why did the console not log if Q #resolve was wrapped in the anonymous function below,
setTimeout (-> d.resolve 'Hi!'), 5000
#Edit 2
Actually the wrapped version Q #resolve also worked if being invoked at first and once.
Why the plain version Q #resolve worked while Backbone #trigger didn't, is the only remaining question.
#Edit 3
R = require 'rx'
src = new R.BehaviorSubject 0
dst = src.map (v) -> v + 1
dst.subscribe console.log
setTimeout src.onNext, 5000, 1
Rx #onNext had similar problem and solution to Backbone #trigger.
All above can be sourced to the varying this context, e.g. a bare version shows the problem below,
o =
f: ->
g: -> #f()
setTimeout o.g
then,
TypeError: Object [object Object] has no method 'f'
The wrapped version below thus goes through.
setTimeout -> o.g()
Below shows this context switch to cause the problem and the wrapper to solve it,
f = (g) -> g.apply #
o =
x: 'Hi!'
g: -> console.log #x
f o.g # undefined
f -> o.g() # 'Hi!'
#Edit 4
Additionally, Coffeescript => binding to this varies to -> below,
x = 'Hi!'
o = f: => #x
console.log o.f() # Hi!
console.log -> o.f() # undefined

Because when you pass o.trigger as an argument to setTimeout you execute trigger function not as an method for o object but instead you execute it in global scope. That is in this case this inside trigger will point not to o but to global object.
So before passing o.trigger as an argument to setTimeout you need to wrap it in anonymous function.
Unfortunately I'm not good in Coffee Script but I can show you how your code should look like in pure JavaScript:
var B, U, o;
B = require('backbone');
U = require('underscore');
o = {};
U.extend(o, B.Events);
o.on('e', console.log);
setTimeout(function () {
o.trigger('e', 'Hi!');
}, 5000);
Hope this will help. Good luck.

Related

Can I modify a Javascript library at runtime?

I use a web based development environment for data entry forms. The environment lets me create rules that are triggered by form events. These events run in js in the browser but there is almost no support for debugging, which makes problem solving a nightmare.
The code in the browser has a central event handler, which has a logging feature but the quantity of information produced by it is so large, it makes finding what you need difficult. Think of info level logging gone mad. Plus you have to open a separate window to access the log.
I need to be able to log certain events to the console, or trigger breakpoints at specified rules. Is there a way to modify the environment's code below to allow it to call my debugger instead of (or in addition) to SFLog?
function handleEvent(n,t,q,r,u,f,e,o,s,h,c,l){
if(eventsCancelled!==!0){
SFLog({type:3,source:"handleEvent",category:"Events",
message:"{2} event fired from {1} - {0}",parameters:[n,t,q]});
var b="Events/Event[#SourceID='"+n+"'][#SourceType='"+t+"'][Name/text()="+q.xpathValueEncode()+"]";
//Rest of the event handler...
function SFLog(n){
if(checkExists(_debug)){var s=translateDebugLevel(n.type);
if(s>=_debug){
varu=n.type,e=n.source,r=n.category,q=n.message,h=n.parameters,o=checkExists(n.exception)? WriteExceptionXml(n.exception):null,t=n.data,l=checkExists(n.humanateData)?
n.humanateData:!0,f=(new Date).format("yyyy-MM-ddTHH:mm:ss:fff");
checkExists(t)&&(dataString=t.xml,checkExists(dataString)||(dataString=t),l===!0&&(dataString=Humanate(dataString)));
//more code for SFLog...
Cleaned Up Code
function handleEvent(n, t, q, r, u, f, e, o, s, h, c, l) {
if (eventsCancelled !== !0) {
SFLog({
type: 3,
source: "handleEvent",
category: "Events",
message: "{2} event fired from {1} - {0}",
parameters: [n, t, q]
});
var b = "Events/Event[#SourceID='" + n + "'][#SourceType='" + t + "'][Name/text()=" + q.xpathValueEncode() + "]";
//Rest of the event handler...
}
}
function SFLog(n) {
if (checkExists(_debug)) {
var s = translateDebugLevel(n.type);
if (s >= _debug)
{
varu = n.type;
e = n.source;
r = n.category;
q = n.message;
h = n.parameters;
o = checkExists(n.exception) ?
WriteExceptionXml(n.exception) :
null;
t = n.data;
l = checkExists(n.humanateData) ?
n.humanateData :
!0;
f = (new Date).format("yyyy-MM-ddTHH:mm:ss:fff");
checkExists(t) &&
(dataString = t.xml, checkExists(dataString) ||
(dataString = t), l === !0 && (dataString = Humanate(dataString)));
//more code for SFLog.
I agree with #Eddie but one solution could be to wrap the logger function and and override it, and only log the events you care about. e.g.:
function SFLog(n){
//old code
}
//run on the console, the first line, and then the second.
var oldLoggger = SFLog;
function SFLog(n) {
if(/*some criteria*/) {
oldLogger(n);
}
}
This way you can run the default logger with different conditions, but it probably would be best if you could modify the logger code itself to accept certain criteria, like, event type to log, or targetElement's ID, class etc.
PD: If you need to modify the eventHandler itself, you should:
remove the event handler first.
create your wrapper function.
add the wrapper function as event handler

How do I call a global function in jquery/coffeescript

class Dashing.Hacircledimmer extends Dashing.Widget
setLevel: ->
levelToSet = '10'
$.post '/homeassistant/dimmerLevel',
widgetId: #get('id'),
command: levelToSet,
(data) =>
json = JSON.parse data
ready: ->
meter = $(#node).find(".meter")
meter.knob
'release': (v) ->
#setLevel
onData: (data) ->
I am trying to call function setLevel after initializing meter.knob but I keep getting an error saying that the function does not exist. Ideally I would like to pass v to the function setLevel.
I did not include the constructor etc.. to keep the code short.
Can someone point out my error? Sorry, I am new to coffeescript and would really appreciate if anyone can help me out.
Thanks!
Try changing 'release': (v) -> to 'release': (v) =>
(-> vs =>)

Using Keen.io with CoffeeScript doesn't work?

I'm trying to use Keen.io, I converted their JS to coffee as follows:
# Keen init
Keen = Keen or
configure: (e) ->
#_cf = e
addEvent: (e, t, n, i) ->
#_eq = #_eq or []
#_eq.push([e, t, n, i])
setGlobalProperties: (e) ->
#_gp = e
onChartsReady: (e) ->
#_ocrq = #_ocrq or []
#_ocrq.push(e)
(->
e = document.createElement("script")
e.type = "text/javascript"
e.async = not 0
e.src = ((if "https:" is document.location.protocol then "https://" else "http://")) + "dc8na2hxrj29i.cloudfront.net/code/keen-2.1.0-min.js"
t = document.getElementsByTagName("script")[0]
t.parentNode.insertBefore e, t
)()
Keen.configure myParams
Keen.addEvent "script_tag_init"
But looks like events aren't hitting. What gives?
Yeah, that would be the problem. The Keen object won't be visible to the global scope due to how the CoffeeScript will compile.
"Exporting" Keen to window after initializing will work.
Alternatively you can initialize Keen directly on the window object:
# Keen init
window.Keen =
configure: (e) ->
#_cf = e
...
Note: This method does exclude checking if Keen already exists on the page first, which is a corner-case performance optimization and isn't necessary for most applications. In other words it should be fine.
Since coffee wraps everything in a closure, you need to include this after the call to configure:
# Keen works with variable as it is attached to window
window.Keen = Keen

Backbone.js setTimeout() loop in CoffeeScript

Seems like every way I try this, it throws some sort of error. Here's what my code looks like now:
runShow: ->
moments = #model.get('moment_stack_items')
if inc == moments.length
inc = 1
pre = 0
$("#" + moments[pre].uid).hide("slide", { direction: "left" }, 1000)
$("#" + moments[inc].uid).show("slide", { direction: "right" }, 1000)
inc += 1
pre += 1
console.log "looping" + inc
t = setTimeout(this.runShow(),2000);
I call the function in my events.
I have inc = 1 and pre = 0 defined outside the Backbone.View.. My current error is "Uncaught TypeError: Object [object DOMWindow] has no method 'runShow'"
BONUS POINTS: how can I reference t from another function (to run my clearTimeout(t))?
You ask the setTimeout function to evaluate "this.runShow()", and setTimeout will do that in the context of window. This means that this is the window object when this code is evaluated.
To avoid this you can create a function and bind it to a the current context, so that everytime the function is called, this is the same as when the function has been created.
In coffee script you can do this with the =>:
func = =>
this.runShow()
setTimeout(func, 2000)
Or on a single line:
setTimeout((=> this.runShow()), 2000)
how can I reference t from another function?
Make t a property of your object:
class Something
t: null
runShow: ->
...
this.t = ...
otherFunction: ->
t = this.t

Recursion function not defined error

Hi i have a problem with recursion.
i followed this example from wc3 http://www.w3schools.com/jsref/met_win_settimeout.asp
But mine seems to not work at all.
function rotateImages(start)
{
var a = new Array("image1.jpg","image2.jpg","image3.jpg", "image4.jpg");
var c = new Array("url1", "url2", "url3", "url4");
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
if(start>=a.length)
start=0;
b.src = a[start];
d.href = c[start];
window.setTimeout("rotateImages(" + (start+1) + ")",3000);
}
rotateImages(0);
Firebug throws the error :
rotateImages is not defined
[Break On This Error] window.setTimeout('rotateImages('+(start+1)+')',3000);
However if i change the timeOut to :
window.setTimeout(rotateImages(start+1),3000);
It recursives but somehow the delay doesn't work and gives me too much recursion(7000 in a sec)
There are many reasons why eval should be avoided, that it breaks scope is one of them. Passing a string to setTimeout causes it to be evaled when the timer runs out.
You should pass a function instead.
window.setTimeout(rotateImages(start+1),3000);
This calls rotateImages immediately, then passes its return value to setTimeout. This doesn't help since rotateImages doesn't return a function.
You probably want:
window.setTimeout(rotateImages,3000,[start+1]);
Or create an anonymous function that wraps a closure around start and pass that instead:
window.setTimeout(function () { rotateImages(start + 1); },3000);
The latter option has better support among browsers.
Be wary of code from W3Schools.
The other answers give a solution. I'll just add that you're recreating the Arrays and repeating the DOM selection every time the rotateImages function is called. This is unnecessary.
You can change your code like this:
(function() {
var a = ["image1.jpg","image2.jpg","image3.jpg", "image4.jpg"];
var c = ["url1", "url2", "url3", "url4"];
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
function rotateImages(start) {
b.src = a[start];
d.href = c[start];
window.setTimeout(function() {
rotateImages( ++start % a.length );
}, 3000);
}
rotateImages(0);
})();
Try this syntax:
window.setTimeout(function() {
rotateImages(start+1);
},3000);
setTimeout() expects a function reference as the 1st parameter. Simply putting a function call there would give the return value of te function as the parameter, this is why the delay did not work. However your first try with evaluating a string was a good approach, but it is not recommended.

Categories

Resources